1.PG事务隔离级别

  在数据库中,并发的操作进行读写数据时,则会遇到脏读、不可重复读、幻读、串行化异常等问题。

  数据库事务的特性: 

  1. 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行;
  2. 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束;
  3. 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行;
  4. 持久性(Durability):一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。
  数据库中存在4种事务隔离级别,读未提交、读已提交、可重复读和可序列化。
  PostgreSQL在9.3版本后,已经支持了这四种标准的事务隔离级别。可以通过SET TRANSACTION命令设置当前事务的隔离级别(Transaction Isolation)。
PostgreSQL事务隔离级别和对应数据库问题的关系
隔离级别 脏读  不可重复读 幻读 串行化异常
读未提交 允许,但pg不支持 可能 可能 可能
读已提交 不可能 可能 可能 可能
可重复读 不可能 不可能 允许,但pg不支持 可能
可序列化 不可能 不可能 不可能 不可能
  从上表中可以看到在PostgreSQL中,“读未提交”隔离级别,不允许脏读;“可重复读”隔离级别,不允许幻读。

  


2.MVCC(Multi-Version Concurrency Control)多版本并发控制

  在PostgreSQL中,MVCC的实现方法是:当插入或者更新一行数据时,旧数据不删除,而是插入一行新数据;通过使用事务id进行标记,把旧数据标记为过期,并保留在数据库直到垃圾收集器回收掉。

MVCC优势

  • 使用MVCC,读操作不会阻塞写,写操作也不会阻塞读,提高了并发访问下的性能
  • 事务的回滚可立即完成,无论事务进行了多少操作
  • 数据可以进行大量更新,不段像MySQL和Innodb引擎和Oracle那样需要保证回滚段不会被耗尽

MVCC缺点

  • 事务ID个数有限制事务ID由32位数保存,而事务ID递增,当事务ID用完时,会出现wraparound问题。
  • 大量过期数据占用磁盘并降低查询性能

多版本元组存储结构

  在PostgreSQL中,使用元组头部信息(HeapTupleHeaderData)的字段来标示元组的版本号,元组头部信息的结构如下:

struct HeapTupleHeaderData
{
union
{
HeapTupleFields t_heap;
DatumTupleFields t_datum;
} t_choice; ItemPointerData t_ctid; /* current TID of this or newer tuple (or a
* speculative insertion token) */ /* Fields below here must match MinimalTupleData! */ uint16 t_infomask2; /* number of attributes + various flags */ uint16 t_infomask; /* various flag bits, see below */ uint8 t_hoff; /* sizeof header incl. bitmap, padding */ /* ^ - 23 bytes - ^ */ bits8 t_bits[FLEXIBLE_ARRAY_MEMBER]; /* bitmap of NULLs */ /* MORE DATA FOLLOWS AT END OF STRUCT */
};
typedef struct HeapTupleFields
{
TransactionId t_xmin; /* inserting xact ID */
TransactionId t_xmax; /* deleting or locking xact ID */ union
{
CommandId t_cid; /* inserting or deleting command ID, or both */
TransactionId t_xvac; /* VACUUM FULL xact ID */
} t_field3;
} HeapTupleFields;
  • t_xmin 存储的是产生这个元组的事务ID,可能是insert或者update语句
  • t_xmax 存储的是删除或者锁定这个元组的事务ID
  • t_cid 包含cmin和cmax两个字段,分别存储创建这个元组的Command ID和删除这个元组的Command ID
  • t_xvac 存储的是VACUUM FULL 命令的事务ID

  数据库中每一个事务中的查询仅能看到:该事务启动之前已经提交的事务所作出的数据更改;该事务之前启动的事务和该事务之后启动的事务修改的数据不可见。

  Postgres中元组版本对一个事务可见,其事务TransactionID要满足以下条件:1. t_xmin<TransactionID;2. t_xmax==0 || t_xmax>TransactionID 。


3.实践

  通过实际操作,观察元组头部信息中的t_xmin和t_xmax的变化。

  • 开启事务,查看事务id,创建表并插入一条记录;再查看该记录的t_xmin。
 [root@localhost ~]# su pguser
