MySQL InnoDB存储引擎启动过程源码分析

InnoDB 存储引擎是MySQL默认的存储引擎,MySQL的架构是Server-Engine架构,从代码层来看,也可以理解为Server-Plugin架构,MySQL很多功能都是以插件Plugin方式实现的,包括存储引擎在内。

本文将简单介绍MySQL InnoDB存储引擎在启动过程中所做的一些事情,主要包括调用了哪些函数,创建了哪些线程,这些函数和线程的大概作用,以便对InnoDB启动过程有一个初步的了解。

源码版本:Percona Server for MySQL 5.7.19

一、InnoDB插件的定义

InnoDB作为MySQL的一个插件,在源码文件handler/ha_innodb.cc 中可以看到InnoDB的插件的定义,如下:
mysql_declare_plugin(innobase)
{
MYSQL_STORAGE_ENGINE_PLUGIN,
&innobase_storage_engine,
innobase_hton_name,
plugin_author,
"Percona-XtraDB, Supports transactions, row-level locking, and foreign  keys",
PLUGIN_LICENSE_GPL,
innobase_init, /* Plugin Init */
NULL, /* Plugin Deinit */
INNODB_VERSION_SHORT,
innodb_status_variables_export,/* status variables             */
innobase_system_variables, /* system variables */
NULL, /* reserved */
0,    /* flags */
},
......


从InnoDB插件的定义来看,其初始化函数为 innobase_init,位于handler/ha_innodb.cc 文件中,这个函数只有一个参数,该参数是handlerton结构体类型的指针。

handlerton 可以理解为MySQL server层与engine层的一个纽带,一个存储引擎对应一个该类型的实例,用来提供在全局范围内访问存储引擎的功能。handlerton 结构体内部定义了很多接口,比如commit, rollback, create , drop_database 等等,在存储引擎初始化阶段,会把这些接口赋值为存储引擎实际的实现,以便在server层能够调用存储引擎的接口。handlerton 结构体定义在 sql/handler.h文件中。

二、InnoDB初始化函数innobase_init

innobase_init函数大约680行代码,是InnoDB的初始化函数,具体做了哪些事情?总结如下:
  1. 初始化innobase_hton,它是一个handlerton类型的指针,在存储引擎初始化阶段,会把这些接口赋值为InnoDB存储引擎实际的实现,以便在server层能够调用存储引擎的接口。
  2. InnoDB相关参数的检查和初始化,包括共享表空间、临时表空间、undo表空间、redo日志文件、double write文件等等。
  3. 调用 innobase_start_or_create_for_mysql 函数,这个函数非常重要,后面详细分析这个函数。

三、innobase_start_or_create_for_mysql 函数

