MySQL启动过程详解一:启动整体流程

MySQL启动流程如下:

1. 设置进程名

2. 处理配置文件及启动参数以及部分模块初始化,这包括:

  2.1 从配置文件中读取选项,把他们放在 argc 和 argv 已有的参数之前

       2.2 处理标记为 early 的命令行选项,这其中包括:performance_schema,--help相关,bootstrap相关的 options

       2.3 初始化 sql_statement_names 数组

       2.4 将所有的系统变量添加到 system_variable_hash 表中

       2.5 调整相关的 options,包括 open_file_limits,max_connections,table_cache_size,table_definition_cache

       2.6 error log,审计接口,查询日志模块[query log, slow log]的初始化

       2.7 options解析失败,err输出到 stderr,退出

 

3. 初始化内部系统变量,timezone & replfilter & binlogfilter & global mysqlbinlog 对象的mutex, default storage engine 等等

4. 信号系统初始化

5. 设置栈大小

6. 配置了 --user 是检查user,并修改mysql各目录的 user

7. 设置 networking dir

8. server-id 设置

9. 核心模块的启动,包括存储引擎等

10. UUID的设置,并将其添加到 sid_map 中

11. GTID的处理。获取 gtid_purged 和 gtid_executed的值。

12. purge binlog files。

13. network初始化,初始化 mysql connection acceptor

14. save pid file

15. 读取优化器成本模型配置表

16. 初始化 show status 中使用的 all_status_vars[] 数组

17. 检查 binlog cache size 和 binlog stmt cache size 是否超过了 max_binlog_cache_size 和 max_binlog_stmt_cache_size

18. 设置 slave_skip_errors,初始化 slave 结构

19. 初始化 information_schema_acl

20. 执行 ddl_log recovery()

21. 创建 thread handle manager 线程

22. 创建 压缩 gtid_executed 表的压缩线程

23. 开始处理信号

24. 设置 super read only

25. connection acceptor 循环接受客户端连接

 

 

MySQL的入口函数位于 sql/main.cc 文件中,源码解析如下:

// 入口函数, 启动MySQL
int main(int argc, char **argv)
{
  return mysqld_main(argc, argv);
}

mysqld_admin代码如下,去除了psi 和 win32 部分:

