MySQL实例启动过程(上):server层

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 前言MySQL是一个庞大的数据库系统,对于数据库内核研发人员来说,非常有必要了解MySQL实例的启动过程。对于数据库使用人员来说,也需要对MySQL实例的启动过程有一个基本的了解。​本文旨在对MySQL Server层的启动过程做一个整体的介绍,让读者对Server层启动整体流程有一个基本的认识。可将本文看作一个手册,用于查询server相关启动过程,需要深入理解时再对启动源码进行分析。下面以My

前言

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项有如下六种,分别解释如下:

NAME

解释

global_table_io_class

TABLE IO相关的instrument

global_table_lock_class

与TABLE 锁相关的instrument

global_idle_class

空闲事件相关的instrument

global_metadata_class

与metadata_lock相关的instrument

global_error_class

错误信息相关的instrument

global_transaction_class

事务相关的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

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
存储 SQL 关系型数据库
创建并配置RDS实例
在阿里云上创建RDS实例涉及登录控制台、进入RDS管理页面、创建实例、选择数据库引擎和版本、配置实例规格与存储、设定网络与安全组、设置实例信息、确认订单并支付,最后初始化数据库。操作步骤可能因界面更新或数据库引擎不同略有差异。
19 1
|
3月前
|
弹性计算 关系型数据库 MySQL
快速上手阿里云RDS MySQL实例创建,轻松管理数据库
快速上手阿里云RDS MySQL实例创建,轻松管理数据库 在数字化时代,数据已成为企业的核心资产。如何高效、安全地存储和管理这些数据,成为企业在云计算时代亟待解决的问题。阿里云的RDS(关系型数据库服务)应运而生,为用户提供稳定、可靠的云上数据库解决方案。本文将详细介绍如何通过阿里云RDS管理控制台快速创建RDS MySQL实例,让您轻松上手,快速部署数据库。
170 2
|
1天前
|
关系型数据库 MySQL 数据库
一台MySQL数据库启动多个实例
一台MySQL数据库启动多个实例
|
1天前
|
SQL 关系型数据库 MySQL
:“You have an error in your SQL syntax; check the manual that corresponds to your MySQL server versi
:“You have an error in your SQL syntax; check the manual that corresponds to your MySQL server versi
6 0
|
8天前
|
关系型数据库 MySQL Linux
Linux联网安装MySQL Server
Linux联网安装MySQL Server
20 0
|
1月前
|
关系型数据库 MySQL 数据库
初始化RDS实例
初始化RDS实例
15 3
|
1月前
|
SQL 关系型数据库 MySQL
购买阿里云RDS实例
购买阿里云RDS实例
165 2
|
1月前
|
弹性计算 关系型数据库 MySQL
连接RDS实例
连接RDS实例
12 1
|
1月前
|
SQL 关系型数据库 MySQL
MySQL多实例部署:从概念到实操的全面指南
MySQL多实例部署:从概念到实操的全面指南
40 0
|
2月前
|
关系型数据库 MySQL 数据库
Host 'XXX' is not allowed to connect to this MySQL server 解决方案
Host 'XXX' is not allowed to connect to this MySQL server 解决方案