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

背景

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

mysql参数

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

  1. class sys_var {
  2. public:
  3. sys_var *next; //next指针,all_sys_vars链表遍历的时候使用
  4. LEX_CSTRING name; //参数名字
  5. protected:
  6. int flags; //参数标记,比如说global变量,session变量
  7. int m_parse_flag; //PARSE_EARLY 优先解析,PARSE_NORMAL 正常解析.
  8. my_option option; //参数min, max, default值
  9. ptrdiff_t offset; //距离global_system_variables的offset值,实际的参数存储地址空间
  10. on_check_function on_check; //check函数
  11. on_update_function on_update; //update函数
  12. };

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

  1. static Sys_var_charptr Sys_basedir(
  2. "basedir", //参数名字,和配置文件里面对应
  3. "Path to installation directory. All paths are "
  4. "usually resolved relative to this", //注释
  5. READ_ONLY NON_PERSIST GLOBAL_VAR(mysql_home_ptr), //flag标记,offset偏移量,size
  6. 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函数。下面举个例子:

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

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

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

  1. #0 innodb_undo_tablespaces_update (thd=0x7efe7fe0b000, var=0x6b7d060 <mysql_sysvar_undo_tablespaces>,
  2. var_ptr=0x6b89f90 <srv_undo_tablespaces>, save=0x7efe7f181150)
  3. at storage/innobase/handler/ha_innodb.cc:23160
  4. #1 0x0000000002da2163 in sys_var_pluginvar::global_update (this=0x7eff3fd9fa40, thd=0x7efe7fe0b000, var=0x7efe7f181130)
  5. at sql/sql_plugin_var.cc:416
  6. #2 0x0000000002c51f34 in sys_var::update (this=0x7eff3fd9fa40, thd=0x7efe7fe0b000, var=0x7efe7f181130)
  7. at sql/set_var.cc:252
  8. #3 0x0000000002c544f9 in set_var::update (this=0x7efe7f181130, thd=0x7efe7fe0b000)
  9. at sql/set_var.cc:1005
  10. #4 0x0000000002c53978 in sql_set_variables (thd=0x7efe7fe0b000, var_list=0x7efe7fe352c8, opened=true)
  11. at sql/set_var.cc:770
  12. #5 0x0000000002d4e935 in mysql_execute_command (thd=0x7efe7fe0b000, first_level=true)
  13. at sql/sql_parse.cc:3880
  14. #6 0x0000000002d543f3 in mysql_parse (thd=0x7efe7fe0b000, parser_state=0x7eff35737860, force_primary_storage_engine=false)
  15. at sql/sql_parse.cc:5728
  16. #7 0x0000000002d48b0d in dispatch_command (thd=0x7efe7fe0b000, com_data=0x7eff35738330, command=COM_QUERY)
  17. at sql/sql_parse.cc:1874
  18. #8 0x0000000002d46db0 in do_command(THD*, std::function<bool (THD*, COM_DATA const*, enum_server_command)>*) (
  19. thd=0x7efe7fe0b000, dispatcher=0x0) at sql/sql_parse.cc:1336
  20. #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。它们组合需要遵守相应的规则。