MileStone
上一篇, 我们讲了 MySQL 的乐观锁和悲观锁, 大家有兴趣可以连起来一起看.
本文需要阅读时间大约在 1 小时, 请抽出完整的时间来阅读, 拒绝一目十行.
后面会按照下图, 分批次对 MySQL 的锁和大家一起分享
image.PNG
花絮
image.PNG
仔细看哦
今天采蜜采到一份看起来不错的简历, 正襟危坐, 电话 call 过去.
对方的电话响着粤语版的《明年今日》, 让我想起了村里的小芳, 鞭子粗又长.
"喂, 哪位?", 对方的声音很 Young 很有钱.
"同学你好, 我是巴巴的面试官, 你对 XX 巴巴有兴趣吗, 现在方不方便聊聊?".
- "... 你好, 有啊, 方便的."
- "okay, 那我们简单的聊一会, 你能先简单的介绍一下自己吗?".
- "好, 我叫小明, 在 OO 上班, 工作 3 年, 无房无车, 有个女朋友叫清风..."
吧啦吧啦......
- "项目用到了 SpringBoot,MQ,Mysql..."
- "好的, 那我们聊一聊 mysql 的锁吧!".
1. MySQL 的事务
伟人说:"想了解一件事情, 你就得了解它的前因后果",
想了解透彻 MySQL 的锁, 不得不说 MySQL 的事务机制.
于是我问小明: 你对 MySQL 的事务了解吗?
小明轻咳一下, 娓娓道来:
1.1 事务概述
数据库事务是数据库执行过程中的一个逻辑单位, 一个事务通常包含了对数据库的读 / 写操作. 它的存在包含有以下两个目的:
为数据库操作序列提供了一个回滚的方法, 同时提供了数据库即使在异常状态下仍能保持一致性的方法.
当多个应用程序在并发访问数据库时, 可以在这些应用程序之间提供一个隔离方法(版面问题下次讨论), 以防止彼此的操作互相干扰.
1.2 事务的特性: ACID
原子性 aotmic
事务必须是原子工作单元; 对于其数据修改, 要么全都执行, 要么全都不执行.
一致性 consistent
事务在完成时, 必须使所有的数据都保持一致状态.
隔离性 isolaton
由并发事务所作的修改必须与任何其它并发事务所作的修改隔离. 事务查看数据时数据所处的状 态, 要么是另一并发事务修改它之前的状态, 要么是另一事务修改它之后的状态, 事务不会查看中间状态的数据
持久性 duration
事务完成后, 它对系统的影响是永久性的
2. 排他锁 & 共享锁
我心想, 这个谁都懂, 继续追问: 那你能在讲讲锁吗?
好的, 那我讲一下 MySQL 的几个锁. 小明窃喜, 我已经看了 imooc(公众号微信)大神的锁了, 我怕你?
2.1 共享锁(shared locks,S 锁)
小明说: 在你没有女朋友的时候, 你想和女人滚床单时候, 只能去找红灯区, 别人也可以找红灯区, 这就是共享!
共享锁又叫读锁, 如果事务 T1 对行 R 加上 S 锁, 则
其它事务 T2/T3/Tn 只能对行 R 再加 S 锁, 不能加其它锁
获得 S 锁的事务只能读数据, 不能写数据(你傻呀, 当然也不能删咯).
select ... lock in share mode;
2.2 排他锁(exclusive locks,X 锁)
小明说: 你有钱找了个女票, 你就要独占这个女票, 别人不能使用, 看 (读) 都不行! 这就是排他! 我们重点说一下排它锁.
排它锁又叫写锁, 如果事务 T1 对行 R 加上 X 锁, 则
其它事务 T2/T3/Tn 都不能对行 R 加任何类型的锁, 直到 T1 事务在行 R 上的 X 锁释放.
获得 X 锁的事务既能读数据, 又能写数据(也可以删除数据).
select ... for update
举例表 USER:
id | name | desc |
---|---|---|
1 | 马云 | 首富 |
2 | 小明 | 首负 |
- T1(事务 1):
- // start T1
- SELECT * FROM USER WHERE id = 1 lock in share mode; (S 锁)
- ......
- // start T2
- UPDATE USER SET name = '小明' WHERE id = 1;
- ......
如果 T1 不进行提交, 则 S 锁不会释放,
那么 T2 就拿着 X 锁眼巴巴的看着, 一直等待 T1(事务 1)释放 S 锁.
此时
- // 接上文代码块
- // start T3
- // 此时, 如果 T3 做同样查询, 可以直接获取 S 锁进行查询
- SELECT * FROM USER WHERE id = 1 lock in share mode; (S 锁)
这个时候如果 T1(事务 1)要进行 DELETE 操作
- // start T1
- SELECT * FROM USER WHERE id = 1 lock in share mode; (S 锁)
- ......
- DELETE FROM USER WHERE id = 1;
- ......
此时:
T1 发现 X 锁被 T2 占据着, 所以 T1 拿不到 X 锁一直等待 T2 释放 X 锁, 而 T2 拿着 X 锁等待 T1 释放 S 锁, 这样互相等待就产生了死锁, deadLock.
发生死锁以后, InnoDB 会产生错误信息, 并且释放锁(后面会专门讲业务中遇到的死锁和解决方案).
上述 X 锁的代码可以用下图来显示:
image.PNG
用时间流程来显示 3 个线程的交互如下:
image.PNG
你可以把排它锁对行的保护, 看作你对你女的保护, 只能你碰, 别人不能动! 前提你得有女, 不认真学习, 你就是个屌丝... 单身狗
听到这里我觉得小明同学很有意思哦, 这个栗子举得很有力气很有代入感! 不愧是有女朋友的人啊!
3. 意向锁
我对小明已经开始有点兴趣了, 继续追问, 那你能否再说一下意向锁?
好!
3.1 意向锁(Intention Locks)
小明说: 他在找苍老师或者女票时候, 先要远程看看对方有没有被人家共享或者独占, 而不是到面前有没有人锁住她, 可以节省成本!
意向锁是表锁, 多用在 innoDB 中, 是数据库自身的行为, 不需要人工干预, 在事务结束后会自行解除.
意向锁分为意向共享锁 (IS 锁) 和意向排它锁(IX 锁)
锁: 表示事务中将要对某些行加 S 锁
IX 锁: 表示事务中将要对某些行加 X 锁
意向锁的主要作用是提升存储引擎性能, innoDB 中的 S 锁和 X 锁是行锁, 每当事务到来时, 存储引擎需要遍历所有行的锁持有情况, 性能较低, 因此引入意向锁, 检查行锁前先检查意向锁是否存在, 如果存在则阻塞线程.
3.2 意向锁的使用
顺上面的思路讲下去, 我们看下使用的逻辑
举个栗子:
- T1:
- SELECT * FROM A WHERE id = 1 lock in share mode;(加 S 锁)
- T2:
- SELECT * FROM A WHERE id> 0 for update; (加 X 锁)
看上面这 2 个 SQL 事务, T1 执行时候, 对 id=1 这行加上了 S 锁, T2 执行前, 需要获取全表的更新锁进行判断, 即:
step1: 判断表 A 是否有表级锁
step2: 判断表 A 每一行是否有行级锁
当数据量较大时候(我们一张表一般 500-5000 万数据),step2 这种判断极其低效.
亚麻跌! 亚麻跌! 亚麻跌! 于是乎, 我们就 need 意向锁协议.
意向锁协议
事务要获取表 A 某些行的 S 锁必须要获取表 A 的 IS 锁
事务要获取表 A 某些行的 X 锁必须要获取表 A 的 IX 锁
Now!do you get me sense ?
这个时候 step2 就改变成了对意向锁的判断
step2: 发现表 A 有 IS 锁, 说明表肯定有行级的 S 锁, 因此, T2 申请 X 锁阻塞等待, 不需要判断全表, 判断效率极大提高(是不是省了很多钱)
4. 间隙锁
小明先飚了一段英语, 想来压制我?
但是我并不 care,who care?9012 年了, 谁不会英语? so easy! 我可是从小用步步高点读机长大的人哦.
我 say:pardon ? (上小学我就会, 小样, 你再飚啊!)
来源: http://www.jianshu.com/p/904f52bde904