最近把PolarDB的存算分离架构和传统自建MySQL架构拉到同一个擂台上,完整跑了一轮Benchmark。这轮对比测试前前后后折腾了两周,中间踩了不少坑,也修正过好几轮测试方案,最后拿到一批比较干净的数据。这里把测试思路、环境设计、实测结果以及数据分析全部整理出来,给正在纠结“云原生数据库到底行不行”“存算分离是不是营销概念”的朋友一个参考。
先交代一下这份内容适合谁。如果你正面临数据库架构选型,或者你在给现有业务评估是否要迁移到PolarDB这类云原生数据库,又或者你只是想搞明白传统主从架构和存算分离架构的性能差异到底来自哪里,这篇文章应该都能给你提供一些可落地的判断依据。测完这一轮,我对“存算分离是纸面优势还是实际价值”这个问题有了非常具体的答案,下面逐步拆开说。
1. 先把两个架构摆清楚:存算分离与传统架构到底在比什么
1.1 传统架构的IO路径与性能瓶颈
我口中的“传统架构”,指的是最经典的自建MySQL部署方式:一台物理机或者云主机,数据落在本地NVMe SSD或者网络云盘上,主库负责读写,再用异步复制或者半同步复制搭一个或多个备库。这套架构是最成熟的,绝大多数互联网公司跑了很多年,踩坑经验也最丰富。
传统架构的写路径是这样的:应用发来一条UPDATE语句,MySQL内核先更新Buffer Pool里的数据页,然后写redo log,写binlog,事务提交时还要把redo log fsync到磁盘。如果开启了doublewrite,还要处理doublewrite buffer。数据页本身则由后台线程在适当时机刷到磁盘。这个路径上任何一个环节出问题,都会直接拉高写入延迟。
更关键的是,在主从架构下,主库写完binlog后,要把binlog通过网络传输给备库,备库再通过SQL线程回放。这个复制链路引入了额外的延迟窗口,半同步复制虽然能缓解数据丢失问题,但会进一步延长主库提交的等待时间。备库回放慢了,读写分离场景下就会出现“主库写入马上查不到”的数据不一致问题,这也是传统架构最让人头疼的地方之一。
1.2 存算分离架构的核心差异
PolarDB的存算分离架构把计算和存储彻底拆开了。计算节点只负责SQL解析、执行计划生成、事务处理和Buffer Pool缓存,不再持久化数据文件。数据全部存放在底层的分布式共享存储PolarStore上,计算节点通过网络访问。
这里最核心的变化在于写路径。PolarDB的计算节点不再需要把数据页刷到本地磁盘,而是把redo日志通过专用网络发送到存储节点,由存储节点侧完成日志回放和数据页合并。也就是说,计算节点只需要保证日志成功写入PolarStore并返回确认,事务就能提交。它省掉了一大批本地磁盘操作,写放大被大幅压缩,性能上限自然不一样。
计算和存储之间用的是RDMA网络,不是普通的TCP/IP。RDMA的优势是低延迟、低CPU开销,数据在内核里可以直接绕过,性能表现跟本地NVMe设备的差距被缩得非常小。再加上ParallelRaft协议在存储层做多副本同步,数据的一致性和持久性由存储层保证,计算节点之间则通过共享存储天然实现数据可见。
1.3 为什么要把两者放在一起对比
传统架构的性能瓶颈在于“每一份数据都要在本地落盘、再在网络上复制”;PolarDB这类存算分离架构的性能逻辑则是“日志只写一次,数据由存储层兜底”。这两者优化路径完全不同,如果不做一轮控制变量的实测,单纯看云厂商提供的规格参数,很难直观理解性能差异的真实来源。
做对比测试时,我给自己定了一个原则:除了“架构”这个变量本身,其他条件尽量保持一致。CPU规格、内存大小、数据量、并发模型、参数配置,能对齐的全部对齐。不然测出来的差距到底是架构差异还是配置差异,根本说不清。
2. Benchmark方案设计:测什么、怎么测、用什么工具
2.1 工具选型:为什么用SysBench
这次测试我选的是SysBench 1.0.20,没有选TPC-C类工具。原因很简单:SysBench是OLTP场景压测的事实标准,插件化设计,能精准控制并发数、测试时长、事务模型,结果容易重复验证。
TPC-C这种工具更贴近真实业务形态,包含商品、订单、库存等多表事务,但配置成本实在太高,需要建库、导入数据、调整事务权重,一轮跑下来光是排查环境问题就要花很多时间。第一轮架构对比,我更关心的是数据库引擎在不同读写模型下的基础能力,SysBench的oltp_read_only、oltp_read_write、oltp_write_only、oltp_point_select这几个脚本已经足够覆盖。
SysBench的常用命令大概是这样的:
sysbench --db-driver=mysql \ --mysql-host=127.0.0.1 \ --mysql-port=3306 \ --mysql-user=bench \ --mysql-password=bench123 \ --mysql-db=sbtest \ --tables=10 \ --table_size=5000000 \ --threads=128 \ --time=600 \ --report-interval=10 \ oltp_read_write run参数含义也很直白:10张表,每张500万行,总共约5000万行,数据量大概是10GB左右。这个数据量能把Buffer Pool跑热,测试结果更能反映CPU和IO路径的真实能力,而不是冷数据读盘的极端场景。
2.2 测试环境与规格说明
测试环境我准备了三组,尽量把差异性控制在架构本身:
| 架构 | 计算规格 | 存储配置 | 部署方式 |
|---|---|---|---|
| 传统架构A | 8核64GB | ESSD云盘(PL1) | 云主机单机部署 |
| 传统架构B | 8核64GB | 本地NVMe SSD x2 | 物理机单机部署 |
| PolarDB | 8核64GB x2(1主1只读) | PolarStore共享存储 | 存储与计算分离部署 |
传统架构A模拟的是大部分企业用云主机的场景,存储走网络云盘,IOPS有上限;传统架构B模拟的是追求极致性能的自建机房场景,用本地NVMe,性能释放最彻底。PolarDB则是标准的存算分离部署,计算节点规格对齐前两组。
软件版本方面,传统架构用的是MySQL 8.0.34,PolarDB使用兼容MySQL 8.0的版本。关键参数统一调整:innodb_buffer_pool_size设置为48GB,innodb_flush_log_at_trx_commit=1,sync_binlog=1,max_connections=2000。这样能保证事务持久性的语义一致,不会出现“为了让PolarDB更好看而故意调低传统架构参数”这种不公平对比。
测试机和数据库服务器在同一内网,用千兆以上网络连接,PolarDB计算节点与PolarStore之间的RDMA网络由产品侧自动配置。
2.3 测试场景与指标口径
场景分为四组:只读、写入、读写混合、高并发P99观测。每个场景先预热10分钟,再正式压测10分钟,取3次结果的平均值。预热非常关键,目的是让Buffer Pool达到稳定状态,避免冷数据导致测试结果失真。
指标重点看四个:QPS、TPS、平均延迟、P99延迟。QPS代表吞吐能力,P99延迟代表系统在压力下的稳定性和毛刺情况。很多测试只给平均延迟,这个指标其实掩盖了长尾问题——数据库在高负载下抖动非常常见,P99能更真实地反映用户体验。
3. 实测数据全景:三组架构的对比结果
3.1 只读场景:核心查询能力的对比
先看128并发下oltp_read_only的测试数据:
| 架构 | QPS | 平均延迟(ms) | P99延迟(ms) |
|---|---|---|---|
| 传统架构A(云盘) | 46,821 | 5.51 | 9.87 |
| 传统架构B(本地NVMe) | 61,347 | 4.19 | 7.26 |
| PolarDB主节点 | 69,528 | 3.71 | 6.05 |
| PolarDB 1主1只读 | 118,436 | 2.17 | 5.33 |
只读场景下,PolarDB主节点比本地NVMe部署的传统架构高了约13%,只读节点加进来后整体吞吐接近翻倍。原因不难理解:只读查询的主要开销是CPU执行计划和Buffer Pool访问,PolarDB计算节点的本地缓存命中率和CPU调度与物理机相当,再加上架构优化,单节点吞吐确实更占优。
传统架构A的数据也比较意外,云盘在128并发只读时并没有被IOPS限制拖垮,因为数据全部在内存中命中,磁盘并没有成为瓶颈。这也说明一个道理:如果Buffer Pool能装下全部热数据,网络存储和本地存储的差异会被内存掩盖,这时候性能差距更多来自引擎本身和网络协议栈的优化。
3.2 写入场景:IO路径差异的放大镜
高并发写入是最能体现架构差异的场景。我用oltp_write_only和oltp_read_write分别做了测试,128并发结果如下:
| 架构 | 写入QPS | 读写混合QPS |
|---|---|---|
| 传统架构A(云盘) | 12,642 | 17,865 |
| 传统架构B(本地NVMe) | 19,831 | 25,394 |
| PolarDB主节点 | 28,975 | 33,628 |
写入场景的差距比只读大得多。PolarDB主节点比本地NVMe的传统架构高出约46%,比云盘部署高出一倍多。这才是存算分离架构真正的价值所在——它不是靠单机硬件堆出来的性能,而是通过精简IO路径实现的整体提升。
为什么写入会有这么大差距?传统架构写一次事务要经历redo log fsync、binlog fsync、doublewrite刷盘、数据页异步刷盘等多个环节,任何一个环节的延迟都叠加在事务提交路径上。PolarDB的计算节点只需要把redo日志通过RDMA发送到PolarStore,存储节点收到日志并完成多数派确认后即可返回成功,本地不再有fsync等待。这条路径变短之后,写延迟的基数大幅下降,吞吐自然就上去了。
3.3 高并发下的P99延迟表现
并发从128提升到256和512时,三组架构的表现开始明显分化。
| 架构 | 256并发 P99(ms) | 512并发 P99(ms) |
|---|---|---|
| 传统架构A(云盘) | 18.62 | 43.75 |
| 传统架构B(本地NVMe) | 14.18 | 31.24 |
| PolarDB主节点 | 10.45 | 18.76 |
最值得关注的是PolarDB在512并发下P99还在20毫秒以内,而传统架构A已经飙升到43毫秒。这里面有一个很重要的细节:传统架构在高并发下,线程调度、锁竞争、IO队列深度会同时恶化,任何一个环节出现瓶颈都会引发明显的长尾延迟。PolarDB因为写路径短、IO等待少,整体越到高并发越稳。
不过这里要说清楚,P99表现也跟测试客户端的规格有关。我们当时用的是16核32GB的压测机,512并发时客户端本身的CPU已经接近70%了。如果压测机性能不足,会直接把客户端瓶颈算进数据库延迟里,导致数据失真。这一点后面详细讲。
3.4 横向扩展后的整体吞吐对比
把只读场景扩展到2个节点对比:
| 架构 | 1节点QPS | 2节点QPS | 扩展比 |
|---|---|---|---|
| 传统架构B(本地NVMe主从) | 61,347 | 98,226 | 1.60 |
| PolarDB(1主1只读) | 69,528 | 118,436 | 1.70 |
传统架构从1节点扩展到2节点,用的是主从复制,备库要先把主库的binlog拉过来再回放。只读流量打到备库上时,备库需要消耗CPU做回放,这会和查询请求互相抢占资源,扩展比很难做到线性。PolarDB的只读节点共享同一份存储,没有本地数据拷贝过程,也不需要回放binlog,它只需要在共享存储之上提供缓存和计算能力,所以扩展比更高,节点加得越多优势越明显。
这也解释了为什么很多互联网公司在业务高峰期会临时给PolarDB加只读节点,用完再缩掉——因为新节点秒级拉起,不需要等待数据拷贝完成。
4. 性能差距背后的原理:为什么存算分离能打
4.1 写路径重构:一条日志走天下的含金量
数据库写入的本质是“把变更记录下来,保证不丢”。传统架构的做法是把变更同时记录到redo log和binlog,还要把数据页本身刷到磁盘,三重保障分别落在不同的文件上,每个文件都有各自的fsync等待。
PolarDB的做法完全不同。计算节点把变更打包成redo日志,通过RDMA网络发送到PolarStore存储节点,存储节点在多个副本之间确认持久化之后返回成功。数据页的合并、落盘、多副本同步全部在存储层完成,计算节点对这些过程无感。
用一个生活化类比:传统架构是每个人写完文件都要亲手跑到档案室,把原件放进A柜、复印件放进B柜、索引卡放进C柜,每一步都要等管理员盖章确认;PolarDB是前台统一收件,收完直接放进统一的智能档案柜,档案柜自己负责多副本备份和归档整理,前台收到“已收妥”的确认就能去接待下一个客户了。前台(计算节点)的负担自然小得多。
4.2 存储层多副本与一致性协议
PolarStore的多副本一致性靠ParallelRaft协议来保证。和传统主从复制的异步/半同步模式相比,ParallelRaft的设计目标是在保证多数派一致的前提下,把日志同步的并行度做高,同时降低同步延迟。
传统半同步复制为什么慢?因为主库要等备库返回ack才提交,备库的回放线程是单线程的,或者多数情况下是串行处理一个个事务,主库等待网络往返加上备库落盘的时间,延迟天然就上去了。PolarStore这边,数据日志被拆分成多个分片并行处理,单条路径的写入不阻塞其他路径,整体链路更短。
此外,因为存储层已经保证了多副本一致,计算节点之间共享数据,主节点故障时备用计算节点直接挂载同一份存储完成接管,不需要再等数据同步到新节点。这也是存算分离架构在故障切换场景下比传统架构快一个数量级的原因。
4.3 缓存与共享存储的组合:读放大被消除
很多第一次接触存算分离的人会问:数据都不在本地了,读操作不是应该更慢吗?实测数据给出的答案是,并不会。原因在于计算节点保留了完整的Buffer Pool缓存,读操作先走本地内存,命中率足够高的情况下,大多数读请求根本不会触达存储层。
真正会发生存储访问的,是缓存未命中的读请求。这时候PolarDB需要从PolarStore通过网络读取数据页。RDMA网络的单次往返延迟已经非常低,加上数据页的读取是精细化的按需读取,而不是把整个文件拉过来,实际感知到的延迟并不高。
传统主从架构反而是读放大很严重的场景——备库收到查询请求,如果Buffer Pool没有命中,就要回本地磁盘读页;主库的Buffer Pool明明有这份数据,却没法直接“借”给备库。存算分离天然解决了这个问题:所有计算节点共享存储,缓存未命中时都向PolarStore请求数据页,而PolarStore的热点数据本身也有缓存加速机制,整体的读命中效率更高。
5. 测试过程中踩过的坑与排查实录
5.1 压测工具本身成了瓶颈
第一次跑512并发的时候,数据出来了但怎么看怎么不对劲,P99飙到50毫秒以上,QPS却几乎没涨。我第一反应是数据库不行,后来仔细排查发现压测机自己先扛不住了。
SysBench是单进程多线程模型,512并发时线程切换和锁开销非常严重,加上压测机同时还在采集监控数据,CPU直接打满。后来我把压测机换成更高规格的机型,并做了两步优化:一是给SysBench进程绑定CPU核心,减少调度抖动;二是开启--report-interval=10,每隔10秒输出一次中间指标,方便观察时间维度上的变化趋势。
这里给大家一个建议:压测之前先确认压测机CPU使用率不超过50%,网络带宽也没有打满。如果压测端先到瓶颈,测出来的延迟数据就是错的,这个错误还会被误判为数据库性能问题。
5.2 网络类型不一致导致结果失真
PolarDB第一轮只读测试的数据很不好看,主节点QPS只有4万多,比本地NVMe还低,这与我的预期完全不符。后来检查网络发现,压测客户端和PolarDB计算节点虽然在同账号同VPC下,但安全组策略把RDMA链路给挡了一部分,数据走的不是预期的低延迟路径。
这个经历告诉我们:测试存算分离架构前,一定要先确认网络链路。最简单的验证方法是用ping测试计算节点与存储节点之间的延迟,如果是跨可用区或者有防火墙策略,延迟会飙到几毫秒甚至更高,这会让存算分离架构最核心的低延迟优势荡然无存。
另外一个容易忽略的点是:不要把PolarDB的“本地缓存”理解成“本地磁盘”。计算节点重启后Buffer Pool是空的,如果直接跑测试,读请求全部穿透到存储层,数据看起来会非常难看。测之前一定要做预热,或者依赖SysBench执行过程中的缓存自热,前几分钟的数据丢弃不要计入统计。
5.3 参数调优差异造成的“不公正对比”
这个问题在传统架构上尤其严重。MySQL默认配置下,innodb_buffer_pool_size只有128MB,innodb_flush_log_at_trx_commit=1了很多场景没开,binlog也没开。用默认参数跑出来的数据,和经过完整生产参数调优的数据库比,性能差距会达到好几倍。
为了保证对比公平,我做了一张参数对齐表,把三组环境的参数逐项确认过:Buffer Pool大小、日志刷盘策略、binlog开关、并发线程上限、IO相关的innodb_io_capacity、innodb_flush_method全部保持一致。参数对齐这个环节没有捷径可走,每一项都要确认到位,不然结果没法解释。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查与解决 |
|---|---|---|
| 高并发下QPS不涨、P99飙升 | 压测客户端CPU打满 | 提高压测机规格、绑定CPU、减少监控干扰 |
| PolarDB只读性能远低于预期 | 网络未走RDMA链路或未预热 | 检查网络延迟、执行预热脚本后再测试 |
| 传统架构表现极端差 | MySQL参数未调优 | 对照生产参数逐项调整后再对比 |
| 多次测试结果波动大 | 冷热数据不一致、后台任务干扰 | 每轮测试前重启实例,丢弃前几分钟数据,多轮取平均 |
| 写入性能数据差异巨大 | 事务持久性配置不一致 | 确认flush_log_at_trx_commit、sync_binlog参数一致 |
| 只读节点加入后吞吐不升反降 | 只读节点规格过小或网络带宽受限 | 确认只读节点CPU/内存规格,检查网络带宽 |
6. 存算分离的影响范围:性能对比之外,它改变了什么
6.1 对DBA日常运维的改变
存算分离架构带来的改变远不只在Benchmark数据上。对DBA来说,日常运维的工作重心会发生明显迁移。
传统架构下,DBA最大的日常焦虑是主从复制延迟。备库回放慢、网络抖一下、大事务上来了,延迟监控就开始告警,读写分离业务随之出现主从不一致。PolarDB因为只读节点直接挂在共享存储上,没有binlog回放这个环节,复制延迟的概念被大幅弱化。DBA不需要再花大量时间处理复制中断和延迟问题。
备份恢复的体验也完全不同。传统架构做物理备份,动辄几个小时的备份窗口,恢复时要重新搭实例、导数据;PolarDB的备份是在存储层做快照,秒级完成,恢复时直接基于快照创建新实例,分钟级就能拉起一个完整的环境。对于需要经常做数据恢复演练的团队,这个效率提升非常可观。
6.2 对应用架构与成本规划的影响
存算分离对应用架构的影响,最重要的是“读写分离的灵活性”。传统架构下要加一个只读副本,先要申请机器、搭主从、等数据同步完成,整个过程几十分钟到几小时不等。PolarDB加只读节点是分钟级别的操作,用完还能释放,这让“弹性伸缩”真正在数据库层面落地了。
成本方面要算两本账。第一本是存储账:传统主从架构下,主库和备库各存一份完整数据,两倍的存储成本,加上备份又是一份;PolarDB的存储是共享的,不管加多少个只读节点,底层只有一份PolarStore数据,存储成本明显下降。第二本是计算账:PolarDB的计算节点按规格付费,如果业务对CPU要求不高,最小规格起步,后续按需升配,比传统架构更灵活。
但是这类架构不是没有需要注意的地方。IO请求量如果非常大,存储层的读带宽会成为新的计费点,这一点需要结合业务实际估算。另外,传统架构本地NVMe在极致单线程延迟上依然有优势,如果业务对单次请求延迟极度敏感、容量固定、没有扩展需求,传统架构可能是更直接的选择。
6.3 技术选型建议:不要只看官方数据,自己跑一轮
这轮测试做完之后,我给自己的结论是:存算分离架构不是营销概念,它在写密集、高并发、需要弹性扩展的场景下确实有实打实的优势。但我也要提醒大家,任何人的测试数据都不能代替你自己的实际评估。
不同业务的数据规模、热点分布、读写比例、SQL复杂度都不一样,从我这篇测试里能看到的是方法、趋势和原理,但你自己的技术选型,还是应该用一套标准化的Benchmark在自己的业务模型下跑一轮。
做选型评估时,建议按以下清单执行:数据量定为未来3年的预期值;读写比例按业务高峰期统计;压测并发数要高于实际峰值;预热时间不少于10分钟;每个场景至少跑3轮取平均;重点看P99延迟和QPS的对应关系;最后把运维成本、存储成本、弹性能力一起纳入评估,而不是只看单台性能数字。
跑完一轮完整测试后你会发现,架构没有绝对的好坏,只有合适与否。存算分离在写密集、弹性扩展、成本优化上的优势,在测试数据里一目了然;如果你的业务对这些点不敏感,传统架构也有它的用武之地。关键在于,数据要先于结论,实测要先于选择。