innodb按照锁的类型划分为:
共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
加锁的方式
对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于SELECT语句,InnoDB不会加任何锁.但是可以通过以下语句显式给记录集加共享锁或排他锁。
共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。
排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE。
一致性非锁定读:
指innodb通过多版本控制的方式来读取当前执行时间数据库中行的数据。如果读取的行正在执行del和update操作,这是读取操作不会因此去等待排他锁的释放,而是去读取行中的一个快照数据(通过事物的undo回滚机制来实现,不存在多余的开销)。
注,1,这是innodb默认的读取方式(因为select 语句默认没有加锁);
2,不同的事物隔离级别下所认为的快照数据是不一样的,如下
Session A | Session B |
begin |
|
Select id from t where id = 1; |
|
| begin |
| Update t set id = 3 where id = 1; |
这个时候commit的话得到的结果如下: (read committed 级别下的结果为 1 Repeatable read 级别下的结果为1) 因为事物B还没有提交 |
|
| Commit |
这个时候commit的话得到的结果如下: (read committed 级别下的结果为 3 Repeatable read 级别下的结果为1 可重复读,即这个时候在执行一遍Select id from t where id = 1;那么得到的结果也是1,也就解释了可重复读的概念) 因为事物B还没有提交 |
|
|
|
|
|
上表说明:对于read committed 其所认为的快照数据总是有事务提交之后的最新的数据;
对于Repeatable read 其所认为的快照数据总是最开始读取的到的数据。
一致性锁定读:
即通过对select 语句显示加锁的方式来实现
丢失更新
在事务并发的场景下,很容易多个事务之间产生丢失更新的情况。
如:
Session A | Session B |
Begin |
|
Select money from t where id = 1; |
|
| begin |
| Select money from t where id = 1; |
程序逻辑检查money的余额 |
|
Update t set money = 300 where id = 1; 原来money为1000,转出700 |
|
| Update t set money = 1300 where id = 1; 另外一个人给她转了300,即转入300 |
Commit |
|
| Commit |
如上,由于session B最后提交,那么最后的结果为1300,其实应该为1000.
解决方案为,把sql Select money from t whereid = 1 变为Select money from t where id = 1 for update;
死锁:
是指两个或者是两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。
死锁的解决方案
1,超时机制,即当两个事务互相等待的时候,当一个等待的时间超过了设定的某一个阈值,那么这个事务回滚。但是超时机制在某些情况下不合理,比如,因为超时需要回滚的事务是一个比较大的事务,那么回滚她,让她重新执行显然不合理。
2,等待图的方式(wait-for graph)。她是一种更为主动的死锁检测方式,innodb也采用这种方式。下面将会详细解释这个算法实现。
死锁的示例:
在事务并发中就可能会产生死锁,锁的粒度越小发生死锁的概率也就越大
Session A | Session B |
Begin |
|
Select money from t where id = 1 for update; |
|
| begin |
| Select money from t where id = 2 for update; |
|
|
Select money form t where id = 2 for update; 这个时候进行等待 |
|
| Select money from t where id = 1 for update; Innodb发生错误: Error1213
Innodb会回滚当前事务 |