学习《MySQL 高级》锁和事务,并参考其余优良相干文章整顿的笔记
表锁
读锁会阻塞写,然而不会梗塞读;写锁会把读锁和写锁都阻塞。
详解:
会话 s1 给表 A 加读锁,s1、s2……都不可写表 A,可读表 A;
会话 s1 给表 A 加写锁,s1 可对表 A 增删改查,s2、s3 等未拿到锁的会话,既不能查也不能写;
会话 s1 不论是表 A 加读锁还是写锁,须要等表 A 的锁开释后能力操作其余 没有加锁的表(能够给多个表加锁,同时操作多个表)。
PS:读锁,又叫共享锁(S 锁);写锁,又叫排他锁(X 锁)。
行锁
行锁反对事务
事务
什么是事务?事务是数据库操作的最小工作单元,组合一组操作一起提交,要么执行都胜利(commit),要么执行都不胜利(rollback)。
事务的个性(ACID)
- Atomicity:原子性
- Consistency:一致性
- Isolation:隔离性
- Durability:持久性
原子性
事务被视为不可分割的最小单元,事务的所有操作要么全副胜利,要么全副失败回滚。
一致性
数据库在事务执行前后都放弃一致性状态,在一致性状态下,所有事务对一个数据的读取后果都是雷同的。
隔离性
一个事务所做的批改在最终提交以前,对其余事务是不可见的。
持久性
一旦事务提交,则其所做的批改将会永远保留到数据库中。即便零碎产生解体,事务执行的后果也不能丢。
并发事务处理带来了哪些问题
- 更新失落 (Lost Update)
- 脏读 (Dirty Reads)
- 不可反复读 (Not-Repeatable Reads)
- 幻读 (Phantom Reads)
更新失落
当两个或多个事务抉择同一行,而后基于最后选定的值更新该行时,因为每个事务都不晓得其余事务的存在,就会产生失落更新问题。最初的 更新笼罩 了由其余事务所做的更新。
脏读
一句话:事务 A 读取到了事务 B 已批改但尚未提交 的数据,还在这个数据根底上做了操作。此时,如果 B 事务回滚,A 读取的数据有效,不合乎一致性要求。
不可反复读
一个事务在读取某些数据后的某个工夫,再次读取以前读过的数据,却发现其读出的数据曾经产生了扭转、或某些记录曾经被删除了!这种景象就叫做“不可反复读”。
一句话:事务 A 读取到了事务 B 已批改且已提交 的数据,不合乎隔离性
幻读
一个事务 按雷同的查问条件 从新读取以前检索过的数据,却发现其余事务插入了满足其查问条件的 新数据,这种景象就称为“幻读”。
一句话:事务 A 读取到了事务 B 新增且已提交 的数据,不合乎隔离性。
PS: 认真了解加黑的中央,脏读、不可反复读、幻读的区别高深莫测。
事务的隔离级别
读未提交(READ UNCOMMITTED)
事务中的批改,即便没有提交,对其余事务也是可见的。
读已提交(READ COMMITTED)
一个事务只能读取曾经提交的事务所做的批改。换句话说,一个事务所做的批改在提交之前对其余事务是不可见的。
可反复读(REPEATABLE READ)(默认级别)
保障在同一个事务中屡次读取同样数据的后果是一样的。
可串行化(SERIALIZABLE)
强制事务串行执行。
每种隔离级别以及会呈现的问题汇总表
隔离级别 | 脏读 | 不可反复读 | 幻影读 |
---|---|---|---|
未提交读 | √ | √ | √ |
提交读 | × | √ | √ |
可反复读 | × | × | √ |
可串行化 | × | × | × |
查看以后数据库的事务隔离级别:
show variables like 'tx_isolation';
《MySQL 实战 45 讲》03 | 事务隔离:为什么你改了我还看不见?一文解析的更加清晰明了
行锁降级表锁
如果 A 事务依据条件 a = 1 更新时,索引生效,事务未提交前;B 事务依据条件 a = 10 更新时,会被阻塞。由此可见,插入时索引生效导致行锁降级为表锁。
总结:没有索引或者索引生效更新数据时,InnoDB 的行锁降级为表锁。
间隙锁(Gap Lock)
什么是间隙锁?
当咱们用 范畴条件 而不是相等条件检索数据,并申请共享或排他锁时,InnoDB 会给符合条件的已有数据记录的索引项加锁;对于键值在条件范畴内但并不存在的记录,叫做“间隙(GAP)”。
InnoDB 也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Gap Lock)。
Next-Key Lock = Record Lock(行锁)+ Gap Lock(间隙锁),它不仅锁定一个记录上的索引,也锁定索引之间的间隙。
危害
因为 Query 执行过程中通过范畴查找的话,他会锁定整个范畴内所有的索引键值,即便这个键值并不存在。
间隙锁有一个比拟致命的弱点,就是当锁定一个范畴键值之后,即便某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无奈插入锁定键值范畴内的任何数据。在某些场景下这可能会对性能造成很大的危害。
如何给某一行数据加锁?
# session 1
> begin; # 开启手动事务
> select * from test where a = 8 for update; # 锁住 a = 8 的这行记录
// 此时其余 session 是不能更新此行数据的,直到 session1 会话提交事务,才会将此行的锁开释
> commit; # 手动提交事务
补充:乐观锁和乐观锁
乐观锁和乐观锁并不是仅限于数据库的锁,它们是两种思维,用于 解决并发场景下的数据竞争 问题。
乐观锁
乐观锁:在操作数据时比拟“乐观”,总认为会有其他人同时批改数据,所以在操作数据时会先把数据加锁,直到操作结束才开释锁,加锁期间其他人不能批改数据。
乐观锁
乐观锁:在操作数据时比拟“乐观”,总认为不会有人同时批改数据,所以在操作数据时不会加锁。更新数据的时候,判断一下在查问后到更新这段时间数据有没有被其他人批改,若他人没有批改数据,则本人失常执行。若他人批改了数据,则本人放弃操作。
乐观锁不加锁并不意味着对数据不负责。它有两种实现形式:CAS 机制
和版本号机制
。
1.CAS 机制(Compare And Swap)
CAS 操作包含了 3 个操作数:
- 须要读写的内存地位(V)
- 进行比拟的预期值(A)
- 拟写入的新值(B)
CAS 操作逻辑 :如果内存地位 V 的值等于预期的 A 值,则将该地位更新为新值 B,否则不进行任何操作。
许多 CAS 的操作是 自旋 的:如果操作不胜利,会始终重试,直到操作胜利为止。
CAS 蕴含了 Compare
和Swap
两个操作,它是由 CPU 反对的原子操作,通过硬件放弃原子性。
2. 版本号机制
实现思路:在数据记录中减少一个 version(版本号)字段,每次数据被批改时版本号减少 1。
当一个会话操作中,先把数据查出,执行操作,对数据进行批改时,
比对版本号是否发生变化,若雷同则证实没有人批改,失常执行批改操作;
若版本号不雷同,则示意这段时间有其余会话操作批改了数据,以后会话须要放弃操作。
乐观锁和乐观锁的优缺点和实用场景?CAS 有哪些毛病?
详见【BAT 面试题系列】面试官:你理解乐观锁和乐观锁吗?
总结
Innodb 存储引擎实现了行锁,反对事务;MyISAM 存储引擎是表锁。
行锁的 性能损耗 要比表锁更大,但整体 并发解决能力 要比表锁更强。
[参考]
MySQL 高级_行锁实践
MySQL 高级_索引生效行锁变表锁
MySQL 高级_间隙锁危害
MySQL 高级_如何锁定一行
打工四年总结的数据库知识点
【BAT 面试题系列】面试官:你理解乐观锁和乐观锁吗?