这个函数位于源码文件storage/innobase/srv/srv0start.cc,主要作用是启动innodb引擎,如果数据目录为空,则会创建一个新的数据库所需要的文件。这个函数比较长,大约1300多行代码,下面将按照代码执行的顺序分析其主要处理逻辑。
  1. 处理参数innodb_flush_method,由于linux和windows平台对该参数可选值的不同,源码在这里做了比较多的处理。
  2. 设置innodb的可能使用的最大线程数量,具体怎么算的,见本文最后的附图。
  3. 处理参数innodb_buffer_pool_size和innodb_buffer_pool_instances,当innodb_buffer_pool_size小于1G时,innodb_buffer_pool_instances只能为1。
  4. 处理srv_n_page_cleaners参数,该参数是脏页清理线程的数量,如果大于innodb_buffer_pool_instances,则调整为与innodb_buffer_pool_instances相等。
  5. 调用 srv_boot 函数,启动innodb server,实际上其内部调用了很多初始化的函数,可以理解为相关参数和组件的初始化。
  6. 调用os_aio_init函数,初始化aio系统。
  7. 调用buf_pool_init函数,初始化buffer pool系统。
  8. 调用log_init函数,初始化redo log系统。
  9. 调用recv_sys_create和recv_sys_init函数,创建及初始化recovery系统。
  10. 调用lock_sys_create函数,创建锁系统。
  11. 调用os_thread_create函数,创建处理io的线程,主要包括io_ibuf_thread线程,io_log_thread线程,srv_n_read_io_threads线程, srv_n_write_io_threads线程。
  12. 调用buf_flush_page_cleaner_init函数,初始化页清理系统,之后创建一个页清理协调线程,多个(srv_n_page_cleaners)页清理工作线程。
  13. 根据srv_buf_pool_instances数量,创建相应数量的线程,做buffer pool 的lru管理。
  14. 等待页清理(page cleaner)变为active状态。
  15. 调用srv_sys_space.check_file_spec()函数,检查数据文件是否存在,是否需要创建一个新的数据库。
  16. 如果需要创建一个新的数据库,调用srv_check_undo_redo_logs_exists来检查undo表空间和redo 文件是否已存在。
  17. 调用srv_sys_space.open_or_create(),打开或者创建数据文件。
  18. 根据create_new_db布尔变量,分为两个分支,创建新的数据库和打开已有数据文件。当create_new_db为true时:
    1. 调用buf_flush_sync_all_buf_pools
    2. 调用log_get_lsn
    3. 调用create_log_files()创建redo log文件
  19. 当create_new_db为false时:
    1. 调用open_log_file函数,依次打开redo log文件。由于redo log文件数量可配置,innodb在启动时也不知道有多少个redo log文件,这里在实现时,通过一个循环,从0开始,一直到SRV_N_LOG_FILES_MAX,直到读取的redo log文件不存在时,退出循环。
  20. 调用fil_space_create函数,创建一个内存中的空间对象。
  21. 调用fil_node_create函数,把redo log文件依次关联到上一步创建的内存空间对象上。
  22. 调用log_group_init函数,为日志系统初始化一个日志组。
  23. 调用fil_open_log_and_system_tablespace_files函数,打开所有的日志文件和系统表空间中的所有数据文件,这些文件会一直打开,直到数据库关闭。这个函数应该在日志和系统表的空间对象创建完成之后来调用,这样的目的是确保读取insert buffer或者写日志不会因为文件描述符不存在而出问题。
  24. 调用srv_undo_tablespaces_init函数打开undo表空间。
  25. 调用dict_stats_thread_init函数,初始化dict_stats_thread线程需要的一些全局变量。
  26. 调用trx_sys_file_format_init函数,初始化变量file_format_max,该变量类型为file_format_t。
  27. 调用trx_sys_create函数,创建trx_sys实例,并初始化purge_queue 和 mutex。
  28. 调用buf_pool_invalidate函数,验证buffer pool。
  29. 创建innodb监控线程,线程函数为srv_monitor_thread。
  30. 调用recv_recovery_from_checkpoint_start函数,做recovery,即使MySQL正常关闭也需要执行该函数。
  31. 调用buf_parallel_dblwr_finish_recovery函数,在recovery结束后,释放未使用的double write页以及申请的缓存。
  32. 调用dict_boot函数,初始化数据字典内存结构。
  33. 调用buf_parallel_dblwr_create函数,初始化double write子系统,创建其数据结构和磁盘文件。
  34. 调用trx_sys_init_at_db_start函数,创建并且初始化事务系统的内存结构。
  35. 调用trx_purge_sys_create函数,创建全局的purge系统控制结构,它必须在事务系统初始化完成之后调用。
  36. 调用recv_recovery_from_checkpoint_finish函数,从一个检查点完成recovery。
  37. 调用srv_open_tmp_tablespace函数,打开临时表空间,直到数据库关闭。
  38. 调用trx_sys_create_rsegs函数,创建回滚段。
  39. 创建锁等待超时线程,线程函数为lock_wait_timeout_thread。
  40. 创建信号量超时监控线程,当信号量等待持续过长的时间时,打印警告信息,线程函数为srv_error_monitor_thread。
  41. 创建innodb监控线程,线程函数为srv_monitor_thread。
  42. 调用dict_create_or_check_foreign_constraint_tables函数,创建innodb内部的外键约束系统表。
  43. 调用dict_create_or_check_sys_tablespace函数,检查innodb内部的表空间和数据文件格式是否正确,如果不存在,则创建它们。
  44. 调用dict_create_or_check_sys_virtual函数,检查innodb内部的虚拟列系统表(SYS_VIRTUAL)格式是否正确,如果不存在,则创建它们。
  45. 调用dict_create_or_check_sys_zip_dict函数,检查innodb内部的zip_dict系统表格式是否正确,如果不存在,则创建它们。
  46. 创建innodb主线程,线程函数为srv_master_thread。
  47. 创建purge系统协调线程,线程函数为srv_purge_coordinator_thread。
  48. 创建purge系统工作线程,数量由srv_n_purge_threads参数决定,线程函数为srv_worker_thread。
  49. 调用srv_start_wait_for_purge_to_start函数,等待purge系统启动。
  50. 创建buffer pool dump/load线程,线程函数为buf_dump_thread。
  51. 创建统计信息收集线程,线程函数为dict_stats_thread。
  52. 调用函数fts_optimize_init,创建优化线程,线程函数为fts_optimize_thread。
  53. 最后创建buffer pool size动态调整线程,线程函数为buf_resize_thread。

从以上的分析可以看出,innobase_start_or_create_for_mysql函数做了很多事情,调用了很多函数,创建了很多线程,我们没有细究每个函数、每个线程都有什么作用,但是这不影响对InnoDB整个初始化过程的了解。后面将会针对InnoDB的具体模块,从源码角度分析其具体的实现。

最后附上InnoDB最大线程数量的计算方法,如下:

srv_max_n_threads = 1   /* io_ibuf_thread */
                     + 1 /* io_log_thread */
                     + 1 /* lock_wait_timeout_thread */
                     + 1 /* srv_error_monitor_thread */
                     + 1 /* srv_monitor_thread */
                     + 1 /* srv_master_thread */
                     + 1 /* srv_redo_log_follow_thread */
                     + 1 /* srv_purge_coordinator_thread */
                     + 1 /* buf_dump_thread */
                     + 1 /* dict_stats_thread */
                     + 1 /* fts_optimize_thread */
                     + 1 /* trx_rollback_or_clean_all_recovered */
                     + 128 /* added as margin, for use of
                         InnoDB Memcached etc. */
                     + max_connections
                     + srv_n_read_io_threads
                     + srv_n_write_io_threads
                     + srv_n_purge_threads
                     + srv_n_page_cleaners
                     /* FTS Parallel Sort */
                     + fts_sort_pll_degree * FTS_NUM_AUX_INDEX
                       * max_connections;

文章评论

0条评论