Author: 贤勇
通过Show Status命令查看MySQL server的状态信息是MySQL日常运维中常见的诊断手段。这个命令可以返回在实例运行期间,或者是当前会话范围内的指定的状态信息。具体语法见https://dev.mysql.com/doc/refman/8.0/en/show-status.html, MySQL8.0可以支持的Status列表见 https://dev.mysql.com/doc/refman/8.0/en/server-status-variables.html。
本文将和大家一起看看Status的内部实现机制,相关的代码是基于MySQL 8.0。
MySQL使用了两张Performance_Schema
数据库的表,session_status
和global_status
分别对应Session级别以及Global级别的状态信息访问。
两张表的定义如下,包含的字段是一样的。
CREATE TABLE `session_status` (
`VARIABLE_NAME` varchar(64) NOT NULL DEFAULT '',
`VARIABLE_VALUE` varchar(1024) DEFAULT NULL
) ENGINE=PERFORMANCE_SCHEMA DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `global_status` (
`VARIABLE_NAME` varchar(64) NOT NULL DEFAULT '',
`VARIABLE_VALUE` varchar(1024) DEFAULT NULL
) ENGINE=PERFORMANCE_SCHEMA DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
Show session|global Status
命令会在MySQL server层解析成对performance_schema.session_status
或者performance_schema.global_status
的Select语句。具体参见
build_show_session_status
或build_show_global_status
方法,这两个方法都会调用build_query
方法来真正生成对应的SELECT_LEX,之后执行。
SHOW_VAR
结构体struct SHOW_VAR {
const char *name; //Status名字
char *value; //Status的值
enum enum_mysql_show_type type; //Status类型,比如SHOW_ARRAY, SHOW_LONGLONG,不同的类型Status处理的方法不同
enum enum_mysql_show_scope scope; //Status作用范围,包括 SHOW_SCOPE_UNDEF,SHOW_SCOPE_GLOBAL, SHOW_SCOPE_SESSION和SHOW_SCOPE_ALL
};
SHOW_SCOPE_UNDEF
SHOW_SCOPE_GLOBAL
SHOW_SCOPE_SESSION
SHOW_SCOPE_ALL
在mysqld.cc文件中定义的Status变量的全局动态SHOW_VAR数组,所有的server以及plugin的Status变量都会在里面定义。在MySQL实例初始化阶段,Status变量都会被组装成这个all_status_vars的全局动态数组,plugin在load的时候,带入的新的status变量会被加到这个数组里面。all_status_vars里面包含的Status的定义都是有序的存放的,以方便Show Status命令或者是对相关系统表session_status或者是global_status的访问。
thread级别的status变量,变量类型必须是long/ulong。
LOCK_status 是一个全局mutex,它被用来在初始化阶段以及SHOW STATUS执行阶段保护all_status_vars。
在做多个thread Status聚合的时候后,global thread manager会持有两把锁来防止thread被移除。Global_THD_manager::LOCK_thd_remove
和Global_THD_manager::LOCK_thd_count
。 但,两种锁的持有时间不同,LOCK_thd_remove会在整个聚合执行期间一直持有, LOCK_thd_count 锁
会在当前thread list的快照做了copy之后就会释放。
对Performance_schema.session_status或者是Performance_schema.global_status的访问,会需要调用join_materialize_scan方法,并进一步调用ha_perfschema::rnd_init/rnd_next, PFS_variable_cache
取session域的Status的调用栈如下
join_materialize_derived-> TABLE_LIST::materialize_derived->…
->ha_perfschema::rnd_init->table_session_status::rnd_init
-> PFS_variable_cache<Status_variable>::materialize_all //Materialize output status
-> PFS_status_variable_cache::do_materialize_all->PFS_status_variable_cache::manifest
join_materialize_derived-> TABLE_LIST::materialize_derived…->ha_perfschema::rnd_next->table_session_status::rnd_next //访问所有的status,做过滤
取global域的Status的调用栈如下,和session域的调用栈很类似
join_materialize_derived-> TABLE_LIST::materialize_derived->…
->ha_perfschema::rnd_init->table_session_status::rnd_init
-> PFS_variable_cache<Status_variable>::materialize_all //Materialize output status
->PFS_status_variable_cache::do_materialize_all->PFS_status_variable_cache::manifest
join_materialize_derived-> TABLE_LIST::materialize_derived->…
->ha_perfschema::rnd_next->table_session_status::rnd_next
type为SHOW_FUNC的Status,例如Aborted_connects,访问方法比较特别,会调用SHOW_VAR里面定义的函数来处理,
PFS_status_variable_cache::manifest
…
/*
If the value is a function reference, then execute the function and
reevaluate the new SHOW_TYPE and value. Handle nested case where
SHOW_FUNC resolves to another SHOW_FUNC.
*/
if (show_var_ptr->type == SHOW_FUNC) {
show_var_tmp = *show_var_ptr;
/*
Execute the function reference in show_var_tmp->value, which returns
show_var_tmp with a new type and new value.
*/
for (const SHOW_VAR *var = show_var_ptr; var->type == SHOW_FUNC;
var = &show_var_tmp) {
((mysql_show_var_func)(var->value))(thd, &show_var_tmp, value_buf.data); //调用指定的函数,函数名在status_vars数组里定义
}
show_var_ptr = &show_var_tmp;
}
例如取Aborted_connects,就会调用show_aborted_connects这个函数来获取真正的值。
{"Aborted_connects", (char *)&show_aborted_connects, SHOW_FUNC, SHOW_SCOPE_GLOBAL},
MySQL当前的架构已经对Status扩展做了很好的支持。我们如果想添加一个新的Status,一般的步骤是在System_status_var里面添加一个相应的变量,并在status_vars数组里面 也对应添加一个对应的SHOW_VAR条目。需要处理的是对Status 变量做累计,这个就要根据具体的逻辑了。