基于机器学习的数据库自动参数配置——以PostgreSQL为例的实践
工作背景与意义
数据库管理系统(DBMS)配置优化是任何数据库应用的努力方向,DBMS有数百个配置参数,这些参数不独立,不通用,往往这些参数的选取通常来自宝贵的经验。随着机器学习领域的发展,近年来,已经有越来越多的软件借助机器学习实现了系统的自动化运维。其中,华为研发的openGauss数据库中的xtuner特性就是AI实现数据库参数调优的典型代表。
在本次专业实践中,我在老师的指导下尝试将xtuner模块复现到开源数据库postgreSQL中,并在数据库性能测试脚本BenchmarkSQL的辅助下对数据库性能进行测试。现将实验中的步骤、细节、成果与遇到的问题分述如下,作为短暂实习经历的总结与记录。
环境搭建与调试
环境配置过程中遇到的问题
首先是硬件资源问题。openGauss需要的编译硬件要求为:4核CPU,8G主存,100G硬盘空间;postgresql在2核、4G内存的机器上能够正常编译;benchmarksql对编译无特殊硬件要求。我的个人电脑最多能提供2核、8G内存、50G硬盘空间的条件,经测试postgresql的主要功能、代码修改、强化学习模型运行都能在这个环境下正常运行,此外我又申请了公司一台联想Y9000笔记本电脑供openGauss环境配置使用。
其次是编译流程与版本问题。openGauss要求的linux 64位操作系统仅为CentOS 7.6。对于操作系统的判定被硬性地写在编译脚本中。此外,openGauss还依赖一整套的软件生态,如gcc-7.3.0等等,这些软件可以自行编译配置,也可以通过安装现成的软件包来实现。在我个人看来,对于一个开源项目来说,openGauss的版本限制略显僵硬。
openGauss相比postgresql,编译过程更加复杂。官方给出了两个编译方式,一键式运行build.sh,或者手动执行makefile。相比较而言,我认为反而是手动编译更为保险,这样可以充分理解每个编译部分的目的,还可以根据硬件条件取消并行编译策略。
openGauss配置细节
主要参考的是官方版本编译\href{https://opengauss.org/zh/docs/2.0.1/docs/Compilationguide/版本编译.html} {openGauss}中的手动编译方法,环境为联想Y9000P,4核8G内存200G硬盘空间的虚拟机,、CentOS 7.6版本。 在最终make与make install时,需要去掉-sj的编译参数以免并行编译导致虚拟机崩溃。
postgresql配置细节
主要参考了\href{https://www.postgresql.org/docs/13/index.html}{postgresql-13官方文档},以及网上的一些经验分享。整体上postgresql的配置分为代码编译、用户配置、环境变量配置、后台进程配置和数据库启停等步骤。详细过程记录在我的个人博客中。
benchmarksql配置细节
主要参考了一些网上的博客。对于openGauss的测试,需要对benchmark的脚本和代码进行一定的适配。由于个人电脑的硬件限制,最终的测试效果数值并不高。
华为openGauss数据库的算法实现
模块使用
xtuner分为train,tune,recommend三种工作模式。其中,train工作模式通过调用强化学习模块来对数据库的参数进行训练,并将最终的结果以报告的形式呈现出来;tune工作模式对数据库参数进行实际调优,根据使用场景不同可以选择导入训练好的报告或者直接全局调优两种策略;recommend对当前数据库的使用场景推荐一个简单的参数配置。
一个简单的参数推荐列表与指标评价列表如下所示:
在上述报告中,推荐了该环境上的数据库参数配置,并进行了风险提示。报告同时生成了当前workload的特征信息
算法介绍
对于强化学习模块,xtuner目前可以支持DQN和DDPG两种方法(其中DQN算法的实现还不完整)。网络的搭建是用keras来完成的。对于tune工作场景下的全局搜索策略,xtuner目前可以支持PSO和贝叶斯两种算法。
xtuner源码精读
功能模块
数据库参数封装模块
主要的定义类为RecommendedKnobs,定义在knobs.py文件中。该类预设了数据库参数的一些基本信息,如数据库总共有哪些参数以及它们的数据类型、在什么场景下推荐调优哪些参数、这些参数的初始值和推荐值等等。
ssh通讯模块
xtuner是独立于数据库来运行的。因此,实现xtuner脚本和数据库的通讯就格外重要。这部分的关键类是ExecutorFactory和SSH。ExecutorFactory是一个工厂类,本质上是通过构造一个SSH类的实例来完成连接的。如果数据库运行的IP地址为本机,则SSH是通过构造一个子进程subprocess来运行命令行参数的;如果IP地址来自互联网,则远程数据库的连接通过调用paramiko来完成。
数据库封装模块
xtuner定义了DB_Agent类来实现数据库的封装。类中包含了一系列数据库操作方法如检查连接、执行指令、改变数据库参数的值等。由于openGauss与postgresql的操作方法、参数细节有所不同,故这部分是我们主要需要修改的部分。
xtuner定义了DB_Env类来对DB_Agent与其他配置信息进行又一层封装。封装之后的对象具有step方法,可以作为参数传给强化学习模块。
强化学习算法模块
定义了强化学习类RL_Agent,包含之前提到的两种强化学习算法。
benchmark模块
以驱动脚本的形式存储在benchmark文件夹下,通过调用get_benchmark_instance()来使用。
本工具默认带有TPC-C、TPC-H、TPC-DS以及sysbench的benchmark运行脚本样例,如果用户使用上述benchmark对数据库系统进行压力测试,则可以对上述配置文件进行适度修改或配置。如果需要适配用户自己的业务场景,需要参照benchmark目录中的template.py文件编写驱动自定义benchmark的脚本文件。
执行流程
首先,在main函数中,根据使用者传入的参数,构造出mode, db_info和config。其中mode为字符串类型,来自args输入,分为train, tune, recommend三种;db_info为字典类型,同为args输入,包含数据库相关信息与IP、端口;config为字典类型,由文件xtuner.conf提供,包含一些基本的设置。
main函数调用xtuner.py中的procedure_main函数,并传入上述三个参数。在procedure_main中,根据db_info中的信息构造一个实例db_agent,作为与数据库进行连接的接口;根据mode和db_info构造出RecommendedKnobs,再将db_agent与knobs合并,封装到实例db_env中。
db_env为算法模块需要的数据。可以直接调用rl_model函数,并传入mode, db_env, config进行训练与调优。
实际上实现的rl模型只有ddpg一种,通过keras搭建的网络。尽管给出了dqn模型的接口,但目前并没有得到完整实现。
各模块之间的关系,以及xtuner的整体结构如图所示:
关键函数
recommend_knobs(mode, metric)
参数:功能模式、工作环境与状态信息
返回值:RecommendedKnobs,若干封装好的参数类。
通过metric可以构造一个advisor类,该类可以给出openGauss适合进行调优的所有参数。通过调用advisor.all_properties(tune_knobs)方法,可以得到再当前模式下推荐的调优参数列表。这里可以看到,xtuner再train模式下实际上只是在对shared_buffers与work_mem进行调优。
DB_Env.step(self, action, delta=False)
参数:knob的当前设置、步长(可选)
返回值:新的观测值、reward、相关信息。(在强化学习ddpg的语境下)
本函数为强化学习模型中每一步的基础操作,因此涉及到observation, reward等强化学习概念。总之,它是一个根据当前参数状态,给出下一步参数状态的函数。具体的参数更新方式为:
obs = self.db.get_knob_normalized_vector() + self.db.metric.get_internal_state()
reward = score - self.mem_penalty * used_mem
DB_Agent. set_knob_normalized_vector(self, nv)
参数:新的参数值列表
返回值:无返回值
被step函数调用,实现参数值的更新。具体地,该函数调用另一个函数set_knob_value,通过生成参数更新指令(openGauss中是gs_guc,postgresql中是ALTER SYSTEM SET)并传给ssh模块进行执行。
实现细节
基于上述对xtuner的认识,我将其代码进行了调整,使得xtuner能对postgresql数据库提供支持。以下称修改过后的xtuner为pg-xtuner。
指令适配
主要的改动有以下几种: \begin{itemize} \item 命令行指令名称,如将’gs_ctl’修改为’pg_ctl’,’gsql’改为’psql’等; \item 数据库名称,如检索数据库进程数量时,将’grep gaussd[b]’改为’grep postgres’; \item 参数修改。opengauss有支持特殊的命令行参数修改指令’gs_guc’,这里需要用SQL语句’ALTER SYSTEM SET’代替; \item 特殊参数。对比两个数据库的官方文档,我发现opengauss中的max_process_memory参数在postgresql中没有对应的名称,鉴于该参数在代码中发挥的作用不是很重要,我将会引起报错的语句进行了删除处理; \item 数据库启停策略。在实践过程中,我发现opengauss支持使用stop+start的语句进行正常的重启,而postgresql需要添加输出重定向的措施防止重启时缓冲区死锁。 \end{itemize}
测试结果
训练参数:rl_steps=100, max_episode_steps=20,调优策略为导入训练结果。
训练前的TPC-C吞吐率与任务延迟如下:
训练后的TPC-C吞吐率与任务延迟如下:
总结与不足
可以看到经过训练调优后的结果与调优前有显著不同。tpm指标在测试起始的5分钟内相比训练前有显著提高,而在测试后半段显现出极大的波动性。这可能是由于随着压力测试的进行,计算机的CPU、内存等硬件条件逐渐受到限制,使得结果出现波动。
事实上,在整个实验的过程中,我都感到个人电脑在应对工业级数据库调优任务上的不足。内存资源限制了work_mem的大小、硬盘空间制约了仓库数量。此外,个人电脑还面临环境配置困难等问题。
抛开硬件问题不谈,本次实践中还有一些有待提升的空间。例如我对数据库的使用流程、场景以及原理尚不清晰,对具体算法的实现还一知半解。本来还有补全DQN算法的计划,由于时间与经历的限制未能成行。
但是通过本次实践,我还是学会了很多东西,掌握快速熟悉新环境、新软件的技巧,对数据库参数调优的理解与应用也有了一定的深入。在此特别感谢王若楠老师的指导,也希望能将实践中学到的技能真正运用在未来的学习和工作中。