前言
MySQL是一个庞大的数据库系统,对于数据库内核研发人员来说,非常有必要了解MySQL实例的启动过程。对于数据库使用人员来说,也需要对MySQL实例的启动过程有一个基本的了解。
本文旨在对MySQL Server层的启动过程做一个整体的介绍,让读者对Server层启动整体流程有一个基本的认识。可将本文看作一个手册,用于查询server相关启动过程,需要深入理解时再对启动源码进行分析。
下面以MySQL 8.0代码为例,深入main函数源码,详细分析MySQL实例启动过程。MySQL实例启动过程涉及大量的模块,本篇分析Server层启动所做的事情,init_server_components函数以及innodb的初始化在后续文章中分析。
MySQL实例启动过程实际上是一系列模块的初始化过程,初始化过程中,是有一些先后顺序需要遵从的。以下按照先后顺序进行源码分析,下文代码块中的‘...’代表省去了中间部分代码,代码块中函数前的锁进代表函数的调用关系。本文中,有一些地方难以理解,笔者尽可能举例子来便于理解。
备注:本文将一些初始化过程分类在了一起,同一类中按先后顺序排列,不同类中并不保证先后顺序。例如,第3点配置参数初始化相关中,3.1-3.8是按先后顺序排列的。而 3.4 读取早期参数 在 4.2 初始化全局数组Pfs_instr_config_array 之后执行。
--initialize选项
通常在mysqld第一次启动前,需要指定--initialize选项对mysqld进行初始化,当指定--initialize初始化mysqld时,mysqld会创建data目录,具体操作流程如下:
1 检查data目录是否存在
-
如果data目录不存在,那么server会创建data目录
-
如果data目录存在并且非空,那么server会退出并且报出相应的错误信息
[ERROR] --initialize specified but the data directory exists. Aborting.
-
如果data目录存在且为空,那么会继续完成后面的初始化过程
-
如果data目录存在且非空,并且每个实体文件名称以‘.’开头,那么会继续完成后面的初始化过程
2 在data目录下,server创建mysql数据库schema和mysql数据库下的表,包括数据字典表、账户权限表、时区表、server层help表等
3 server初始化管理innodb表所需的系统表空间以及相应的数据结构
4 server创建root用户以及其他的保留账户。root账户的密码设置:
-
使用initialize选项时,日志中打印一个临时的随机密码,登陆后最好修改密码
-
使用initialize_insecure选项时,使用空密码,登陆后最好修改密码
5 server填充server端help表,用于HELP语句。
6 启动时,如果使用命令行选项“--init-file=file_name”指定了‘init-file’系统变量,server会执行file_name文件中的sql语句。开启这个选项意味着启动了bootstrap模式,在bootstrap模式下,可执行的sql语句有所限制,例如账户管理相关的语句(CREATE USER、GRANT)、replication相关、global transaction identifiers相关的语句不能被执行。
7 退出server
对于其他的启动过程,在initialize模式下,会跳过。
在正常模式下启动mysqld时,会跳过上述initialize模式执行的流程,通过这样的方式来区分initialize模式和正常启动mysqld模式,可以减少mysqld正常重启所花费的时间。
mysqld_main()函数
mysql的main函数实际上就是mysqld_main()函数。mysqld_main()函数中,主要做的事情包括:配置参数、各种内部系统变量的初始化、各种模块的初始化、信号初始化等等等等。下面对mysqld_main中的主要过程进行详细解读。
1 处理进程名
substitute_progpath(argv); //将argv[0]变为全路径的程序名称
sysd::notify_connect(); //从环境变量中获取”NOTIFY_SOCKET“,如果存在该环境变量则连接到NOTIFY_SOCKET
sysd::notify("STATUS=Server startup in progress\n"); //向NOTIFY_SOCKET发送启动消息
...
my_progname = argv[0];
calculate_mysql_home_from_my_progname(); //根据argv[0]计算mysql_home,也就是安装目录
2 my_init()函数
my_init中初始化了线程环境pthread,一些锁资源,因此后续的mysql_mutex_init类型为fast和errorcheck的锁需要在这之后。由于my_init函数在mysqld_main的靠前位置初始化,因此我们修改的代码一般在这之后,一般不会修改my_init之前的代码,所以会满足这个条件。
my_init(); //初始化mysys lib的函数、资源和变量
my_umask = 0640; // 默认创建新文件的权限
my_umask_dir = 0750; // 默认创建新目录的权限
if ((str = getenv("UMASK")) != nullptr)
my_umask = (int)(atoi_octal(str) | 0600); //权限范围为 0600 to 0666
if ((str = getenv("UMASK_DIR")) != nullptr)
my_umask_dir = (int)(atoi_octal(str) | 0700); //权限范围为 0700 to 0777
my_thread_global_init(); //初始化线程环境(初始化锁资源),my_fast_mutexattr
MY_MUTEX_INIT_FAST初始化
MY_MUTEX_INIT_ERRCHK初始化
my_thread_init(); //为mysys、dbug所使用的线程分配内存
MyFileInit(); //初始化静态对象fivp,FileInfoVector类型
my_init函数中设置了创建新文件、新目录的权限。mysql默认创建新文件、新目录的权限为0640、0750。my_init会读取环境变量UMASK、UMASK_DIR。当设置了环境变量UMASK、UMASK_DIR时,会根据规则设置权限。新文件允许的权限范围为0600 to 0666,意味着新文件不具备可执行权限。新目录允许的权限范围为 0700 to 0777。当我们需要修改mysqld的文件、目录创建权限时,修改环境变量即可。上述以‘0’开头的数为8进制数。以下例子修改了UMASK环境变量,将创建新文件权限修改为0600。384为10进制数,转换为8进制为0600。
# 将创建新文件权限修改为0600
UMASK=384 # = 0600
export UMASK
3 配置参数初始化相关
MySQL有着许多的参数,例如innodb_flush_log_at_trx_commit和sync_binlog,在高可靠需求下,将这两个参数设置为1,即“双一配置”。MySQL的参数来自于命令行输入、配置文件、持久化配置文件、默认参数。在MySQL实例启动过程中,会读取这些参数、使这些参数生效。这一小节对参数初始化的相关过程做一个介绍。
3.1 all_sys_vars链表、系统变量构造函数调用
本节中有两个重要的概念,配置参数和系统变量,需要注意区分:
-
配置参数
本文第3节中,配置参数指的是my.cnf或者命令行输入的配置参数,例如:命令行输入的“--sync_binlog=1” ,my.cnf中指定的配置参数 “sync_binlog = 1”
-
mysql内部系统变量
本文第3节中,mysql内部系统变量指的是mysqld源代码中类型为Sys_var的变量。一个配置参数对应一个Sys_var系统变量。当配置参数读取到对应的系统变量中时,配置参数开始生效。例如:配置参数sync_binlog对应的系统变量名称为Sys_sync_binlog_period。
在sys_vars.cc文件中,定义了各种系统变量。在main函数之前,会执行这些系统变量的构造函数。构造函数中,会对系统变量设置默认值,并将系统变量添加到all_sys_vars链表中。all_sys_vars是一个链表,定义在sys_vars.cc,其中存放了所有的系统变量,这些系统变量都是sys_var的子类。在sys_var_init函数中,会将这些系统变量添加到系统变量哈希表system_variable_hash中。
all_sys_vars由两个sys_var*类型的指针first 、last构成,first指针指向了链表的头,last指针指向的链表的尾,如下图所示。sys_vars.cc中定义了各种系统变量,这些系统变量的父类是sys_var。在系统变量构造函数中,将系统变量添加到链表all_sys_vars中。
3.2 读取my.cnf配置文件参数
mysqld_main中,load_defaults函数负责读取my.cnf配置文件,主要作用是读取配置文件选项,将参数选项放置在argc和argv之前,这样可以用命令行选项覆盖配置文件中的参数选项。当命令行中输入的配置参数与my.cnf中的配置参数冲突时,以命令行中输入的配置参数为准。
举个栗子1:
在my.cnf中设置了“innodb_flush_log_at_trx_commit = 0”,load_defaults函数会将这个参数读取到argv中,并将其放置在argv中的正确位置,于是argv就多了一项 “--innodb_flush_log_at_trx_commit=0”。
my.cnf配置文件读取规则:
1 若代码中指定了配置文件my.cnf,则读取之,并跳过规则2、3
2 若命令行中指定了配置文件my.cnf,则读取之,并跳过规则3
3 从/etc, /etc/mysql, MYSQL_HOME,~目录中依次查找配置文件my.cnf,如果这些目录存在配置文件,就读取配置文件。如果这些目录中的配置文件参数重复,以最后一次读到的配置文件为准。
-
mysql --help | grep my.cnf 查看规则3中搜索my.cnf的目录
3.3 读取持久化变量参数
持久化变量参数存放在mysqld-auto.cnf文件,mysqld-auto.cnf文件的读取在my.cnf之后,如果mysqld-auto.cnf 中的参数与my.cnf的参数重复,以mysqld-auto.cnf 中的参数为准。
DYNAMIC类型变量:在线修改后,不需要重启mysqld就可以生效,例如innodb_buffer_pool_size。但是当mysqld重启之后,mysqld将会从my.cnf中读取这些变量,也就是说这些变量不具备持久化能力。
SET GLOBAL innodb_buffer_pool_size = 512 * 1024 * 1024;
持久化变量:8.0引入,所有的GLOBAL DYNAMIC configuration variable可以用SET PERSIST命令持久化
SET PERSIST innodb_buffer_pool_size = 512 * 1024 * 1024;
持久化配置文件: mysqld-auto.cnf。持久化变量时,mysqld在data目录下创建该文件,将变量写入该文件。下一次mysqld启动时,该配置文件具有最高优先级,也就是说这些持久化配置会覆盖my.cnf中的相应参数。
mysqld通过persisted_variables_cache变量来为持久化变量初始化缓存、读取持久化变量参数、追加只读持久化参数到命令行选项中。
3.4 读取早期参数
main函数中的handle_early_options函数用于处理早期参数。在3.2和3.3中,配置文件中的参数只是被读取到了argc和argv中,并没有被读取到系统变量中,也就是说在这里参数并没有生效。handle_early_options函数会处理早期选项、将早期选项读取到系统变量中,这些早期选项在此时生效。
mysql初始化过程中,有一些参数需要用于初始化过程,这些参数需要提前载入。在sql/sys_vars.cc文件中,
所有parse_flag= PARSE_EARLY 的变量,均会在此处被读取。这些参数可以被分为三类:
1 pfs模块相关参数 2 help相关参数 3 bootstrap相关参数
对于pfs参数,进一步可分为三类:
1. Performance Schema 控制开关:performance_schema
2. pfs内存分配&监控的相关参数
控制监控项数目参数,如:performance_schema_max_stage_classes
控制监控实体参数:如 performance_schema_max_thread_instances
控制记录数目参数 如:performance_schema_events_stages_history_size
3.5 调整相关参数
open_files_limit\max_connections\table_open_cache\table_definition_cache四个参数有严格的先后依赖关系,并且有一些取值的限制规则,adjust_related_options()调整了这4个参数,可能会和my.cnf中设置的值不一样。
adjust_related_options();
adjust_open_files_limit(); //打开文件限制
limit_1= 10 + max_connections + table_cache_size * 2;
limit_2= max_connections * 5;
limit_3= open_files_limit ? open_files_limit : 5000;
request_open_files= max(max(limit_1, limit_2), limit_3);
adjust_max_connections(); //最大连接数
min(requested_open_files - 10 - TABLE_OPEN_CACHE_MIN * 2,max_connections);
adjust_table_cache_size(); //打开表数量限制
min(max((requested_open_files - 10 - max_connections) / 2, TABLE_OPEN_CACHE_MIN),table_cache_size);
adjust_table_def_size(); //可以打开的表定义数量限制
min(400 + table_cache_size / 2, 2000);
3.6 设置持久化参数
main函数中的set_persist_options函数用于设置持久化变量。3.3只是将持久化配置文件中的配置参数读取到了argv中,并没有被读取到系统变量中,也就是说在3.3中持久化参数并没有生效。在set_persist_options函数中,将argv中的持久化参数读取到系统变量中,此时持久化参数开始生效。
3.7 参数生效时间
3.2 3.3是将配置参数读取到命令行选项中,参数并没有开始生效。这里说的生效指的是配置参数被读取到对应的系统变量中。对于命令行输入的配置参数、my.cnf、mysqld-auto.cnf中的配置参数,其生效时间各不相同,对于不同模块的参数其生效时间也不相同。handle_options函数负责将argv中的参数读取到系统变量中。
-
对于早期选项,在handle_early_options之后开始生效。
-
对于innodb相关的参数,在innodb启动过程中生效。
-
对于持久化参数,在set_persist_options函数后开始生效。
-
对于server层相关参数,例如sync_binlog,在init_common_variables函数后生效。
3.8 参数配置优先级
根据上述介绍,总结配置参数优先级由高到低为:
1 mysqld-auto.cnf ,持久化配置参数文件(位于DATA目录)
2 命令行输入的配置参数
3 代码中指定配置文件 my.cnf中的配置参数
4 命令行输入配置文件 my.cnf中的配置参数
5 /etc目录中的配置文件my.cnf中的配置参数
6 /etc/mysql目录中的配置文件my.cnf中的配置参数
7 MYSQL_HOME目录中的配置文件my.cnf中的配置参数
MYSQL_HOME是一个环境变量,与 1 处理进程名 中的mysql_home有所区分。
8 ~目录中的的配置文件my.cnf中的配置参数
当上述配置文件参数发生冲突时,以优先级高的配置文件为准。当你启动mysql设置的配置参数没有生效时,很可能是被优先级高的参数配置给覆盖了。
4 PFS初始化相关
pfs初始化涉及一系列过程,在这里简要介绍一下。由于pfs对性能影响较大,因此线上实例一般会关闭PFS。当开启PFS时,才会执行PFS初始化相关函数。
4.1 PFS预初始化
main函数中的pre_initialize_performance_schema函数负责PFS的预初始化,该函数主要做了两件事情。一是初始化内存监控相关的instrument,该监控无法被用户修改,只要开启pfs,内存监控便默认开启。二是重置各种统计信息,包括PFS_table_stat、global_idle_stat、global_table_io_stat、global_table_lock_stat、g_histogram_pico_timers、global_statements_histogram。
4.2 初始化全局数组Pfs_instr_config_array
main函数中的init_pfs_instrument_array函数对全局数组Pfs_instr_config_array进行初始化,该数组用于记录用户在配置文件中关于表 setup_instrument 的配置,这里主要是分配该数组缓存。
在配置文件中,用户可以使用如下参数对instrument进行配置:
performance-schema-instrument='instrument_name = value'
例如,开启MDL监控:
performance_schema_instrument='wait/lock/metadata/sql/mdl = ON'
注意:此处主要是给Pfs_instr_config_array分配缓存,在3.4中的handle_early_options中,会读取PFS相关的参数,将PFS instrument相关参数填充到Pfs_instr_config_array中。
4.3 核心初始化函数
main函数中的initialize_performance_schema函数是PFS核心初始化函数,PFS主要的初始化过程在该函数中完成,主要过程包括:
-
配置默认参数
PFS会根据当前服务器的table_def_size、table_cache_size、max_connections三个参数,来自动选择合适的配置。
-
注册全局instrument
注册全局的instrument监控项。在MySQL中,全局控制的instrument项有如下六种,分别解释如下:
在对上述六类全局instrument进行注册后,会根据Pfs_instr_config_array数组对相应的instrument进行配置。
-
pfs内存初始化
对PFS所需要的内存进行初始化。
init_xxx_class();
init_events_xxx_history_long();
init_xxxx_xxxx();
-
setup_consumer表配置
在这里对setup_consumer表进行配置。pfs采用了生产-消费者模型,关闭对应consumer时,对应的instrument不会收到相关信息。
-
获取相关实现的接口
MySQL定义了PFS相关的接口,PFS实现了这些接口,在这里获取实现的接口:
*xxxx_bootstrap = &pfs_xxxx_bootstrap;
4.4 注册剩余监控项
main函数中的init_server_psi_keys函数会注册除4.2中全局监控项之外的所有监控项。包括mutex、rwlock、cond、thread、file、stage、socket、statement。以mutex为例,其注册instrument的过程为:
init_server_psi_keys()
-- mysql_mutex_register()
-- inline_mysql_mutex_register()
-- PSI_THREAD_CALL(register_mutex)
-- pfs_register_mutex_v1()
-- register_mutex_noop()
4.5 重新创建当前监控对象
main函数中的my_thread_global_reinit函数将锁销毁并重新创建。由于当前线程中也存在一些需要检测的线程相关的锁对象,这里使用重新创建的方式,来监测这些锁对象。由于目前为止都是单个线程在运行,没有新的线程被创建,所以这里是线程安全的。
4.6 注册PSI相关服务
/* 注册PFS通知服务 */
register_pfs_notification_service();
/* 注册PFS资源组服务 */
register_pfs_resource_group_service();
4.7 psi监控主线程序
在这里,PFS模块开始监控main线程。
//监控main thread
PSI_thread *psi = PSI_THREAD_CALL(new_thread)(key_thread_main, nullptr, 0);
PSI_THREAD_CALL(set_thread_os_id)(psi);
PSI_THREAD_CALL(set_thread)(psi);
4.8 初始化performance_schema库的权限
main函数中的initialize_performance_schema_acl函数初始化performance_schema库的权限。由于无论是否启用Performance Schema功能,该库下面的表始终是可见的。因此需要进行权限控制。
5 日志初始化相关
本文主要关注server层启动过程,这里不涉及innodb相关的日志。
5.1 全局对象mysql_bin_log相关
-
mysql_bin_log构造
在main函数之前,会执行mysql_bin_log的构造函数。mysql_bin_log是一个全局对象,定义在sql_servers.cc中,binlog模块使用这个对象与存储系统打交道,包括打开binlog文件、关闭binlog文件、刷新binlog缓存到磁盘等等。在sql_servers.cc中执行mysql_bin_log的构造函数。
-
mysql_bin_log.set_psi_keys()
在main函数中的init_common_variables函数中,会执行mysql_bin_log.set_psi_keys函数,用于设置psi key。psi keys需要在pfs初始化之后设置,不能在mysql_bin_log的构造函数(在main之前被调用)中设置。
-
mysql_bin_log.init_pthread_objects()
在main函数中的init_common_variables函数中,会执行mysql_bin_log.init_pthread_objects函数,为全局变量mysql_bin_log初始化锁,不可以在mysql_bin_log构造函数中初始化,需要在my_init之后。
5.2 错误日志初始化
main函数中,init_error_log函数用于初始化错误日志,初始化错误日志锁资源,这里只是初始化,并没有开启错误日志。 错误日志在init_server_components中启动。
5.3 初始化审计接口
main函数中,调用mysql_audit_initialize初始化审计接口、锁、审计全局变量,审计插件的初始化在plugin_initialize中完成,plugin_initialize在init_server_components中完成。
审计插件:将用户对数据库的各类操作行为记录审计日志,以便日后进行跟踪、查询、分析,以实现对用户操作的监控和审计。
5.4 查询日志初始化
main函数中,query_logger.init()函数初始化查询日志,包括general log, slow log,主要是初始化锁资源。这部分需要在my_init函数之后执行。
5.5 binlog相关
binlog_unsafe_map中设置了一系列不安全的混合语句类型规则。main函数中,init_binlog用于初始化binlog。
//检查BINLOG_CACHE_SIZE是否超过MAX_BINLOG_CACHE_SIZE,超过就设置为MAX_BINLOG_CACHE_SIZE
check_binlog_cache_size();
//检查BINLOG_STMT_CACHE_SIZE是否超过MAX_BINLOG_STMT_CACHE_SIZE,超过就设置为MAX_BINLOG_STMT_CACHE_SIZE
check_binlog_stmt_cache_size();
binlog_unsafe_map_init();//给binlog_unsafe_map分配内存,设置unsafe的语句类型
Binlog_recovery::end();//Binlog_recovery::begin()在init_server_components函数中被调用
init_binlog();//需要在binlog recovery完成后调用
mysql_bin_log_ext.init();//binlog extension初始化
mysql_bin_log.open_binlog();//打开binlog文件
从gtid_executed表和binlog文件中初始化GTID,这里需要加载表,需要在init_server_components之后执行
init_slave();//初始化slave
5.6 读取error log
//读取之前产生的error log,并让performance_schema.error_log可用,
//刷新buffered 错误信息,关闭错误日志buffer
setup_error_log_components();
6 Conponent初始化相关
6.1 初始化 Component subsystem
mysql中有一个Component subsystem,用于扩展server功能。Component提供服务给server和其他Component使用。在这个概念上,server也是一个Component。Component之间只能通过它们提供的服务进行交互。
包含如下Component:
-
配置error log的Component
-
密码验证Component
-
keyring Component
-
审计消息Component:允许应用程序将他们的消息事件添加到审计日志
-
Query Attributes Component:实现可加载函数的组件
main函数中的component_infrastructure_init用于初始化Component subsystem:
//需要使用PSI,所以要在pfs初始化之后
component_infrastructure_init();
registrator->set_default(
"dynamic_loader_scheme_file.mysql_server_path_filter");//设置默认file scheme loader
registrator->set_default("mysql_rwlock_v1.mysql_server");//设置默认rw_lock
registrator->set_default("mysql_psi_system_v1.mysql_server");//设置默认psi_system event service
registrator->set_default("mysql_runtime_error.mysql_server");//设置默认mysql_runtime_error
6.2 keyring component初始化
keyring用于表空间加密,8.0.24之前,mysql使用keyring Plugin。8.0.24开始,mysql引入了Keyring Component,默认使用Keyring Component。
keyring_lockable_init(); //初始化keyring组件,keyring组件锁的初始化
https://dev.mysql.com/doc/refman/8.0/en/keyring.html
6.3 迁移keyring
当指定了mysql_migrate_keyring选项时,迁移keyring。迁移完成后退出server。正常启动过程不涉及这一部分。
Migrate_keyring mk;
mk.init();//1 读取命令行迁移选项 2 获取插件目录 3 获得连接到server的句柄
mk.execute();//将keys从源keyring迁移到目的keyring
Migrate_keyring:Keyring Key Migration Utility,在两个keyring component之间迁移keys,支持离线、在线迁移。https://dev.mysql.com/doc/refman/8.0/en/mysql-migrate-keyring.html
6.4 加载component
这里的component与 6.1 初始化 component subsystem 中的component是同一种概念。
initialize_manifest_file_components//从manifest file中读取component
Deployed_components::load();//加载component
/* 需要在initialize_manifest_file_components之后,
如果initialize_manifest_file_components加载了keyring component,
那么就将keyring component设置为默认keyring(优先级高于keyring plugin)*/
set_srv_keyring_implementation_as_default;
6.5 component infrastructure初始化
mysql_component_infrastructure_init();
Auto_THD thd;//temporary THD
persistent_dynamic_loader_init();//初始化持久化存储,从组件表中加载部分注册的组件
trans_rollback_stmt(thd.thd);//加载失败,回滚
trans_rollback(thd.thd);
server_component_init();//组件服务初始化
mysql_comp_sys_var_services_init();
trans_commit_stmt(thd.thd);//提交事物
trans_commit(thd.thd);
7 初始化核心组件
init_server_components();
这个函数是一个关键函数,包括众多核心模块的初始化,mdl、innodb启动、插件初始化、error log启动、初始化gtid server、xa recovery、DD初始化等等等等。此处不展开,后续文章中再详细分析。
gtid的设置、读表相关操作、RDS组件初始化需要在这个函数的后面执行。
注意:mysql源代码中许多地方用到了‘components’,这在不同的地方含义不一样。1 init_server_components中的‘components’含义为‘模块’2 component_infrastructure_init中的‘component’含义为‘component service infrastructure’
8 加载相关表
这里加载了部分表,因此需要在init_server_components之后执行。
8.1 设置gtid
系统表 mysql.gtid_executed 存放了所有执行过的GTID(在活动的binlog中的除外),但是由于不包含活动的binlog当中的GTID,因此需要查看精确值时,可以查看 global variable gtid_executed 的值,这个变量的值是准确的。
这里需要读取gtid_executed表,因此必须在init_server_components之后。
gtid_state->init();
gtid_state->read_gtid_executed_from_table();
8.2 加载优化器cost
main函数中的reload_optimizer_cost_constants会创建一个临时线程,用于从成本常量表中加载优化器成本常量。
8.3 删除临时表
main函数中的mysql_rm_tmp_tables会删除上一次server遗留的临时表,该函数只在server启动时调用。
8.4 权限&时区初始化
acl_init();//初始化用户/DB级别权限数据结构,并从‘mysql‘数据库中加载权限信息
init_acl_memory();//初始化acl内存,--initialize选项时执行
my_tz_init();//时区初始化
grant_init();//初始化表、行级别权限数据结构,并从‘mysql‘数据库中加载权限信息
dynamic_privilege_init();//加载动态权限服务
8.5 mysql库表相关数据加载
servers_init();//从‘mysql’数据库表中初始化结构数据
servers_reload();
udf_read_functions_table();//从mysql.func表中加载函数
9 信号相关
9.1 信号初始化&设置堆栈
//信号初始化
my_init_signals();
//设置堆栈
my_thread_attr_setstacksize();
my_thread_attr_getstacksize(&connection_attrib, &stack_size);
my_thread_stack_size = static_cast<ulong>(stack_size - guardize);
9.2 启动信号处理线程
启动一个线程,用于处理信号。注意:此时信号处理线程并没有真正开始处理信号,在这里进行等待,直到mysqld完成启动后再开始处理信号。
start_signal_handler();//启动signal_hand线程处理信号
signal_hand();//线程执行函数
server_components_init_wait();//等待所有server components初始化完成
9.3 发送COND_server_started
设置mysqld_server_started状态为true,标志着mysqld启动完成。
server_components_initialized();//通知所有已经初始化的服务组件等待线程
mysqld_server_started = true;
//发送COND_server_started,唤醒 9.2启动信号处理线程 中的信号处理线程
mysql_cond_broadcast(&COND_server_started);
10 网络初始化相关
10.1 Srv_session初始化
-
Srv_session类提供了mysql session相关的api操作,包括:打开、关闭、attach、detach、get_session_id等
main函数中的Srv_session::module_init()用于初始化Srv_session:
Srv_session::module_init();//需要在mysqld启动时初始化
server_session_list.init();//注册、初始化相关的锁
server_session_threads.init();//注册、初始化相关的锁
10.2 设置监听端口
设置默认tcp端口和unix socket名称。
-
1 mysql代码默认端口号:MYSQL_PORT,3306。修改这个变量可以修改代码中默认的端口号。
-
2 通过环境变量MYSQL_TCP_PORT设置端口号,该方式会覆盖1中的端口号
-
3 通过配置文件设置端口号,会覆盖2中的端口号
-
通过环境变量MYSQL_UNIX_PORT设置unix socket名称
main函数中,set_ports函数用于设置监听端口、初始化socket
10.3 ssl & network初始化
init_ssl_communication();//
set_fips_mode();//设置fips
TLS_channel::singleton_init();//初始化ssl接收器
network_init();//
mysqld_socket_listener构造 ;//设置listener端口、地址。。
mysqld_socket_acceptor(mysqld_socket_listener);//
mysqld_socket_acceptor->init_connection_acceptor();//
mysqld_socket_listener->setup_listener();//设置几个listener
admin socket listener
tcp socket listener
unix socket listener
admin socket listener
setup_connection_events(m_socket_vector);//设置poll/select连接事件
10.4 循环接受客户端连接
在这里就完成了mysqld的启动过程,在此处开始循环接受客户端连接。
mysqld_socket_acceptor->connection_event_loop();
11 其他的一些初始化过程
另外有一些零散的初始化过程,放置在这一小节中
11.1 初始化sql_statement_names数组
//初始化sql语句名称,包括dml ddl acl dql,将sql语句存放在sql_statement_names数组中
init_sql_statement_names();
sql_statement_names[i].str = 'alter_table';
11.1 Lock Order
LO_init();//初始化Lock Order,主要用Lock Order Graph描述锁依赖关系
Lock Order Tool:8.0.17引入,用于debug,使用lock-order 依赖图描述锁依赖,server运行时确保加锁不会造成循环等待,并且加锁顺序服从Lock Order Graph
11.2 初始化内部资源、变量
init_common_variables();
init_thread_environment();//初始化线程环境,锁、条件变量
//初始化mysql全局变量为默认值,之前从配置文件读取的参数不会被设置
mysql_init_variables();
global_system_variables.time_zone = my_tz_SYSTEM;//设置时区变量
default_storage_engine = "InnoDB";//设置默认存储引擎
get_options();//处理剩余可选项
Connection_handler_manager::init();//初始化连接池,各种锁的初始化
/* 创建Global_THD_manager实例,是个单例模式,用户管理thd,
由于只会在主线程中调用这个,所以不需要考虑单例的线程安全问题 */
Global_THD_manager::create_instance();
mysql_client_plugin_init(); //客户端插件初始化,需在客户端插件函数之前
配置日志
debug_sync_init();//debug sync 初始化.debug_sync.cc.
//创建data目录,当指定--initialize时执行
initialize_create_data_directory(mysql_real_data_home));
11.3 初始化my_str_malloc、free、realloc
my_str_malloc = &my_str_malloc_mysqld;
my_str_free = &my_str_free_mysqld;
my_str_realloc = &my_str_realloc_mysqld;
11.4 创建pid文件
create_pid_file();//创建一个文件,存放程序pid
11.5 初始化all_status_vars数组
init_status_vars();//初始化SHOW STATUS中使用的all_status_vars数组
11.6 初始化事件调度器
mysql事件调度器管理了事件的调度与执行,下面的初始化过程创建了临时线程来做初始化过程。
Events::init();
11.7 process_bootstrap
process_bootstrap();// --initialize或--init-file时调用,执行bootstrap线程
11.8 启动handle管理线程&压缩线程
start_handle_manager();//开启handle管理线程
handle_manager();//线程执行函数,监听COND_manager条件变量等
create_compress_gtid_table_thread();//创建压缩线程,压缩gtid_executed表
compress_gtid_table();//线程执行函数,当收到压缩信号时,压缩gtid_executed表
总结
1 main函数之前会做一些全局对象的初始化。
2 启动过程很繁琐,主要是很多模块的初始化,大部分初始化过程是对一些锁、条件变量的初始化,以及内存空间的申请,初值的设定。
3 初始化过程有一些先后顺序需要遵循
4 初始化过程比较繁琐,本篇介绍了大体的流程,对于具体的问题,需要分模块的来看,对于模块间的相互依赖需要有所了解与认识。需要对具体模块具体分析,深入到每一个模块当中去详细理解。
参考资料
https://dev.mysql.com/blog-archive/mysql-8-0-persisting-configuration-variables/
数据库内核月报:MySQL · 源码分析 · Performance Schema 初始化过程
https://cloud.tencent.com/developer/news/239634
https://icode.best/i/06415035173089
https://www.cnblogs.com/juanmaofeifei/p/16111355.html