[pguser@localhost root]$ psql -d test
test=# create table t2(id int);
CREATE TABLE
test=# begin;
BEGIN
test=# select txid_current();
txid_current
-------------- ( row) test=# insert into t2(id) values();
INSERT
test=# commit;
COMMIT
test=# select ctid, xmin,xmax,cmin,cmax,id from t2;
ctid | xmin | xmax | cmin | cmax | id
-------+------+------+------+------+----
(,) | | | | |
( row)
  • 先开启事务A,查看事务id。当事务B更新数据后,查看表数据信息,观察事务B更新数据前后的行数据信息。该行数据中t_xmax=763(事务Bid),表明该行被标记为过期,但是对该事务是可见的。
 test=# -- 启动事务A
test=# begin;
BEGIN
test=# select txid_current();
txid_current
-------------- ( row) test=# select ctid, xmin,xmax,cmin,cmax,id from t2;
ctid | xmin | xmax | cmin | cmax | id
-------+------+------+------+------+----
(,) | | | | |
( row)
test= -- 事务B update 后
test=# select ctid, xmin,xmax,cmin,cmax,id from t2;
ctid | xmin | xmax | cmin | cmax | id
-------+------+------+------+------+----
(,) | | | | |
( row)
  • 开启事务A后,再开启事务B,事务A的id比事务B的id小。在事务B中更新表数据,观察当前表的行数据信息,t_xmix=763。
 test=# -- 启动事务B
test=# begin;
BEGIN
test=# select txid_current();
txid_current
-------------- ( row) test=# select ctid, xmin,xmax,cmin,cmax,id from t2;
ctid | xmin | xmax | cmin | cmax | id
-------+------+------+------+------+----
(,) | | | | |
( row) test=# update t2 set id= where id=;
UPDATE
test=# select ctid, xmin,xmax,cmin,cmax,id from t2;
ctid | xmin | xmax | cmin | cmax | id
-------+------+------+------+------+----
(,) | | | | |
( row)
  • 通过pageinspect的函数,查看page信息,发现表t2存在两条数据记录,与事务A、事务B的数据相对于。当事务A和事务B提交后,进行手动Vacuum清理后,只剩下一条最新的记录。
 test=# select * from heap_page_items(get_raw_page('t2',));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid | t_data
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------+------------
| | | | | | | (,) | | | | | | \x01000000
| | | | | | | (,) | | | | | | \x02000000
( rows) test=# -- commit 事务A 事务B
test=# vacuum full;
VACUUM
test=# select * from heap_page_items(get_raw_page('t2',));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid | t_data
----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------+------------
| | | | | | | (,) | | | | | | \x02000000
( row)

3.总结

  PostgreSQL引入了MVCC多版本机制,保证了事务的原子性和隔离性,实现不同的事务隔离级别。

  PostgreSQL的MVCC实现方法有利有弊。从上面可以看到,多版本控制,会导致旧数据没有删除,最直接的问题就是导致表膨胀。PostgreSQL为了解决这个问题引入了AutoVacuum自动清理辅助进程,定时清理MVCC的过期数据。

