InnoDB引擎中的锁

1.概述

    相对其他数据库而言,mysql的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。比如:myisam和memory存储引擎采用的是表级锁,bdb采用的是页面锁,但也支持表级锁,innodb存储引擎即支持行级锁也支持表级锁,但默认情况下是行级锁。
     三种锁的特性大致归纳如下:
    (1)表级锁:开销小,加锁快;不会出现死锁;锁粒度大,发生冲突的概率高,并发度低。
    (2)行级锁:开销大,加锁慢;会出现锁死;锁粒度最小,发生锁冲突的概率最低,并发度也高。
    (3)页面锁: 开销和加锁时间介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般。

   所以,表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用。而行级锁则更适合于有大量按照索引条件并发更新少量不同数据,同时又有并发查询的应用。

2.InnoDB存储引擎中的锁

2.1锁的类型

 innodb实现了以下两种类型的行锁:
(1)共享锁(s):允许一个事务读一行,阻止其他事务获得相同数据集的排它锁。又称读锁,若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务T'只读A不能修改A ,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改。
(2)排它锁(x):允许获得排它锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。又称写锁。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。
    另外,为了允许行锁和表锁的共存,实现多粒度锁机制,innodb还有两种内部使用的意向锁(imtention locks),这两种意向锁都是表锁:
    (1) 意向共享锁(is):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的is锁。
   (2)意向排它锁(ix):事务打算给数据行加行排他锁,事务在给一个数据行加排它锁前必须先取得该表的ix锁。

这里写图片描述

    如果一个事务请求的锁模式与当前锁兼容,innodb就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。

    注:意向锁是innodb自动加的,不需要用户干预。对于update,delete,insert语句,innodb会自动给涉及数据集加行排他锁;对于普通select语句,innodb不会加任何锁。
    可以通过一下语句显示加行共享锁或者行排他锁:
   (1)共享锁:select * from table_name where .... lock in share mode;
   (2)排它锁:select * from table_name where ... for update;
   用select ... in share mode获得共享锁,主要用在需要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行update 或delete,但是如果当前事务也需要对该记录进行更新操作,则很可能造成锁死,对于锁定行记录后需要进行更新操作的应用,应该使用select .. from update方式获得排它锁。
     另外,select ... lock in share mode,select ... for update必须在一个事务中,当事务提交了,锁也就自动释放了。因此在使用上述两句select锁定语句时,必须家加上begin,start transaction或者set autocommit=0。

共享锁和排它锁例子

2.2一致性的非锁定读

     一致性的非锁定行读是指InnoDB存储引擎通过行多版本控制的方式来读取当前执行时间数据库中行的数据。如果读取的行正在执行delete,update操作,这时读取操作不会因此而会等待行上锁的释放,相反,InnoDB存储引擎会去读取行的一个快照数据。
     快照数据是指该行之前版本的数据,该实现时通过undo段来实现的。而undo段用来在事务中回滚数据,因此快照数据本身是没有额外开销的。此外,读取快照是不需要上锁的,因为没有必要对历史数据上锁。

这里写图片描述

   注:在read committed和repeatable read事务隔离级别下,InnoDB引擎使用一致性的非锁定读。然而,对于快照数据的定义却不相同。在read committed事务隔离级别下,对于快照数据,一致性非锁定读总是读取被锁定行的最新一份快照数据;在repeatable read事务隔离级别下,对于快照数据,一致性非锁定读总是读取事务开始时被锁定行的数据版本。

2.3InnoDB行锁实现方式

      innodb行锁是通过索引上的索引项加锁来实现的,如果没有索引,innodb将通过隐藏的聚簇索引来对记录加锁。innodb行锁分为3中情形。
      (1)Record Lock:单个行记录上的锁。(对索引项加锁。)
      (2)Gap Lock:间隙锁,锁定一个范围,但不包括记录本身。(对索引项之间的
               间隙,第一条记录前的间隙和最后一条记录后的间隙加锁。)
      (3)Next-Key Lock:Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身。(前两种的组合,对记录及其前面的间隙加锁。)
           innodb引擎默认的事务隔离级别是Repeatable Read,采用Next-Key Lock算法。
      innodb这种行锁实现特点意味着:如果不通过索引条件检索数据,那么innodb将对表中的所有记录加锁,实际效果跟表锁一样。

      注意:①因为mysql的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现冲突的,设计的时候要注意这一点。
      ②当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,不论是使用主键索引,唯一索引或普通索引,inodb都会使用行锁来对数据加锁。

有关Gap Lock可参考该博客Gap Lock
有关Next-Key Lock可参考该博客Next-Key Lock

2.4阻塞

  
因为不同锁之间的兼容性关系,所有有些时刻,一个事务中的锁需要等待另一个事务中的锁释放它锁占用的资源。在InnoDB引擎的源代码中,用Mutex数据结构来实现锁。在访问资源前需要用mutex_enter函数进行申请,在资源访问或修改完后立即执行mutex_exit函数。当一个资源已被一个事务占用时,另一个事务执行mutex_enter函数会发生等待,这就是阻塞。
在InnoDB引擎中,参数innodb_lock_wait_timeout用来控制等待的时间(默认是50秒),innodb_rollback_on_timeout用来设定是否在等待超时时对进行中的事务进行回滚操作(默认是off,代表不回滚)。参数innodb_lock_wait_timeout是动态的,可以在MySQL数据库运行时进行调整,而innodb_rollback_on_timeout时静态的,不可再启动时进行修改。
<.pre>

2.5死锁

    如果程序是串行的,那么不可能发生死锁。死锁只发生于并发的情况,数据库就是一个并发着的程序,因此可能会发生死锁。InnoDB引擎有一个后台的锁监控程序,该线程负责查看可能的死锁问题。
    InnoDB引擎不会回滚大部分的错误异常,但是死锁除外,发现死锁后,InnoDB引擎会马上回滚一个事务,这点是需要注意的。

参考文献:
《MySQL技术内幕—InnoDB存储引擎》
fuzhongyu2的博客(强烈推荐,写的非常好)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值