日前,阿里云PolarDB云原生数据库以超越原记录2.5倍的性能一举登顶TPC-C基准测试排行榜,以每分钟20.55亿笔交易(tpmC)和单位成本0.8元人民币(price/tpmC)的成绩刷新TPC-C性能和性价比双榜的世界纪录。每一个看似简单的数字背后,都蕴含着无数技术人对数据库性能、性价比和稳定性的极致追求,PolarDB的创新步伐从未止步。
一、TPC-C 基准测试模型
TPC-C 是由国际数据库事务性能委员会 TPC 组织制定的针对衡量OLTP系统性能的基准测试,涵盖了数据库的增删改查等典型处理路径,最终性能以 tpmC (transaction per minute,每分钟订单创建的数目)来衡量。TPC-C 测试模型能够直接和客观地评估出一个数据库的性能表现,是全球最具公信力的测试标准。
本次TPC-C基准测试,我们所使用的是PolarDB for MySQL 8.0.2版。作为阿里云瑶池数据库自研的旗舰产品,云原生数据库 PolarDB 通过 90 多种单机优化和性能提升方式,将单核性能提升了1.8 倍(对比原纪录),取得了数据库领域的里程碑式成就。本文将揭秘 PolarDB 单机优化的技术内幕。
在打榜过程中,通过对数据库压力模型的深入分析,我们梳理了以下4个核心特征及对应的优化方案:
- 海量用户连接 → 高并发优化
- 高 CPU 占用和内存访问 → CPU 和内存效率优化
- 高 IO 吞吐 → IO链路优化
- 更长的日志写入链路 → 复制性能优化
这4个特征也是真实线上用户业务常见的性能瓶颈,下面我们将简述 PolarDB 的整体性能链路,再基于这4个典型特征,分别介绍单机性能链路上做的关键优化。
二、PolarDB 性能链路
作为共享存储的云原生数据库,PolarDB 不仅提供了超强易用性(快速备份和恢复,高可用),以及超强弹性(存储/计算解耦)的能力,还通过软硬协同演进,提供了和本地盘一致的 I/O 时延能力和更强的 IOPS 能力,使得在性能、易用性、扩展性三方面都做到了极致。
传统部署在本地盘的 MySQL 架构,受益于本地盘的低 I/O 时延,但也存在着存储容量有限,弹升困难的限制,并且跨机主备复制的高时延,也掩盖了本地盘在性能上的收益。对于直接部署在云盘的 MySQL 架构,虽然能够利用云盘的存储资源的扩展性和高可用,但云盘的高时延又无法发挥 MySQL 的性能,并且无法做到计算资源的横向扩展。
为了解决性能和扩展性的问题,用户会考虑分布式数据库,但存在业务改造大,运维成本高等问题,PolarDB 通过 Proxy 到底层存储的全链路软硬协同优化,很好地解决了这三点。
图1:兼具高性能,易用性和扩展性的PolarDB
图2 展示了 PolarDB 的整个性能的优化链路概览,用户连接的 SQL 查询通过代理的转发,到达数据库内核,通过 SQL 解析、索引查找,最终通过文件系统到达磁盘存储。整个性能优化链路横跨了从上层的 Proxy 代理到底层存储,PolarDB 通过全链路的性能优化,使得在高压力的 tpcc 负载下,保持高效事务处理性能。本篇文章主要介绍数据库内核层面的单机优化,后续我们将会有文章继续介绍 PolarDB 如何做到软硬结合协同演进。
图2:PolarDB 性能链路概览
三、高并发优化
首先对于 TPC-C 基准测试负载的第一个典型特征:海量的用户连接,在 PolarDB 的集群测试中,客户端一共创建了 16 亿的用户连接,即便通过多级的连接池,到达单个数据库节点的用户连接仍然达到 7000+,这对数据库的并发处理能力提出了考验。
(一)PolarIndex
为了解决大量并发在索引写入上的锁瓶颈,PolarDB 提出了高性能的 Polar Index,以提升多线程并发的索引读写性能。
在 Polar Index 1.0 上,解决了因为全局索引锁而不允许并发分裂和合并(SMO)操作问题,每次遍历索引,遵循 latch coupling 的原则,即成功获取下个节点的写锁时才释放父节点写锁,将 SMO 分解为多个阶段,允许了 SMO 期间分裂节点的并发读取。
在 Polar Index 2.0 版本,PolarDB 进一步优化了索引的加锁粒度,和 latch coupling 相比,每次对 btree 的自上而下遍历,只拿一个节点的锁,同时优化 SMO 为自底向上的加写锁顺序,缩短了锁范围更大的上层节点加锁时间,允许更高的并发读写操作。
PolarIndex 的高性能读写使得 TPC-C 基准测试中,索引的并发读写不再是写入的瓶颈。
图 3:PolarIndex 的多阶段 SMO 流程
(二) PolarTrans
为了解决大量并发在事务系统上的扩展瓶颈,PolarDB 提出了无锁的事务管理系统 PolarTrans,提高事务提交和可见性判断的效率。
原生 Innodb 存储引擎,事务系统维护一个全局集中的活跃事务列表,通过一个全局锁保护,事务操作存在扩展性瓶颈。PolarTrans 使用提交时间戳技术(CTS),通过 CTS log 每个事务 id 和 事务提交序列号(CSN)的映射关系,并进行大量无锁优化,使得事务状态的操作逻辑更加轻量。
在事务启动,只需要将事务在 CTS log 中注册即可,提交时在 CTS log 标记提交的 CSN,在可见性判断时,通过事务系统的最大提交时间戳来代替原生活跃事务数组,极大地提高了事务管理的效率。
图 4:CTS log 的实现
(三)多级分片 Buffer Pool
为了解决大量并发访问在 buffer pool 缓存的扩展瓶颈,PolarDB 使用了多级分片的 buffer pool 缓存系统。
PolarDB 将访问拆分到多个 LRU 缓存上,并采用异步的 LRU manager 线程进行 LRU 链表尾部 page 的淘汰,前台用户线程不主动淘汰 LRU 的 page,避免增加事务处理的 CPU 时间。
LRU manager 与 LRU 链表数目采用一对多关系,LRU manager 通过维护空闲页的堆结构,每次从中选择空闲页最少的 LRU 链表扫描尾部的淘汰区,刷写脏页,提供内存空闲页给前台线程,避免了大量后台线程切换。
对于 I/O 密集型的负载,为了减少关键 page 淘汰和频繁移动数据页位置带来的锁开销,PolarDB 在 LRU 链表的头部建立 hot cache list,对于特定 page(索引中间页,元数据页)和根据频率识别的热点表会被优先放入 hot cache,减少淘汰频率。
图 5:多级分片 Buffer pool 的实现
(四)全异步执行架构
为了解决传统线程模型下高并发请求导致的 CPU 争抢、频繁上下文切换等问题,PolarDB 设计了基于协程的全异步执行架构,实现了鉴权、事务提交、锁等待等核心逻辑的异步化执行,显著提升数据库的高并发处理能力。
1.全异步架构设计
PolarDB 通过引入协程技术,将用户请求的生命周期与物理线程解耦,重构为全异步执行模型:
- 请求协程化:事务请求被封装为独立协程,由用户态调度器管理。
- 主动让出机制:协程挂起后释放执行权,调度器立即切换到其他就绪协程。
- 资源高效复用:单线程可并行处理数百个协程,降低线程的调度开销。
2.协程通信机制
PolarDB 设计了基于 eventfd 的轻量级通信协议。每个协程绑定独立的 eventfd 作为信令通道,当协程挂起时,由 epoll 线程实时捕获事件;资源就绪后,通过写入 eventfd 触发信号,立即唤醒挂起线程。该机制突破传统线程广播唤醒的局限,实现三大提升:零无效唤醒、纳秒级响应及百万级并发管理能力。
图 6:异步执行逻辑
四、CPU 和内存效率优化
在 TPC-C 基准测试中,大量的 SQL 的解析执行和数据表的访问仍然会消耗较多的 CPU 和内存资源。PolarDB 针对表元数据管理,存储过程等模块做了深入分析,做了大量节省 CPU,提高计算效率的工作。
(一)乐观开表复用
对于结构化数据库,在做 DML 前需要持有表的元信息锁进行操作,识别行结构的同时,也避免其他 DDL 造成数据的不一致。
对于传统悲观加锁,用户线程每次执行 SQL 访问数据前,都需要生成所有表的元信息,并加元数据锁(MDL),在执行完事务后释放。这是极大地消耗 CPU 时间的,尤其是事务通常是由数十条 SQL 组成,每次循环执行时,CPU 开销更加被放大。
PolarDB 实现了乐观的开表复用机制,为执行重复事务的连接,减少其每次重复构建和销毁表的元信息操作,以及重复加/解 MDL 锁的开销。通过维护一个连接的私有缓存,保存将事务访问的数据表和用户连接信息,提供给下次事务执行复用,如果访问的数据表是私有缓存的子集,可以复用已缓存的表元信息和 MDL 锁。为了避免死锁发生,如果访问新的数据表或者断连后,会清空私有缓存和释放对应的 MDL 锁,重新走悲观加锁流程。
图 7:乐观开表复用优化
(二)存储过程缓存机制
TPC-C 事务执行依赖存储过程的解析和执行,存储过程的执行效率极大决定了事务处理性能。
为了提高存储过程的执行效率,PolarDB 针对存储过程的缓存机制做了以下优化。
- 将用户连接级别的结构缓存转换为全局级别的结构缓存,避免因大量的连接造成内存使用过多,从而来提高 buffer pool 的 page 内存,减少 I/O 开销。
- 实现存储过程中 SQL 的 prepare 结果缓存,结合乐观开表,将需要绑定的数据表的列信息缓存在 SQL 表达式的 item 中,避免每次存储过程调用时的重复 prepare 开销,造成 CPU 浪费。
- 实现执行计划缓存,并基于索引统计信息固化简单 SQL 的执行路径,如单主键索引查询,无索引的范围查询,避免优化器下潜到存储引擎,占用额外 I/O 和 CPU 资源。
图 8:PolarDB 存储过程的缓存机制
五、IO 链路优化
TPC-C 一次事务的执行可能涉及数十次读 IO,使得事务的数据访问性能高度依赖于磁盘 I/O 的性能。
PolarDB 针对 IO 链路提出了一套 PolarIO 解决方案,如图 9 (a) 所示,从存储引擎 Page 和 Redo I/O 两大主要类型出发, 对 Buffer Pool,Redo buffer,I/O 队列进行改造,最后通过自研用户态文件系统 PFS ,持久化到底层的弹性存储中。
除了前文介绍的 buffer_pool 模块,图 9 (b) 展示了 PolarDB 并行 redo 写入的设计,将 redo buffer 划分为多个分片,并发地发出异步 I/O 的任务,结合异步的 redo prepare 机制(如 redo 对齐,checksum 计算)等。在高 redo 压力的实测中,PolarDB 的 redo 吞吐就能达到 4GB/s。
图 9: PolarIO 解决方案和并行 redo 日志落盘
在 PolarIO 解决方案中,另一关键是文件系统和底层存储的持久化,在写路径上,数据通过 PolarFS 写入 PolarStore ,经过 100Gb loosy RDMA 网络写入有接近内存的延迟的高速设备 AliSCM 中。在读路径上,PolarDB 基于 DRAM 和 AliSCM 构建的百TB级的超大弹性内存池。整个 IO 全链路 ByPass Kernel 实现软件栈,I/O 路径无任何内存Copy,有极低的软件栈开销,通过软硬协同的方式提供媲美甚至优于本地盘的I/O 延迟能力。
六、复制性能优化
TPC-C 在故障容灾测试中,要求数据库采用半同步的事务提交,即事务提交前,要保证 redo 日志通过网络传输到备节点的另一台主机,这样在主节点发生机器故障后,能正常从备节点恢复。
但是,跨机的复制链路带来了两方面的影响:
- 复制链路延长了事务等待日志持久化的时间。
- 在主节点高负载的写入下,备节点也需要更强的复制能力。
为了解决因跨机复制的性能问题,PolarDB 基于 RDMA 的低延迟优势,降低了网络包传输的时延。并且专门维护了复制同步使用的 redo cache,使用了异步的并发链路提高 redo 传输的吞吐。并使用多信号量的唤醒机制,防止事务无效唤醒。
PolarDB 在备节点使用全链路并行的复制框架,整个 PolarDB 的复制流程从 redo log 的读取,解析和应用都做了并行处理。基于备机的并行复制优化,在 TPC-C 的 20 亿 tpmc 测试压力下,备机的复制延迟维持在毫秒级别。
图 10:PolarDB 主从同步链路和并行复制框架
七、总结
TPC-C 的压力模型涵盖了对 CPU 和 IO 资源全面测试,极大考验了数据库内部大部分模块的并发执行和协同效率。PolarDB 秉承着客户第一的原则,结合客户实际应用场景,持续不断地对数据库进行性能优化,尽可能地让客户所买的每个核都能物尽其用,充分发挥最优的性能。
PolarDB MySQL 版国产轻量版正式售卖,点此咨询 https://survey.aliyun.com/apps/zhiliao/blKbuny7U
国产轻量版介绍:
随着技术的不断进步和国产市场需求的变化,PolarDB推出了满足国产市场需要且更具性价比的轻量化版本。与公有云在线化版本不同,轻量化版本采用软件输出的方式,可以部署在客户自主环境中。更为关键的是,该产品仍保留并承载了PolarDB技术团队深入的内核优化成果,使其既能保持高性能,又能大幅降低成本。