写点什么

PostgreSQL 和 InnoDB 的多版本实现原理和比较

  • 2009-11-12
  • 本文字数:2343 字

    阅读完需:约 8 分钟

在风清扬的博客多版本并发控制:PostgreSQL vs InnoDB 中很好的阐述了 PostgreSQL 和 InnoDB 的多版本并发控制原理,并根据自己的丰富经验进行了深入的对比。

多版本并发控制技术被很多数据库或存储引擎采用,如 Oracle,MS SQL Server 2005+, PostgreSQL, Firebird, InnoDB, Falcon, PBXT, Maria 等等。新的数据库存储引擎,几乎毫无例外的使用多版本而不是单版本加锁的方法实现并发控制,可以说多版本已经成为未来的发展趋势。

PostgreSQL 的多版本实现原理 (基于 8.4.1 版本)

PostgreSQL 采用堆 +B+ 树索引(忽视 R 树、哈希、GiST 等不常用的索引)的存储结构,堆与索引的存储模式不同。

PostgreSQL 里记录的新老版本都存在堆里,堆中每条记录 header 里都通过 t_xmin 和 t_xmax 来存储创建事务 ID(creation transaction id)和销毁事务 ID(destruction transaction id)。在他的另一篇博客从对数据访问扭曲的适应性评价 PostgreSQL 与 InnoDB 里很简洁地介绍了这个实现策略:

PostgreSQL 处理多版本的简单逻辑是这样的:一个事务更新一个记录时产生一个新版本,并设置新版本的产生事务 ID 和和原版本的消亡事务 ID 为该事务 ID。新旧版本除被组织成一个链表外,其物理存储没有任何其它联系,就好像是两个记录一般。事务在读取到一个版本时,通过这两个事务 ID 与当前事务 ID 之间的关系来判断它是否应该看到这个版本。这一实现策略相当简洁(对索引的多版本存储尤其是如此),通过索引访问时更不会有对回滚段的额外访问,可以消除事务回滚的代价。

更详细的数据结构和函数可以参考 Inside PostgreSQL Shared Memory 等文章。除此之外,事务提交日志和事务快照也被用来进一步保存版本的信息,包括事务的状态和当时活跃事务的列表等。而对于 PostgreSQL,其索引是没有版本信息的。 通常更新每一条记录都会在该记录所在表的所有索引中插入相应的索引项。他在文中说这样会导致进行索引扫描时,即使查询所需所有属性在索引中都存在,也需要从堆中取出对应的记录判断是否可见除了:

在 PostgreSQL 8.3 中引入了 HOT(Heap-Only-Tuple)技术,如果新老版本在同一页面,并且 UPDATE 没有更新任何索引属性,则不插入新版本对应的索引项。

对于事务的提交和回滚操作,风清扬认为:

事务提交或回滚时操作简单,除事务提交时要写出事务外,只需要更新事务提交日志中对应的事务状态。也就是说回滚时并不需要将事务所作的操作从物理上清理掉,只要将事务状态设为已经回滚,则该事务产生的版本对其它事务自然就不可见了。

他也给出了不需要的老旧版本的不可见处理方式:

老旧的不再需要的版本,即不会被将来的任何事务见到的版本的清理是通过 VACUUM 实现的。由于新老版本混杂在一起,进行 VACUUM 时本质上是需要扫描所有数据。8.4 版中引入了 Visibility Map 技术,用来在 VACUUM 时跳过那些肯定不包含老旧版本的页面,但如果系统更新频繁且离散,这一技术就派不上大用场。在线的 VACUUM 只能清理页面中的老旧版本,但不能缩减表占用的空间,其实是产生碎片。要缩减表空间时的 VACUUM 会锁住表导致期间表不能被更新。

InnoDB 的多版本实现(基于 MySQL 5.1.33 版本带的 InnoDB)

InnoDB 采用索引组织表的存储结构,没有堆,记录存储在主键索引中,其它索引称为二级索引,其中每个索引项都包含所对应记录的主键。主键索引与二级索引的存储格式也不同。

InnoDB 的主键索引拥有版本化信息,除了主键被更新的情况需要存储多个版本,其他情况主键索引中只存储记录的最新版本,把旧版本的信息则集中存储在回滚段中:

主键索引记录的头上包含有 6 字节的事务 ID 与 7 字节指向回滚段中旧版本的指针。DELETE 时只是标记而不真正删除。UPDATE 时进行本地更新,并将前像写到回滚段中。

InnoDB 中读视图(和 PostgreSQL 中事务快照类似),也记录了事务开始时的活跃事务列表:

根据读视图和记录头上的事务 ID,可以判断出一个版本在事务开始时是否已经提交,即是否可见。如果存储在主键索引中的记录不可见,则根据指向回滚段中旧版本的指针找到旧版本信息,构造出旧的记录。回滚段采用的是 append-only 的日志型存储,记录的旧版本信息并不是一条完整的记录,而只是被更新的属性的前像。回滚段中的旧版本信息中也包含更旧的版本的位置,即版本链表是从新到旧的。

对于 InnoDB 的事务处理:

由于没有事务日志表示事务是否回滚,在事务回滚时必须清理该事务所进行的修改,插入的记录要删除,更新的记录要更新回来。事务提交时则无需处理。

如前所说,InnoDB 的二级索引中的每个索引项并没有版本化信息:

但在页面头记录了对该页面操作的事务的 ID 的最大值,通过这一值可以判断页面中是否可能包含不可见的数据,如果是,则需要访问主键索引判断可见性。否则,可以直接从索引中获取查询所需属性。二级索引中可能存储一条记录的多个索引对应的索引项,如果 UPDATE 操作更新了某个索引的属性,则类似于 PostgreSQL,插入新索引项到二级索引中,老索引项并不删除。但没有被 UPDATE 操作更新的索引则不需要插入新索引项。

在文章的最后,作者分享了他个人对这两种实现方式的评价,优势为:

PostgreSQL 与 InnoDB 的多版本实现最大的区别在于最新版本和历史版本是否分离存储,PostgreSQL 不分,InnoDB 分。

相对于 InnoDB,PostgreSQL 的优势似乎主要的只有一条:事务回滚可以立即完成,无论事务进行了多少操作。查询以前的历史数据的功能并不常用,在目前的 PostgreSQL 中也并不实用。

劣势为:

InnoDB 的主要劣势在于事务回滚时需要清理事务所作的所有修改,因此使用 InnoDB 时要避免使用超大型事务,否则回滚可能超慢无比。

而 PostgreSQL 的主要劣势在于清理老版本的扫描代价,索引属性更新时引起的索引项频繁插入,堆空间的回收问题以及判断算法的复杂性和开销等。更深入的介绍请参考风清扬博客。

2009-11-12 23:414194
用户头像

发布了 42 篇内容, 共 19.5 次阅读, 收获喜欢 6 次。

关注

评论

发布
暂无评论
发现更多内容

北京大学讲座:大模型时代的软件研发:技术、范式与工具

人工智能 华为云

报名最后一天!Farris-Vue前端组件创新挑战赛火热报名中!

inBuilder低代码平台

AI大模型助力:离线生成中英双语字幕

百度开发者中心

人工智能 数字化时代 大模型

华为云数据库创新发展论坛,打造行业更优数据库底座!

华为云开发者联盟

数据库 后端 华为云 云原生数据库 华为云开发者联盟

ByteHouse白皮书即将发布,揭秘如何让万台节点的OLAP性能大涨

极客天地

第七在线聚焦中国零售企业提升供应链效能

第七在线

[自研开源] MyData v0.7.3 更新日志

LIEN

开源 数据集成 业务融合 API对接 mydata

精选!常用的10款会议软件推荐+4款会议必备模板!

彭宏豪95

会议 在线白板 办公软件 在线协作 效率软件

Manz亚智科技 RDL先进制程加速全球板级封装部署和生产

科技热闻

官宣|Apache Flink 1.19 发布公告

Apache Flink

[图像处理]基于Rembg的图像背景自动去除工具

alexgaoyh

图像处理 抠图 rembg 去除背景 开源模型

区块链DAPP质押合约代币系统开发

l8l259l3365

HTTP 基础:详解 GET 和 POST 请求的关键区别

Apifox

前端 后端 HTTP post GET

Pytest+Allure,快速提升我们的测试效率

霍格沃兹测试开发学社

第43期 | GPTSecurity周报

云起无垠

中国滑雪运动趋势洞察2024

易观分析

报告 滑雪

Databend 开源周报第 136 期

Databend

ByteHouse白皮书即将发布,揭秘如何让万台节点的OLAP性能大涨

字节跳动数据平台

大数据

【AAAI 2024】M2SD:通过特征空间预构建策略重塑小样本类增量学习

阿里云大数据AI技术

#人工智能

数据平台“国产替代”掣肘在迁移?奇点云的工业制造实践解读

先锋IT

大模型微调实战:利用INT8/FP4/NF4量化技术提升性能与效率

百度开发者中心

深度学习 大模型

纯C语言框架助力降低大模型运行成本

百度开发者中心

人工智能 C语言 大模型

百度交易中台之系统对账篇

百度Geek说

数据 交易 对账系统

深度解析:Allure报告如何提升你的测试效率?

测试人

软件测试

PostgreSQL和InnoDB的多版本实现原理和比较_数据库_晁晓娟_InfoQ精选文章