当前位置: 首页 > news >正文

postgresql 从应用角度看快照snapshot使用,事务隔离控制不再神密

专栏内容:postgresql内核源码分析
个人主页:我的主页
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.

快照使用

快照是事务中使用,配合事务的隔离级别,体现出不同的可见性。
快照在事务中自动获取,我们可以通过查看当前事务的快照和事务号来判断分析。

为了方便演示,我们先创建一张表

postgres=> create table neworder(o_id integer primary key, o_info varchar, o_time timestamp);
CREATE TABLE

快照查询

第一个事务

postgres=*> select txid_current();txid_current
--------------685103
(1 row)

查看当前事务号为685103
注意 txid_current() 每调一次这个函数,会消费一个事务号,在事务中只消费一个。

postgres=*> select pg_current_snapshot();pg_current_snapshot
---------------------685103:685103:
(1 row)

查看当前事务的快照,格式为xmin:xmax:运行中的xid列表。
我们看到xmin,xmax 为685103,说明当前没有运行的事务,最新分发的事务号就是685103

注意 这里并没有分配正在运行的事务号,只有执行写操作后才开始分配
我们在另一个终端开启一个新事务,查看快照,并没有发现685103在正在运行的列表中

postgres=> begin;
BEGIN
postgres=*> select txid_current();txid_current
--------------685104
(1 row)postgres=*>  select pg_current_snapshot();pg_current_snapshot
---------------------685103:685103:
(1 row)postgres=*> end;
COMMIT

第二个事务

  • 在终端一的事务上插入一行数据
postgres=*> insert into neworder values(1,'football',now());
INSERT 0 1

这样做,让此事务有运行中的事务号

  • 我们这里在第二个终端重新开启一个事务
postgres=> begin;
BEGIN
postgres=*> select txid_current();txid_current
--------------685105
(1 row)postgres=*>  select pg_current_snapshot();pg_current_snapshot
----------------------685103:685105:685103
(1 row)

我们看到当前快照中xmin为685103,正在运行的最小事务号;
xmax为685105 ,正在运行中只有一个事务

不同隔离级别的快照

读已提交

我们启动了两个默认为读已经提交的事务

  • 现在在终端二上继续执行
postgres=*> create table neworder1(o1_id int);
CREATE TABLE
postgres=*>  select pg_current_snapshot();pg_current_snapshot
----------------------685103:685105:685103
(1 row)

创建了一张表,快照没有发生变化

  • 终端一上提交事务
postgres=*> commit;
COMMIT
  • 在终端二事务中插入数据
postgres=*> insert into neworder values(2,'basketball',now());
INSERT 0 1
postgres=*>  select pg_current_snapshot();pg_current_snapshot
---------------------685105:685105:
(1 row)

此时查看到的快照发生了变化,xmin为自己事务,当前运行中的事务没有了。

  • 原理
    读已经提交的快照,在每次命令都会获取新的快照,如果有提交的事务,那么就会变为可见
postgres=*> select * from neworder;o_id |   o_info   |           o_time
------+------------+----------------------------1 | football   | 2023-06-21 08:56:43.5416682 | basketball | 2023-06-21 09:15:10.313662
(2 rows)

此时就可以看到前一事务插入的数据

可重复读

  • 终端一启动可重复读事务
postgres=> begin isolation level repeatable read ;
BEGIN
postgres=*> insert into neworder values(3,'delicious food',now());
INSERT 0 1
postgres=*> select txid_current();txid_current
--------------685107
(1 row)postgres=*> select pg_current_snapshot();pg_current_snapshot
---------------------685107:685107:
(1 row)

快照中,此时没有正在运行中的事务,xmin就是自己的事务号

  • 终端二启动可重复读事务
postgres=>  begin isolation level repeatable read ;
BEGIN
postgres=*> insert into neworder1 values(1);
INSERT 0 1
postgres=*> select txid_current();txid_current
--------------685108
(1 row)postgres=*>  select pg_current_snapshot();pg_current_snapshot
---------------------685107:685107:
(1 row)

快照中,此时运行中的事务xmin为685107,xmax也为685107

  • 终端二提交事务
postgres=*> commit;
COMMIT
  • 终端一执行插入
postgres=*> insert into neworder values(4,'delicious tomato',now());
INSERT 0 1
postgres=*> select pg_current_snapshot();pg_current_snapshot
---------------------685107:685107:
(1 row)

事务快照并没有变化

  • 原理
    对于可重复读,每个事务只获取一次快照,这样可见性在事务开始时就已经确定

快照对于系统字典的隔离

在使用对应的表后,对于读写会产生冲突,导致阻塞;
如果没有使用要修改的表,则不会产生冲突。

读已提交

如果事务没有操作过该表,在另一事务修改提交,就可以看到修改;

先启动事务1,操作表neworder1,然后在事务2操作neworder的表定义,提交事务2后,在事务1进行查看。

事务1

postgres=> begin;
BEGIN
postgres=*> insert into neworder1 values(5);
INSERT 0 1

事务2 修改数据字典

postgres=> begin;
BEGIN
postgres=*>
postgres=*> alter table neworder drop column o_time;
ALTER TABLE
postgres=*> commit;
COMMIT

