本文是《postgresql实战》的读书笔记,感兴趣可以参考本书对应章节
一、事务与并发控制
事务在关系型数据库中十分重要;而并发则带来了更大的吞吐量、资源利用率 和 更好的性能。但是当多个事务并发执行时,即使每个单独的事务都正确执行,数据库一致性也可能被破坏。为了控制并发事务之间的相互影响,解决并发可能带来的资源争用及数据不一致问题,数据库引入了基于锁的并发控制机制(Lock-Based Concurrency Control)和基于多版本的并发控制机制 MVCC(Multi-Version Concurrency Control)
1.1 事务的基本概念
事务是数据库系统执行过程中最小的逻辑单位。事务有四个重要的特性:原子性、一致性、隔离性 和 持久性。
名称 | 描述 |
---|---|
原子性(Atomicity) | 一个事务的所有操作,要么都执行,要么都不执行 |
一致性(Consistency) | 执行事务时保证数库从一个一致的状态变更到另外一个一致的状态 |
隔离性(Isolation) | 确保事务与事务并发执行时,不会彼此影响 |
持久性(Durability) | 一个事务完成以后,即使数据库发生故障,它对数据库的改变应该保存到数据库中 |
事务一致性由数据库的主键、外键这类约束保证,持久性由预写日志(WAL)和数据库管理系统的恢复子系统保证,隔离性由事务管理器和MVCC来控制。
1.2 并发引发的现象
事务并发可能引起一些现象:脏读(Dirty )、不可重复读(Non-repeatable read)、幻读(Phantom Read) 和 序列化异常(Serialization Anomaly
- 脏读:当第一个事务读取了第二个事务已经修改但未提交的数据,当第二个事务不提交进行回滚(rollback)后,第一个事务读到的数据就是脏数据,这就是脏读。
- 不可重复读:当第一个事务读取数据后,第二个事务修改第一个事务读取的数据,第一个事务再一次读取时发现第一个次和第二次读取的不一样。这种现象称为不可重复读。
- 幻读: 指一个事务的两次查询的结果记录数不一致。幻读可以认为是受INSERT 和 DELETE 影响的不可重复读的一种特殊场景。
1.3 ANSI SQL 标准的事务隔离级别
为了避免事务与事务之间并发造成的副作用,串行化地逐个执行事务是最简单的,但是这种方式会大大降低系统吞吐率,降低资源的利用率。为此指定了四种隔离级别,每个级别都包含了一些特定的规则,用来限定事务内外那些改变对其他事务是可见的,那些事不可见的。也就是允许不允许出现脏读、不可重复读、幻读的现象。通过这些事务级别规定了一个事务必须与其他事务所进行的资源或数据更改相隔离的程度。
隔离级别 | 描述 |
---|---|
Read Uncommitted(读未提交) | 脏读现象,这一隔离级别很少应用 |
Read Committed(读已提交) | postgresql的默认隔离级别,满足了一个事务只能看见已经提交事务对关联数据所做的改变的隔离要求 |
Repeatable Read (可重复读) | 会确保同一事务的多个实例在并发读取数据时,会看到同样的数据行 |
Serializable(可序列化 | 最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。它是在每个读的数据行上加上共享锁。在这个级别下,可能导致大量的超时现象和锁竞争 |
隔离级别越高,越能保证数据的完整性和一致性,但降低了并发性。隔离级别越低并发性越好,但增加了副作用。一般推荐使用读已提交(read committed)。尽管会导致不可重复读和、幻读和丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
1.4 PostgreSQL的事务隔离级别
postgresql内部将 Read uncommitted
与Read Committed
设计成一样。也就是postgresql数据库中不会出现脏读。(可能会出现不可重复读和幻读)。而postgresql的Repeatable Read
实现不允许幻读。 这种隔离级别与其他数据库定义隔离级别稍有不同。
PostgreSQL 还引入了一个新的数据冲突问题:序列化异常。序列化异常时指成提交的一组事务的执行结果与这些事务按照串行执行方式的执行结果不一致。 Serializable 与 Repeatable Read 在PostgreSQL中基本一样,除了Serialization 不允许序列化异常。
一、ANSI SQL 标准定义的事务隔离级别与读现象的关系
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read Uncommitted | 可能 | 可能 | 可能 |
Read Committed | 不可能 | 可能 | 可能 |
Repeatable Read | 不可能 | 不可能 | 可能 |
Serializable | 不可能 | 不可能 | 不可能 |
二、PostgreSQL 的事务隔离级别与读现象的关系
隔离级别 | 脏读 | 不可重复读 | 幻读 | 序列化异常 |
---|---|---|---|---|
Read Uncommitted | 不可能 | 可能 | 可能 | 可能 |
Read Committed | 不可能 | 可能 | 可能 | 可能 |
Repeatable Read | 不可能 | 不可能 | 不可能 | 可能 |
Serializable | 不可能 | 不可能 | 不可能 | 不可能 |
1.5 查看和设置数据库的事务隔离级别
-- 1. 查看全局事务隔离级别
--方法一:
SELECT name,setting FROM pg_settings WHERE name = 'default_transaction_isolation';
/*
name setting
default_transaction_isolation read committed
*/
--方法二:
SELECT current_setting('default_transaction_isolation');
-- read committed
-- 2. 修改全局的事务隔离级别
--方法一:通过postgresql.conf 文件中的default_transaction_isolation参数修改全局事务隔离级别。
--方法二: 通过ALTER SYSTEM 命令修改全局事务隔离级别
ALTER SYSTEM SET default_transaction_isolation TO 'REPEATABLE READ';
SELECT pg_reload_conf();
-- 查询更改情况
SELECT current_setting('default_transaction_isolation');
-- repeatable read
--3.查看当前会话的事务隔离级别
--方式一:
SHOW transaction_isolation;
--方式二:
SELECT current_setting('transaction_isolation');
--4.设置当前会话的事务隔离级别 characteristics
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 查看情况
SHOW transaction_isolation;
-- read uncommitted
--5. 设置当前事务的事务隔离级别
--方式一
START TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION
...
...
END;
COMMIT
--方式二
BEGIN ISOLATION LEVEL READ UNCOMMITTED READ WRITE;
...
...
END/COMMIT/ROLLBACK;
二、PostgreSQL的并发控制
并发控制模型有基于锁的并发控制,基于多版本的并发控制。而封锁、时间戳、乐观并发控制和悲观并发控制是并发控制采用的主要技术手段
2.1 基于锁的并发控制
为了解决并发问题,数据库引入了“锁”的概念。基本的锁类型有两种:排它锁和共享锁
- 排它锁:被加锁的对象只能被持有锁的事务读取和修改,其他事务无法再改对象上加锁,也不能读取和修改该对象。
- 共享锁:被加锁的对象可以被持有该锁的事务读取,但不能被修改,其他事务可以在该对象上加共享锁。
2.2 基于多版本的并发控制
基于多个旧值版本的并发控制即是MVCC。一般基于锁的并发控制机制称为悲观机制,而把MVCC机制称为乐观机制。锁机制是一种预防机制,读会阻塞写,写会阻塞读。当封锁粒度大时较大、时间较长时并发性能会严重下降。而MVCC是一种后验性,读不阻塞写、写不阻塞读,等到提交的时候才校验是否有冲突,这样避免大粒度和长时间的锁定。
MVCC通过保存数据在某一个时间点的快照,并控制元组的可见性来实现。
一个事务无论运行多长时间,在同一个事务里都能够看到一致的数据。根据事务开始的时间不同,在同一个时刻不同事务看到的相同表的数据可能是不同的。