暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PostgreSQL特性矩阵解析系列21之Serializable Snapshot Isolation

424

概念

什么是MVCC ?

MVCC是来处理并发的问题,提高并发的访问效率,读不阻塞写。

为什么需要MVCC?

数据库在并发操作下,如果数据正在写,而用户又正在读,可能会出现数据不一致的问题。比如一行数据只写入了前半部分,后半部分还没有写入,而此时用户读取这行数据时就会出现前半部分是新数据、后半部分是旧数据的现象,造成前后数据不一致。解决这个问题最简单的方法就是加读写锁,写的时候不允许读,读的时候不允许写,不过这样大大降低了数据库的并发性能,此时便引入了MVCC的概念,它的目的便是实现读写事务互不阻塞,从而提高数据库的并发性能。

事务ACID

A 原子性

C 一致性

I 隔离性

D 持久性

高并发的场景下的问题

脏读

不可重复读

幻读

不可重复读的重点是修改:

同样的条件, 你读取过的数据, 再次读取出来发现值不一样了。

幻读的重点在于新增或者删除。

同样的条件, 第1次和第2次读出来的记录数不一样。

当然, 从总的结果来看, 似乎两者都表现为两次读取的结果不一致。

但如果你从控制的角度来看, 两者的区别就比较大。

对于前者, 只需要锁住满足条件的记录。

对于后者, 要锁住满足条件及其相近的记录。

事物隔离级别

1.RU读未提交    脏读/不可重复读/幻读  。不适用MVCC读,可以读到其他事务修改甚至未提交的。

2.RC读已提交   不可重复读/幻读  。其他事务对数据库的修改,只要已经提交,其修改的结果就是可见的,与这两个事务开始的先后顺序无关,不完全适用于MVCC读。

3.RR可重复读   幻读 。完全适用MVCC,只能读取在它开始之前已经提交的事务对数据库的修改,在它开始以后,所有其他事务对数据库的修改对它来说均不可见。

4.S串行化   完全不适合适用MVCC,这样所有的query都会加锁,再它之后的事务都要等待。

隐藏字段说明

事务id

在PostgeSQL中,每个事务都存在一个唯一的id,也称为xid,可通过txid_current()函数获取当前的事务id

tuple

每一行数据,称为一行元组,一个tuple

ctid

tuple中的隐藏字段,代表tuple的物理位置

xmin

tuple中的隐藏字段,在创建一个tuple时,记录此值为当前的事务id

xmax

tuple中的隐藏字段,默认为0,在删除tuple时,记录此值为当前的事务id

cmin/cmax

tuple中的隐藏字段,标识同一个事务中多个语句的顺序,从0开始

实验

    要设置一个事务的事务隔离级别,使用SET TRANSACTION命令。

    Session 1
    postgres=# SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    SET
    设置串行化的事务隔离级别

    Session 2
    postgres=# SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    SET
    设置串行化的事务隔离级别

    postgres=# create table test(id int);
    CREATE TABLE
    postgres=# insert into test(id) values(1);
    INSERT 0 1
    postgres=# insert into test(id) values(2);
    INSERT 0 1
    postgres=# insert into test(id) values(3);
    INSERT 0 1

    Session 1
    postgres=# SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    SET
    设置串行化的事务隔离级别

    Session 2
    postgres=# SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    SET
    设置串行化的事务隔离级别

    Session 1
    postgres=# BEGIN;
    BEGIN
    postgres=# update test set id=11 where id=1;
    UPDATE 1

    Session 2
    postgres=# begin;
    BEGIN
    postgres=# update test set id=1 where id=1;
    阻塞状态

    Session 1
    postgres=# ROLLBACK;
    ROLLBACK

    Session 2
    提示以下信息
    ERROR: could not serialize access due to concurrent update

    参考

    https://www.postgresql.org/docs/13/transaction-iso.html

    文章转载自CP的PostgreSQL厨房,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论