事务1 查询数据字典的同步,可以看到neworder已经没有了o_time列

postgres=*> select * from neworder;o_id |      o_info
------+------------------1 | football2 | basketball3 | delicious food4 | delicious tomato10 | potato
(5 rows)postgres=*> commit;
COMMIT

可重复读

和上面顺序一样,先启动事务1,操作表neworder1,然后在事务2操作neworder的表定义,提交事务2后,在事务1进行查看。

事务1

postgres=> begin;
BEGIN
postgres=*> insert into neworder1 values(5);
INSERT 0 1

事务2 修改数据字典,增加列o_time

postgres=> begin isolation level repeatable read ;
BEGIN
postgres=*> alter table neworder add column o_time timestamp;
ALTER TABLE
postgres=*> commit;
COMMIT

事务1 查询数据字典的同步

postgres=*> select * from neworder;o_id |      o_info      | o_time
------+------------------+--------1 | football         |2 | basketball       |3 | delicious food   |4 | delicious tomato |10 | potato           |
(5 rows)postgres=*> select pg_current_snapshot();pg_current_snapshot
---------------------685125:685125:
(1 row)postgres=*> select txid_current();txid_current
--------------685125
(1 row)postgres=*>

与预期不一样的事情发了 这里在可重复读时,系统字典的更新可以被看到了。
这一点在源码分析时,没有被关注到。

导出快照

快照还可以保存,被重复使用,有点像科幻中保存记忆一样。
下面我们来看看如何使用这一特异功能。

导出介绍

开启事务1,保存事务1的快照,然后在别一个事务中应用此快照,那么这两个事务看到的内容是一样的。

事务1 启动可重复读事务,并导出快照

postgres=> begin isolation level repeatable read ;
BEGIN
postgres=*> insert into neworder1 values(5);
INSERT 0 1postgres=*> select pg_current_snapshot();pg_current_snapshot
---------------------685125:685125:
(1 row)postgres=*> select txid_current();txid_current
--------------685125
(1 row)postgres=*> select * from neworder;o_id |      o_info      | o_time
------+------------------+--------1 | football         |2 | basketball       |3 | delicious food   |4 | delicious tomato |10 | potato           |
(5 rows)postgres=*> SELECT pg_export_snapshot();pg_export_snapshot
---------------------00000004-00000058-1
(1 row)

使用旧快照

旧快照能被使用的前提是,生成旧快照必须有事务在使用,否则快照就是一个过期快照

事务2 在启动事务2前,向neworder表中插入数据,然后启动事务2,在其中使用保存的快照

postgres=> insert into neworder values(100,'test dic',now());
INSERT 0 1
postgres=> insert into neworder values(101,'erase',now());
INSERT 0 1
postgres=> select * from neworder;o_id |      o_info      |           o_time
------+------------------+----------------------------1 | football         |2 | basketball       |3 | delicious food   |4 | delicious tomato |10 | potato           |100 | test dic         | 2023-06-22 10:50:35.238614101 | erase            | 2023-06-22 10:55:12.268132
(7 rows)
postgres=> BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGINpostgres=*> set transaction snapshot '00000004-00000058-1';
SET
postgres=*> select * from neworder;o_id |      o_info      | o_time
------+------------------+--------1 | football         |2 | basketball       |3 | delicious food   |4 | delicious tomato |10 | potato           |
(5 rows)postgres=*>  select pg_current_snapshot();pg_current_snapshot
---------------------685125:685125:
(1 row)

结尾

非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!

作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。

注:未经同意,不得转载!

http://www.lryc.cn/news/94310.html

相关文章:

  • mysql(mariadb)读写分离部署
  • ES-工作原理
  • C++小结(4)
  • Java框架之spring 的 messaging
  • linux使用grep命令查询nginx的进程情况时总是出现 grep --color=auto nginx
  • FFmpeg音视频开发知识点(二)
  • 【Java可执行命令】(十)JAR文件签名工具 jarsigner:通过数字签名及验证保证代码信任与安全,深入解析 Java的 jarsigner命令~
  • c#调用c++ dll,Release版本内存访问错误
  • 内网安全:Cobalt Strike 与 MSF 联动( 会话 相互转移 )
  • 性能测试讲解超详细Jmeter
  • 微服务 – Spring Cloud – Nacos 配置中心
  • 超细,设计一个“完美“的测试用例,用户登录模块实例...
  • 【C#】文件拖拽,获取文件路径
  • SAP PI/PO初步了解 2023.07.03
  • Java中生产者消费者模型
  • 测试Hyperledger Fabric环境
  • ClickHouse查询sql长度超超过最大限制
  • 【Axure教程】拖动调整行高列宽的表格
  • 中间件-netty(1)
  • 【方法】想把PDF文档转换成PPT,如何操作?
  • Linux--设置目录或文件的默认权限:umask权限掩码
  • C++实现websocket单server单client全双工通信(基于boost!!!)
  • 好用的网址5
  • 做项目去实习到底做的什么?
  • VSC++: 验证身份证
  • 机器学习-方差和偏差理论
  • 力扣 669. 修剪二叉搜索树
  • ChatGPT在多轮对话中的表现如何?
  • C++ 虚函数 (virtual function) 介绍
  • 写给小白的ChatGPT和AI原理