重生之我在金仓开发数据库:KES 性能飞跃的秘密

【我...居然重生了】
大家好,我叫柯益思,一枚程序猿,从事数据库开发工作。
你敢相信吗?我...居然重生了!!!
上一世,在国产数据库百团大战中,我站错了队伍,公司惨遭市场淘汰,中年失业,最后落魄一生,郁郁而终...
这一世,带着前世的记忆和对未来的洞察,凭借先知者的优势,我势必一雪前耻,站在胜利者的行列重新夺回属于自己的荣耀!
【打不过就加入】
俗话说,站在风口上,猪都能飞起来~
现在是北京时间 2024 年 1 月 1 日,仍处在国产数据库行业的风口上。
伴随着“战争”带来的硝烟弥漫,在残酷的“下半场”淘汰赛即将开启之际,
我加入了属于我的的风口--金仓数据库,
准备大展拳脚。
看,缘分就是那么妙不可言,柯益思 VS KES
上一世我到底错过了什么?
OMG!
【钮钴禄氏·柯益思】
从上文你应当看出来了,我是一只有追求的程序猿,
重生后去大 A 当股神实非我愿(毕竟股市水太深,股神和韭菜/天堂与天台之间有时候也就一念之间)
研究高性能的数据库才是我的真爱与使命!
譬如现在,我正在研究一个关键问题
“在高并发、大数据量的 TP 业务场景下,如何提高业务系统的性能?”
【让数据库自己解决】
针对上述问题,执行计划缓存,无疑是其中的关键一环。在一个业务系统中,数据库会处理大量的 SQL 语句。但许多的 SQL 语句只是其中的常量值不同,其 SQL 的形式和最终的执行计划完全相同。数据库需要不断重复的为这些 SQL 语句计算最优的执行计划,最终得到的结果却是相同的结果。大量的 SQL 处理时间和系统中的 CPU 资源用在了重复的工作上面。
那么,我们为什么不让数据库自己把之前生成的执行计划缓存起来,下次执行相同格式的 SQL 时直接使用!
【执行计划缓存,KES 性能飞跃的秘密】
研究中,我们发现执行计划缓存如同一把隐形的钥匙,能够解锁系统性能的无限潜能。
执行计划,是数据库管理系统在接收到 SQL 查询后,经过解析、优化等阶段生成的执行蓝图。而执行计划缓存,则是数据库管理系统为了提升查询效率,将这些精心计算的执行计划存储起来,以便在后续遇到相同或相似的查询时,能够直接复用,避免重复劳动。这大大减少了查询编译的时间开销,是提升业务系统性能的关键一环。
金仓数据库支持两种执行计划缓存方案。
1. 进程本地内存的执行计划缓存方案
金仓数据库本地的执行计划缓存方案是每个用户连接在本地进程内存中保存各自的执行计划,由客户端来控制执行计划缓存的行为。
以一个 JDBC 客户端为例。JAVA 程序首先定义一个 PreparedStatement 对象,SQL 中常量全部以参数标识。每次当需要发送 SQL 请求时,将参数赋值并发送请求到客户端。通过这种方式,可以保证同样形式的 SQL(只有常量不同)可以重用相同的执行计划,从而提高执行计划的利用率。
在数据库所在的服务器内存资源充足,并且数据库的客户端能够按照这种参数化的格式发送 SQL 的情况下,这种执行计划缓存的方式的性能最好。

执行本地内存的计划缓存性能测试结果:
对于使用本地内存的执行计划缓存和不使用执行计划缓存的场景,TPC-C 的测试结果表明,在启用执行计划缓存后,通过使用进程本地执行计划缓存的方式,系统的性能可提升 1.6 倍!
数据集:TPCC 1000 Warehouses,100G
测试服务器:HiSilicon Kunpeng-920 96c

未使用执行计划缓存,tpmC 为 92 万,使用执行计划缓存,tpmC 为 148 万。

