命令
select @@tx_isolation;//查看当前隔离级别
概述
锁机制用于管理对共享资源的并发访问
InnoDB中的锁
- 行级锁介绍
- 共享锁(S),允许事务读一行数据
- 排他锁(X),允许事务删除或者更新一行数据
当一个事务获取到一行数据的S锁,其他事务可以获取到该行数据的S锁,但不能获取到X锁;当一个事务获取到一行数据的X锁,其他事务不能获取到该行数据的S锁和X锁。
show engine status
中 transactions 部分可以查看锁的状态。
- 一致性非锁定读
- 一致性非锁定读:读取某行的数据时,不需要获取S锁;并且如果当前某事务占有该行的X锁,读取也不会阻塞。
- 当一致性非锁定读取的行已被加X锁,则会去Undo段中获取历史版本数据(MVCC),(Undo段用来在事务回滚数据)。
- 可重复读(读取事务一开始的数据版本)和已提交读(读取已提交数据最新版本)的事务隔离级别下,这是默认的读取方式。
- 一致性锁定读
- select for update (X锁)
- select .. lock in share mode (S锁)
- 当事务提交时,锁释放
- 开始一个事务:begin, start transaction,set autocommit = 0.
- 自增长与锁(不算太懂)
- InnoDB的内存结构中,对每个含有自增值的表都有一个自增长计数器
- Auto_INC locking:在对含有自增长列的表插入时,会执行
select max(auto_inc_col) from t for update
(这个会锁表)获取下个自增长列的值,然后把相应记录插入表中之后,相应的表锁就会释放(不用等到整个事务执行完,仅需等待插入操作完成) - Auto_INC locking的缺点:会锁表(虽然不用等到整个事务提交),对于bulk inserts (表锁时间会比较长)来说可能导致其他事务等待太长的时间。
- innodb_autoinc_lock_mode 参数:0代表atuo_inc locking的方式;1代表对于simple inserts ,会用互斥量去对内存中的值进行自增(无表锁,这种方式只有在无回滚,无删除的情况下才能保证完全自增),对于bulk inserts 还是使用传统的 auto_inc locking.2不议。
- InnoDB中,自增长列必须是索引,且必须是索引的第一个列。
- 外键和锁
InnoDB默认对外键列加索引,以避免表锁。?
- 对于外键值的插入或者更新,首先查询父表的记录,这里查询父表会使用一致性锁定读(lock in share mode).
锁的算法
- Record Lock:单个行记录上的锁(已提交读下的默认算法)
- Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
- Next-Key Lock :Gap Lock + Record Lock (可重复读下的默认算法)
Next-Key Lock详解
参考博文(http://hedengcheng.com/?p=771)
假设有表如下:其中id列为主键列,b列为索引列
- 对主键的where条件查询的锁定情况。
select * from a where id=2;//只锁定id=2 这一条记录,
其实是Next-Key Lock 退化为 Record Lock.
select * from a where id >2; //理论上,应该只锁定id>2的部分,但是这里全表都都锁了。(再议)
2、对辅助索引的锁定
select * from a where b =5 for update;//这里会把聚集索引里面id=4和辅助索引区间(3,5] 和(5,101]加锁
3、对非索引列的锁定
非索引列会造成很多的行锁和间隙锁。要注意使用。
利用Next-Key Lock在应用层面实现唯一性检查
select * from table where col=** in share mode;
if not found any row:
##insert into tables values(....) //这里插入的话在并发情况下能保证只有一个会话成功,其余的会获取锁失败(?why)
锁问题
- 脏读:脏页和脏读不同,脏读是读取另外事务中未被提交的数据。一般事务在 read-ncommitted 隔离级别下才会出现。
- 不可重复读。如果事务的隔离级别未read-committed,那么就是不可重复读的,在repeatable read隔离级别下,利用Next-Key Lock解决不可重复读(幻读)问题。
- 丢失更新
银行转账例子:
事务一: 序列1-A账号有10000元(select),序列2:转出9000元(update) 序列3:提交
事务二:序列4:A账号有10000元(select),序列5:转出1元(update) 。序列6:提交
然后 上面连个事务如果按照 序列 1,2,4, 3 , 5, 6的顺序执行,就会发生事务一更新丢失
分析可知:在上面序列2(或者5)执行的时候,序列1查询的值不应该被改变过,所以我们需要对序列1的的查询加锁(这里应该加X锁,如果加S锁的话个人感觉可能死锁)。所以上面需要用 `select for update'
阻塞
-
innodb_lock_wait_timeout
超时时间 -
innodb_rollback_on_timeout
超时是否回滚事务,默认不会回滚
死锁
- A等待B释放锁,同时B等待A释放锁。AB-BA死锁问题
- 死锁会回滚事务(虽然默认大多数异常不回滚),一般会回滚undo段小的事务。
锁升级
在 InnoDB中不存在锁升级问题,因为行锁不是按照每个记录来产生的,相反,其根据每个事务访问的每个页来对锁进行管理,采用的是位图模式(所以锁资源的开销应该不大)。
网友评论