MySql 锁

158 阅读3分钟

一.从粒度上可以分为以下三种

1.全局锁

就是对整个数据库实例加锁,可以通过Flush tables with read lock(FTWRL)加全局锁,之后其它线程的以下语句会被阻塞:数据更新语句(DML)、数据定义语句(DDL)、更新类事务的commit语句。(对于不支持一致性试图的引擎如MyISAM在进行逻辑备份的时候使用,具有一致性试图的可以通过mysqldum添加-single-transaction拿到一致性试图去做逻辑备份)

2.表锁

Mysql的表级锁分为两种:一种是表锁,一种是原数据锁(MDL)

  • 表锁

    表锁的语法是 lock tables ...read/write. 可以用unlock tables主动释放锁,也可在客户端断开连接自 动释放锁,lock tables除了会限制别的线程的读写外,也限定了本线程接下来的操作对象。

  • MDL

    MDL不需要显示使用,在访问一个表的时候会被自动加上,为了保证读写的正确性,如果一个查询正在遍历一个表的 数据,而执行期间另一个线程对这个表结构做了变更,那查询线程拿到的结果和表结构对不上,肯定是不行的

    DML读锁:对表做增删改查操作的时候加DML读锁

    DML写锁:当要对表做结构变更操作的时候加DML写锁

    读锁之间不互斥,读写锁和写锁之间是互斥的。

    DML锁有个问题:有的时候给一个小表加字段,导致整个库挂掉了。因为锁的原因可能会导致之后加了重试方案的请求会一直因为请求阻塞不断发起新的请求而导致线程爆满。

    如何解决呢(安全的给小表加字段):设置个等待的时间,在时间内拿到了就更,拿不到就先放弃,不断重新尝试这个过程。

行锁

行锁是由存储引擎自己实现的,和server层无关,行锁顾名思义就是锁单行数据。

基于二阶段锁

image.png

也就是说在InnoDB事务中,行锁是在需要的时候才加上的,但并不是不需要了就立即释放,而是等到事务结束时才释放。基于这个我们就可以得出这么样一个结论:如果你的事务中需要锁多个行,要把最可能造成锁冲突,最可能影响并发度的锁尽量往后放,commit之后就释放了,就不要一直站着茅坑,不xx。

二.其它角度分锁

  • 共享锁 || S Lock(in share mode):允许事务读一行数据

    排他锁 || X Lock(for update):允许事务删除 或 更新一行数据

image.png 普通 select 语句默认不加锁,而CUD操作默认加排他锁。

  • 间隙锁(Gap Lock)锁定一个范围,但不包含记录本身。作用于RR级别下

关闭间隙锁的2种方式:

(1)将事务隔离级别变为read committed

(2)将参数innodb_locks_unsafe_for_binlog设置为1

在上述配置下,除了外键和唯一性检查依然需要间隙锁,其余情况仅适用行锁进行锁定。

  • 临键锁

    Next-Key Lock,等于记录锁 + 间隙锁,锁定一个范围,并且锁定记录本身。主要是阻止多个事务将记录插 入到同一个范围内,从而避免幻读。

Mysql INnoDB RR 加锁规则:

1.加锁的基本单位是next-key lock。前开后闭(5,10]

2.查找过程中访问到的对象才加锁

3.索引上的等值查询,给唯一索引加锁的时候,next-key lock会退化为行锁

4.索引上的等值查询,向右遍历时且最后一个值不等于满足条件的时候,next-key lock退化为间隙锁

5.唯一索引上的范围查询会访问到不满足条件的第一个值为止