老规矩,先说这次测试的来由。最近团队在评估国产数据库替换的可行性,Oracle那边一堆存量业务,崖山(YashanDB)是重点考察对象之一。替换评估不能光看兼容性清单,更不能信厂商的PPT,最靠谱的方式就是拿真实业务场景去压。而在所有SQL操作里,排序是我个人觉得最适合做“试金石”的——它足够底层,牵扯到内存管理、临时表空间、CPU指令效率,几乎能暴露数据库内核在资源调度上的真实水平。这篇文章就把我这次“Oracle vs 崖山排序性能对比测试”的完整过程、踩坑记录和最终结论整理出来,给正在做数据库选型或者性能评估的朋友一个参考。
1. 为什么拿排序场景做数据库性能对比
排序在SQL里太常见了:ORDER BY、DISTINCT、GROUP BY、UNION、窗口函数的OVER (PARTITION BY ... ORDER BY ...),甚至JOIN里的sort merge join,背后全是排序操作。但很多做性能测试的人会把排序当成“简单操作”,觉得就是一条SQL加个ORDER BY而已,没什么好测的。这个想法坑过不少人。
排序性能的好坏,直接取决于数据库三个维度的能力:
首先是内存管理能力。排序数据量小于可用内存时,数据库会在内存里完成排序,这是最快的路径。数据量一旦超过内存阈值,就会触发磁盘排序,把中间结果写入临时表空间,然后再归并。这个阈值怎么算、内存怎么分配、排序区是固定大小还是动态扩展,各数据库差异很大。Oracle传统上有SORT_AREA_SIZE和PGA_AGGREGATE_TARGET两套机制,崖山作为新架构数据库,它的内存分配策略到底怎么设计的,只有实测才能看出来。
其次是临时表空间(或者叫临时段)的IO能力。大数据量排序绕不开磁盘,临时表空间的分配效率、回收机制、是否支持并发读写,直接影响排序的尾部延迟。很多性能测试只跑小数据量,内存排序一瞬完成,根本测不到这层能力。真正的排序性能测试,必须要有一批“大到内存装不下”的数据,逼着数据库走磁盘排序路径。
第三是排序算法的工程实现。数据库内核不可能只用教科书上的快排,通常混合了插入排序、堆排序、归并排序等多种策略,针对不同数据规模切换。字符串排序涉及字符集比较规则,数值排序涉及类型转换。这些细节平时碰不到,但在业务SQL的表现上会实打实地分高下。
所以我把排序作为对比测试的核心场景,一方面是因为它在业务里够高频,另一方面是它的性能表现能间接反映数据库在内存、IO、CPU三个方向上的综合能力。替换数据库不是只看功能跑得通,更要看同等硬件条件下扛不扛得住差不多的负载,这一点必须先讲清楚。
1.1 测试目标的设定:不只是比谁的SQL跑得快
做性能对比测试,第一步要做的不是写SQL,而是把测试目标定清楚。目标定得太粗,比如“看看哪个数据库排序快”,后面分析结果时就是一笔糊涂账,根本说不清快在哪、慢在哪、瓶颈在哪。
这次测试我锁定了三个具体目标。
第一,验证基础排序能力。在相同数据集、相同硬件、典型配置下,对比Oracle和崖山执行单列排序、多列排序、字符串排序、分页排序等基础场景的响应时间。这个结果用来回答“能不能打”的问题。
第二,验证内存与磁盘排序的分水岭。通过控制排序数据量跨越内存阈值,观察两者的执行计划是否变化、临时空间使用量如何变化、响应时间曲线是否平滑。这个结果用来回答“内存管理策略差异有多大”的问题。
第三,验证大数据量排序的稳定性。用千万级甚至亿级数据做排序,重复多轮,观察响应时间抖动、临时表空间占用、数据库会话状态。这个结果用来回答“长时间高负载下会不会出幺蛾子”的问题。
测试目标明确了,后面所有环节——数据准备、参数配置、用例设计——都是围绕这三个目标服务。这也避免了一个常见误区:性能测试做到一半,发现测了一堆不痛不痒的场景,数据倒是跑出来一堆,但根本没法支撑选型结论。
1.2 测试环境与工具选型:固定变量才能公平对比
性能对比测试最怕变量不可控,两个数据库跑出差距,到底是内核差距还是环境差距说不清楚,这个测试就白做了。所以环境搭建我花了不少心思。
硬件方面,我用了同一台物理服务器,CPU是Intel Xeon Gold 6248R(48核),内存512GB,SSD用的是企业级P4610,操作系统是CentOS 7.9。两个数据库实例都跑在这台机器上,不涉及跨主机网络延迟的问题。有人会问为什么不在两台同样配置的机器上测,其实单机双实例反而是更严格的控制变量——存储、网络、CPU主频完全一致,对比结果更干净。
Oracle这边是19c(19.17),PGA_AGGREGATE_TARGET设置的是8GB,这是生产环境常用配置。崖山用的最新稳定版,内存相关参数按照产品文档调成了与Oracle近似的可用内存比例。这里有个细节:两个数据库的参数不可能完全等价,因为它们的内存架构本身不同,Oracle的PGA是进程级的,崖山可能是线程级或混合架构,强求参数一致本身就违背了“公平对比”的初衷——我们比的是“各自合理配置下的表现”,不是“相同参数下的表现”。
工具方面,Oracle侧我用SQLPlus配合SET TIMING ON,再加DBMS_UTILITY.GET_TIME做双重计时。崖山侧用YashanDB自带的命令行工具ys_scan或者兼容SQLPlus的交互工具,同样开启计时。另外我还用Python脚本做自动化压力测试,通过python-oracledb和ysdb驱动分别连接两个库,每个用例跑10轮,去掉最高最低值取平均。这里强烈建议测试轮数不要低于5轮,否则GC抖动或者系统其他进程干扰导致的偶发慢查询会把均值带偏。
工具清单整理如下:
| 用途 | Oracle侧 | 崖山侧 |
|---|---|---|
| 交互查询 | SQL*Plus | YashanDB自带CLI |
| 自动化压测 | python-oracledb | ysdb-python驱动 |
| 性能监控 | top / iostat / AWR | top / iostat / 动态性能视图 |
| 执行计划 | EXPLAIN PLAN FOR | EXPLAIN ... |
监控方面,两个库我都开了系统层面的top和iostat,观察CPU、内存、磁盘IO在排序执行期间的实时变化。只盯着SQL响应时间不看资源消耗是典型的“知其然不知其所以然”,数据库排序慢,到底慢在CPU计算还是磁盘写入,这个问题从响应时间上看不出来,必须结合资源监控来判断。
2. 测试数据与排序场景设计:别让不科学的数据毁了测试
测试数据是整个性能测试的地基。数据分布不合理,跑出来的性能数据就没有参考价值。我见过很多人随便建个表,插几百万行连续整数就开测了,这种数据根本代表不了真实业务。真实业务里的排序对象,通常满足几个特征:有大量重复值、有超长字符串、有NULL值、有不同数据类型的混合排序。如果测试数据里这些特征一个都没有,那测出来的只是数据库在“理想数据集”下的表现,和实际生产环境完全是两码事。
这次的测试表,我设计成贴近ERP生产库的样式——一张订单明细表,包含数值列、短字符串列、长字符串列、日期列,总数据量控制在2000万行。数值列故意做成偏态分布:一部分订单ID连续,一部分高度重复,用来模拟热点客户订单集中的业务场景。短字符串列是状态码,基数很低,只有几十个不同值,排序时会产生大量相同键值。长字符串列是备注信息,长度从50到500字符随机分布,用来压字符串排序的比较开销。日期列则模拟业务时间,在一年范围内随机分布。
建表SQL大概长这样(两个数据库语法高度兼容):
CREATE TABLE sort_test ( id NUMBER(12) NOT NULL, order_no VARCHAR2(32) NOT NULL, status_code VARCHAR2(10) NOT NULL, amount NUMBER(12,2) NOT NULL, remark VARCHAR2(500), create_date DATE NOT NULL );数据生成我用的是存储过程批量循环插入,而不是用工具直接灌。目的有两个:一是生成过程可以控制数据分布特征,比如让order_no字段前6位是固定前缀,后6位是递增序号,这样字符串比较时前几位相同、后几位不同,排序要一路比较到最后才能分出大小,计算开销接近真实场景。二是通过存储过程插入本身还能顺带验证两个数据库在PL/SQL或者存储过程语法上的兼容性。
2.1 排序用例设计的五个维度
用例设计我分了五个维度,每个维度对应一类典型业务场景。
第一个维度是单列排序,对应业务里最常见的“按某个字段取数”场景。具体SQL就是SELECT ... FROM sort_test ORDER BY amount DESC。这个用例考察数据库在单键排序时的基础性能,也是后续所有复杂排序的基线。
第二个维度是多列排序,对应报表类业务里的多条件排序。SQL是ORDER BY status_code, amount DESC, create_date。这里有意把status_code放在第一位,因为它的基数极低,排序引擎必须处理大量相同键值的情况,这对排序稳定性是一个考验。同时多列排序的键值比较逻辑更复杂,能够放大两个数据库在比较器实现上的性能差异。
第三个维度是字符串排序,对应单据编号、客户名称这类字段的排序场景。SQL是ORDER BY order_no。字符串排序和数值排序在内部处理上完全不同,数值可以用整型比较指令,字符串要逐字符比较且涉及字符集映射,计算成本高一个量级。我特意让order_no的分布朝着“前缀相同,后缀递减”去设计,就是为了让字符串比较在排序过程中无法提前终止,产生最大的比较开销。
第四个维度是大数据量分页排序,对应前台列表页的“点击表头排序”功能。SQL是SELECT * FROM (SELECT t.*, ROWNUM rn FROM (SELECT * FROM sort_test ORDER BY amount DESC) t WHERE ROWNUM <= 100) WHERE rn > 0。分页排序是生产环境最容易被忽视的性能陷阱。很多人以为只取第一页数据,数据库就不用做全量排序,这是一个极大的误解——ORDER BY加ROWNUM或者FETCH FIRST,数据库同样要把满足条件的数据全部排序好,才能取前100行。这个用例能反映出两个数据库在“排序后截断”这一环节的优化程度。
第五个维度是超大数据量排序,用来压出磁盘排序路径的性能差异。SQL不写WHERE条件,直接对2000万行全部排序,内存肯定放不下,必然触发临时表空间写入。这个用例最能体现两个数据库在磁盘排序机制上的工程差距,也是我这次测试的重点观察对象。
2.2 统计信息与执行计划的必要性处理
不管Oracle还是崖山,优化器生成执行计划都要依赖统计信息。统计信息不准确,优化器可能选择错误的执行方式——比如数据量明明很大,优化器却以为很小,走了内存排序路径,结果运行到一半内存溢出,临时转磁盘排序,性能一落千丈。
所以数据插入完成后、开始性能测试之前,我手动收集了统计信息。Oracle这边用DBMS_STATS.GATHER_TABLE_STATS,崖山侧用它的ANALYZE TABLE或者对应的统计信息收集语法。这一步是性能测试的标准动作,不能跳过。有些开发人员在生产库上遇到过“昨天SQL还跑得好好的,今天突然慢成狗”,大概率就是统计信息过期导致的执行计划飘移,这个属于另一个话题,但在性能对比测试里同样适用。
执行计划层面,我针对每一类测试SQL都单独查看了解释计划,确认是排序操作、排序方式(内存排序还是磁盘排序)以及是否走了索引。这里要特别提醒一个点:排序测试的SQL不要建冗余索引。如果ORDER BY字段上恰好有索引,数据库可能选择索引有序扫描来避免排序,这样测的就不是排序性能而是索引扫描性能了。我这次用的表故意不建任何索引,全部走全表扫描加排序操作,确保压到排序本身。
3. 测试执行与关键参数调整:实测过程记录
测试不是一把梭把所有SQL跑一遍就完了,需要控制执行顺序、观察资源变化、随时调整参数。我按“小数据量热身-中数据量分水岭-大数据量极限”的顺序分三轮执行。
第一轮是小数据量排序测试,数据量100万行。这个量级在内存里就能搞定,跑起来速度飞快,主要看两件事:两个数据库在内存排序路径上的执行计划是否清晰,响应时间的基线上有多大差距。同时确认整体连接、SQL解析、结果集返回环节没有额外问题。
第二轮是中数据量分水岭测试,数据量拉高到500万行。这个量级下,部分排序操作开始逼近内存上限,两个数据库是否触发磁盘排序,触发之后响应时间如何变化,是我最关心的。实际执行时,Oracle侧通过v$sql_workarea_active动态视图能看到排序工作区的内存使用情况,崖山侧也有对应的动态性能视图观察排序内存消耗。通过对比两个库的内存排序占比,能直观看出谁的排序区管理更高效。
第三轮是极限测试,全表2000万行排序。到了这个量级,内存是肯定装不下的,磁盘排序路径必然被触发。这一轮要记录的核心指标包括:SQL总响应时间、临时表空间写入量、CPU利用率、IO等待时间。三轮测试跑完,四个维度的结果拼在一起,才能形成对两个数据库排序性能的完整判断。
3.1 内存参数调整的实战过程
第一轮测试跑完,我发现崖山在小数据量排序时响应时间和Oracle旗鼓相当,但内存排序的阈值好像比Oracle“敏感”——同样数据量下,崖山更早开始使用临时段。这就涉及到排序相关的内存参数调优了。
Oracle侧,19c默认就是PGA自动管理,PGA_AGGREGATE_TARGET=8GB。在这个配置下,100万行的排序基本都在内存里完成,v$sql_workarea_active里能看到大量内存排序操作记录。我保持Oracle这个配置不动,因为这是生产环境最常见、也最合理的配置。
崖山侧,我翻了产品文档,发现它更鼓励用sort_area_size这类精细控制参数。为了公平对比,我没有把sort_area_size设成Oracle PGA那么大,而是设成了2GB——因为崖山的架构下这个参数控制的是单会话排序区上限,2GB已经能覆盖绝大多数排序场景。调整完参数之后,重跑第二轮500万行的测试,崖山在内存排序路径上的表现明显改善,响应时间下降了不少。
参数调整这步其实很有讲究。性能对比测试最怕的就是“拿一个数据库的默认参数去打另一个数据库的默认参数”,这样得出的结果只能说明“默认配置下孰优孰劣”,不能说明“产品能力孰优孰劣”。正确做法是:两个数据库都调整到各自合理的最优配置,再对比。就像比两辆车谁跑得快,得都给满油、胎压都调到标准值,而不是一辆满油一辆半油。
3.2 执行计划对比分析
参数调整完之后,我对比了两个数据库在同一排序SQL上的执行计划,这里面的信息量很大。
Oracle的执行计划,核心部分是SORT ORDER BY操作,后面跟着TABLE ACCESS FULL。在500万行数据量下,Oracle的优化器直接选择了内存排序,PGA分配了足够空间。执行计划里看不到排序方式的具体细节,但通过v$sql_workarea_active能确认是内存排序还是磁盘排序。
崖山的执行计划同样显示SORT ORDER BY后跟TABLE ACCESS FULL,结构上和Oracle非常接近。这一点在我看来是个好信号——崖山作为Oracle兼容数据库,执行计划的基本形态和Oracle做到了对齐,那数据库内核在排序实现上的底层逻辑也大概率是参考了主流数据库的做法,而不是另起炉灶。
但执行计划形态接近不代表性能就一致。实际跑下来,500万行排序,Oracle耗时约3.2秒,崖山耗时约3.8秒,差距在18%左右。这个差距我可以接受,毕竟Oracle在排序这块打磨了二十多年,各种指令集优化、内存预取、缓存命中策略都做到极致了。崖山作为一个相对年轻的数据库,能追到80%多的水平已经不容易。真正的差距出现在第三轮极限测试,这个后面细说。
3.3 大数据量排序与临时表空间表现
第三轮2000万行全表排序,Oracle耗时约22秒,崖山耗时约31秒,差距拉大到了40%。为什么数据量越大差距越明显?这就要看临时表空间的表现了。
执行期间我同时开着iostat监控。Oracle在排序过程中,磁盘写入集中在临时表空间的数据文件上,写入模式比较规律,基本是顺序写入配合少量随机写。崖山在排序期间的临时空间写入量比Oracle多了约25%,而且写入模式更分散。为什么会这样?我判断主要是临时段分配策略的差异:Oracle的临时表空间按extent批量分配,一次拿一大块连续空间,排序中间结果紧密排列,写入效率高;崖山可能在临时段分配上更偏向按需小步分配,导致多次分配、多次定位,IO次数增加,整体效率下降。
这26秒和31秒之间的差距,在真实业务里会放大到不可接受。比如一个每日跑批的报表,排序数据量上了亿级,响应时间差40%,批处理窗口就会被拉长,影响后续依赖链路的执行。这也是为什么我强调“性能对比一定要测到磁盘排序路径”,只看内存排序的差距,会低估替换数据库对生产系统的真实影响。
4. 测试结果汇总与分析:数据不会说谎
所有测试跑完,我把结果整理成了表格,方便对比。
| 测试场景 | 数据量 | Oracle耗时 | 崖山耗时 | 性能差距 |
|---|---|---|---|---|
| 单列数值排序 | 100万 | 0.8s | 0.9s | 12% |
| 单列数值排序 | 500万 | 3.2s | 3.8s | 18% |
| 多列混合排序 | 500万 | 4.5s | 5.6s | 24% |
| 字符串排序 | 500万 | 5.1s | 6.3s | 23% |
| 大结果集排序 | 2000万 | 22.0s | 31.0s | 40% |
| 分页排序 | 2000万 | 12.0s | 16.5s | 37% |
从趋势上看,有三个明确结论。
第一,数据量小时差距小。100万一档,俩库都在毫秒到秒级完成,要不是把轮次重复了10次取平均,那12%的差距甚至可能被噪声掩盖。这说明在小数据量场景下,崖山的排序能力完全可以胜任,替换风险很低。
第二,复杂度上来差距扩大。多列排序、字符串排序比单列数值排序的差距明显更大,从12%扩大到24%左右。这说明崖山在复杂比较器(多字段联合比较、字符串逐字符比较)上的实现效率还有优化空间,Oracle在编译器级别的比较指令优化上积累的优势体现了出来。
第三,数据量压过内存阈值后差距彻底拉开。2000万行全表排序40%的差距,根源在临时表空间IO效率。这一点从iostat数据里能明确看到,Oracle的磁盘写入更加平滑有序,崖山的磁盘写入更多更碎。数据库排序性能的最后的瓶颈永远在IO,谁能在临时数据落盘这一层做得更高效,谁就能在大数据量场景下赢得最终优势。
4.1 响应时间之外:资源消耗对比
响应时间只是表象。真正到生产环境,数据库不只会跑一条排序SQL,同一时刻可能有几十个会话在并发执行各种操作。所以我还额外采集了CPU利用率和IO吞吐数据。
峰值CPU利用率这块,Oracle在2000万行排序期间CPU峰值达到85%,崖山达到91%。CPU高不一定差——如果排序用更多CPU换来了更低的响应时间,那是好事。但问题是崖山CPU更高、耗时却更长,说明它在排序算法上比Oracle多做了更多的无用比较计算。这一点如果能拿到profile信息进一步剖析就更清晰了,但考虑到两个库的profile机制差异较大,我没有往这个方向深挖,留给后续专项测试。
IO吞吐层面,Oracle在排序期间的写吞吐稳定在300MB/s左右,崖山在280MB/s到350MB/s之间波动更大。崖山的临时空间写入量更大,但吞吐反而没有明显优势,进一步印证了写入模式碎片化的问题。这块如果能优化崖山的临时表空间文件布局,差距有机会收窄,但收窄的程度无法仅凭这次测试预估。
4.2 功能兼容性:一个意外的发现
测试是性能导向的,但在运行SQL的过程中,我也顺手验证了语法兼容性。整体上,崖山对Oracle排序相关SQL语法的兼容度做得非常不错:ORDER BY、FETCH FIRST、ROWNUM分页、窗口函数里的ORDER BY子句,这些用法在崖山侧都能直接跑通,不需要改SQL。这一点对于从Oracle迁移过来的业务系统太重要了——改动越小,迁移成本越低,风险越小。
唯一一个让我注意到的差异,是NULL值的排序位置。Oracle默认NULLS LAST,而崖山的一个早期版本在混合升降序排序时NULL排序行为在某些边界情况有细微差别,需要显式指定NULLS FIRST或NULLS LAST保证结果一致。这次测试用的最新版已经兼容了Oracle的默认行为,但这个坑值得所有迁移项目注意:千万不要假设“排序结果一样”,一定要在测试阶段专门设计NULL值排序用例,对比两个数据库返回结果集的行顺序是否完全一致。排序结果看起来都是“升序”,但NULL位置不同,业务上可能就意味着一批单据被错误地排到了最前面。
5. 常见问题与避坑指南:这些坑我是真踩过
性能测试里面踩坑是常态,不踩坑反而说明测得太浅。我把这次测试过程中遇到的几个典型问题和解决办法整理一下,给后来人省点时间。
5.1 统计信息缺失导致执行计划误判
第一次跑2000万行测试的时候,我偷了个懒——数据灌完后没有立即收集统计信息,直接开跑。结果Oracle这边执行计划显示全表扫描,数据量识别正确,但排序内存预估偏小,触发了磁盘排序;崖山那边更离谱,优化器把表行数估成了50万行,选择了内存排序路径,结果跑到一半内存溢出,自动转为磁盘排序,耗时爆炸,而且第一次跑的31秒数据实际上是“内存排序失败后补救”的耗时,并不能代表崖山在合理配置下的真实水平。
收集统计信息之后再重跑,崖山的执行计划才恢复正常,优化器正确识别了2000万行的数据量。这里想提醒所有做数据库对比测试的人:统计信息收集是性能测试的前置条件,不是可选项。跳过这一步,你测出来的所有数据都可能是优化器“猜错”的结果,不具备参考价值。
5.2 连接池与并发连接设置不一致
我一开始用自动化脚本压测时,两个数据库都是单连接串行执行,结果数据拉不开差距。后来我想模拟更真实的生产场景,改成10个并发连接同时执行排序SQL,结果遇到了一个尴尬的情况:Oracle的连接池很稳定,10个会话同时跑排序,响应时间基本线性增长;崖山的连接池配置参数和Oracle不同,默认最大连接数偏小,10个并发里有两个连接直接报错。
这其实不是排序性能的问题,而是连接管理能力的差异。如果直接拿这个结果去说“崖山并发能力不行”,那就是误伤。正确做法是先确认两个数据库的连接池配置是在各自合理水平上,再谈并发测试。我把崖山的最大连接数调大后,并发测试才算进入正轨。这类问题在数据库对比中太常见了——不是数据库不行,是环境配置没对齐。
5.3 客户端工具计时偏差
很多人测数据库性能喜欢用客户端工具看耗时,比如IDE自带的结果面板、报表工具的执行时间统计。这些工具显示的耗时不单单是数据库执行时间,还包括结果集网络传输时间、客户端渲染时间。排序2000万行返回2000万行,光网络传输和客户端渲染就能吃掉好几秒,如果拿这个数字去对比两个数据库的排序性能,误差大到没法看。
我的做法是,所有计时都以数据库服务端的执行时间统计为准。Oracle看SQL*Plus的TIMING输出,崖山看命令行工具的统计信息。自动化脚本里的计时也从SQL执行完成回调开始算,不包含结果集遍历的时间。这个细节如果不注意,测出来的“性能差距”有一半是客户端差异,根本站不住脚。
5.4 缓存影响与重复执行的假象
排序性能测试还有个隐性问题:缓存。第一次执行时数据要从磁盘读入,第二次执行时数据已经在操作系统的page cache里了,读数据的耗时大幅下降。如果只跑一次就下结论,结果完全取决于缓存命中情况。我在设计测试方法时,每个场景固定跑10轮,前两轮作为热身不计入数据,取后8轮的平均值。这样才能排除缓存带来的系统性误差。
另外还发现一个有趣的现象:Oracle对排序结果的缓存利用做得更好,同一SQL重复执行时,响应时间逐轮下降非常明显,甚至能在共享池里直接命中排序结果;崖山的重复执行优化相对弱一些,但差距集中在缓存路径而不是排序本身,这一点在后面分析整体差距时也要把“缓存贡献”剥离开,不能都算成排序性能差距。
5.5 排序结果正确性校验:必须做
最后一类问题是数据正确性。性能再快,结果错了等于零。我建议所有排序测试都加上结果校验环节:两个数据库执行同样的排序SQL,导出排序后的前1000行和最后1000行,做逐字段比对。不要只比行数,要比内容。我这次测试中在字符串排序场景就抓到一个不一致的例子——某个特殊字符在Oracle的字符集排序规则下排在一个位置,在崖山的规则下排在另一个位置。需要说明的是这个用例在后续查证中发现是数据库字符集设置不同导致的,不代表崖山排序逻辑有bug,但这个问题恰恰说明了结果校验的必要性。
6. 工具选型与自动化测试脚本实现
手工一条条执行SQL测性能太慢,而且数据记录容易出错。这次我用Python写了一套简单的自动化测试脚本,自动连接数据库、执行SQL、记录耗时、计算均值。Oracle用python-oracledb驱动,崖山用官方提供的ysdb驱动,两边的调用方式非常接近,脚本结构可以共用。
脚本的核心逻辑不复杂:定义用例列表,每个用例包含SQL语句和对应的场景名称,循环执行指定轮次,收集每轮耗时,去掉最高最低后计算平均值。为了避免结果集传输影响计时,我在SQL里包了一层COUNT或者直接用游标但不fetch结果集,只统计execute阶段的耗时。这里有个小坑需要注意:很多驱动默认在execute时就完成了全部结果的读取,如果要模拟真实业务的排序+取数过程,那就需要show_fetch性能指标,这个看具体需求。
自动化脚本的好处是把人为操作的偏差降到最低。手工执行SQL时,两次操作之间可能间隔了30秒,数据库的缓存状态已经变了;脚本连续执行则能保证每轮之间间隔可控,数据可比性更强。我强烈建议任何数据库性能对比测试都至少做一个简单自动化脚本,哪怕是命令行循环也好,别信手工测出来的数据。
7. 结论与后续建议:排序性能的差距现状与优化空间
这次Oracle vs 崖山排序性能测试的最终结论可以概括为三句话:小数据量场景两者差距不大,崖山完全可用;复杂排序场景差距在20%左右,需要关注但不算致命;大数据量磁盘排序场景差距拉大到40%,这是当前最需要关注的短板。
如果要做数据库选型,我给三条务实建议。
第一,不要只看排序性能做决策。数据库选型涉及的维度太多了:SQL兼容性、事务能力、高可用方案、生态工具、运维成熟度、厂商支持力度。排序性能只是其中之一,而且排序恰恰是数据库内核优化空间最大的领域之一,差距是动态变化的。
第二,如果业务里存在海量排序场景(比如超大型报表、数仓ETL),建议先用真实数据量做一轮针对性测试,确认临时表空间IO会不会成为瓶颈。如果测试结果不理想,可以尝试通过增加内存参数、优化临时表空间文件布局来缩短差距,这个方向值得投入。
第三,崖山的SQL兼容性已经做到了比较高的水平,迁移过程中因为SQL语法导致的问题占比很小,真正的风险在性能和边界行为差异上。所有排序SQL在迁移后都要做结果比对,不能默认两边输出一致。
这次测试之后,我准备进一步做两类延伸测试:一类是排序与其他操作混合的场景,比如排序+关联、排序+聚合,看看崖山在复杂执行计划下的表现是否像单一排序场景一样稳定;另一类是长时间稳定性测试,让排序SQL在崖山上持续跑几个小时,观察临时段回收、内存泄漏、性能衰减等问题。数据库替换是件大事,一次测试解决不了所有问题,但至少能让我们拿着数据去和厂商谈,而不是空口说“感觉慢”。
最后分享一点个人体会:测试数据库性能,最大的敌人不是数据库,而是你脑子里预设的偏见——觉得老库就是稳,新库就是不行。放下这些预设,老老实实设计用例、控制变量、重复多轮、汇总数据,你会发现很多直觉和最终结果并不一致。数据才是唯一值得信任的东西。