不涉及具体数据库实现
不涉及具体数据库实现
不涉及具体数据库实现
1. 数据库事务里的锁
1.1 写锁(排它锁)
执行到 写 操作的 sql 语句时
- 加写锁
- 写数据
- 事务结束 释放写锁
这里很关键的一点是在 事务结束 才释放写锁
注意: 被写锁锁定的数据,不可以被施加读锁,但不是不可以读,在不施加读锁的隔离级别中,就可以不加读锁,直接读数据。
1.2 读锁(共享锁)
执行到 读 操作的 sql 语句时
- 加读锁
- 读数据
- 根据不同的隔离级别,释放锁的时机不同
隔离级别 | 释放读锁的时机 |
---|---|
串行化 | 事务结束 |
可重复读 | 事务结束 |
读已提交 | 查询语句执行完就释放 |
读未提交 | 完全不加读锁 |
注意:
- 多个事务可以对同一数据施加读锁,被读锁锁定的数据不能再施加写锁。
- 读未提交 隔离级别中的,不加读锁,不是不能读数据,是读数据之前不用加读锁了。
1.3 范围锁
执行到 范围 操作的 sql 语句时
- 加范围锁,排斥此范围内的其他事务的 写 操作
- 本事务读写数据都可
- 事务结束释放锁
只在 串行化 中使用
注意: 范围锁是针对一块区域的锁,不是一堆锁的集合,区别在于范围锁 不允许在其他事务在此范围内插入,删除数据 ,影响的是数据量,而一堆锁的集合是可以在被锁数据的中间插入,删除数据的。
2. 事务隔离级别
2.1 串行化
所有的锁能利用都利用上,并发操作变成串行操作,最高隔离性,性能最差
2.2 可重复读
锁类型 | 释放时机 |
---|---|
写锁 | 事务结束 |
读锁 | 事务结束 |
范围锁 | 不施加 |
存在问题
问题 | 原因 |
---|---|
幻读 | 不施加范围锁 |
因为不施加 范围锁,所以在此隔离级别下,一个事务中执行两次对于范围数据的操作时,就可能会读到另外一个事务里在此范围插入和删除的数据,造成本事务两次范围读写到的 数据量 不同。
2.3 读已提交
锁类型 | 释放时机 |
---|---|
写锁 | 事务结束 |
读锁 | 读操作以后立即释放 |
范围锁 | 不施加 |
存在问题
问题 | 原因 |
---|---|
幻读 | 不施加范围锁 |
不可重复读 | 读锁在读操作完之后立即释放 |
首先因为不施加 范围锁,会存在和可重复读隔离级别中一样的 幻读 问题,因为 读完以后立即释放读锁,在其他事务中就可以对此数据加写锁,进行修改了,那在此事务中 再次 读取 此条 记录时,可能读到的是其他事务更改后的值,这就是 不可重复读。
2.4 读未提交
锁类型 | 释放时机 |
---|---|
写锁 | 事务结束 |
读锁 | 不施加 |
范围锁 | 不施加 |
存在问题
问题 | 原因 |
---|---|
幻读 | 不施加范围锁 |
不可重复读 | 不施加读锁 |
脏读 | 不施加读锁 |
不再赘述 幻读 和 不可重复读 的造成原因。一个事务读取数据时不再施加读锁的意思,不是不能读数据,而是在读数据之前不需要加读锁,即便此数据有其他事务写锁锁定着,但因为不需要加读锁,也就不用顾忌那写锁,数据依旧可以被读取,那可能读到另一事务未提交的数据,这就是 脏读。