int mysqld_main(int argc, char **argv)
{
  /*
    Perform basic thread library and malloc initialization,
    to be able to read defaults files and parse options.
    执行基本线程库和malloc初始化, 以便能够读取默认文件和解析选项。
  */
  // 进程名
  my_progname = argv[0];
orig_argc = argc; orig_argv = argv; my_getopt_use_args_separator = TRUE; my_defaults_read_login_file = FALSE; // 处理配置文件及启动参数 // 从配置文件中读取选项, 并把他们放在 argc 和 argv 中已有的参数之前。通过这种方式, 调用程序 // 可以轻松的将命令行中的参数项覆盖到配置文件中的参数项 if (load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv)) { flush_error_log_messages(); return 1; } my_getopt_use_args_separator = FALSE; defaults_argc = argc; defaults_argv = argv; remaining_argc = argc; remaining_argv = argv; /**
charset_info & message_handle
*/ /* Must be initialized early for comparison of options name */ system_charset_info = &my_charset_utf8_general_ci; /* Write mysys error messages to the error log. */ local_message_hook = error_log_print; int ho_error; /* * 处理标记为 early 的命令行选项。 * 有些组件需要尽早初始化, 因为服务器初始化的其余部分依赖他们。 * 需要提前分析的选项包括: * performance_schema * help相关的options * bootstrap相关的options */ ho_error = handle_early_options(); // 将所有的 SQL_COM 存储在 sql_statement_names 全局数组中 init_sql_statement_names(); // 将所有的系统变量 system variables 加入到哈希表 system_variable_hash 中 sys_var_init(); ulong requested_open_files; // 调整相关的 options[open_file_limits,max_connections,table_cache_size,table_definition_cache] adjust_related_options(&requested_open_files); // 初始化 error log init_error_log(); /* Initialize audit interface globals. Audit plugins are inited later. 初始化审计接口, 审计插件在之后初始化。 */ mysql_audit_initialize(); #ifndef EMBEDDED_LIBRARY /* * Srv_session 模块初始化。 * Srv_session: Srv_session 将 THD, DA 打包在一个包中, 以供内部API使用。 * Srv_session 还提供物理线程初始化和相应的去初始化方法。 */ Srv_session::module_init(); #endif /* Perform basic query log initialization. Should be called after MY_INIT, as it initializes mutexes. 查询日志模块初始化。general_log & slow_log */ query_logger.init(); // 解析命令行option失败, err打印到 stderr, 退出 if (ho_error) { flush_error_log_messages(); exit(MYSQLD_ABORT_EXIT); } // 初始化很多内部系统变量, timezone & replfilter & binlogfilter & global mysqlbinlog 对象的mutex 等等 if (init_common_variables()) unireg_abort(MYSQLD_ABORT_EXIT); // Will do exit // 信号系统初始化 my_init_signals(); size_t guardize = 0; #ifndef _WIN32 // 获取为栈溢出保护创建的保护区域的大小 int retval = pthread_attr_getguardsize(&connection_attrib, &guardize); assert(retval == 0); if (retval != 0) guardize = my_thread_stack_size; #endif #if defined(__ia64__) || defined(__ia64) /* Peculiar things with ia64 platforms - it seems we only have half the stack size in reality, so we have to double it here */ guardize = my_thread_stack_size; #endif // 设置栈大小 my_thread_attr_setstacksize(&connection_attrib, my_thread_stack_size + guardize); { /* Retrieve used stack size; Needed for checking stack overflows */ size_t stack_size = 0; my_thread_attr_getstacksize(&connection_attrib, &stack_size); /* We must check if stack_size = 0 as Solaris 2.9 can return 0 here */ if (stack_size && stack_size < (my_thread_stack_size + guardize)) { sql_print_warning("Asked for %lu thread stack, but got %ld", my_thread_stack_size + guardize, (long)stack_size); #if defined(__ia64__) || defined(__ia64) my_thread_stack_size = stack_size / 2; #else my_thread_stack_size = static_cast<ulong>(stack_size - guardize); #endif } } #ifndef _WIN32 // 当配置了 --user 时 check user if ((user_info = check_user(mysqld_user))) { #if HAVE_CHOWN
// chown if (unlikely(opt_initialize)) { /* need to change the owner of the freshly created data directory */ MY_STAT stat; char errbuf[MYSYS_STRERROR_SIZE]; bool must_chown = true; /* fetch the directory's owner */ if (!my_stat(mysql_real_data_home, &stat, MYF(0))) { sql_print_information("Can't read data directory's stats (%d): %s." "Assuming that it's not owned by the same user/group", my_errno(), my_strerror(errbuf, sizeof(errbuf), my_errno())); } /* Don't change it if it's already the same as SElinux stops this */ else if (stat.st_uid == user_info->pw_uid && stat.st_gid == user_info->pw_gid) must_chown = false; if (must_chown && chown(mysql_real_data_home, user_info->pw_uid, user_info->pw_gid)) { sql_print_error("Can't change data directory owner to %s", mysqld_user); unireg_abort(1); } } #endif #if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) if (locked_in_memory) // getuid() == 0 here set_effective_user(user_info); else #endif set_user(mysqld_user, user_info); } #endif // !_WIN32 /* We have enough space for fiddling with the argv, continue */ // 设置 net working dir if (my_setwd(mysql_real_data_home, MYF(MY_WME)) && !opt_help) { sql_print_error("failed to set datadir to %s", mysql_real_data_home); unireg_abort(MYSQLD_ABORT_EXIT); /* purecov: inspected */ } //If the binlog is enabled, one needs to provide a server-id // binlog 开启, 那么也应该提供 server—id if (opt_bin_log && !(server_id_supplied)) { sql_print_error("You have enabled the binary log, but you haven't provided " "the mandatory server-id. Please refer to the proper " "server start-up parameters documentation"); unireg_abort(MYSQLD_ABORT_EXIT); } /* The subsequent calls may take a long time : e.g. innodb log read. Thus set the long running service control manager timeout */ #if defined(_WIN32) Service.SetSlowStarting(slow_start_timeout); #endif /* * 核心模块启动, 包括存储引擎等 */ if (init_server_components()) unireg_abort(MYSQLD_ABORT_EXIT); /* Each server should have one UUID. We will create it automatically, if it does not exist. 每一个 server 都有一个 UUID。如果不存在, 将自动的创建。 */ if (init_server_auto_options()) { sql_print_error("Initialization of the server's UUID failed because it could" " not be read from the auto.cnf file. If this is a new" " server, the initialization failed because it was not" " possible to generate a new UUID."); unireg_abort(MYSQLD_ABORT_EXIT); } /* Add server_uuid to the sid_map. This must be done after server_uuid has been initialized in init_server_auto_options and after the binary log (and sid_map file) has been initialized in init_server_components(). No error message is needed: init_sid_map() prints a message. Strictly speaking, this is not currently needed when opt_bin_log==0, since the variables that gtid_state->init initializes are not currently used in that case. But we call it regardless to avoid possible future bugs if gtid_state ever needs to do anything else. */ // 将 server_uuid 添加到 sid_map 中。 global_sid_lock->wrlock(); int gtid_ret = gtid_state->init(); global_sid_lock->unlock(); if (gtid_ret) unireg_abort(MYSQLD_ABORT_EXIT); // Initialize executed_gtids from mysql.gtid_executed table. // 从 mysql.gtid_executed 表中初始化 executed_gtids if (gtid_state->read_gtid_executed_from_table() == -1) unireg_abort(1); if (opt_bin_log) { /* Initialize GLOBAL.GTID_EXECUTED and GLOBAL.GTID_PURGED from gtid_executed table and binlog files during server startup. 在 server 启动的时候从 gtid_executed 表和 binlog 文件中初始化 GLOBAL.GTID_EXECUTED 和 GLOBAL.GTID_PURGED。 */ Gtid_set *executed_gtids = const_cast<Gtid_set *>(gtid_state->get_executed_gtids()); Gtid_set *lost_gtids = const_cast<Gtid_set *>(gtid_state->get_lost_gtids()); Gtid_set *gtids_only_in_table = const_cast<Gtid_set *>(gtid_state->get_gtids_only_in_table()); Gtid_set *previous_gtids_logged = const_cast<Gtid_set *>(gtid_state->get_previous_gtids_logged()); Gtid_set purged_gtids_from_binlog(global_sid_map, global_sid_lock); Gtid_set gtids_in_binlog(global_sid_map, global_sid_lock); Gtid_set gtids_in_binlog_not_in_table(global_sid_map, global_sid_lock); if (mysql_bin_log.init_gtid_sets(&gtids_in_binlog, &purged_gtids_from_binlog, opt_master_verify_checksum, true /*true=need lock*/, NULL /*trx_parser*/, NULL /*gtid_partial_trx*/, true /*is_server_starting*/)) unireg_abort(MYSQLD_ABORT_EXIT); global_sid_lock->wrlock(); purged_gtids_from_binlog.dbug_print("purged_gtids_from_binlog"); gtids_in_binlog.dbug_print("gtids_in_binlog"); if (!gtids_in_binlog.is_empty() && !gtids_in_binlog.is_subset(executed_gtids)) { gtids_in_binlog_not_in_table.add_gtid_set(&gtids_in_binlog); if (!executed_gtids->is_empty()) gtids_in_binlog_not_in_table.remove_gtid_set(executed_gtids); /* Save unsaved GTIDs into gtid_executed table, in the following four cases: 1. the upgrade case. 2. the case that a slave is provisioned from a backup of the master and the slave is cleaned by RESET MASTER and RESET SLAVE before this. 3. the case that no binlog rotation happened from the last RESET MASTER on the server before it crashes. 4. The set of GTIDs of the last binlog is not saved into the gtid_executed table if server crashes, so we save it into gtid_executed table and executed_gtids during recovery from the crash. */ if (gtid_state->save(&gtids_in_binlog_not_in_table) == -1) { global_sid_lock->unlock(); unireg_abort(MYSQLD_ABORT_EXIT); } executed_gtids->add_gtid_set(&gtids_in_binlog_not_in_table); } /* gtids_only_in_table= executed_gtids - gtids_in_binlog */ if (gtids_only_in_table->add_gtid_set(executed_gtids) != RETURN_STATUS_OK) { global_sid_lock->unlock(); unireg_abort(MYSQLD_ABORT_EXIT); } gtids_only_in_table->remove_gtid_set(&gtids_in_binlog); /* lost_gtids = executed_gtids - (gtids_in_binlog - purged_gtids_from_binlog) = gtids_only_in_table + purged_gtids_from_binlog; */ assert(lost_gtids->is_empty()); if (lost_gtids->add_gtid_set(gtids_only_in_table) != RETURN_STATUS_OK || lost_gtids->add_gtid_set(&purged_gtids_from_binlog) != RETURN_STATUS_OK) { global_sid_lock->unlock(); unireg_abort(MYSQLD_ABORT_EXIT); } /* Prepare previous_gtids_logged for next binlog */ if (previous_gtids_logged->add_gtid_set(&gtids_in_binlog) != RETURN_STATUS_OK) { global_sid_lock->unlock(); unireg_abort(MYSQLD_ABORT_EXIT); } /* Write the previous set of gtids at this point because during the creation of the binary log this is not done as we cannot move the init_gtid_sets() to a place before openning the binary log. This requires some investigation. /Alfranio */ Previous_gtids_log_event prev_gtids_ev(&gtids_in_binlog); global_sid_lock->unlock(); (prev_gtids_ev.common_footer)->checksum_alg = static_cast<enum_binlog_checksum_alg>(binlog_checksum_options); if (prev_gtids_ev.write(mysql_bin_log.get_log_file())) unireg_abort(MYSQLD_ABORT_EXIT); mysql_bin_log.add_bytes_written( prev_gtids_ev.common_header->data_written); if (flush_io_cache(mysql_bin_log.get_log_file()) || mysql_file_sync(mysql_bin_log.get_log_file()->file, MYF(MY_WME))) unireg_abort(MYSQLD_ABORT_EXIT); // 更新 binlog end pos mysql_bin_log.update_binlog_end_pos(); #ifdef HAVE_REPLICATION // purge binlog files if (opt_bin_log && expire_logs_days) { time_t purge_time = server_start_time - expire_logs_days * 24 * 60 * 60; DBUG_EXECUTE_IF("expire_logs_always_at_start", { purge_time = my_time(0); }); if (purge_time >= 0) mysql_bin_log.purge_logs_before_date(purge_time, true); } #endif // after engine recovery hook (void)RUN_HOOK(server_state, after_engine_recovery, (NULL)); } // init_ssl if (init_ssl()) unireg_abort(MYSQLD_ABORT_EXIT); // network_init; 初始化 mysql connection acceptor if (network_init()) unireg_abort(MYSQLD_ABORT_EXIT); #ifdef _WIN32 #ifndef EMBEDDED_LIBRARY if (opt_require_secure_transport && !opt_enable_shared_memory && !opt_use_ssl && !opt_initialize && !opt_bootstrap) { sql_print_error("Server is started with --require-secure-transport=ON " "but no secure transports (SSL or Shared Memory) are " "configured."); unireg_abort(MYSQLD_ABORT_EXIT); } #endif #endif /* Initialize my_str_malloc(), my_str_realloc() and my_str_free() 初始化 my_str_malloc, my_str_realloc, my_str_free */ my_str_malloc = &my_str_malloc_mysqld; my_str_free = &my_str_free_mysqld; my_str_realloc = &my_str_realloc_mysqld; error_handler_hook = my_message_sql; /* Save pid of this process in a file pid file */ if (!opt_bootstrap) create_pid_file(); /* Read the optimizer cost model configuration tables 读取优化器成本模型配置表 */ if (!opt_bootstrap) reload_optimizer_cost_constants(); // remove tmp tables || acl init || rz init || grant init if (mysql_rm_tmp_tables() || acl_init(opt_noacl) || my_tz_init((THD *)0, default_tz_name, opt_bootstrap) || grant_init(opt_noacl)) { abort_loop = true; sql_print_error("Fatal error: Failed to initialize ACL/grant/time zones " "structures or failed to remove temporary table files."); delete_pid_file(MYF(MY_WME)); unireg_abort(MYSQLD_ABORT_EXIT); } // servers init if (!opt_bootstrap) servers_init(0); if (!opt_noacl) { #ifdef HAVE_DLOPEN udf_init(); #endif } // 初始化 show status 中使用的 all_status_vars[] 数组 init_status_vars(); /* If running with bootstrap, do not start replication. */ if (opt_bootstrap) opt_skip_slave_start = 1; // 检查 binlog cache size 是否超过 max_binlog_cache_size check_binlog_cache_size(NULL); // 检查 BINLOG_STMT_CACHE_SIZE 大小是否超过了 MAX_BINLOG_STMT_CACHE_SIZE check_binlog_stmt_cache_size(NULL); binlog_unsafe_map_init(); /* If running with bootstrap, do not start replication. */ if (!opt_bootstrap) { // Make @@slave_skip_errors show the nice human-readable value. // 设置 slave_skip_errors set_slave_skip_errors(&opt_slave_skip_errors); /* init_slave() must be called after the thread keys are created. */ if (server_id != 0) // 初始化 slave 结构 init_slave(); /* Ignoring errors while configuring replication. */ } // 初始化 information_schema_acl initialize_information_schema_acl(); // 执行 ddl_log recovery. execute_ddl_log_recovery(); // after_recovery hook (void)RUN_HOOK(server_state, after_recovery, (NULL)); if (Events::init(opt_noacl || opt_bootstrap)) unireg_abort(MYSQLD_ABORT_EXIT); #ifndef _WIN32 // Start signal handler thread. start_signal_handler(); #endif if (opt_bootstrap) { start_processing_signals(); int error = bootstrap(mysql_stdin); unireg_abort(error ? MYSQLD_ABORT_EXIT : MYSQLD_SUCCESS_EXIT); } if (opt_init_file && *opt_init_file) { if (read_init_file(opt_init_file)) unireg_abort(MYSQLD_ABORT_EXIT); } /* Event must be invoked after error_handler_hook is assigned to my_message_sql, otherwise my_message will not cause the event to abort. */ if (mysql_audit_notify(AUDIT_EVENT(MYSQL_AUDIT_SERVER_STARTUP_STARTUP), (const char **)argv, argc)) unireg_abort(MYSQLD_ABORT_EXIT); // 创建 handle manager 线程 start_handle_manager(); // 创建 压缩 gtid_executed 表的压缩线程 create_compress_gtid_table_thread(); sql_print_information(ER_DEFAULT(ER_STARTUP), my_progname, server_version, #ifdef HAVE_SYS_UN_H (opt_bootstrap ? (char *)"" : mysqld_unix_port), #else (char *)"", #endif mysqld_port, MYSQL_COMPILATION_COMMENT); #if defined(_WIN32) Service.SetRunning(); #endif // 开始处理信号 start_processing_signals(); if (!opt_bootstrap) { /* Execute an I_S query to implicitly check for tables using the deprecated partition engine. No need to do this during bootstrap. We ignore the return value from the query execution. Note that this must be done after NDB is initialized to avoid polluting the server with invalid table shares. */ if (!opt_disable_partition_check) { sql_print_information( "Executing 'SELECT * FROM INFORMATION_SCHEMA.TABLES;' " "to get a list of tables using the deprecated partition " "engine."); sql_print_information("Beginning of list of non-natively partitioned tables"); (void)bootstrap_single_query( "SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES " "WHERE CREATE_OPTIONS LIKE '%partitioned%';"); sql_print_information("End of list of non-natively partitioned tables"); } } /* Set opt_super_readonly here because if opt_super_readonly is set in get_option, it will create problem while setting up event scheduler. */ // 在此处设置 super_read_only set_super_read_only_post_init(); DBUG_PRINT("info", ("Block, listening for incoming connections")); (void)MYSQL_SET_STAGE(0, __FILE__, __LINE__); server_operational_state = SERVER_OPERATING; // before_handle_connection hook (void)RUN_HOOK(server_state, before_handle_connection, (NULL)); mysql_mutex_lock(&LOCK_socket_listener_active); // Make it possible for the signal handler to kill the listener. socket_listener_active = true; mysql_mutex_unlock(&LOCK_socket_listener_active); if (opt_daemonize) mysqld::runtime::signal_parent(pipe_write_fd, 1); /* * connection acceptor 循环接受客户端连接 */ mysqld_socket_acceptor->connection_event_loop(); server_operational_state = SERVER_SHUTTING_DOWN; DBUG_PRINT("info", ("No longer listening for incoming connections")); mysql_audit_notify(MYSQL_AUDIT_SERVER_SHUTDOWN_SHUTDOWN, MYSQL_AUDIT_SERVER_SHUTDOWN_REASON_SHUTDOWN, MYSQLD_SUCCESS_EXIT); terminate_compress_gtid_table_thread(); /* Save set of GTIDs of the last binlog into gtid_executed table on server shutdown. */ if (opt_bin_log) if (gtid_state->save_gtids_of_last_binlog_into_table(false)) sql_print_warning("Failed to save the set of Global Transaction " "Identifiers of the last binary log into the " "mysql.gtid_executed table while the server was " "shutting down. The next server restart will make " "another attempt to save Global Transaction " "Identifiers into the table."); #ifndef _WIN32 mysql_mutex_lock(&LOCK_socket_listener_active); // Notify the signal handler that we have stopped listening for connections. socket_listener_active = false; mysql_cond_broadcast(&COND_socket_listener_active); mysql_mutex_unlock(&LOCK_socket_listener_active); #endif // !_WIN32 #ifdef HAVE_PSI_THREAD_INTERFACE /* Disable the main thread instrumentation, to avoid recording events during the shutdown. */ PSI_THREAD_CALL(delete_current_thread) (); #endif DBUG_PRINT("info", ("Waiting for shutdown proceed")); int ret = 0; #ifdef _WIN32 if (shutdown_thr_handle.handle) ret = my_thread_join(&shutdown_thr_handle, NULL); shutdown_thr_handle.handle = NULL; if (0 != ret) sql_print_warning("Could not join shutdown thread. error:%d", ret); #else if (signal_thread_id.thread != 0) ret = my_thread_join(&signal_thread_id, NULL); signal_thread_id.thread = 0; if (0 != ret) sql_print_warning("Could not join signal_thread. error:%d", ret); #endif clean_up(1); mysqld_exit(MYSQLD_SUCCESS_EXIT); }

  

posted @ 2022-04-07 14:17  卷毛狒狒  阅读(1564)  评论(0编辑  收藏  举报