Innodb锁机制总结

1.锁的目的
1)最大程度提高数据库的并发访问 2)确保每个用户能一致性的读取和修改数据

2.lock与latch
latch主要是保证并发线程访问临界资源的正确性,其保护的是内存数据结构,针对的是线程
lock的对象是事务,操作的是数据库内容

3.锁(lock)类型
共享锁:事务在读行数据时加共享锁
排他锁:在修改行数据时加排它锁
S锁兼容S锁,不兼容X锁;X锁不兼容其它锁

意向锁:也分为意向共享锁(IS Lock)和意向排它锁(IX Lock)
意向锁的目的是为了解决表锁与行锁的冲突问题,即如下情况:
事务A锁住了表中的 一行 ,让这一行只能读,不能写。之后,事务B申请 整个表 的写锁。如果事务B申请成功,那么理论上它就能修改表中的任意一行,这与A持有的行锁是冲突的。
数据库需要避免这种冲突,就是说要让B的申请被阻塞,直到A释放了行锁。
数据库要怎么判断这个冲突呢?
step1:判断表是否已被其他事务用表锁锁表
step2:判断表中的每一行是否已被行锁锁住。
注意step2,这样的判断方法效率实在不高,因为需要遍历整个表。于是就有了意向锁。在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。在意向锁存在的情况下,上面的判断可以改成
step1:不变
step2:发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。
注意:申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请,同时意向锁是意向锁之间不会冲突, 因为意向锁仅仅代表要对某行记录进行操作。在加行锁时,会判断是否冲突。

4. 一致性非锁定 ( Multi Version Concurrency Control,MVCC)(共享锁兼容排它锁的原因)
一致性非锁定读是指Innodb存储引擎通过行多版本控制的方式来读取当前数据库的行数据。
(读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能)
如果一个事务读取的行正在执行DELETE或UPDATE操作,这时事务不会阻塞等待锁释放,而是直接读取行的快照数据。快照数据指的是该行的之前版本数据,是通过undo段来实现的。
不同的隔离级别对快照数据的定义不同:
在事务隔离级别RC(读已提交)和RR(可重复读)下,InnoDB存储引擎引擎使用非锁定的一致性读。然而,对于快照数据的定义却不相同。在rc事务隔离级别下,对于快照数据,非一致性读总是被锁定行的最新一份快照数据.而在RR事务隔离级别下,对于快照数据,非一致性读总是读取事务开始(读取快照数据的事务,目的是保证可重复读)时的行数据版本。

5.一致性锁定
在某些情况下,用户需要显式地对数据库读取操作进行加锁以保证数据逻辑的一致性,
Innodb支持两种加锁语句:
select ......for update:对行数据加X锁,其他事务不能对行数据进行操作
select ......lock in share mode:对行数据加S锁

6. 自增长与锁
对于包含自增列的表,当对其插入时,会根据自增计数器的值加1并赋予给自增长列。
对于含自增长的表来说,插入包含两种类型:1)simple insert即在插入操作之前就可以确定插入的行数,对于这种并不会使用锁,而是使用互斥量对内存计算器进行累加操作 2)bulk insert即使用传统表锁,AUTO-INC Locking方式不是在一个事务完成后才释放表,而是在插入完成就释放
(保证自增列顺序)

7.外键与锁
对于外键值的插入与更新,需要首先查询父表记录,对父表不是采用非锁定读,而采用锁定读,对其主动加s锁(share mode),这样父表就不能加X锁。

8. 行锁的三种算法
innodb的行锁是通过给索引项加锁实现的,这就意味着只有通过索引条件检索数据时,innodb才使用行锁
Record Lock:单个记录上的锁(当查询索引含唯一属性时)
Gap Lock:间隙锁,锁定一个范围,但不包括记录本身
Next-Key Lock: 锁定一个范围,包括记录本身(Innodb对行查询采用此算法),该算法锁定的区间为左闭右开区间[,)
注:如果检索条件没有使用索引,将会采用表锁
为何使用Next-key Lock?
主要是为了解决幻读问题,所谓幻读指的是在同一事务下,执行 两次同样的sql语句 得到的结果不同(在RR级别下才能解决幻读)
对于此表而言, id为主键,number字段上有非唯一索引的二级索引,有什么方式可以让该表不能再插入number=5的记录?
根据上面生活中的例子,我们自然而然可以想到,只要控制几个点,number=5 之前 不能插入记录,number=5现有的记录 之间 不能再插入新的记录,number=5 之后 不能插入新的记录,那么新的number=5的记录将不能被插入进来。
那么,mysql是如何控制number=5之前,之中,之后不能有新的记录插入呢(防止幻读)? 答案是用间隙锁,在RR级别下,mysql通过间隙锁可以实现锁定number=5之前的间隙,number=5记录之间的间隙,number=5之后的间隙,从而使的新的记录无法被插入进来

9.和锁有关的几个概念
脏读:一个事务读到另一个事务未提交的数据(在read committed中解决)
不可重复读:读取到了另一个事务已提交的对某数据的 修改
幻读:由于另一个事务 增加和删除 ,使得两次读取的 记录数不同
不可重复读和幻读的区别于 如何通过锁机制来解决他们产生的问题
在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复 读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会 发现莫名其妙多了一条之前没有的数据,在innodb中采用 Next-Key Lock
丢失更新:如果多个事务操作,基于同一个查询结果(获取时相同)对表中的记录进行修改,那么后修改的记录将会覆盖前面修改的记录,前面的修改就丢失掉了,这就叫做更新丢失
采用最高隔离级别:Serializable(可串行化)来解决丢失更新的问题
所谓可串行化是在 读取的每一行数据上都加锁 强制事务串行执行,使之不可能相互冲突





猜你喜欢

转载自blog.csdn.net/yzc11111111/article/details/78938950