该文章举例都是基于 InnoDB 可重复读(RR)隔离级别的,mysql 版本 8.0

根据加锁的范围,MySQL 里面的锁大致可以分成全局锁、表级锁和行锁三类

全局锁

全局锁就是对整个数据库实例加锁。MySQL 提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL)。整个库处于只读状态的时候,其他线程的 DML 和 DDL 语句都会阻塞。

适用场景:全库逻辑备份,就是把整库每个表都 select 出来存成文本。主要是针对 myisam 这种不支持事务一致性读的引擎,InnoDB 引擎可通过 mysqldump 使用参数–single-transaction 的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。

表级锁

MySQL 里面表级别的锁有两种:一种是表锁,一种是元数据锁(meta data lock,MDL)

表锁

表锁的语法是 lock tables … read/write,通过 unlock tables 主动释放锁,也可以通过客户端断开连接进行释放。

举个例子, 如果在某个线程 A 中执行 lock tables t1 read, t2 write; 这个语句,则其他线程写 t1、读写 t2 的语句都会被阻塞。同时,线程 A 在执行 unlock tables 之前,也只能执行读 t1、读写 t2 的操作。连写 t1 都不允许,自然也不能访问其他表

元数据锁(meta data lock,MDL)

MDL在访问一个表的时候会被自动加上的,增删查改时加 MDL 读锁,DDL 加写锁。其中读读不互斥,读写、写写互斥。

目的:保证读写的正确性。

假如线程 A 查询 C1,C2 两列,线程 B 执行删除列 C1,如果不加锁进行互斥,那么线程 A 查询的结果跟表结构不一致,这是不允许的。

注意事项:给表加个列时,注意程序的长事务(事务没提交时一直占用 MDL 读锁),会长时间阻塞DDL(MDL写锁),进而阻塞后面的增删查改的事务。例子如下:(只有当 sessionA commit 时,其他阻塞线程还会继续执行)

sessionA

sessionB

sessionC

sessionD

sessionE

begin;

select * from tb limit 1;//事务没提交,一直持有 MDL 锁

select * from tb limit; //不阻塞。注意没有 begin,事务自动提交。

alter table tb add c int;//阻塞,因为想加 MDL 写锁,以 sessionA 持有的 MDL 读锁互斥

sselect * from tb limit 1;//阻塞,因为前面sessionC MDL 写锁

update tb set b = 1 where a = 0;//阻塞,因为前面sessionC MDL 写锁

自增锁

auto-inc锁是一种特殊的表级锁,由插入带有自动递增列的表中的事务获取。自增 id 锁并不是一个事务锁,而是每次申请完就马上释放,以便允许别的事务再申请。通过参数 innodb_autoinc_lock_mode 不同值控制不同行为,默认值是 1。

0: 即语句执行结束后才释放锁;

1:

普通 insert 语句,自增锁在申请之后就马上释放;

类似 insert … select 这样的批量插入数据的语句,自增锁还是要等语句结束后才被释放;

2: 所有的申请自增主键的动作都是申请后就释放锁。

特点:自增 id 不是连续递增,开发时不要以自增 id 连续做判断逻辑,原因如下:

唯一键冲突。申请到的自增主键弃用

事务回滚。申请到的自增主键弃用

行锁

InnoDB支持行锁,MyISAM 不支持。行锁可以减少并发冲突,提高并发读,这个是 InnoDB 代替 MyISAM 原因之一。

InnoDB 行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB 才使用行级锁,否则,InnoDB 将使用表锁。

加锁特点:根据隔离级别需要的时候加上,在事务提交的时候才释放,并不是不需要就立刻释放。所以,在开发时,一个事务需要锁多行(可能多表不同行),把最可能造成锁冲突的语句放在后面,减少事务之间等待时间,提高并发度。锁的是索引本身,而非记录本身。

行锁的两种形式

共享锁/读锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁

排它锁(X): 允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。

SS 可并发、SX 或 XX 不能并发

显示锁定

select ... lock in share mode //共享锁(S)

select ... for update //排他锁(X)

select ... for update

加锁:将查询访问到的索引条目加上排他锁(X),注意如果没有命中索引,走全部扫描时,相当于锁全表。

特点:保证只能一个人去处理数据,其他人不能读也不能写

场景:为了让自己查到的数据确保是最新数据(当前读),并且查到后的数据只允许自己来修改。

select ... lock in share mode

加锁:将查询访问到的索引条目加上共享锁(S),其他事务针对加锁的数据不能进行 DML 操作

特点:保证大家可以一起读,但只能一个人写

场景:为了让自己查到的数据确保是最新数据(当前读),不允许其他人进行修改。(自己不一定能修改,因为其他事务可能持有 S锁)

隐式锁定

不用上面显示锁定方式,隔离级别在需要的时候自动加锁。例如:

