锁:为了支持对共享资源进行并发访问,提供数据的完整性和一致性
不同的数据库对锁的实现方式不同,innodb的锁实现和oracle类似,提供一致性的非锁定读,行级锁支持。(行级锁没有额外的开销,可以同时得到并发性和一致性)
lock和latch在数据库中都称为锁:
latch:称为闩锁(轻量级锁,锁定的时间短),在innodb中可分为mutes(互斥量)和rwlock(读写锁):用来保证并发线程操作临界资源的正确性,通常没有死锁检验机制
lock:对象是事务,用来锁定数据库中的对象(如:表,页,行)
lock和latch的比较:
说明:可以通过show engine innodb mutex查看innodb中的latch
通过show engine innodb status查看lock信息
锁的类型:
innodb实现了两种标准的行级锁:共享锁(S lock),允许事务读一行数据;排他锁(X lock),允许事务删除或更新一行数据。
innodb还支持一种额外的锁方式:意向锁,意向共享锁(IS lock):事务想要获得一张表某几行的共享锁;意向排他锁(IX lock):事务想要获得一张表某几行的排他锁
X锁与任何锁都不兼容,S锁仅与S锁兼容。(兼容:对同一记录锁的兼容情况)
innodb支持多粒度锁定,即允许事务在行级锁和表级锁同时存在。
对最细粒度的对象进行上锁,则需要对粗粒度的对象上锁(如:需要对页上的行记录加x锁,则需要对数据库、表、页加上意向锁IX,最后对行记录加x锁)
意向锁不会阻塞除全表扫以外的任何请求。意向锁和行级锁的兼容情况:
一致性非锁定读:innodb通过多版本控制来读取当前数据库中的数据
如:如果读取的行正在进行delete或update,这时读取操作不会等待锁释放,而是去读取行的一个快照数据。
快照数据的实现是通过undo段来完成的,没有额外开销(undo用来回滚数据的)。
快照数据是不需要上锁的,因为没有事务需要对历史的数据进行修改操作
非锁定读提高了数据库的并发性,读取不会占用和等待表上的锁,但是在不同的隔离级别下读取的方式不同,RC下非一致性读总是读取被锁定行最新一份快照数据;RR下总是读取事务开始时的行数据版本。
一致性锁定读:显式地对数据库读取操作进行加锁以保证数据逻辑的一致性。对于select有两种加锁的操作:
select ... for update :对读取行加X锁
select ...lock in share mode:对读取行加S锁
在使用上面的两个select语句时,必须加上begin,start transaction或set autocommit=0(因为事务提交了,锁就释放了)
自增长和锁
在innodb内存中,每个含有自增长值得表都有一个自增长计数器,当对该表进行插入时,这个计数器会被初始化,会依据计数器加1赋予自增长列。这个实现方式称为:auto-inc locking(特殊的表锁) 在完成自增长值插入的SQL语句后立即释放。
对于有自增长的列的并发插入性能较差(事务必须等待前一个插入的完成);对于insert ...select的大数据量的插入会影响插入性能。
innodb提供了一种轻量级互斥量的自增长实现机制,提供了innodb_autoinc_lock_mode来控制自增长的模式。默认值为1。
对自增长的插入分类如下:
innodb_autoinc_lock_mode参数0、1、2设置对自增的影响
自增长的列必须是索引且必须是索引的第一个列。
外键和锁
外键主要用于引用完整性的约束检查。在innodb中,对于外键列如果没有显式加索引,innodb会自动对其加上一个索引。
对于外键值的插入或更新,首先需要查询父表中的记录并对父表加一个S锁。如果父表已经加上X锁,子表的操作会被阻塞
锁的算法
innodb行锁的三种算法:
record lock:单个行记录上的加锁
gap lock:间隙锁,锁定一个范围,但不包含记录本身
next-key lock:gap lock+record lock,锁定一个范围且锁定记录本身
record lock总是去锁住索引记录,如innodb没有索引,则会使用隐失的主键来进行锁定
next-key lock为了解决幻读。当查询的索引还有唯一属性,innodb会对next-key lock进行优化,降低为record lock即仅锁住索引本身。
对辅助索引会加上next-key lock还会对下一个键值加上gap lock
说明:对于辅助索引会给(1,3)加next-key lock 且对(3,6)加gap lock
用户可以通过以下两种方式来显式关闭gap lock:
1.设置隔离级别为RC
2.将参数innodb_locks_unsafe_for_binlog设置为1
设置为上面的情况下,除了外键约束和唯一性检查需要gap lock其余使用record lock。但是破坏了事务的隔离性,会导致主从数据的不一致
幻读:在同一个事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次的SQL语句可能返回之前不存在的行。
锁虽然提高了并发,但是会带来三个问题:脏读,不可重复读,丢失更新
脏页:在缓冲池中已经被修改的页还没刷新到磁盘,由于数据库实例内存和磁盘异步造成,不会影响数据的一致性
脏数据:事务对缓冲池中行记录的修改,且还没提交
脏读:当前事务读到了脏数据,违反了数据库的隔离性
不可重复读:在一个事务内多次读取同一数据集合,可能是不一样的
和脏读的区别:脏读读到的是未提交的,不可重复读 读到的是提交的
丢失更新:一个事务的更新操作被另一个事务的更新操作所覆盖
译者介绍:家华,从事mysqlDBA的工作,记录自己对mysql的一些总结