关于 PolarDB PostgreSQL 版
PolarDB PostgreSQL 版是一款阿里云自主研发的云原生关系型数据库产品,100% 兼容 PostgreSQL,高度兼容Oracle语法;采用基于 Shared-Storage 的存储计算分离架构,具有极致弹性、毫秒级延迟、HTAP 、Ganos全空间数据处理能力和高可靠、高可用、弹性扩展等企业级数据库特性。同时,PolarDB PostgreSQL 版具有大规模并行计算能力,可以应对 OLTP 与 OLAP 混合负载。
简介
我们知道,在数据库操作中,特别是在含有多个索引的表中执行插入操作时,由于每个索引插入都需要逐个访问索引页,这导致了频繁的I/O操作,成为性能瓶颈。标准流程包括表行插入后,顺序地为每个索引执行查找、读取、插入操作,这一系列步骤在高索引数量下尤为耗时。本文描述了在向包含多个索引的表中插入数据时可能遇到的I/O瓶颈问题,并探索一种通过预读取索引页来优化插入操作的方法。通过在实际插入操作之前预先读取可能需要的叶子页,以减少插入过程中的I/O延迟。通过实践分析,该方法在某些场景下能够提高约四成的buffer命中率,并在一些情况下能够显著提高插入效果。
原理介绍
在处理多行插入、普通插入和更新操作中添加预取逻辑(ExecInsert, CopyFrom, ExecSimpleRelationInsert, ExecSimpleRelationUpdate)方法中,在插入索引前进行所有索引提前读取操作。
预取索引页的目的是在实际执行插入操作之前,对要插入的数据所在的索引页进行预读取(仅预取leaf page),主要流程如下:
1. 启动预取:ExecInsertPrefetchIndexes
在insert/update/copy场景,启动索引预取。
2. 生成索引元组&执行预取:
判断预取条件&预取数据组装
3. 执行预取:
根据 B 树结构递归查找目标页,通过PrefetchBuffer实际执行预取操作,提前将这些页加载到缓冲区中。
-对pg来讲是操作系统缓存。测试中发现的实现缺陷:
冗余实现:在预取中需要重复构建索引元组等。
暂时无法在复合索引和有其他类型的索引情况下工作
当前只能一次性预取所有索引和元组
在仅包含一个索引的情况下也进行了预取
用并行的方式来解决更具有普适性
aminsert_futures = NIL;
/* create a future for each index insert */
for (<all indexes>)
{
aminsert_futures = lappend(aminsert_futures, aminsert(...));
}
/* wait for all the futures to finish */
await aminsert_futures;
注意,这里期望使用主干中异步方法ForeignAsyncRequest() and ForeignAsyncConfigureWait(),但是这两个方法是从上次配置异步文件的读取,对作为异步基础设施还有一段距离。测试效果:
1G shared_buffer,5个索引。
批量数据 | 未启用 | 启用加速特性 |
100 | 1.025 | 0.942 |
1000 | 52.526 ms | 50.245 |
10000 | 3660.453 | 1429.513 |
100000 | 7816.608 | 5008.967 |
分析:
-buffer命中提高约40%,部分场景insert效果显著
-在特殊场景下的刷脏更高效,预取在高压下会有刷脏放大的情况,所以效果预测会更佳。
总结分析:
从原理上讲,在insert索引操作之前通过对索引page预取(原生pg都是异步并行),理论上是在索引数量大于1的情况下都可以缩减io耗时。




