最近在分析PostgreSQL-14版本性能提升的时候,关注到了Snapshots的这一部分。发现在PostgreSQL-14版本,连续合入了好几个和Snapshots相关的patch。
并且,Andres Freund也通过这些改进显著减少了已确定的快照可扩展性瓶颈,从而改进了Postgres处理大量连接的问题,因为在连接数很高时计算一个快照的代价是很昂贵的。
(对于分析连接可扩展性的局限性可以参考 Andres Freund 的这篇文章 https://techcommunity.microsoft.com/t5/azure-database-for-postgresql/analyzing-the-limits-of-connection-scalability-in-postgres/ba-p/1757266)
本篇文章,我会结合 Andre Freund 的几个patch进行一些分析,看看在PostgreSQL-14版本里对于Snapshots做的这部分优化。
在PostgreSQL里是使用SnapshotData结构体的xip来记录哪些事务正在运行中,正常情况下,对于普通的MVCC快照,xip记录了所有运行中的事务ID。(除了快照是处于recovery状态产生的,这个时候它为空
typedef struct SnapshotData
{
TransactionId xmin; /* all XID < xmin are visible to me */
TransactionId xmax; /* all XID >= xmax are invisible to me */
TransactionId *xip;
uint32 xcnt; /* # of xact ids in xip[] */
TransactionId *subxip;
int32 subxcnt; /* # of xact ids in subxip[] */
...
在PostgreSQL-14版本之前,每个PG进程都对应一个PGPROC结构、一个PGXACT结构,而从PostgreSQL-14版本开始,把PGXACT合到PGPROC里面了。
这个 patch 的地址:https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=dc7420c2c9274a283779ec19718d2d16323640c0
为了使 GetSnapshotData() 具有更强的可伸缩性,它不能不查看每个进程的xmin:尽管快照内容不需要在提交只读事务或释放快照时更改,但在这些情况下会修改进程的xmin。xmin修改的频率很快,它会导致 GetSnapshotData() 中的许多缓存丢失,尽管快照底层的数据没有改变,尤其是在核心计数较高的系统中。这是GetSnapshotData() 在大型系统上扩展性差的最重要原因。
这个提交其实只是删除对 PGXACT->xmin 的访问,让 GetSnapshotData() 不再需要访问xmins,这本身不会带来显著的改善,但是为后续的几个优化都是针对GetSnapshotData()不需要访问xmins去做的。
这个 patch 的地址:https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=1f51c17c68d05c28d5b9294d8013cb9e7e653160
早在2011年其实就已经发现了 GetSnapshotData 存在瓶颈,当时做的优化是把PGPROC 里面把快照需要的变量拆出来,放到 PGXACT 中,这样数据结构小很多,可以装到一个 cpu cacheline 中。
这个 patch 它把第一部分 GetSnapshotData() 不再需要的xmin移回了 PGPROC。原本在 PGXACT 里使用xmin的时候,它比 PGXACT 里的其他成员更新更频繁,进而导致 cacheline 有很多不必要的 ping-pong ,所以又将 PGXACT->xmin 移回了PGPROC 结构体。
这几句话大一看好像有点一头雾水,什么是cacheline,什么是ping-pong。
这里给大家大致解释这几个名词,方便我们更好去理解,文章底部也有我参考的链接。
1、cacheline
内存是DRAM,速度慢,容量大。属于CPU外部设备,通过总线与CPU进行通信。而cache 在 CPU内部,属于片上硬件。所以内存的速度会远远慢于cache。
cache的单位是cacheline,是一块连续的内存。以cacheline 64字节为例。所有的cacheline 又划到各个way下。row: 叫做set。columne: 叫做way
当CPU访问一个内存的时候,通过内存中间的6bits定位在哪个set,再通过24bits定位响应的cacheline。
对同一块cache不停的进行写操作,会增加cache之间更新缓存的花销。包括不停的去刷新主存的内容。缓存的cacheline的state machine 频繁的从 S -> I 状态
2、ping-pong
Ping-pong是一种数据缓冲的手段(是一种数据传输技术),能够同时利用两个相同数据缓冲区达到数据连续传输的目的(对象类型可以是任意的),两者交替地被读和被写,从而提高数据传输速率。
然后再去看开头的那句话,是不是大致理解了,这个patch主要减少了在这个函数里使用xmin所导致的cache之间频繁更新缓存的花销,对于高度并发、快照获取量大、工作负载大的情况,仅此更改就可以显著提高可伸缩性。
移动到ProcGlobal->vacuumFlags
这个 patch 的链接为:https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=5788e258bb26495fab65ff3aa486268d1c50b123
这个 patch 把 PGXACT->vacuumFlags 移至 ProcGlobal,增加了GetSnapshotData() 经常需要的数据保存在 L2 缓存中的机会,L2 缓存位于 CPU 与内存之间的临时存储器,容量比内存小但交换速度快,二级缓存容量大小决定了 cpu的性能。
L1 L2 L3缓存和内存的快慢关系,可以参考如下:
L1的存取速度:4个cpu时钟周期
L2的存取速度:11个cpu时钟周期
L3的存取速度:39个cpu时钟周期
RAM的存取速度:107个cpu时钟周期
可以看到,L2缓存的速度比在内存里快,因此这样的改动,相当于加快了GetSnapshotData()执行的速度。
这个 patch 的链接为:https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=73487a60fc1063ba4b5178b69aee4ee210c182c4
与第三个优化的更改类似,这个patch增加了 GetSnapshotData() 经常需要的数据保存在L2缓存中的机会。在许多 workload 中,子事务是非常罕见的,这使得检查的代价很小。
把 PGXACT 的所有成员都转移了,所以不需要再保留它了,因此删除了 PGXACT 结构体。
这个 patch 的链接为:https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=941697c3c1ae5d6ee153065adb96e1e63ee11224
之前构建快照时,GetSnapshotData 会遍历 procArray->pgprocnos 找到所有存在的 PGPROC、PGXACT 结构,收集所有的 xid,即通过 pgprocnos 拿到索引,通过索引在离散的 allPgXact 数组中拿到 xid。而现在的版本,其实改成了将 xids 单独拿出来放到连续存储的密集数组中,来显著提高其命中率。
这个 patch 的链接为:https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=623a9ba79bbdd11c5eccb30b8bd5c446130e521c
这个提交在VariableCacheData的结构体里引入了xactCompletionCount(VariableCacheData是共享内存中的一个数据结构,用于跟踪OID和XID赋值状态),它跟踪自服务器启动以来以某种形式完成的带有xid(即可能修改了数据库)的Top事务的数量。PostgreSQL-14版本仅用于检查GetSnapshotData(是否需要重新计算快照的内容。
只要当前的xactCompletionCount与最初构建快照时相同,就可以避免重新构建快照的内容。
这个改动的意图很明显了,前几个优化都是让计算快照变得更快/更可伸缩,而这个改动是:避免重新构建快照的内容,减少资源占用。(也就是能不重新做快照就不做快照)。
参考列表
1.Improving Postgres Connection Scalability: Snapshots(Andres Freund)
https://www.citusdata.com/blog/2020/10/25/improving-postgres-connection-scalability-snapshots/#unfortunately-a-minimal-transaction-visibility-primer
2.cacheline
https://www.jianshu.com/p/f8d3d17e9e8b
3.ping-pong机制
https://blog.csdn.net/chenyu201003/article/details/81449762
4.德哥的PostgreSQL 20200819当天代码 - 14 对比 13 高并发性能优化 数据对比 -
get snapshot improve
https://github.com/digoal/blog/blob/master/202008/20200817_01.md
点击此处阅读原文
↓↓↓
本文分享自微信公众号 - 开源软件联盟PostgreSQL分会(kaiyuanlianmeng)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。