然而,本地执行计划缓存方案并非适用于任何场景。一方面,本地执行计划缓存将会占用过高的内存;另一方面如果用户不能按照参数化的格式发送 SQL 请求本地执行计划缓存的重用率不高。在有限的 CPU 和内存资源场景下,本地内存的执行计划缓存方案将会产生性能瓶颈,无法达到预期的性能优化目标。
当问题从“在高并发、大数据量的业务场景下,如何提高业务系统的性能?”转向“在高并发的业务场景下,如何利用有限的 CPU 和内存资源,高效的处理 SQL 请求,从而提高整个业务系统的性能?”后,金仓数据库共享内存的执行计划缓存方案闪亮登场。
2. 共享内存的执行计划缓存方案
为了解决执行计划缓存占用内存高的问题,金仓数据库采用了在共享内存中保存执行计划的方案。
金仓数据库共享内存的执行计划缓存方案是每个连接不再保存执行计划,而是从共享内存中存取执行计划。通过这种方式,执行计划缓存的空间不再受到客户端连接数的影响,并且可以进一步提高缓存计划的复用率。当执行计划缓存占用空间达到门限值时,数据库会释放一定比例重用率比较低的执行计划,从而保证内存占用在一个可控的范围之内。

此外,为了解决 SQL 中带有常量从而执行计划缓存复用率不高的问题,数据库在接收到 SQL 之后,首先会进行常量的参数化,即将 SQL 中的常量替换为参数,然后根据参数化后的 SQL 去查询执行计划缓存。通过这种方式保证多条 SQL 如果只有常量不同,他们可以重用相同的执行计划。

执行共享内存的计划缓存性能测试:
在有限的 CPU 和内存资源场景下,当将缓存方案从本地内存的执行计划缓存改为采用共享内存的执行计划缓存方案后,数据库不再成为整个系统的性能瓶颈。
数据集:业务系统数据量 20G,涉及 800 多种不同的 SQL 语句
测试服务器:Intel(R) Xeon(R) Platinum 8268
在数据库的压力逐渐增加到 5 万并发过程中,整个系统的 TPS 在稳步的增长,波动幅度很小。到 5 万并发时,TPS 稳定在 1500 左右。经过几个小时的运行,一直非常稳定。

其次,整个数据库系统占用的内存从 1.2T 降低至 200G,并且随着并发连接数量的增加,整个数据库系统占用的内存增幅不大。

3. 精准施策,KES 的两套方案
综上所述,我们设计的两套执行计划缓存方案可以满足不同业务场景下系统对性能的要求:
当系统内存充足并且客户端的程序能够按照数据库要求的格式发送 SQL 语句时,本地缓存的执行计划效率更高。
当系统内存资源有限,并发数量大,并且用户发送的 SQL 语句直接带有常量时,采用共享内存的方案效果更好。
在实际应用中,用户可以根据业务系统的特点和需求,选择合适的缓存策略,并结合执行计划缓存等优化手段,共同提升系统的整体性能。

因为数据库支持两种通信协议,所以目前实现的方式是每种协议对应一种执行计划缓存的方法。在将来的版本中,我们会研究将这两者融合成一套方案。
【没有终点】
我叫柯益思~
前世的我,3 岁便与 Python 结缘,5 岁已深谙 Java 之道,满怀着成为数据库界璀璨新星的梦想翱翔。然而,命运弄人,我在那条路上遭遇了意想不到的挫折,未能如愿照亮天际。
三十年河东,三十年河西。
如今,在这一世,我终于得以弥补前世的遗憾,达成了心中的夙愿,实现了圆满。
但人生没有终点,只有更高的起点。在金仓数据库的每一天,我都在与一群志同道合的伙伴共同探索高性能数据库的奥秘,用代码给性能加速,用智慧为数据护航,不断挑战技术的极限。每一次技术突破,都是对“不可能”的一次有力回击,也是我们向更高起点迈进的坚实步伐。每一次与用户的深入交流,都是为了让数据更好地服务于社会,赋能各行各业的发展。
版权声明: 本文为 InfoQ 作者【金仓技术】的原创文章。
原文链接:【http://xie.infoq.cn/article/2d03c362d525f6af9cfcaf942】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论