PostgreSQL之隔离级别
概述
谈到事务,就离不开它的四个属性,三个问题,四个隔离级别以及如何实现对应的隔离级别。
四个属性(ACID)
- 原子性
Atomicity,主要是指一个事务要么做,要么不做,没有二义性。其最终对数据库的影响是确定的,要么全部成功,要么全部失败。
- 一致性
Consistency,主要是指数据库在事务开始之前是处于一致性状态的,符合其所有约束和约定的;在事务结束之后同样要处于一致性状态,符合共所有约束和约定的,不能够受事务成功或者失败的影响。
- 隔离性
Isolation,主要是指多个事务同时进行时互不干扰,互不影响。再通俗点来讲,2个事务的并发其效果,要么等同于事务1先完成,事务2再完成;要么等同于事务2先完成,事务1再完成。
- 持久性
Durability,主要是指事务一旦提交之后,它对数据库的作用就是永久性的、不再消失的;即使数据库后续再发生各种故障,这种作用都应该保证是存在的、不会丢失的。
三个问题
重点来说明下事务的隔离性,当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性,在介绍数据库提供的各种隔离级别之前,我们先看看如果不考虑事务的隔离性,会发生的几种问题:
- 脏读
脏读是指在一个事务处理过程里读取了另一个未提交的事务中写的数据。
- 不可重复读
是指一个事务重新读取前面读取过的数据,发现该数据已经被另一个已提交的事务修改过(数值变了)
- 幻读
一个事务重新执行一个查询,返回一个符合查询条件的数据集,发现这些数据因为最近其他提交的事务而发生了改变(记录的数目多了)。
需要注意的是,不可重复读主要强调的是“相同条件的数据,再次读取出来之后数据不一样了;幻读主要强调的是”相同条件的查询结果,两次读取出来的记录数目不一样。
四种隔离级别
- 读未提交
允许一个事务读取到另一个事务已修改的、未提交的数据。
在 READ UNCOMMITTED 级别运行的事务,不会发出共享锁来防止其他事务修改当前事务读取的数据。READ UNCOMMITTED 事务也不会被排他锁阻塞,排他锁会禁止当前事务读取其他事务已修改但尚未提交的行。
1. 很明显地,读未提交是会有脏读的问题
2. 如果事务T修改了符合事务T1查询条件的某个数据,那么事务T1再次使用该条件进行查询的时候,就会发生不可重复读的问题;
3. 如果事务T删除了符合事务T1查询条件的某个数据,那么事务T1再次使用该条件进行查询的时候,就会发现查询结果少了一条记录,发生了幻读问题;
4. 如果事务T新增了某条记录,而这条记录正好符合事务T1的查询条件,那么事务T1再次使用该条件进行查询的时候,就会发现查询结果多了一条记录,发生了幻读问题;
- 读已提交
指定下列要求:
1. 指定语句不能读取已由其他事务修改但尚未提交的数据。
这样可以避免脏读。其他事务可以在当前事务的各个语句之间更改数据,从而产生不可重复读取和幻读。
- 可重复读
指定下列要求:
1. 指定语句不能读取已由其他事务修改但尚未提交的行,
2. 并且指定,其他任何事务都不能在当前事务完成之前修改由当前事务读取的数据。
这样可以避免脏读和不可重复读,但是不能阻止幻读。因为事务可能插入符合其他事务查询条件的记录。
- 串行化
指定下列要求:
1. 语句不能读取已由其他事务修改但尚未提交的数据。
2. 任何其他事务都不能在当前事务完成之前修改由当前事务读取的数据。
3. 在当前事务完成之前,其他事务不能使用当前事务中任何语句读取的键值插入新行。
这四个隔离级别与上面三个问题的对应关系如下表
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 可能 | 可能 | 可能 |
读已提交 | 不可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 可能 |
串行化 | 不可能 | 不可能 | 不可能 |
在PostgreSQL中,用户可以请求的隔离级别可以是以上的任意一种,但是内部的实现其实只有2种隔离级别,分别为读已提交和串行化。如果选择了读未提交的话,是按照读已提交的隔离级别进行的;如果选择了可重复读的话,是按照串行化的隔离级别进行的。
实现
- 读已提交的实现
这是PostgreSQL里的默认隔离级别,当一人事务运行在这个隔离级别是,一个查询只能看到查询开始之前提交的数据、而永远无法看到未提交的数据或是在运行过程中其他并发事务提交所做的修改。如果两个事务对同一个元组进行更新,第二个更新事务将等待第一个更新事务的提交或回滚。
如果第一个更新回滚,那么它的作用将会忽略,而第二个更新将继续更新最初发现的元组。
如果第一个更新提交,那么第二个更新需要重新计算查询条件,如果元组不符合条件,则忽略即可;如果元组仍然符合条件,那么第二个更新继续其操作,从该元组的已更新版本开始。
在这个隔离级别下,快照信息是在每条SQL语句执行之前获取的。
- 可串行化
它提供最严格的事务隔离。这个级别模拟串行的事务执行,就好像事务一个接着一个地串行执行。如果两个事务对同一个元组进行更新,可串行化的事务将等待第一个更新事务的提交或回滚。
如果第一个更新回滚了,它的影响将忽略,这个可串行化的事务就可以在该元组上继续完成其更新操作。
如果第一个更新提交了,那么可串行化事务将回滚,从头开始重新执行整个事务。
在这个隔离级别一,快照信息是在事务一开始(START TRANSACTION)执行之前获取的。
参考
- 事务隔离级别
- PostgreSQL数据库内核分析