数据库的锁

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. 聚簇索引,隔离级别<RClock 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-8gap锁,但一个插入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

 

DDLdata definition language 数据定义语言

DML:data manipulation language 数据操作语言

 

 

 

LOCK_IS/LOCK_IX

意向锁

可以理解为:暗示未来需要什么样的行级锁,is表示未来可能需要在这个表的某些记录加共享锁。只有iss不冲突,剩下三种排列组合方式都是冲突的

 

在对记录加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锁,具体不深入了

 

 

加行级锁

第一个参数impltruexnotgap,则无需创建锁对象

 

Implfalse

尝试Fast lock不能fast lockslow 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住。

 

posted @ 2021-06-20 19:11  concise_d  阅读(103)  评论(0)    收藏  举报