PostgreSQL学习笔记(四):MVCC、WAL、数据库事务
1. MVCC:多版本并发控制
1.1 事务的实现:
ACID | 实现 |
---|---|
原子性 | MVCC:Multiverion concurrency Control,多版本并发控制 |
一致性 | 约束(主键,外键等) |
隔离性 | MVCC |
持久性 | WAL:Write-Ahead Logging,前写日志 |
1.2 MVCC特性
- PostgreSQL的特色之一是它的并发控制机制,在维护一致性和完整性的同时,尽量避免读写的堵塞。
- 对于传统数据库,为了维护一致性和完整性,避免一个事务看到其它并发事务更新而到会不一致的数据,通常采用的是LOCK机制。这样付出的代价是,当锁请求无法被响应时,待处理的请求必须进入等候队列,甚至等待超时不被处理。
- 人们一般把基于锁的并发控制机制称成为悲观机制,而把MVCC机制称为乐观机制。
- 这是因为锁机制是一种预防性的,读会阻塞写,写也会阻塞读,当锁定粒度较大,时间较长时并发性能就不会太好;而MVCC是一种后验性的,读不阻塞写,写也不阻塞读,等到提交的时候才检验是否有冲突,由于没有锁,所以读写不会相互阻塞,从而大大提升了并发性能。
- MVCC通过避开传统数据库的LOCK机制,最大限度的减少锁竞争以允许合理的多用户环境中的性能。
- 恰当地使用MVCC总会提供比LOCK更好的性能。对于那些无法轻松接收MVCC行为的应用,PostgreSQL也提供了表和行级别的LOCK机制
1.3 MVCC多版本标记关键词
- XID:数据库的事务ID;
- xmin,xmax,行头部的XID信息,xmin表示插入这条记录的事务XID,xmax表示删除这条记录的事务XID;
- xid_snapshot:当前集群中的未结束事务;
- clog:事务提交状态日志;
每个表的每行数据(称为一个tuple),包含有4个隐藏字段,可直接访问;
xmin 在创建 (insert) 记录 (tuple) 时,记录此值为插入tuple的事务ID,xmax 默认值为0,在删除tuple时,记录此值,cmin和cmax标识在同一个事务中多个语句命令的序列值,从0开始,用于同一个事务中实现版本可见性判断。
1.4 数据的修改过程
数据的修改过程可简单描述为:
-
首先backend开启是一个事,获得一个事务号 XID;
-
在这个事务中对数据的任意修改,都被XID标记;
-
其他 backend 在扫描数据时,会看到被这个 XID 修改过的数据,根据当前的隔离级别,选择对这些
数据是否可见(默认的读已提交隔离级别看不到这些数据);
-
只有当此XID最后被标记成 commit(写 WAL commit log 和写cog)后,其他的 backend 才能看到这个XID修改的数据;
1.5 MVCC优劣势
-
MVCC优势
-
读操作不会阻塞写,写操作也不会阻塞读,提高了并发访问的性能;
-
事务的回滚可立即完成,无论事务进行了多少操作;
-
数据可以进行大量更新,不像MySQL和Innodb引擎和Oracle那样需要保证回滚段不会被耗尽;
-
-
MVCC劣势
-
事务ID个数有限制;
-
大量过期数据占用磁盘降低查询性能;
-
-
克服劣势的机制:VACUUM(用于维护数据库磁盘空间的工具,作用是删除那些已经标示为删除的数据并释放空间);
2. WAL:前写日志
1.1 WAL特性
- WAL全称是write ahead log,是PG中的online redo log;
- WAL是一种保证数据完整性的标准方法;
- 数据文件的改变必须先写入日志,即日志记录刷新到永久储存之后,才能被写;
- 遵循这个过程,不需要在每个事务提交时都刷新数据页到磁盘;
- 在宕机时可以用日志来恢复数据库;
- 任何没有应用到数据页上的改动都可以根据日志记录重做(即回滚恢复REDO);
1.2 WAL优势
-
当宕机发生时,Data Buffer的内容还没有全部写入到永久存储中,数据丢失,但是WAL Buffer的内容已写入磁盘,根据WAL日志的内容,可以恢复库丢失的内容
-
在提交时,仅把WAL刷新到了磁盘,而不是Data刷新:
-
从IO次数来说,WAL刷新是少量IO,Data刷新是大量IO,WAL刷新次数少得多;
-
从IO花销来说,WAL刷新是连续IO,Data刷新是随机IO,WAL刷新花销小得多;
-
-
WAL机制在保证事务持久性和数据完整性的同时,成功地提升了系统性能;
1.3 WAL的存放
存储在$PGDATA/pg_wal内,类似于00000001 00000002 000000D4的文件存储,依次为timeline、Logld、LogSeg;
每一个wal文件,大小为4G(16*256),由256个segment组成;
Segment由2048个Block组成,其大小为16M;
Block为WAL日志的最小单位,其大小8k;
1.4 WAL的归档
-
手动强制切换 select pg_switch_xlog();
-
wal日志写满后会自动归档
wal日志文件默认为16MB,这个值可以在编译 PostgreSQL 时通过参数“–with-wal-segsize”更改,编译则后不能修改。
-
参数archive_timeout
是postgresql.conf 文件中的参数,
如果设置archive timeout=60s,意思是,wal日志60s切换一次,同时会触发日志归档;
1.5 WAL应用场景
- 基于时间点恢复;
- 流复制;
- 逻辑复制;
- 误操作恢复;
3. 数据库事务
数据库事务:单个逻辑工作单元执行的一系列操作,对数据库进行读或写的一个操作序列操作,只有两种执行结果:提交或回滚。
3.1 隔离级别:
-
Read uncommitted(未提交的读取)
一个session的事务即使没有commit,也对其他session可见。这其实是dirty read(脏读)。
-
Read committed(提交的读取)
某session的事务只有commit了,才会对其他session可见;未commit的事务,对其他session不可见;所以脏读现象将不会再发生;
- Repeatable read(可重复读)
当前事务的所有语句只能看到这个事务中执行的第一个查询或者数据修改语句之前提交的行。
- Serializable(串行化)
当前事务的所有语句只能看到这个事务中执行的第一个查询或者数据修改语句之前提交的行。
这是最严格的隔离级别,事务串行化的执行。
隔离级别 | 脏读 | 幻读 | 不可重复读 |
---|---|---|---|
读未提交 (read uncommitted) | 可能 | 可能 | 可能 |
可重复读 (repeatable read) | 不可能 | 可能 | 可能 |
可重复读 (repeatable read) | 不可能 | 可能 | 不可能 |
可串行化(serializable) | 不可能 | 不可能 | 不可能 |
在PostgreSQL内部,实际上只有两种独立的隔离级别:
-
Read committed(默认的隔离级别):一个SELECT查询只能看到查询开始前已提交的数据
-
SERIALIZABLE:最严格的事务隔离。这个级别模拟串行的事务执行;这个级别的应用必须准备在串行化失败的时候重新启动事务
PostgreSQL只提供两种隔离级别的原因:
这是把标准的隔离级别与多版本并发控制架构映射相关联的唯一合理方法。
-- 查看默认事务级别
SHOW default_transaction_isolation;
-- 修改事务级别,仅对当前事务有效,不影响下一个事务
SET transaction isolation level serializable;
3.2 事务管理
常用的事务块管理语句:
常用的事务块管理语句:
- START TRANSACTION:此命令表示开始一个新的事务块;
- BEGIN:初始化一个事务块它和START TRANSACTION是一样的;
- COMMIT:提交事务;
- ROLLBACK:事务失败时执行回滚操作;
- SET TRANSACTION:设置当前事务的特性,对后面的事务没有影响;
务块管理语句: - START TRANSACTION:此命令表示开始一个新的事务块;
- BEGIN:初始化一个事务块它和START TRANSACTION是一样的;
- COMMIT:提交事务;
- ROLLBACK:事务失败时执行回滚操作;
- SET TRANSACTION:设置当前事务的特性,对后面的事务没有影响;
- SAVEPOINT:大事务通过创建保存点把操作过程分成几个部分,来避免失败回滚整个事务;