数据库内核月报

数据库内核月报 - 2021 / 08

MySQL · 源码分析 · 参数解析流程

Author: yixiong

背景

mysql有很多参数,innodb存储引擎也有自己独立的参数,这篇文章分析一下参数解析的流程。代码版本:8.0.13

mysql参数

sys_vars.cc 里面定义了很多参数,各种类型都有,这些参数都是sys_var的子类,所有的参数在构造函数里面都会加到all_sys_vars链表中。
在看实际参数之前,我们先学习一下 sys_var类的主要成员变量

class sys_var {
 public:
  sys_var *next;				//next指针,all_sys_vars链表遍历的时候使用
  LEX_CSTRING name;				//参数名字                     

 protected:

  int flags;                      		//参数标记,比如说global变量,session变量
  int m_parse_flag;               		//PARSE_EARLY 优先解析,PARSE_NORMAL 正常解析.
  my_option option;               		//参数min, max, default值
  ptrdiff_t offset;  				//距离global_system_variables的offset值,实际的参数存储地址空间
  on_check_function on_check;                   //check函数
  on_update_function on_update;                 //update函数
};

下面看一个例子:basedir。 我们先看下这个参数的定义

static Sys_var_charptr Sys_basedir(
    "basedir",                                            	//参数名字,和配置文件里面对应
    "Path to installation directory. All paths are "
    "usually resolved relative to this",                  	//注释
    READ_ONLY NON_PERSIST GLOBAL_VAR(mysql_home_ptr),     	//flag标记,offset偏移量,size
    CMD_LINE(REQUIRED_ARG, 'b'), IN_FS_CHARSET, DEFAULT(0));	//参数校验,编码, 默认值

参数的flag是read_only + 非持久化 + 全局变量

persisted_variables_cache

set PERSIST命令持久化全局变量到mysqld-auto.cnf,mysql启动的时候先读这个文件。保证启动的时候参数修改不会丢失。

early_options

主要是一些基础参数,其他参数会依赖这些参数。struct my_option my_long_early_options 里面定义了那些参数是要提前解析的。
此外还有Sys_max_connections,Sys_open_files_limit,performance_schema等参数也是属于提前解析的。
handle_early_options函数找出这些需要提前解析的参数,然后调用handle_options解析参数。
handle_options函数会遍历argv参数,然后和option对应起来,调用setval设置value。

update

参数更新会调用定义的类里面的update函数。下面举个例子:

#0  Sys_var_struct<CHARSET_INFO, (anonymous namespace)::Get_csname>::global_update (this=0x6db4980 <Sys_character_set_server>, 
    var=0x7efe7f21c160) at sql/sys_vars.h:1892
#1  0x0000000002c51f34 in sys_var::update (this=0x6db4980 <Sys_character_set_server>, thd=0x7efe7fe0b000, var=0x7efe7f21c160)
    at sql/set_var.cc:252
#2  0x0000000002c544f9 in set_var::update (this=0x7efe7f21c160, thd=0x7efe7fe0b000)
    at sql/set_var.cc:1005
#3  0x0000000002c53978 in sql_set_variables (thd=0x7efe7fe0b000, var_list=0x7efe7fe352c8, opened=true)
    at sql/set_var.cc:770
#4  0x0000000002d4e935 in mysql_execute_command (thd=0x7efe7fe0b000, first_level=true)
    at sql/sql_parse.cc:3880
#5  0x0000000002d543f3 in mysql_parse (thd=0x7efe7fe0b000, parser_state=0x7eff35737860, force_primary_storage_engine=false)
    at sql/sql_parse.cc:5728
#6  0x0000000002d48b0d in dispatch_command (thd=0x7efe7fe0b000, com_data=0x7eff35738330, command=COM_QUERY)
    at sql/sql_parse.cc:1874
#7  0x0000000002d46db0 in do_command(THD*, std::function<bool (THD*, COM_DATA const*, enum_server_command)>*) (
    thd=0x7efe7fe0b000, dispatcher=0x0) at sql/sql_parse.cc:1336
#8  0x0000000002d46f4e in do_command (thd=0x7efe7fe0b000) at sql/sql_parse.cc:1373

全局参数就直接修改对应的内存值。

再来看一个innodb参数的例子:

#0  innodb_undo_tablespaces_update (thd=0x7efe7fe0b000, var=0x6b7d060 <mysql_sysvar_undo_tablespaces>, 
    var_ptr=0x6b89f90 <srv_undo_tablespaces>, save=0x7efe7f181150)
    at storage/innobase/handler/ha_innodb.cc:23160
#1  0x0000000002da2163 in sys_var_pluginvar::global_update (this=0x7eff3fd9fa40, thd=0x7efe7fe0b000, var=0x7efe7f181130)
    at sql/sql_plugin_var.cc:416
#2  0x0000000002c51f34 in sys_var::update (this=0x7eff3fd9fa40, thd=0x7efe7fe0b000, var=0x7efe7f181130)
    at sql/set_var.cc:252
#3  0x0000000002c544f9 in set_var::update (this=0x7efe7f181130, thd=0x7efe7fe0b000)
    at sql/set_var.cc:1005
#4  0x0000000002c53978 in sql_set_variables (thd=0x7efe7fe0b000, var_list=0x7efe7fe352c8, opened=true)
    at sql/set_var.cc:770
#5  0x0000000002d4e935 in mysql_execute_command (thd=0x7efe7fe0b000, first_level=true)
    at sql/sql_parse.cc:3880
#6  0x0000000002d543f3 in mysql_parse (thd=0x7efe7fe0b000, parser_state=0x7eff35737860, force_primary_storage_engine=false)
    at sql/sql_parse.cc:5728
#7  0x0000000002d48b0d in dispatch_command (thd=0x7efe7fe0b000, com_data=0x7eff35738330, command=COM_QUERY)
    at sql/sql_parse.cc:1874
#8  0x0000000002d46db0 in do_command(THD*, std::function<bool (THD*, COM_DATA const*, enum_server_command)>*) (
    thd=0x7efe7fe0b000, dispatcher=0x0) at sql/sql_parse.cc:1336
#9  0x0000000002d46f4e in do_command (thd=0x7efe7fe0b000) at sql/sql_parse.cc:1373

有自定义update函数的参数,最终都会回调自定义的update函数。

参数prefix

特殊前缀有”skip”, “disable”, “enable”, “maximum”, “loose”, disable和skip是一样的,设置参数off, enable刚好相反,有个特殊情况–skip-option=0这种双重否定表示肯定,等价于option = true
maximum限制客户端设置session 变量的最大值
loose前缀表示参数不能识别,程序不会退出。
比较参数的时候认为-和_是相同的。

innodb插件参数

参数都在innobase_system_variables结构里面。test_plugin_options 里面会给所有的参数加上前缀innodb_, 这样可以和配置文件里面参数名称对齐

my.cnf 文件解析

search_default_file_with_ext函数会解析配置文件,并且把参数拼成–前缀, 然后会重新设置argc, argv。后续解析参数直接就从argv里面获取。

特殊情况

有少部分参数,修改一个参数值,另外一个参数值也会一起修改,原因是2个参数对应的同一个内存数据结构。
比如说:character_set_server和collation_server。它们组合需要遵守相应的规则。