深入理解MySQL系列之锁

按锁思想分类
- 悲观锁
- 优点:适合在写多读少的并发环境中使用,虽然无法维持非常高的性能,但是在乐观锁无法提更好的性能前提下,可以做到数据的安全性
- 缺点:加锁会增加系统开销,虽然能保证数据的安全,但数据处理吞吐量低,不适合在读书写少的场合下使用
- 乐观锁
- 优点:在读多写少的并发场景下,可以避免数据库加锁的开销,提高DAO层的响应性能,很多情况下ORM工具都有带有乐观锁的实现,所以这些方法不一定需要我们人为的去实现。
- 缺点:在写多读少的并发场景下,即在写操作竞争激烈的情况下,会导致CAS多次重试,冲突频率过高,导致开销比悲观锁更高。
- 实现:数据库层面的乐观锁其实跟CAS思想类似, 通数据版本号或者时间戳也可以实现。
按锁粒度分类
- 表锁
表锁是指对一整张表加锁,一般是 DDL 处理时使用
表锁由 MySQL Server 实现,表级锁更适合于以查询为主,并发用户少,只有少量按索引条件更新数据的应用,如Web 应用
- 优点:开销小,加锁快;不会出现死锁;
- 缺点:锁定粒度大,发生锁冲突的概率最高,并发度最低。
页锁:介于表锁和行锁之间。BDB 存储引擎采用的是页面锁(page-level locking)
行锁
行锁则是锁定某一行或者某几行,或者行与行之间的间隙。
行锁则是存储引擎实现,行级锁更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统
- 优点:锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
- 缺点:开销大,加锁慢;会出现死锁;
InnoDB行锁类型
- 标准
- 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁
- 排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。

数据库并发场景主要有三种:
- 读-读:不存在任何问题,也不需要并发控制
- 读-写:有隔离性问题,可能遇到脏读,幻读,不可重复读
- 写-写:可能存更新丢失问题,比如第一类更新丢失,第二类更新丢失
- 更细粒度
- 共享锁(S)
- 排他锁(X)
- 意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁。
- 意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的 IX 锁。

一致性(非)锁定读
一致性非锁定读:
一致性非锁定读由MVCC(多版本并发控制)实现。MVCC其实就是在每一行记录后面增加两个隐藏列,记录创建版本号和删除版本号,而每一个事务在启动的时候,都有一个唯一的递增的版本号
详细可参考前文《深入理解MySQL系列之redo log、undo log和binlog》
一致性锁定读:
SELECT ... FOR UPDATE //加X锁
SELECT ... LOCK IN SHARE MODE //加S锁
锁的算法
行锁的三种算法:
- Record Lock:当个行记录上的锁
- Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
- Next Key Lock:Gap Lock + Record Lock ,锁定一个范围,并且锁定记录本身(只有REPEATABLE READ隔离级别有)

在默认事务隔离级别(REPEATABLE READ)下,InnoDB采用Next Key Lock解决幻读(Phantom Problem)。对一个范围加X锁,从而这个范围不允许插入,所以避免了幻读。
此外,还可以用Next key lock实现唯一性检查。如下,用户通过索引查询一个值,并对该行加一个S Lock;如果行不存在,锁定一个范围,新插入值也是唯一的。
SELECT * FROM TABLE WHERE COL = xxx LOCK IN SHARE MODE;
// 如果没有找到任何行
INSERT INTO TABLE VALUES(...)

间隙锁加锁原则:
- 1、加锁的基本单位是 NextKeyLock,是前开后闭区间。
- 2、查找过程中访问到的对象才会加锁。
- 3、索引上的等值查询,给唯一索引加锁的时候,NextKeyLock退化为行锁。
- 4、索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,NextKeyLock退化为间隙锁。
- 5、唯一索引上的范围查询会访问到不满足条件的第一个值为止。
自增锁
AUTOINC 锁又叫自增锁(一般简写成 AI 锁),是一种表锁,当表中有自增列(AUTOINCREMENT)时出现。
当插入表中有自增列时,数据库需要自动生成自增值,它会先为该表加 AUTOINC 表锁,阻塞其他事务的插入操作,这样保证生成的自增值肯定是唯一的。AUTOINC 锁具有如下特点:
AUTO_INC 锁互不兼容,也就是说同一张表同时只允许有一个自增锁;
自增值一旦分配了就会 +1,如果事务回滚,自增值也不会减回去,所以自增值可能会出现中断的情况。
显然,AUTOINC 表锁会导致并发插入的效率降低,为了提高插入的并发性,MySQL 从 5.1.22 版本开始,引入了一种可选的轻量级锁(mutex)机制来代替 AUTOINC 锁,可以通过参数 innodbautoinclockmode 来灵活控制分配自增值时的并发策略。具体可以参考 MySQL 的 AUTOINCREMENT Handling in InnoDB 一文
死锁
概念:死锁是指两个或两个以上的事务执行过程中,因争夺资源而造成的一种互相等待的现象。
- 解决死锁第一种方法:超时,然后对事务回滚
缺点:若超时的事务所占权重比较大,如事务更新了很多行,占用了较多的undo log,这时采用FIFO方式就不合适了,因为回滚这个事务的时间相对另一个事务所占用的时间可能会多很多。
- wait-for graph等待图方式进行死锁检测
InnoDB采用的方式。
wait-for graph要求数据库保存两种信息,通过下面两个链表构造出一张图,如果这个图中存在回路,就代表存在死锁,因此资源间发生相互等待。
- 锁的信息链表
- 事务等待链表
如下图示例,事务等待链表有4个事务,而右边锁信息链表中,row1中事务t1要等待事务t2资源,所以在下wait-for graph图中就有一条t1指向t2的箭头;而row2中tt2、t3需要等待t1中占用row2资源,故t2、t3各有个箭头指向t1(t1、t4两个S锁兼容)。依次最后得到下面的图。
可以发现(t1, t2)存在回路,因此存在死锁。
所以wait-for graph是一种主动的死锁检查机制,若存在死锁,选择回滚undo量最小的事务。(wait-for graph采用图的深度优先算法)


