- 一、事务处理机制
- 1. 事务的4个属性及相应的实现技术
- 2. MVCC简介
- 3. WAL简介
- 4. 事务管理常用语句
- 二、数据库隔离级别
- 1. 并行机制下存在的问题
- 2. 各隔离级别
- 2.1. 可读取未确认(Read uncommitted)
- 2.2. 可读取确认(Read committed)
- 2.3. 可重复读(Repeatable read)
- 2.4. 可串行化(Serializable)
- 3. 各级别解决问题
一、事务处理机制
事务是PostgreSQL中的基本工作单元,它是用户定义的一个数据库操作程序,这些操作要么全做、要么全部做,是一个不可分割的工作单位,事务的恢复及检查点保证了系统的完整和可恢复。
1. 事务的4个属性及相应的实现技术
2. MVCC简介
MVCC(Multiversion Concurrency Control) 即多版本并发控制,它可以避免读写事务之间的互相阻塞,相比通常的封锁技术可极大的提高业务的并发性能。
实现原理可简单概括如下:
- 数据文件中存放同一逻辑行的多个行版本(称为Tuple);
- 每个行版本的头部记录创建以及删除该行版本的事务的ID(分别称为 xmin 和 xmax );
- 每个事务的状态(运行中,中止或提交)记录在clog 文件中;
- 根据上面的数据并运用一定的规则每个事务只会看到一个特定的行版本。
通过 MVCC 读写事务可以分别在不同的行版本上工作,因此能够在互不冲突的情况下并发执行。
3. WAL简介
当系统意外宕机后,恢复时需要回退未完成事务所做的更改并确保已提交事务所作的更改均已生效。通过前面提到的 MVCC 很容易做到的第一点,只要把所有clog 文件中记录的所有“运行中”的事务的状态置为“中止”即可,这些事务在宕机时都没有结束。对于第二点,必须确保事务提交时修改已真正写入到永久存储中。但是直接刷新事务修改后的数据到磁盘是很费时的,为解决这个问题于是引入了 WAL(Write-Ahead Log) 。
WAL 的基本原理如下:
- 更新数据页前先将更新内容记入WAL日志;
- 异步刷新数据Buffer的脏页和WAL Buffer到磁盘;
- Buffer管理器确保绝不会先于对应的WAL记录刷新脏数据到磁盘;
- 事务提交时,将WAL日志同步刷新到磁盘;
- Checkpoint 发生时,将数据Buffer的所有脏页刷新到磁盘。
注:检查点是一个事件,当该事件发生时,数据库缓存中的脏缓存块将被写入数据文件,同时对数据库控制文件进行更新,其实就是数据内外存同步。
4. 事务管理常用语句
- START TRANSACTION:此命令表示开始一个新的事务块;
- BEGIN:此命令表示初始化一个事务块,在BEGIN命令后的语句都将在一个事务里面执行,直到出现COMMIT或ROLLBACK。此命令和START TRANSACTION等价;
- COMMIT:此命令表示提交事务;
- ROLLBACK:此命令表示事务失败时执行回滚操作;
- SET TRANSACTION:设置当前事务的特性,对后面的事务没有影响。
ROLLBACK TO是唯一的途径来重新控制一个由于错误被系统置为中断状态的事务块,而不是完全回滚它并重新启动。
二、数据库隔离级别
1. 并行机制下存在的问题
数据库事务具有隔离性,一个事务的运行不会影响另一个事务,即并行机制,但会带来如下问题:
补充:
- 不可重复读的重点是修改:
同样的条件,已读取过的数据,再次读取后值不一样 - 幻读的重点在于新增:
同样的条件,第1次和第2次读出来的记录数不一样,在于新增条目,仅锁定已存在条目,新条目无法锁定
2. 各隔离级别
2.1. 可读取未确认(Read uncommitted)
写事务阻止其他写事务,避免了更新遗失。但是没有阻止其他读事务。
存在的问题:脏读。
即读取到不正确的数据,因为另一个事务可能还没提交最终数据,这个读事务就读取了中途的数据,这个数据可能是不正确的。
解决办法就是下面的“可读取确认”。
2.2. 可读取确认(Read committed)
写事务会阻止其他读写事务。读事务不会阻止其他任何事务。
大部分数据库使用的默认隔离级别,兼顾速度和正确性。
存在的问题:不可重复读。
即在一次事务之间,进行了两次读取,但是结果不一样,可能第一次id为1的人叫“李三”,第二次读id为1的人就叫了“李四”。因为读取操作不会阻止其他事务。
解决办法就是下面的“可重复读”。
2.3. 可重复读(Repeatable read)
读事务会阻止其他写事务,但是不会阻止其他读事务。
存在的问题:幻读。
可重复读阻止的写事务包括update和delete(只给存在的表加上了锁),但是不包括insert(新行不存在,所以没有办法加锁),所以一个事务第一次读取可能读取到了10条记录,但是第二次可能读取到11条,这就是幻读。
解决办法就是下面的“串行化”。
2.4. 可串行化(Serializable)
读加共享锁,写加排他锁。这样读取事务可以并发,但是读写,写写事务之间都是互斥的,基本上就是一个个执行事务,所以叫串行化。