乐趣区

关于mysql:MySQL高级锁和事务笔记

学习《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 蕴含了 CompareSwap两个操作,它是由 CPU 反对的原子操作,通过硬件放弃原子性

2. 版本号机制

实现思路:在数据记录中减少一个 version(版本号)字段,每次数据被批改时版本号减少 1。

当一个会话操作中,先把数据查出,执行操作,对数据进行批改时,
比对版本号是否发生变化,若雷同则证实没有人批改,失常执行批改操作;
若版本号不雷同,则示意这段时间有其余会话操作批改了数据,以后会话须要放弃操作。

乐观锁和乐观锁的优缺点和实用场景?CAS 有哪些毛病?

详见【BAT 面试题系列】面试官:你理解乐观锁和乐观锁吗?

总结

Innodb 存储引擎实现了行锁,反对事务;MyISAM 存储引擎是表锁。

行锁的 性能损耗 要比表锁更大,但整体 并发解决能力 要比表锁更强。

[参考]

MySQL 高级_行锁实践
MySQL 高级_索引生效行锁变表锁
MySQL 高级_间隙锁危害
MySQL 高级_如何锁定一行
打工四年总结的数据库知识点
【BAT 面试题系列】面试官:你理解乐观锁和乐观锁吗?

退出移动版