update tb set b = b+1 where id = 1;//因为 ID 是主键索引,因此针对 id =1 这行加了行锁

对于insert、update、delete,InnoDB会自动给涉及的数据加排他锁(X);普通 select 不加共享锁和排他锁,会加 MDL锁

意向锁

意向锁的目的就是为了允许行锁和表锁共存,提升判断性能的(高效的互斥判断,加表锁时的判断)

假如没有意向锁,事务 A 给 id=1 加了排他锁,事务 B 想给该表加表排他锁时,需要判断表里面是否有行锁,就需要遍历每条记录,看看是否有行锁,如果有,就意味整个表加了表排他锁。

意向锁有 2 种,都是表锁,具体如下:

意向共享锁(IS): 事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁

意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的 IX 锁。

所以的 IX锁、IS 锁之间都不互斥,IX 锁、IS 锁只是为了与表共享锁和表排他锁互斥(不是行锁哦)。

举例说明

例子 1: 事务 A 准备给 id= 1 行加行排他锁,获取到该表的 IX 锁,这时事务 B 准备加表锁(例如 DDL),判断 IX 锁已经被其他事务获取,因此说明有其他事务针对某些行数据加行排他锁,因此事务B 阻塞(并不需要遍历表每行数据判断是否有行锁,所以高效)

例子 2:事务 A 准备给 id =1 行加行排他锁,给表加了 IX 锁,这时事务 B 准备给 id= 2行加行排他锁,给表加了 IX 锁。这说明了:IX 锁与 IX锁之间不互斥,仅仅为了表锁高效的判断,作为行锁和表锁之间的桥梁,通过意向锁来实现不同粒度(表、行)的锁之间如何做互斥判断

间隙锁(Grap Lock)

间隙锁指锁索引上的一个区间(左右开区间),为了防止读已提交隔离级别出现的幻读

临键锁(Next-Key Lock)

next-key lock = 间隙锁(grapLock)+ 行锁(record lock),左开右闭区间,例如 (0,10]

Innodb 使用 next-key lock 来锁定记录,例如 select .... for udpate

插入意向锁(Insert Intention Locks)

对已有数据行的修改与删除,必须加互斥锁X锁,那对于数据的插入,怎么实现互斥呢?插入意向锁,孕育而生

插入意向锁是一种 grap lock,专门针对 insert 使用的。当插入时,插入的间隙已经有了 grap 锁,那么就申请插入意向锁。

特点:

如果插入到同一索引间隙中的多个事务没有插入到间隙中的同一位置,那么它们就不需要等待对方

插入意向锁不会阻止任何锁,对于插入的记录会持有一个记录锁。

例子:tb 表,其中 a 是二级索引

+----+------+------+------+

| id | a | b | c |

+----+------+------+------+

| 40 | 40 | 40 | NULL |

| 50 | 50 | 50 | NULL |

+----+------+------+------+

事务 A

事务 B

begin;

select * from tb where a > 40 for update;//基于索引a加了next-key lock,(40,50]

begin;

insert into tb(id,a,b) values(45,45,45);//阻塞,因为命中 next-key lock

commit;

//不阻塞,执行但未提交

begin;

insert into tb(id,a,b) values(42,42,42);//不阻塞,执行成功

insert into tb(id,a,b) values(45,45,45);//阻塞,因为与事务 B的id=45记录锁冲突

commit;

//报重复主键的错,因为 事务 B 的 id=45已经插入,这时候不阻塞,但是 id=45主键重复了

死锁

死锁指的是不同线程都是等待对方释放资源,形成环状的资源依赖,导致几个线程进行无限等待状态。例如下面:

事务 A

事务 B

T1

begin;

update tb set b = b+1 where id = 1;

T2

begin;

update tb set b=b+1 where id =2;

T3

update tb set b= b+1 where id = 2;

T4

update tb set b = b+1 where id = 1;

上面的死锁说明:T3时,事务 A 等待事务 B释放 id=2的锁,事务 B 在 T4 时又等待 事务 A 释放 id=1的锁。事务 A 和事务 B 在相互等待对方的资源释放,因此进入死锁状态。

死锁策略

等待超时:在 InnoDB 中等待 50s后,出现死锁的第一个线程超时退出,其他线程可以继续执行。由于超时时间太长,或者设置太短(假如 1s),可能会出现误伤(例如不是死锁,普通锁等待)

死锁检测:通过检测,主动回滚死锁链中某个事务,InnoDB 默认开启死锁检测(innodb_deadlock_detect=on)

死锁检测性能问题

每条阻塞的线程,都要判断自己的加入是否会导致死锁,时间复杂度 O(n).例如:假如有 2000 条并发线程同时更新同一行,那么死锁检测就是需要判断 2000 * 2000 = 400万次,需要消耗大量 CPU 资源。

