mirror of
https://github.com/CyC2018/Interview-Notebook.git
synced 2026-10-31 19:13:42 +00:00
Update 数据库系统原理.md
补充提交读下的加锁范围和没有索引时当前读的加锁,增加实测附录。
This commit is contained in:
@@ -45,6 +45,7 @@
|
||||
* [A3 事务 ID 的分配](#a3-事务-id-的分配)
|
||||
* [A4 快照读与当前读混用](#a4-快照读与当前读混用)
|
||||
* [A5 Next-Key Locks 的加锁范围](#a5-next-key-locks-的加锁范围)
|
||||
* [A6 没有索引时的加锁](#a6-没有索引时的加锁)
|
||||
* [参考资料](#参考资料)
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
@@ -259,6 +260,8 @@ SELECT ... FOR UPDATE;
|
||||
|
||||
一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。
|
||||
|
||||
InnoDB 在提交读下不加间隙锁(只在检查外键和唯一键时使用),UPDATE、DELETE 扫描过但不满足条件的行,判断完条件就释放锁。插入不再被间隙锁阻塞,死锁也少得多,所以不少公司把隔离级别改为提交读,代价是会出现不可重复读和幻读,并且 binlog 必须使用 ROW 格式(见 [binlog](MySQL.md#binlog))。
|
||||
|
||||
### 可重复读(REPEATABLE READ)
|
||||
|
||||
保证在同一个事务中多次读取同一数据的结果是一样的。
|
||||
@@ -415,6 +418,8 @@ SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
|
||||
|
||||
按 c = 11 查询时,c 不是唯一索引,后面还可能有 c = 11 的记录,所以除了 (10, 11] 的 Next-Key Lock,还要锁住到下一条记录 13 之间的间隙;如果 c 是唯一索引,就只锁 c = 11 这一条记录。
|
||||
|
||||
当前读的条件列没有索引时,InnoDB 只能扫描整个聚簇索引,扫描过的每一行都要加锁。例如表 t_nl 有 5 行,d 列没有索引,执行 UPDATE t_nl SET d = d + 1 WHERE d = 30 只修改 1 行:在可重复读下锁住了全部 5 行和最后的间隙,其它事务插入新行、修改 id 为 5 的行都要等待,效果相当于锁住了整张表;在提交读下只锁住 1 行,插入和修改都不受影响(MySQL 8.0 实测,见[附录 A6](#a6-没有索引时的加锁))。所以 UPDATE、DELETE 的条件列要建索引。
|
||||
|
||||
## 七、关系数据库设计理论
|
||||
|
||||
### 函数依赖
|
||||
@@ -670,6 +675,29 @@ SELECT * FROM t WHERE c BETWEEN 10 AND 20 FOR UPDATE
|
||||
把 idx_c 改为唯一索引后,c = 11 FOR UPDATE 只有 uk_c X,REC_NOT_GAP 11, 2 和主键上的记录锁,插入 c = 12 成功。
|
||||
```
|
||||
|
||||
### A6 没有索引时的加锁
|
||||
|
||||
```sql
|
||||
CREATE TABLE t_nl (id INT PRIMARY KEY, d INT);
|
||||
INSERT INTO t_nl VALUES (1, 10), (2, 20), (3, 30), (4, 40), (5, 50);
|
||||
```
|
||||
|
||||
A、B 两个会话的锁等待超时设为 1 秒。A 执行 UPDATE t_nl SET d = d + 1 WHERE d = 30 后不提交,查看 data_locks,B 再插入一行、修改另一行:
|
||||
|
||||
```text
|
||||
可重复读:A 的锁 7 个
|
||||
TABLE IX
|
||||
RECORD X 1 / 2 / 3 / 4 / 5 / supremum pseudo-record
|
||||
B: INSERT INTO t_nl VALUES (100, 1000) -> 1205 Lock wait timeout exceeded
|
||||
B: UPDATE t_nl SET d = 51 WHERE id = 5 -> 1205 Lock wait timeout exceeded
|
||||
|
||||
提交读:A 的锁 2 个
|
||||
TABLE IX
|
||||
RECORD X,REC_NOT_GAP 3
|
||||
B: INSERT INTO t_nl VALUES (100, 1000) -> OK
|
||||
B: UPDATE t_nl SET d = 51 WHERE id = 5 -> OK
|
||||
```
|
||||
|
||||
## 参考资料
|
||||
|
||||
- AbrahamSilberschatz, HenryF.Korth, S.Sudarshan, 等. 数据库系统概念 [M]. 机械工业出版社, 2006.
|
||||
@@ -678,6 +706,7 @@ SELECT * FROM t WHERE c BETWEEN 10 AND 20 FOR UPDATE
|
||||
- [The InnoDB Storage Engine](https://dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html)
|
||||
- [MySQL 8.0: Transaction Isolation Levels](https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html)
|
||||
- [MySQL 8.0: InnoDB Locking](https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html)
|
||||
- [MySQL 8.0: Locks Set by Different SQL Statements in InnoDB](https://dev.mysql.com/doc/refman/8.0/en/innodb-locks-set.html)
|
||||
- [MySQL 8.0: The data_locks Table](https://dev.mysql.com/doc/refman/8.0/en/performance-schema-data-locks-table.html)
|
||||
- [Transaction isolation levels](https://www.slideshare.net/ErnestoHernandezRodriguez/transaction-isolation-levels)
|
||||
- [Concurrency Control](https://web.archive.org/web/20260614192237/https://scanftree.com/dbms/2-phase-locking-protocol)
|
||||
|
||||
Reference in New Issue
Block a user