描述隔离级别
PostgreSQL 具有三个级别的事务隔离,可防止三种类型的并发冲突:脏读、不可重复读和幻读。
并发冲突的类型
脏读
当一个事务读取另一个事务正在编辑的数据的更新版本时,会发生脏读。 但是,此更新永远不会提交。
例如,在储蓄帐户上发生的事务将使帐户余额小于零。 在提交事务之前,会检查帐户余额,并回滚该事务,因为储蓄帐户的余额不得小于零。 同时,将运行一个报告以显示所有储蓄帐户的当前余额。 如果允许脏读,则返回负余额,即使它从未提交。
不可重复读
如果事务读取数据,另一个事务更新数据,而且初始事务再次读取数据并查看新的更新,则会发生不可重复读。
例如,连接 A 启动一个事务并读取订单的单位成本和单位数,即成本为 10,单位数为 3。 然后,连接 B 启动另一个事务并更新相同的订单,将单位成本设置为 12。 在与原始查询相同的事务中,连接 A 计算订单的总成本。 如果允许不可重复读,则连接 A 将返回单位成本 10、单位数 3 和总成本 36,这显然没有意义。
虚拟读取
如果一个事务读取数据,另一个事务向数据添加一行或多行,而且初始事务再次读取数据并查看新的更新,则会发生幻读。
例如,连接 A 启动一个事务并计算巴黎的每日发票总数。 巴黎所有 10 家商店的发票总计数为 1,100 张。 然后,连接 B 启动另一个事务,并在巴黎增加一家新的零售店,开业日这个新商店的发票数为 200 张。 在连接 A 事务中,系统现在计算商店数以计算每个商店的发票数。 连接 A 现在将 1,100 个事务除以 11 家商店,而不是运行发票计数查询时存在的原始 10 家商店。 此计算现在返回不正确的平均值 100,即便新商店有 200 张发票,连接 A 事务在其平均值计算中并未考虑该值。
隔离级别
Azure Database for PostgreSQL 具有三个事务隔离级别:已提交读、可重复读和可序列化。 未提交读在 Azure Database for PostgreSQL 中不可用。
隔离级别如何影响并发冲突:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读* | 可能 | 可能 | 可能 |
已提交读 | 不可用 | 可能 | 可能 |
可重复读 | 不可用 | 不可用 | 可能 |
可序列化 | 不可用 | 不可用 | 不可用 |
* 在 PostgreSQL 中不可用
读取已提交的内容是 Azure Database for PostgreSQL 中的默认隔离级别。 已提交读最适合大多数场景,因为它可以防止脏读取,同时提供良好的性能。 可以有不可重复读和幻读,但这些条件仅在有多个 SELECT 语句同时查询同一数据时才会发生。
可重复读与已提交读不同,因为即使一个事务在执行事务的两个 SELECT 语句之间更新了行,另一个事务中的多个 SELECT 语句也会看到相同的结果。 如果另一个事务插入了新行,则这些行不会出现在第二个 SELECT 语句的结果中。
可序列化隔离级别提供最高级别的事务隔离,其执行方式就像不同的事务在串行中运行一样,即一个接一个地运行。 可序列化隔离级别的缺点是,如果一个事务正在执行更新,则其他事务可能会阻止它。 可序列化事务必须等到正在阻塞的事务完成,这将影响性能。
可序列化事务也无法对在可序列化事务期间其他事务所修改的任何行进行更改。 如果发生这种形式的冲突,则会返回一条错误消息,因此,在使用序列化事务时,请务必将重试逻辑内置到应用程序中。
要更新事务隔离级别,请在事务中使用 TRANSACTION ISOLATION LEVEL 命令。
例如:
BEGIN TRANSACTION
TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT * FROM humanresources.department
COMMIT;