前面介绍了 PostgreSQL 常用函数、锁操作、执行计划、视图与触发器、存储过程、索引、分区分表等相关的知识点,今天我将详细的为大家介绍 PostgreSQL 事务与并发控制相关知识,希望大家能够从中收获多多!如有帮助,请点在看、转发支持一波!!!
当多个事务并发执行时, 即使每个单独的事务都正确执行, 数据库的一致性也可能被破坏.。
为了控制 并发事务 之间的相互影响, 解决并发可能带来的数据不一致问题, 数据库的并发控制系统 引入了 基于锁的并发控制(Lock-Based Concurrency Control) 和 基于多版本的并发控制机制 MVCC (Mult-Version。
事务
事务 是数据库系统执行过程中最小的逻辑单位。
当事务被提交时, 数据库管理系统 要确保一个事务中的 所有操作都成功完成, 并在数据库中永久保存; 如果一个事务中的一部分没有成功, 则系统会把数据库回滚到操作执行之前的状态。
事务有 4 个特性:
-
原子性(Atomicity): 一个事务的所有操作, 要么全部执行, 要么全部不执行。
-
一致性(Consistency): 保证数据库从一个正确的状态(满足约束)到另一个正确的状态。
-
隔离性(Isolation): 事务并发执行时, 可能会交叉执行, 从而导致不一致的情况发生. 确保事务并发执行时, 每个事务都感觉不到有其他事务在并发的执行。
-
持久性(Durability): 一个事务完成后, 它对数据库的改变应该永久保存在数据库中。
这 4 个特性也称之为 ACID.
-
事务一致性 由主键, 外键这类约束保证。
-
持久性 由预写日志(WAL) 和数据库管理系统的恢复子系统保证。
-
原子性和隔离性 由 事务管理器 和 MVCC 来控制。
事务并发引发的问题
如果所有的事务都按照顺序执行, 那么执行时间就没有重叠交错, 也就不会有并发问题。
PostgreSQL 把 事务并发 导致的问题 总结为:
-
脏读(Dirty read), 事务A 读取了 事务B 已经修改但是还没有提交的数据。
-
不可重复读(Non-repeatable read), 事务A 读取了数据X; 然后 事务B 修改了数据X 并提交; 然后事务A 再次读取数据X. 对于事务A来说, 两次读取结果不一致. 这种现象就是 不可重复读。
-
幻读(Phantom read), 一个事务的两次执行相同的查询, 结果集数目不一致. 幻读 可以 认为是 受
INSERT
和DELETE
影响 不可重复读 的特例。 -
序列化异常(Serialization anomaly), 在可重复读情况下, 可能会出现序列化异常. 比如
X=1
事务A 执行X++
操作; 在事务A 提交之前, 事务B 修改X=10
并提交成功; 由于事务A 是可重复读的, 事务A 看到的数据还是X=1
. 这就发生了序列化异常: 先执行事务A 和 先执行事务B 的结果是不一样的。 -
更多关于 PostgreSQL 系列的学习文章,请参阅:PostgreSQL 数据库,本系列持续更新中。
ANSI SQL 标准的事务隔离级别
为了避免 事务与事务之间 并发执行 引发的副作用, 最简单的方法是 串行地 执行事务, 但是 串行化 会大幅降低系统吞吐量, 降低系统资源利用率。
为此, ANSI(American National Standards Institute, 美国国家标准学会) SQL 标准定义了 4 类事务隔离级别:
-
读未提交(Read Uncommitted): 所有事务都可以看到其他未提交事务的执行结果. 可以看到 读未提交 允许 脏读 发生, 脏读是非常危险的, 查询结果非常不可控, 所以 读未提交 事务隔离级别 很少实际应用。
-
读已提交(Read Committed): 这是 PostgreSQL 默认的隔离级别, 它满足了 一个事务 只能看到 已提交事务 对关联数据所做的改变。
-
可重复读(Repeatable Read): 确保同一个事务在看到的数据内容是一致的。
-
可序列化(Serializable): 最高的隔离级别, 通过