最近在分析PostgreSQL-14版本性能提升的时候,关注到了Snapshots的这一部分。发现在PostgreSQL-14版本,连续合入了好几个和Snapshots相关的patch。
并且,Andres Freund也通过这些改进显著减少了已确定的快照可扩展性瓶颈,从而改进了Postgres处理大量连接的问题,因为在连接数很高时计算一个快照的代价是很昂贵的。(对于分析连接可扩展性的局限性可以参考Andres Freund的这篇文章 Analyzing the Limits of Connection Scalability in Postgres - Microsoft Community Hub)
本篇文章,我会结合Andres 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里面了。
一、优化一:在构建快照时不计算全局 horizons
这个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去做的。
二、优化二:将PGXACT->xmin移回PGPROC
这个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之间频繁更新缓存的花销,对于高度并发、快照获取量大、工作负载大的情况,仅此更改就可以显著提高可伸缩性。
三、优化三:将PGXACT->vacuumFlags移动到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()执行的速度。
四、优化四:移动subxact信息到ProcGlobal,删除PGXACT
这个patch的链接为https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=73487a60fc1063ba4b5178b69aee4ee210c182c4
与第三个优化的更改类似,这个patch增加了GetSnapshotData()经常需要的数据保存在L2缓存中的机会。在许多workload中,子事务是非常罕见的,这使得检查的代价很小。
把PGXACT的所有成员都转移了,所以不需要再保留它了,因此删除了PGXACT结构体。
五、优化五: 引入正在进行的xid的密集数组
这个patch的链接为https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=941697c3c1ae5d6ee153065adb96e1e63ee11224
之前构建快照时,GetSnapshotData会遍历procArray->pgprocnos找到所有存在的PGPROC、PGXACT结构,收集所有的xid,即通过pgprocnos拿到索引,通过索引在离散的allPgXact数组中拿到xid。而现在的版本,其实改成了将xids单独拿出来放到连续存储的密集数组中,来显著提高其命中率。
六、优化六:使用xact完成计数器缓存快照
这个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)
Improving Postgres Connection Scalability: Snapshots
2.cacheline
Cache Line - 简书
3.ping-pong机制
ping-pong机制_c振的博客-CSDN博客_ping-pong
4.德哥的PostgreSQL 20200819当天代码 - 14 对比 13 高并发性能优化 数据对比 - get snapshot improve
blog/20200817_01.md at master · digoal/blog · GitHub