openGauss源码解析(161)
openGauss源码解析:AI技术(8)
5. 算法模块源码解析
可以将数据库的离线参数调优过程看作一个组合优化过程,即找到使数据库性能最好时的参数配置,该过程可以通过下述数学表达式描述。
其中,表示数据库在某个参数配置下的性能,表示数据库的参数配置,表示数据库参数的可配置集合。
X-Tuner支持的算法包括DDPG、PSO、贝叶斯优化,虽然实现原理不同,但他们都可以搜寻上述表达式中的,即数据库的最优参数配置。DDPG算法和贝叶斯优化算法通过引入第三方库实现,PSO算法则自行实现,实现代码在algorithms/pso.py文件中。
在tuner/xtuner.py文件中定义了全局搜索算法与强化学习算法的执行流程,其中强化学习算法的流程代码如下:
def rl_model(mode, env, config):
# 由于加载tensorflow的过程过于耗时,且并非是必要的,因此采用懒加载的模式
from tuner.algorithms.rl_agent import RLAgent
# 启动强化学习代理类
rl = RLAgent(env, alg=config['rl_algorithm'])
# 训练和调优两种模式对应不同的执行流程
# 模型需要先训练,然后才可以利用该模型进行调优。训练和调优过程的输出是待调优参数列表,由于共用一套模型,因此两种模式下,要求待调优的参数列表必须是一致的,否则会抛出输出维度不同的异常。
if mode == 'train':
logging.warning('The list of tuned knobs in the training mode '
'based on the reinforcement learning algorithm must be the same as '
'that in the tuning mode. ')
# 比较关键的参数是最大迭代轮次rl_steps,理论上越长越精准,但是也更加耗时
# max_episode_steps是强化学习算法中的每一轮的最大回合次数,在X-Tuner实现中,该参数被弱化了,一般默认即可
rl.fit(config['rl_steps'], nb_max_episode_steps=config['max_episode_steps'])
rl.save(config['rl_model_path'])
logging.info('Saved reinforcement learning model at %s.', config['rl_model_path'])
elif mode == 'tune':
…
全局优化算法的流程代码如下:
def global_search(env, config):
method = config['gop_algorithm']
# 判断选择使用哪种算法
if method == 'bayes':
from bayes_opt import BayesianOptimization
action = [0 for _ in range(env.nb_actions)]
pbound = {name: (0, 1) for name in env.db.ordered_knob_list}
# 定义一个黑盒函数,用于适配第三方库的接口
def performance_function(**params):
assert len(params) == env.nb_actions, 'Failed to check the input feature dimension.'
for name, val in params.items():
index = env.db.ordered_knob_list.index(name)
action[index] = val
s, r, d, _ = env.step(action)
return r # 期望结果越大越好
optimizer = BayesianOptimization(
f=performance_function,
pbounds=pbound
)
optimizer.maximize(
# 最大迭代轮次越大结果越精准,但是也更耗时
n_iter=config['max_iterations']
)
elif method == 'pso':
from tuner.algorithms.pso import Pso
def performance_function(v):
s, r, d, _ = env.step(v, False)
return -r # 因为PSO算法的实现中是寻找全局最小值,这里取相反数,就改为取全局最大值
pso = Pso(
func=performance_function,
dim=env.nb_actions,
particle_nums=config['particle_nums'],
# 最大迭代轮次越大结果越精准,但是也更耗时
max_iteration=config['max_iterations'],
x_min=0, x_max=1, max_vel=0.5
)
pso.minimize()
else:
raise ValueError('Incorrect method value: %s.' % method)
上述代码描述的是离线调优过程的策略,对于在线调优,则主要是以启发式规则的方法实现的,其主要代码存在于tuner/recommend.py中,此处逻辑大同小异,下面以shared_buffer参数推荐为例:
@cached_property
def shared_buffers(self):
# 此处应用的是DBA普遍认同的调优策略:在大内存环境下,shared_buffer占比可更高一些,小内存情况下占比应下调
mem_total = self.metric.os_mem_total # unit: kB
if mem_total < 1 * SIZE_UNIT_MAP['GB']:
default = 0.15 * mem_total
elif mem_total > 8 * SIZE_UNIT_MAP['GB']:
default = 0.4 * mem_total
else:
default = 0.25 * mem_total
recommend = default / self.metric.block_size
if self.metric.is_64bit:
database_blocks = self.metric.all_database_size / self.metric.block_size
# 如果数据库文件的比较小,则shared_buffer 也无须设置得太大,否则便是浪费资源
if database_blocks < recommend:
self.report.print_warn("The total size of all databases is less than the memory size. "
"Therefore, it is unnecessary to set shared_buffers to a large value.")
recommend = min(database_blocks, recommend)
upper = recommend * 1.15
lower = min(0.15 * mem_total / self.metric.block_size, recommend)
return Knob.new_instance(name="shared_buffers",
value_default=recommend,
knob_type=Knob.TYPE.INT,
value_max=upper,
value_min=lower,
restart=True)
else:
# 对于非64位操作系统,shared_buffer无须设置得太大
upper = min(recommend, 2 * SIZE_UNIT_MAP["GB"] / self.metric.block_size) # 32-bit OS only can use 2 GB mem.
lower = min(0.15 * mem_total / self.metric.block_size, recommend)
return Knob.new_instance(name="shared_buffers",
value_default=recommend,
knob_type=Knob.TYPE.INT,
value_max=upper,
value_min=lower,
restart=True)
不同的参数应用的规则都不相同,主要参考数据库的workload特征、硬件环境、当前状态等。即对于AP与TP场景,参数配置是不同的,如果用户没有通过配置文件明确指定场景的类型,则根据character.py文件中定义的workload_type()方法自动判断,获取数据库特征的方法都在character.py文件中定义。