数据库的锁
http://mysql.taobao.org/monthly/2016/01/01/
LOCK_REC_NOT_GAP
只锁记录,不锁gap;rc一般都加这个类型的锁,不能防止脏读,幻读,不可重复读和丢失数据
LOCK_GAP
间隙锁,锁两个索引记录之间的一段范围但是不锁数据的本身。(锁个开区间);RR隔离级别下用到
LOCK_ORDINARY(Next-Key Lock)
Next-key锁,包含记录以及记录之前 的gap;可解决mysql rr级别下的幻读问题。
插入数据的以后需要判断下一条记录是否上锁。
如果上了gap锁说明可能有事务在对它进行修改,那这之后再加是不是可能会引起幻读?
比如说在13行插入数据,如果说下一行就是15,它加锁的是开区间所以1415之间的间隙没有锁,这时候直接加锁,那第一次和第二次select。。Where id>13就会不一样了。
LOCK_S(共享锁)
保证并发读不冲突?所有读请求的lock s不冲突
(1)serializable的普通查询加lock s;但非事务读(auto-commit)不加锁
(2)Select。。。In share mode,会加s锁
A. Rc 为lock rec not gap+lock s(这也可以看出来不能防止幻读嘛)
B. Rr 查询条件为唯一索引 :lock rec not gap+lock s
多条记录:lock ordinary +locks
解释一下就是如果查的索引只有一条,那就只需要s锁,保证读的时候没有人更改,如果查的索引多条,需要多一个gap锁,防止幻读。
C.insert通常不加锁,但如果检测到duplicate key(业务需求,需要根据某字段查找数据库中是否有记录,有则更新,没有则插入),会对普通insert/update加lock s。如果是select。。on duplicate/replace into 则是先清空,再插入要导入的数据,这时候加x锁
这个是因为普通insert update不清空,所以读无所谓,但是replace into会清空,所以要加排他锁不然万一别人读个空的不就gg
D. 聚簇索引,隔离级别<RC加lock rce not gap 的s/x锁;否则是lock ordinary
E.二级唯一索引,有重复键(因为这是普通索引,可以重复了)加lock ordinary
LOCK_X(排他锁)
目的是避免同一条记录的并发修改,一般对update/delete或者select。。for update都会用排他锁
通过二级索引来查询时,需要对二级索引和聚簇索引都加锁;其中rc级别二级索引和聚簇索引都加nogap x锁;rr级别二级索引加lock ordinary +lock x,聚簇索引则为nogap x锁
因为在rc,除了主键/唯一键冲突之外的情况下不会有gap锁???
那为什么在主键冲突的情况下就有gap锁?这个我真的懒得理解了。。
rr是有gap的属性的,所以可以加。
通过聚集索引查询,但更新的是二级索引时,只需要先对聚集索引加nogap和x锁,这样可以保证别的线程不会来修改这条记录。因为修改记录总是先聚簇索引再二级索引,因此二级索引不需要加锁,只需要确保二级索引上没有和聚簇索引冲突的锁。
LOCK_INSERT_INTENTION(插入意向锁)
(1)这是gap锁的一种
(2)多个session同时插入一个gap无需互相等待,但需要保证插入但记录不冲突,比如两个事务同时插入,都有4-8的gap锁,但一个插入6一个插入7,那就是OK的。
(3)插入数据的时候会进行锁检查(除了构建索引时候的插入以外),检查的是当前插入位置的下一条记录(逻辑顺序的下一条,就是12的下一条锁13)是否有锁
如果没锁,先更新二级索引的事务id,然后插入成功?
如果有锁,看该锁是否锁住gap,锁住就等待,这时候锁类型就是x+gap+lock insert intention ;前两个就是典型的锁二级索引的锁
锁表更新
如果发生插入/删除,则gap会发生变化,InnoDB需要对锁表进行更新
(1) 插入数据;【3,9】之间有锁,现在插入5;首先要遍历所有在记录9上的记录锁(什么是记录锁)?如果不是插入意向锁,
锁的冲突判定
IA锁是什么?(应该是插入意向锁?)
记录锁:lock s,x;
其他的flag都是锁的描述:lock gap/lock rec not gap/lock ordinary/lock insert intention;
(看不懂)
可以看到锁对象会加入到不同的集合或者链表中,通过挂载到事务对象上,可以快速检查当前事务是否已经持有表锁;通过挂到表对象的锁链表上,可以用于检查该表上的全局冲突情况。
表级锁
InnoDB表级锁:lock IS,lock IX,lock X,lock S,lock auto inc
DDL:data definition language 数据定义语言
DML:data manipulation language 数据操作语言
LOCK_IS/LOCK_IX
意向锁
可以理解为:暗示未来需要什么样的行级锁,is表示未来可能需要在这个表的某些记录加共享锁。只有is和s不冲突,剩下三种排列组合方式都是冲突的
在对记录加S锁或者X锁时,必须保证其在相同的表上有对应的意向锁或者锁强度更高的表级锁。
LOCK_X
加了x表级锁后,其他所有的表级锁请求都需要等待
Ddl最后一个阶段(commit xxx table)加x锁,确保没有别的事务持有表级锁;
关闭自动提交后,对表写数据会加x锁
对某空间执行discard /import,需要x锁(增加/删除数据)
LOCK_S
Ddl不可以online执行?(啥意思),加s锁
关闭自动提交后,对表读数据会加s锁
LOCK_AUTO_INC
自增锁,很复杂。。。
事务锁管理
InnoDB 所有的事务锁对象都是挂在全局对象lock_sys上,同时每个事务对象上也维持了其拥有的事务锁,每个表对象(dict_table_t)上维持了构建在其上的表级锁对象。
加表级锁
首先看当前事务有没有更高级别/同级别的表锁,有,则直接返回成功。
再检查当前锁对象是否和要申请的那个存在冲突:
检查方式是直接遍历locks这个链表
不存在冲突,直接创建锁对象
存在冲突,进入等待,创建等待锁对象
存在死锁,(牺牲者)移除当前请求,返回错误码;(胜利者)当前会话成功获得锁
然后创建锁对象分为auto-inc锁和非auto-inc锁,具体不深入了
加行级锁
第一个参数impl为true且x和notgap,则无需创建锁对象
Impl为false
尝试Fast lock不能fast lock再slow lock;具体不深入了
等待及死锁判断
当发现有冲突的锁时,调用函数RecLock::add_to_waitq进行判断
持有冲突锁是哪部后台线程;则不会被高优先级事务取消,因为总是优先保证内部线程优先执行
然后比较当前会话和持有锁会话的优先级选择被牺牲的事务,一般优先级低的先牺牲;优先级相同则请求者先牺牲。然后返回上层,回滚事务。
实际操作中死锁检测代价高昂
释放锁及唤醒
大多数时候事务锁都在事务提交时释放;
意外情况:auto-inc在sql结束时直接释放
Rc级别下,dml(数据操作语言),从引擎层(MySql分四层架构:网络连接层、服务层、存储引擎层、物理层)返回server,如果不满足where条件,则需要立刻unlock
行锁
全局hash中删除后,要判断其他等待的锁会不会被唤醒;例如释放x锁,可能s锁的请求会话就会被唤醒。
检查开销会比较大
表锁
调用函数lock_table_dequeue。
注意在释放锁时,如果该事务持有的锁对象太多,每释放1000(LOCK_RELEASE_INTERVAL)个锁对象,会暂时释放下lock_sys->mutex再重新持有,防止InnoDB hang住。

浙公网安备 33010602011771号