作者:江疑、华洛
1、引言
为了解决数据库多线程调度模型下CPU争抢、频繁上下文切换等导致的性能问题,阿里云瑶池旗下的云原生数据库PolarDB MySQL版设计了基于协程的全异步执行架构,实现鉴权、事务提交、锁等待等核心逻辑的异步化执行,这是业界首个真正意义上实现全异步执行架构的MySQL数据库产品,显著提升了PolarDB MySQL的高并发处理能力,其中通用写入性能提升超过70%,长尾延迟降低60%以上。基于异步执行架构,数据库前台线程能够专注于执行代码指令,避免不必要的上下文切换开销。与传统的MySQL线程模型相比,这是一次巨大的进步和彻底的革新。目前PolarDB MySQL全异步执行架构在多个客户场景中落地,未来随着功能的完善以及推广,会将该技术应用到更多的客户场景中。
图1:sysbench各写入模型性能对比
1.1 传统数据库架构中的CPU资源消耗黑洞
在典型OLTP核心业务场景中(如淘宝交易,支付系统等),数据库的Buffer Pool命中率普遍超过99%,性能瓶颈主要集中于CPU计算密集型操作,存储层IO延迟已非主要矛盾。 传统MySQL架构在高并发场景下暴露出的线程调度低效、上下文切换冗余、事务锁竞争等内核层问题,往往导致CPU资源无法有效转化为实际业务吞吐量。因此,在OLTP业务系统中,数据库性能损耗主要发生在内核层的以下方面:
- 线程调度风暴:内核传统数据库在高并发场景下产生数十万次/秒的上下文切换,CPU时间片被无效切割;
- 同步阻塞陷阱:事务提交、锁等待等关键路径存在同步点,导致线程空转等待;
- 原子操作雪崩:全局资源(如Lock Manager)争抢触发CAS操作指数级增长;
- 内存墙效应:NUMA架构下跨节点内存访问延迟高达300+周期,传统线程迁移加剧局部性失效。
PolarDB MySQL基于协程的全异步执行架构正是瞄准以上核心痛点,通过重构数据库内核调度模块,突破线程模型对CPU资源的低效利用模式。
1.2 无独有偶,DeepSeek也在做类似的事情
DeepSeek开源周压轴发布的3FS分布式文件系统,进一步印证了协程化架构在存储领域的革命性价值。该文件系统创新性地采用全异步化IO栈设计,通过用户态协议栈与RDMA网络的深度协程化改造,实现了元数据操作与数据平面的无锁流水线处理。其核心的Coroutine-Aware Zero-Copy机制,使IO路径的上下文切换次数降低两个数量级,实测达到百万级IOPS与微秒级延迟的极致性能。这种基于协程的全栈异步化架构,正在重塑数据库系统的设计范式——从数据库引擎到底层文件系统,从网络协议栈到硬件加速层,全链路的非阻塞执行将成为下一代基础设施的标配。可以预见,这种突破线程模型约束的异步IO范式,必将推动云计算基础设施向更高吞吐、更低时延、更强弹性的方向演进。
2、高并发性能问题分析
以写入请求为例,事务提交时需执行事务日志持久化,IO同步逻辑会导致线程空转或者频繁切换,MySQL社区基于此也做过非常多的优化,比如group commit,合并多次事务提交写盘操作。在此背景下,为了提升写入吞吐,需要通过提高前台写入并发来解决。提高并发的问题:
- 在传统的one-thread-per-connection调度中,高并发会引入一系列的问题,比如lock sys,trx sys等非IO场景下的锁竞争,并且高并发下,cpu争抢,频繁的上下文的切换,也会导致线程执行效率变差。
- 在thread pool调度下,通过限制并发线程可以降低非IO场景下的锁竞争,但是问题在于,thread pool限制threads数量很难控制,限制的太小,会导致写入性能上不去,限制过大,又会出现上述one-thread-per-connection的问题。
3、为什么【异步任务】机制解决不了高并发问题
诸如PG等其他数据库的异步任务设计,大多是基于客户端的异步处理。这里的异步指的是:客户端通过特定api发送请求,在请求实际执行完成前,客户端可以处理其他逻辑。但对于DB内核而言:
- 异步请求任务执行在DB内核依然是以线程或进程绑定模式运行
- 高并发的异步请求任务会导致线程资源枯竭,内核需要创建更多的线程来处理用户请求,或者任由任务堆积。
因此,部分其他数据库的异步任务处理机制,只能让客户端异步的执行SQL任务,并非DB内核线程的异步执行,解决不了高并发下的内核性能问题。
4、PolarDB异步执行方案设计
基于MySQL历史代码的异步化执行改造,涉及连接管理、SQL执行和事务处理等多个核心模块,挑战极大,要求在保持事务完整性的同时,提升系统的吞吐量并减少长尾延迟。PolarDB MySQL异步执行方案的核心是通过协程技术来实现用户请求跟线程之间解耦,利用eventfd来实现协程之间的通信。
- 请求协程化:事务请求被封装为独立协程,由用户态调度器管理。
- 主动让出机制:协程挂起后释放执行权,调度器立即切换到其他就绪协程。
- 资源高效复用:单线程可并行处理数百个协程,降低线程的调度开销。
图2:PolarDB MySQL全异步执行流程图
4.1. 基于协程的请求处理
在现有设计中,无论是one-thread-per-connecition还是thread-pool线程模型,用户请求生命周期跟同一个thread绑定,即:请求从开始到返回结果都由一个固定的thread来执行处理,如果执行过程中遇到锁/IO等待,线程会被操作系统挂起,等待IO就绪后,由操作系统调度继续执行。一个更加合理的执行逻辑应该是:线程执行SQL请求时,如果遇到等待场景,主动将当前SQL挂起,并在该cpu时间片内,继续执行其他SQL请求。这就要求数据库线程必须跟用户请求解耦。为了将SQL执行与线程解耦,PolarDB MySQL异步执行框架引入了协程技术,将用户请求封装到协程内来执行。用户请求生命周期与协程绑定,内核线程可以同时处理多个协程请求。
4.2. 基于eventfd的协程通知机制
通过协程来执行数据库请求的核心问题是协程之间如何进行同步操作。现有线程之间的同步方式:os_event_t(pthread_mutex + pthread_cond), 比如线程A通过os_event_t进入等待状态,线程B在完成IO或者释放资源后,通过pthread_cond_broadcast唤醒线程A。而在异步执行过程中,用户请求会主动挂起,此时无法响应pthread_cond_broadcast,因此需要在MySQL中设计一种全新的线程跟协程,以及协程与协程间的等待与唤醒机制。PolarDB MySQL的解决方案如下:
▶︎ 协程等待机制
为了解决协程的状态同步问题,我们引入eventfd。每一个协程对象会创建efd对象,用于跟其他线程或者协程进行通信,在协程挂起时,将efd注册到epoll实例中,由后台线程统一监听,随后进入挂起状态(等待事件就绪)。
▶︎ 协程唤醒机制
后台线程完成IO请求,或者其他协程在释放资源后,通过写入eventfd,通知其他协程,后台监听线程会捕获所有就绪的协程任务,直接处理或者放入队列中由其他线程继续处理。该机制突破传统线程广播唤醒的局限,实现三大提升:零无效唤醒、纳秒级响应及百万级并发管理能力。
4.3. 请求调度
PolarDB MySQL全异步执行架构中,请求(任务)根据客户端ip地址,用户类型,请求类型,事务类型,当前的挂起点等多个维度进行优先级划分,调度器会根据优先级自动将任务放入不同队列来进行调度,同时设计了一套防饿死机制,允许线程超发以及跨线程调度等。
4.4. 异步执行动态开关设计
在PolarDB MySQL的异步执行架构中,我们不仅关注于新的方案所能带来的收益,同时更加注重方案如何安全的带入到生产环境。因此动态开关的设计尤为重要,PolarDB异步执行方案允许用户通过全局变量形式一键开关,并且允许控制多个关键路径上是否通过异步执行。比如鉴权,binlog/redo log write,row lock wait等。
5、PolarDB MySQL异步执行框架收益
如下是基于最新的PolarDB MySQL 8.0.2版本的测试汇总记录。 基于现有的异步执行能力,写入吞吐以及长尾延迟都有非常显著的优化。
5.1 OLTP insert场景
图3:sysbench oltp_insert性能对比
5.2 OLTP update_non_index场景
图4:sysbench oltp_update_non_inex性能对比
5.3 OLTP update_index场景
图5:sysbench oltp_update_inex性能对比
5.4 OLTP write_only场景
图6:sysbench oltp_write_only性能对比
6、总结
基于不断创新的驱动力以及对高并发数据库技术难题的深入理解,PolarDB在MySQL架构中引入了革新的全异步执行架构,旨在解决传统方案无法处理的数据库内核高并发瓶颈。同时全异步执行架构还在快速迭代中,随着进一步的性能测试和优化,PolarDB异步执行方案会在更多应用场景中持续展现其技术优势。