PostgreSQL学习(2)-- mvcc的更多相关文章

  1. PostgreSQL学习手册

    事实上之前有很长一段时间都在纠结是否有必要好好学习它,但是始终都没有一个很好的理由说服自己.甚至是直到这个项目最终决定选用PostgreSQL 时,我都没有真正意识到学习它的价值,当时只是想反正和其它 ...

  2. PostgreSQL学习手册(目录)

    原文地址:http://www.cnblogs.com/stephen-liu74/archive/2012/06/08/2315679.html 事实上之前有很长一段时间都在纠结是否有必要好好学习它 ...

  3. PostgreSQL学习----命令或问题小结

    PostgreSQL学习--命令或问题小结 小序 接触PostgreSQL也有好长时间了,知识不总结梳理,似乎总不是自己的,继续努力吧少年!以此记录我的软件工艺之路! 1,查看模式搜索路径 SHOW ...

  4. PostgreSQL学习----模式schema

    PostgreSQL学习---模式schema 小序 接触PostgreSQL也有好长时间了,知识不总结梳理,似乎总不是自己的,继续努力吧少年!以此记录我的软件工艺之路! 模式(Schema) 一个 ...

  5. PostgreSQL学习资料

    我的PostgreSQL学习笔记:http://note.youdao.com/share/?id=2e882717fc3850be9af503fcc0dfe7d0&type=notebook ...

  6. PostgreSQL学习手册(五) 函数和操作符

    PostgreSQL学习手册(五) 函数和操作符 一.逻辑操作符:    常用的逻辑操作符有:AND.OR和NOT.其语义与其它编程语言中的逻辑操作符完全相同. 二.比较操作符:    下面是Post ...

  7. postgresql学习记录1

    数据库9.3.5,系统fedora20,不同系统操作略有不同. 使用yum 命令安装即可:sudo yum install postgresql,postgresql-server 安装完毕后系统中会 ...

  8. 2015第15周日PostgreSQL学习

    英文版官网地址:http://www.postgresql.org/ 上面显示的最新版本信息是PostgreSQL 9.4.1, 9.3.6, 9.2.10, 9.1.15 & 9.0.19 ...

  9. PostgreSQL学习之【用户权限管理】说明

    背景 最近在学习PostgreSQL,看了用户权限管理文档,涉及到的知识点比较多,顺便写篇文章进行整理并不定时更新,也方便自己后续进行查阅. 说明 注意:创建好用户(角色)之后需要连接的话,还需要修改 ...

随机推荐

  1. 教程 | Linux常用命令大全

    Linux常用命令 目录操作命令 ls 命令名称:ls 命令英文原意:list 命令所在路径:/bin/ls 执行权限:所有用户 功能描述:显示目录文件 ls (显示当前目录下文件) ls 目录名 ( ...

  2. Count the string (KMP+DP)

    题目链接 #include <bits/stdc++.h> using namespace std; typedef long long ll; inline int read() { , ...

  3. 目前最全的浏览器/CSS选择器兼容性总结(2009-8-10更新)

    2009年2月24日,Safari 4.0 beta版正式发布,Safari从它的3.2版本开始就已经支持所有的CSS选择器(包括最新的CSS3).不过为了方便大家的工作,下面提供了最新版本的CSS选 ...

  4. moment.js插件的简单上手使用

    开发过程中看长篇幅的技术文档是件多么影响多发效率的事情丫,哼哼,人家明明只是想用个简单的功能而已丫,下面文档很好的解决了这个问题,yeah~~~ 一.monent.js时间插件 1.Moment.js ...

  5. [Android]《Android艺术开发探索》第一章读书笔记

    1. 典型情况下生命周期分析 (1)一般情况下,当当前Activity从不可见重新变为可见状态时,onRestart方法就会被调用. (2)当用户打开新的Activity或者切换到桌面的时候,回调如下 ...

  6. Netty(7)-传对象

    改造timer,即客户端与服务端建立连接后,服务端主动向客户端发送当前时间. server: ch.pipeline().addLast(new TimeEncoder()); ch.pipeline ...

  7. (转)Inode详解

    Inode详解  原文:http://www.cnblogs.com/adforce//p/3522433.html 一.inode是什么 理解inode,要从文件储存说起. 文件储存在硬盘上,硬盘的 ...

  8. P4869 罪犯分组

    思路: 明显的dp,虽然我想到了二进制模拟,想到了转移,但还是先看了题解,原来真是这样,,,,不是第三题吗? 用f[i]表示,对于前i个罪犯最少需要分几组. 对于每个状态用二进制表示,第i位上1,0表 ...

  9. c# ExpandoObject动态扩展对象

    js中的Object 对象. php中的stdClass. c# 也有动态可扩展对象 ExpandoObject,需要添加System.Dynamic引用 用法: dynamic model = ne ...

  10. 帝国empirecms数据库数据表详细说明

    表名   解释 phome_ecms_infoclass_news 新闻采集规则记录表 phome_ecms_infotmp_news 采集临时表 phome_ecms_news 新闻主数据记录表 p ...