参考:
《MySQL技术内幕》
https://mp.weixin.qq.com/s/PPfXH9a3jlNyxXpwB_DZDg
https://zhuanlan.zhihu.com/p/29150809
深入理解MySQL系列之锁的更多相关文章
- Mysql加锁过程详解(7)-初步理解MySQL的gap锁
Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...
- 深入理解MySQL系列之redo log、undo log和binlog
事务的实现 redo log保证事务的持久性,undo log用来帮助事务回滚及MVCC的功能. InnoDB存储引擎体系结构 redo log Write Ahead Log策略 事务提交时,先写重 ...
- 深入理解MySQL系列之索引
索引 查找一条数据的过程 先看下InnoDB的逻辑存储结构: 表空间:可以看做是InnoDB存储引擎逻辑结构的最高层,所有的数据都存放在表空间中.默认有个共享表空间ibdata1.如果启用innodb ...
- 深入理解 MySQL ——锁、事务与并发控制
本文首发于vivo互联网技术微信公众号 mp.weixin.qq.com/s/JFSDqI5ya… 作者:张硕 本文对 MySQL 数据库中有关锁.事务及并发控制的知识及其原理做了系统化的介绍和总结, ...
- [转帖]2019-03-26 发布 深入理解 MySQL ——锁、事务与并发控制
深入理解 MySQL ——锁.事务与并发控制 https://segmentfault.com/a/1190000018658828 太长了 没看完.. 数据库 并发 mysql 639 次阅读 ...
- [转帖]深入理解 MySQL—锁、事务与并发控制
深入理解 MySQL—锁.事务与并发控制 http://www.itpub.net/2019/04/28/1723/ 跟oracle也类似 其实所有的数据库都有相同的机制.. 学习了机制才能够更好的工 ...
- 深入理解mysql之BDB系列(1)---BDB相关基础知识
深入理解mysql之BDB系列(1) ---BDB相关基础知识 作者:杨万富 一:BDB体系结构 1.1.BDB体系结构 BDB总体的体系结构如图1.1所看到的,包括五个子系统(见图1.1 ...
- MySQL强人“锁”难《死磕MySQL系列 三》
系列文章 一.原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 二.一生挚友redo log.binlog<死磕MySQL系列 二> 前言 最近数据库 ...
- S 锁与 X 锁的爱恨情仇《死磕MySQL系列 四》
系列文章 一.原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 二.一生挚友redo log.binlog<死磕MySQL系列 二> 三.MySQL强 ...
随机推荐
- C语言讲义——预处理
C预处理器是一个文本替换工具,在实际编译之前完成一些预先的处理. C预处理器(C Preprocessor)简写为 CPP 预处理器命令都是以#开头,如: #include <stdio.h&g ...
- 知识解析:C语言函数有一些什么?为你呈现最全函数大全
大家双节快乐呀~国庆节过去了一半,大家放了几天假呀?玩的开心吗? 如果假日没有其他安排,不要宅在家虚度光阴哦~看看我的文章或者视频学习一些知识吧~ 今天为大家分享C语言库函数知识. 以下图片以字母 ...
- kafka入门之broker--通信协议
kafka的通讯协议是基于tcp之上的二进制协议,所有类型的请求和响应都是结构化的,由不同的初始类型构成.kafka使用这组协议完成各个功能的实现. 单个kafka client通常需要同时连接多个b ...
- Spring Boot 项目瘦身指南,瘦到不可思议!129M->1.3M
之前在 从使用传统Web框架到切换到Spring Boot后的总结 中提到关于 Spring Boot 编译打包,Spring Boot 应用程序不用额外部署到外部容器中,可以直接通过 Maven 命 ...
- 给你一个亿的keys,Redis如何统计?
前言 不知你大规模的用过Redis吗?还是仅仅作为缓存的工具了?在Redis中使用最多的就是集合了,举个例子,如下场景: 签到系统中,一天对应一系列的用户签到记录. 电商系统中,一个商品对应一系列的评 ...
- 20190713_windows 2008 R2在启动网站时报错_另一个程序正在使用此文件,进程无法访问
80端口已经被占用了; 换个端口就好了, 如果你是云服务器, 记得控制台也要开放对应的端口
- PyQt(Python+Qt)学习随笔:图例解释QFrame类的lineWidth、midLineWidth以及frameWidth属性
老猿Python博文目录 老猿Python博客地址 QFrame类有四个跟宽度相关的属性,分别是width.lineWidth.midLineWidth以及frameWidth属性.width是整个Q ...
- 凌乱的与ctf无关的小知识点
(1)在网页中一般不要用记住密码.否则想要知道你的密码很简单. 例子:看样子很安全,别人无法通过这些来获得你的其他密码(尤其是想我这样密码强度不高的人),但是知道要修改前端的选项,你的密码就会被暴露. ...
- 差分约束系统——POJ1275
之前做过差分,但是没做过差分约束系统. 正好在学军机房听课讲到这道题,就顺带学了一下. 其实...就是列不等式组然后建图 作为蒟蒻,当然是不会加二分优化的啦...但是poj上还是94ms跑过了qwq ...
- git相关操作
git相关命令 基本操作 git init git add xxx git commit -m "first commit" git tag -a V1.0 -m '我的标签' g ...