InnoDB 存储引擎是MySQL默认的存储引擎,MySQL的架构是Server-Engine架构,从代码层来看,也可以理解为Server-Plugin架构,MySQL很多功能都是以插件Plugin方式实现的,包括存储引擎在内。
本文将简单介绍MySQL InnoDB存储引擎在启动过程中所做的一些事情,主要包括调用了哪些函数,创建了哪些线程,这些函数和线程的大概作用,以便对InnoDB启动过程有一个初步的了解。
InnoDB作为MySQL的一个插件,在源码文件handler/ha_innodb.cc 中可以看到InnoDB的插件的定义,如下:
从InnoDB插件的定义来看,其初始化函数为 innobase_init,位于handler/ha_innodb.cc 文件中,这个函数只有一个参数,该参数是handlerton结构体类型的指针。
handlerton 可以理解为MySQL server层与engine层的一个纽带,一个存储引擎对应一个该类型的实例,用来提供在全局范围内访问存储引擎的功能。handlerton 结构体内部定义了很多接口,比如commit, rollback, create , drop_database 等等,在存储引擎初始化阶段,会把这些接口赋值为存储引擎实际的实现,以便在server层能够调用存储引擎的接口。handlerton 结构体定义在 sql/handler.h文件中。
innobase_init函数大约680行代码,是InnoDB的初始化函数,具体做了哪些事情?总结如下:
初始化innobase_hton,它是一个handlerton类型的指针,在存储引擎初始化阶段,会把这些接口赋值为InnoDB存储引擎实际的实现,以便在server层能够调用存储引擎的接口。InnoDB相关参数的检查和初始化,包括共享表空间、临时表空间、undo表空间、redo日志文件、double write文件等等。调用 innobase_start_or_create_for_mysql 函数,这个函数非常重要,后面详细分析这个函数。innobase_start_or_create_for_mysql 函数:
这个函数位于源码文件storage/innobase/srv/srv0start.cc,主要作用是启动innodb引擎,如果数据目录为空,则会创建一个新的数据库所需要的文件。这个函数比较长,大约1300多行代码,下面将按照代码执行的顺序分析其主要处理逻辑。
处理参数innodb_flush_method,由于linux和windows平台对该参数可选值的不同,源码在这里做了比较多的处理。设置innodb的可能使用的最大线程数量,具体怎么算的,见本文最后的附图。处理参数innodb_buffer_pool_size和innodb_buffer_pool_instances,当innodb_buffer_pool_size小于1G时,innodb_buffer_pool_instances只能为1。处理srv_n_page_cleaners参数,该参数是脏页清理线程的数量,如果大于innodb_buffer_pool_instances,则调整为与innodb_buffer_pool_instances相等。调用 srv_boot 函数,启动innodb server,实际上其内部调用了很多初始化的函数,可以理解为相关参数和组件的初始化。调用os_aio_init函数,初始化aio系统。调用buf_pool_init函数,初始化buffer pool系统。调用log_init函数,初始化redo log系统。调用recv_sys_create和recv_sys_init函数,创建及初始化recovery系统。调用lock_sys_create函数,创建锁系统。调用os_thread_create函数,创建处理io的线程,主要包括io_ibuf_thread线程,io_log_thread线程,srv_n_read_io_threads线程, srv_n_write_io_threads线程。调用buf_flush_page_cleaner_init函数,初始化页清理系统,之后创建一个页清理协调线程,多个(srv_n_page_cleaners)页清理工作线程。根据srv_buf_pool_instances数量,创建相应数量的线程,做buffer pool 的lru管理。等待页清理(page cleaner)变为active状态。调用srv_sys_space.check_file_spec()函数,检查数据文件是否存在,是否需要创建一个新的数据库。如果需要创建一个新的数据库,调用srv_check_undo_redo_logs_exists来检查undo表空间和redo 文件是否已存在。调用srv_sys_space.open_or_create(),打开或者创建数据文件。根据create_new_db布尔变量,分为两个分支,创建新的数据库和打开已有数据文件。当create_new_db为true时:调用buf_flush_sync_all_buf_pools调用log_get_lsn调用create_log_files()创建redo log文件当create_new_db为false时:调用open_log_file函数,依次打开redo log文件。由于redo log文件数量可配置,innodb在启动时也不知道有多少个redo log文件,这里在实现时,通过一个循环,从0开始,一直到SRV_N_LOG_FILES_MAX,直到读取的redo log文件不存在时,退出循环。调用fil_space_create函数,创建一个内存中的空间对象。调用fil_node_create函数,把redo log文件依次关联到上一步创建的内存空间对象上。调用log_group_init函数,为日志系统初始化一个日志组。调用fil_open_log_and_system_tablespace_files函数,打开所有的日志文件和系统表空间中的所有数据文件,这些文件会一直打开,直到数据库关闭。这个函数应该在日志和系统表的空间对象创建完成之后来调用,这样的目的是确保读取insert buffer或者写日志不会因为文件描述符不存在而出问题。调用srv_undo_tablespaces_init函数打开undo表空间。调用dict_stats_thread_init函数,初始化dict_stats_thread线程需要的一些全局变量。调用trx_sys_file_format_init函数,初始化变量file_format_max,该变量类型为file_format_t。调用trx_sys_create函数,创建trx_sys实例,并初始化purge_queue 和 mutex。调用buf_pool_invalidate函数,验证buffer pool。创建innodb监控线程,线程函数为srv_monitor_thread。调用recv_recovery_from_checkpoint_start函数,做recovery,即使MySQL正常关闭也需要执行该函数。调用buf_parallel_dblwr_finish_recovery函数,在recovery结束后,释放未使用的double write页以及申请的缓存。调用dict_bo源码街ot函数,初始化数据字典内存结构。调用buf_parallel_dblwr_create函数,初始化double write子系统,创建其数据结构和磁盘文件。调用trx_sys_init_at_db_start函数,创建并且初始化事务系统的内存结构。调用trx_purge_sys_create函数,创建全局的purge系统控制结构,它必须在事务系统初始化完成之后调用。调用recv_recovery_from_checkpoint_finish函数,从一个检查点完成recovery。调用srv_open_tmp_tablespace函数,打开临时表空间,直到数据库关闭。调用trx_sys_create_rsegs函数,创建回滚段。创建锁等待超时线程,线程函数为lock_wait_timeout_thread。创建信号量超时监控线程,当信号量等待持续过长的时间时,打印警告信息,线程函数为srv_error_monitor_thread。创建innodb监控线程,线程函数为srv_monitor_thread。调用dict_create_or_check_foreign_constraint_tables函数,创建innodb内部的外键约束系统表。调用dict_create_or_check_sys_tablespace函数,检查innodb内部的表空间和数据文件格式是否正确,如果不存在,则创建它们。调用dict_create_or_check_sys_virtual函数,检查innodb内部的虚拟列系统表(SYS_VIRTUAL)格式是否正确,如果不存在,则创建它们。调用dict_create_or_check_sys_zip_dict函数,检查innodb内部的zip_dict系统表格式是否正确,如果不存在,则创建它们。创建innodb主线程,线程函数为srv_master_thread。创建purge系统协调线程,线程函数为srv_purge_coordinator_thread。创建purge系统工作线程,数量由srv_n_purge_threads参数决定,线程函数为srv_worker_thread。调用srv_start_wait_for_purge_to_start函数,等待purge系统启动。创建buffer pool dump/load线程,线程函数为buf_dump_thread。创建统计信息收集线程,线程函数为dict_stats_thread。调用函数fts_optimize_init,创建优化线程,线程函数为fts_optimize_thread。最后创建buffer pool size动态调整线程,线程函数为buf_resize_thread。从以上的分析可以看出,innobase_start_or_create_for_mysql函数做了很多事情,调用了很多函数,创建了很多线程,我们没有细究每个函数、每个线程都有什么作用,但是这不影响对InnoDB整个初始化过程的了解。后面将会针对InnoDB的具体模块,从源码角度分析其具体的实现。
本文来自,经授权后发布,本文观点不代表老铁博客立场,转载请联系原作者。