一种思路:控制 MySQL 并发度,比如在 MySQL 端或中间件 proxy 层控制同行的更新并发度

减少死锁的主要方向

控制访问相同资源的并发事务量。

mysql不同锁解释_MySQL 各种锁理解总结相关推荐

  1. mysql二级封锁协议_MySQL 行锁、两阶段锁协议、死锁以及死锁检测

    行锁 MySQL的行锁都是在引擎层实现的,但是 MyISAM 不支持行锁,意味着并发控制只能使用表锁,同一张表任何时刻只能被一个更新在执行,影响到业务并发度.InnoDB 是支持行锁的,这也是 MyI ...

  2. mysql BDB支持表锁吗_mysql 表锁问题

    本文转自:http://www.cnblogs.com/itdragon/p/8194622.html MySQL 表锁和行锁机制 行锁变表锁,是福还是坑?如果你不清楚MySQL加锁的原理,你会被它整 ...

  3. mysql 前后代表什么_MySQL的锁

    1.什么是锁 锁机制用于管理对共享资源的并发访问. lock与latch latch一般称为闩锁(轻量级的锁),因为其要求锁定的时间必须非常短.若持续的时间长,则应用的性能会非常差.在InnoDB存储 ...

  4. mysql 悲观锁 共享锁_MySQL 乐观锁 悲观锁 共享锁 排他锁

    乐观锁 乐观锁是逻辑概念上的锁,不是数据库自带的,需要我们自己去实现.乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁 ...

  5. mysql行锁还需要乐观锁吗_mysql行锁、表锁。乐观锁,悲观锁

    锁定用于确保事务完整性和数据库一致性. 锁定可以防止用户读取其他用户正在更改的数据,并防止多个用户同时更改相同的数据. 如果不使用锁定,数据库中的数据可能在逻辑上变得不正确,而针对这些数据进行查询可能 ...

  6. mysql某个表被行锁了_MySQL 行锁和表锁的含义及区别详解

    一.前言 对于行锁和表锁的含义区别,在面试中应该是高频出现的,我们应该对MySQL中的锁有一个系统的认识,更详细的需要自行查阅资料,本篇为概括性的总结回答. MySQL常用引擎有MyISAM和Inno ...

  7. mysql数据库什么情况下会锁表_mysql数据库锁的产生原因及解决办法

    数据库和操作系统一样,是一个多用户使用的共享资源.当多个用户并发地存取数据 时,在数据库中就会产生多个事务同时存取同一数据的情况.若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性 ...

  8. mysql间隙锁 打开_MySQL间隙锁问题

    间隙锁(Gap Lock):锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间. 最近用户反馈说系统老是出现insert时,等待超时了,最后发现是ins ...

  9. mysql内置乐观锁吗_mysql 乐观锁详解

    乐观锁: 乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制.悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性.但随之而来的就是 ...

最新文章

  1. idea 运行jmeter源码_学会BeanShell,才敢说自己懂Jmeter
  2. English learning
  3. hdu 2896 病毒侵袭
  4. 6.HBase时髦谨慎财会会计
  5. 多层GCN的over-smooth问题
  6. Ubuntu 18.04 通过 ufw route 配置网关服务器
  7. 跟小静学MVC3[03]--相关语法特性小补习
  8. 《HTML5游戏编程核心技术与实战》——2.3 图像API
  9. [LeetCode] Rotate Array
  10. ESXI安装威联通NAS系统
  11. could not resolve property: qid of: org.lxh.myzngt.vo.Answer [SELECT COUNT(q.qid) FROM org.lxh.myzn
  12. 【转载】Altera FPGA使用通用SPI Flash(代替EPCS的方法)
  13. java解析20万Excel
  14. BZOJ[1984]月下“毛景树” 树链剖分+线段树
  15. C Primer Plus 第二章 复习题编程练习 答案
  16. 【总结】从0到1的项目经历
  17. Redis - increment 递增方法 | 处理防重复和并发问题
  18. c++ IO多路复用
  19. 关于SFM的材质vmt科普
  20. 百度地图API——修改infowindow样式

热门文章

  1. 软考归来~分享一下历程和心得体会
  2. 堆球问题,开普勒猜想(格密码相关)
  3. 2020小迪培训(第20天WEB 漏洞-文件上传之基础及过滤方式)
  4. Linux 虚拟内存 交换空间
  5. PTFE高频微波材料的制作工艺指引
  6. hadoop分布式安装部署详细视频教程(网盘附配好环境的CentOS虚拟机文件/hadoop配置文件)
  7. (附源码)小程序 心理治愈 毕业设计041506
  8. 惊呆了,竟然可以用这种方式秒建Redis集群?
  9. 大数据课程有必要学吗?
  10. 2021-07-28 2021年A特种设备相关管理(锅炉压力容器压力管道)免费试题及A特种设备相关管理(锅炉压力容器压力管道)证考试