移动云海山数据库(He3DB)-InnoDB启动

srv0start.cc–>innobase_start_or_create_for_mysql

image.png
1、重置start state为0

image.png

2、判断是否以只读模式启动
image.png
3、阻止二次启动InnoDB
image.png
4、将InnoDB正在启动状态设置为ture
image.png

5、将是否使用native AIO设置为false
image.png

5.1为false则使用线程创建的模拟AIO
image.png

6、注册performance schema stages性能模式阶段
image.png

7、文件刷新模式设置为FSYNC同步刷新
image.png

8、设置可以在InnoDB内等待信号量的最大线程数
image.png

9、判断缓冲池大小(默认128MB,通过innodb_buffer_pool_size设置)是否大于等于1GB

image.png

9.1大于等于:设置srv_buf_pool_instances为8
image.png

9.2小于:设置srv_buf_pool_instances为1
image.png

10、调整缓冲池块的大小
image.png

10.1
chunk 是物理内存分配的基本单位,instance 由一块一块的 chunk 组成。每个 chunk 被切分成 block 串和 frame 串,block 串在前,frame 串在后,两者之间可能存在不圆整的内存碎片。其中 block 为数据页控制体,包含了指向具体数据页的*frame指针,以及具体的控制体buf_page_t;frame 存储了实际的数据,由 bytes 组成。
image.png

11、调用buf_poll_size_align函数,重新计算对齐缓冲池大小
image.png
image.png

12、将page cleaner的值改为不超过buf pool instances
image.png
13、调用srv_boot函数,启动innodb server, 进行相关参数和组件的初始化。
image.png

13.1初始化MySQL给的参数值
synchronization primitives、memory management、thread local storage、server
image.png
14、非只读模式
image.png
14.1创建srv_monitor_file_mutex互斥锁
image.png
14.2判断srv_innodb_status的值
image.png
14.2.1如果为0
在指定路径下创建monitor临时文件,如果指定路径为NULL,则在MySQL服务器配置参数指定的路径下创建
image.png
14.3创建srv_dict_tmpfile_mutex互斥锁
image.png
14.4创建dict临时文件
image.png
14.5创建srv_misc_tmpfile_mutex互斥锁
image.png
14.6创建misc临时文件
image.png
15、文件IO线程数为读IO和写IO之和,并且如果是非只读模式,再加上一条log线程和一条ibuf线程
image.png
16、调用os_aio_init函数,初始化AIO子系统
image.png
16.1初始化异步io系统。为ibuf和log i/o各创建一个数组。还为read和write分别创建一个数组,其中每个数组在逻辑上分别划分为n_readers和n_writers。
image.png
17、调用fil_init函数,初始化表空间内存缓存
image.png
18、调用buf_poll_init函数,创建buffer pool
image.png
buf0buf.cc–>buf_pool_init
image.png
18.1构建buf pool instance的指针数组
image.png
18.2初始化buf pool instance
image.png
18.2.1构建锁信息
image.png
18.2.2计算chunks数量,申请chunk指针数组
image.png
18.2.3初始化各链表
image.png
LUR list:
所有从数据文件中新读取进来的Page都会缓存在LRU List,并通过LRU策略对这些Page进行管理。LRU List实际划分为Young和Old两个部分,其中Young区保存的是较热的数据,Old区保存的是刚从数据文件中读取出来的数据。
当InnoDB读取Page时,首先会从当前Buffer Pool Instance的page_hash查找,并分为三种情况来处理:
1、如果在page_hash找到,即Page在LRU List中,则会判断Page是在Old区还是Young区,如果是在Old区,在读取完Page后会把它添加到Young区的链表头部。
2、如果在page_hash找到,并且Page在Young区,需要判断Page所在Young区的位置,只有Page处于Young区总长度大约1/4的位置之后,才会将其添加到Young区的链表头部。
3、如果未能在page_hash找到,则需要去数据文件中读取Page,并将其添加到Old区的头部。
LRU List采用非常精细的LRU淘汰策略来管理Page,并且用以上机制避免了频繁对LRU 链表的调整。
unzip LRU list:
InnoDB支持压缩页的功能,将实际的16KB Page压缩为8KB、4KB、2KB、1KB,使用unzip LRU list管理非16KB的页。例如需要从缓冲池申请4KB的页,则会先检查unzip LRU list是否有4KB的空闲页,如果没有再去看8KB的,有8KB的话则分成2个4KB的页存放到unzip LRU list,如果没有8KB再去从LRU list申请16KB的页,分成8、4、4KB的页存放到unzip LRU list。
Free List:
Free List中存放的都是未曾使用的空闲Page,InnoDB需要Page时从Free List中获取,如果Free List为空,即没有任何空闲Page,则会从LRU List和Flush List中通过淘汰旧Page和Flush脏Page来回收Page。在InnoDB初始化时,会将Buffer chunks中的所有Page加入到Free List中。
Flush List:
所有被修改过且还没来得及被flush到磁盘上的Page,都会被保存在这个链表中。所有保存在Flush List上的数据都会在LRU List中,但在LRU List中的数据不一定都在Flush List中。在Flush List上的每个Page都会保存其最早修改的lsn,即oldest_modification,虽然一个Page可能被修改多次,但只记录最早的修改。Flush List上的Page会按照其各自的oldest_modification进行降序排序,链表尾部保存oldest_modification最小的Page,在需要从Flush List中回收Page时,从尾部开始回收。
18.2.4每次分配一个chunk的大小,在循环中完成对整个缓冲池实例的内存分配。
image.png
1、buf_block_init负责完成每一个chunk的内存分配。
image.png
1.1重新计算本次需要分配的内存大小,因为除了要申请用户指定的内存大小外,还要申请每一个内存块控制区域(buf_block_t)的内存
image.png
1.2分配内存,默认每个chunk的大小为128M
image.png
1.2.1allocate_large通过调用os_mem_alloc_large进行大内存的分配
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
image.png
1.3从内存的头部开始分配block控制信息
image.png
1.4计算每一个chunk中真正会有多少个数据页,以及数据页面的起始位置,切分block和frame
image.png
一个Page包含一个的16KB的Page和一个对应的控制信息(buf_block_t),一个buf_block_t对应一个Page,所有的Page页面都是连续在一起存储的组成了Page区,buf_block_t也是连续存储的组成了控制信息区。控制信息区处于这块内存的前半部分,Page区域位于后半部分。
如何把一块连续的内存分为两个区域,即控制信息区和Page区,且每个Page必须要有一个对应的buf_block_t,我们把整个连续内存拆分为一个个16KB大小的Page,然后把其中第一个Page用于存储所有的buf_block_t。如果buf_block_t的数量太多导致第一个Page放不下,则需要把第二个Page也用于存储buf_block_t。依次类推,每使用一个Page页用于存储buf_block_t,那么chunk的Page size就要减1。frame是一个指向Page页的指针,它从chunk的头部出发,当有足够的空间用于存储buf_block_t,即frame的地址大于整个buf_block_t控制信息需要的总长度,就会跳出While循环。 反之,空间不足则需要再花费一片Page,同时size–。
image.png

1.5循环初始化所有的控制信息buf_block_t和Page
image.png
1.5.1初始化buffer_block_t,并将其frame指针指向对应的Page地址
image.png
1.5.2 ut0lst.h–>UT_LIST_ADD_LAST
把所有的空闲Page添加到Buffer Pool Instance的Free List中
image.png
free list实际上是一个双向链表,通过ut_list_append添加元素
image.png
1.6注册chunk
image.png
18.2.5设置instance参数
image.png
18.2.6构建page_hash和locks,page_hash的锁数必须是二的幂
image.png
在每个buffer pool instance中都会包含一个独立的Page_hash,其作用主要是为了避免对LRU List的全链表扫描,通过使用space_id和page_no就能快速找到已经被读入Buffer Pool的Page。
18.2.7初始化flush相关信息,如Hp指针等
image.png
18.3初始化AHI
image.png
19、调用fsp_init函数,初始化fsp系统
image.png
20、调用log_init函数,初始化redo log系统
image.png
log0log.cc–>log_init
image.png
20.1创建log_sys对象,负责维护redo log buffer
image.png
20.2构建锁信息
image.png
20.3 lsn起始值设为16x512=8192
image.png
20.4 buf size设置为16MB
image.png
20.5构建buf指针数组
image.png
20.6 buf指针指向最近的当前日志缓冲区正在使用的对齐地址
image.png
20.7 first_in_use设置为true,如果buf指向对齐的(buf_ptr)的前半部分,则为true;如果后半部分为false
image.png
20.8设置正在使用的缓冲区的buf_free的建议最大值
image.png
20.9设置check_flush_or_checkpoint为true,该值在可能需要刷新日志缓冲区、预刷新缓冲池页面或创建检查点时设置
image.png
20.10设置上次打印输出的日志I/O数、最后一次打印输出时间
image.png
20.11设置write_lsn为lsn起始值
image.png
20.12创建flush事件
image.png
20.13设置last_checkpoint_lsn为lsn起始值
image.png
20.14创建checkpoint锁对象
image.png
20.15构建checkpoint buf指针数组,将checkpoint buf指针指向最近的当前日志缓冲区正在使用的对齐地址
image.png
20.16初始化log block
image.png
1、log0log.ic
image.png
1.1通过 log_block 的 lsn 值计算出 log_block 的编号,即 log_block_hdr_no,该编号唯一标识了 log_block 在 log buffer 中的位置
image.png
1.1.1日志是按 log_block_size(512 Bytes) 存储的,并且 lsn 单调增加,因此 lsn 其实是 log_block_size 的倍数再加上当前偏移量。
image.png
1.2 log_block_set_hdr_no() 函数将该编号记录在 log_block 的前 4 个字节中
image.png
image.png

1.3设置该 log_block 默认已使用的空间 LOG_BLOCK_HDR_SIZE,即 12 Bytes。
image.png
1.4在实际记录 log 的过程中,一次写 log 的大小可能大于 512 Bytes,即连续占用多个 log_block,所以 log_block 中可能包含多个 log 的内容,因此用 log_block_first_rec_group 记录了 log_block 中第一个日志的偏移量,这边设置为0
image.png
20.17设置log_block 中第一个日志的偏移量,设置为 LOG_BLOCK_HDR_SIZE,即 12 Bytes。
image.png
20.18 buf free设置为LOG_BLOCK_HDR_SIZE
image.png
20.19 lsn设置为LOG_START_LSN + LOG_BLOCK_HDR_SIZE
image.png
20.20设置monitor
image.png
21、调用lock_sys_create创建recovery系统
调用recv_sys_init初始化recovery系统
image.png
22、调用lock_sys_create初始化锁系统
调用srv_start_state_set启动锁超时线程
image.png
23、调用os_thread_create函数,创建处理I/O的线程,包括io_ibuf_thread线程,io_log_thread线程,srv_n_read_io_threads线程, srv_n_write_io_threads线程
image.png
24、初始化page cleaner系统,创建一个协调线程,多个工作线程(这里默认1个)。
image.png
25、确保page cleaner thread活跃状态,否则循环睡10000微秒
image.png
26、启动I/O线程
image.png
27、调用srv_sys_space.check_file_spec()函数,检查数据文件是否存在,是否需要创建一个新的数据库。
image.png
28、如果需要创建一个新的数据库,调用srv_check_undo_redo_logs_exists来检查undo表空间和redo 文件是否已存在。
image.png
29、调用srv_sys_space.open_or_create函数,打开或者创建数据文件。
image.png
30、如果创建新的数据库
image.png
30.1调用buf_flush_sync_all_buf_pools从所有缓冲池实例的刷新列表末尾同步刷新脏页
image.png
30.2调用log_get_lsn获取此时的lsn
image.png
30.3调用create_log_files()创建redo log文件
image.png
31、如果不创建新的数据库,打开已有数据文件
image.png
31.1调用open_log_file函数,依次打开redo log文件。由于redo log文件数量可配置,innodb在启动时也不知道有多少个redo log文件,这里在实现时,通过一个循环,从0开始,一直到SRV_N_LOG_FILES_MAX,直到读取的redo log文件不存在时,退出循环。

32、用fil_space_create函数,创建一个内存中的空间对象。
image.png
33、调用fil_node_create函数,把redo log文件依次关联到上一步创建的内存空间对象上。
image.png
34、调用log_group_init函数,为日志系统初始化一个日志组。
image.png
35、调用fil_open_log_and_system_tablespace_files函数,打开所有的日志文件和系统表空间中的所有数据文件,这些文件会一直打开,直到数据库关闭。
image.png
36、调用srv_undo_tablespaces_init函数打开undo表空间。
image.png
37、非只读模式,调用dict_stats_thread_init函数,初始化dict_stats_thread线程需要的一些全局变量。
image.png
38、调用trx_sys_file_format_init函数,初始化表空间标记系统,初始化变量file_format_max,该变量用于跟踪InnoDB已知的最大文件格式id。
image.png
39、调用trx_sys_create函数,创建trx_sys实例,并初始化purge_queue 和 mutex。
image.png
40、如果创建新的数据库
image.png
40.1调用 fsp_header_init,在 ibdata 文件的开始分配空间,以便可以存储管理一些系统模块,如事务系统等
image.png
40.2调用 trx_sys_create_sys_pages,创建事务系统的文件页
image.png
40.3调用 trx_sys_init_at_db_start,创建并初始化事务系统内存结构
image.png
40.4调用 trx_purge_sys_create,创建并初始化 trx purge 系统
image.png
40.5调用 dict_create, 创建新的数据字典并初始化
image.png
40.6调用buf_flush_sync_all_buf_pools从所有缓冲池实例的刷新列表末尾同步刷新脏页
image.png
40.7调用log_get_lsn获取此时的lsn
image.png
40.8调用fil_write_flushed_lsn将flushed lsn写入系统表空间第一页的页头
image.png
40.9调用create_log_files_rename重命名第一个日志文件
image.png
41、如果不创建新的数据库
image.png
41.1调用trx_sys_file_format_max_check检查是否支持系统表空间上的最大文件格式
image.png
41.2调用buf_pool_invalidate使整个缓冲池无效, 来确保在 recovery的过程中我们重新读取之前读取的页。这是一个很轻量级的操作, 此时在 LRU 列表中只有一个数据页, 在 flush 列表中没有任何数据页
image.png
41.3调用scan_and_parse扫描并定位截断的日志文件
image.png
41.4调用recv_recovery_from_checkpoint_start函数,从redo log checkpoint位置做recovery,即使MySQL正常关闭也需要执行该函数。
image.png
1、log0recv.cc–>recv_recovery_from_checkpoint_start
image.png
1.1初始化 flush 红黑树, 以便在恢复的过程中快速插入 flush 列表。
image.png
1.2在 log groups 中查找 latest checkpoint
image.png
1.3读取 latest checkpoint 所在的 redo log 页到 log_sys->checkpoint_buf中
image.png
1.4获取 checkpoint_lsn 和 checkpoint_no(偏移)
image.png
1.5从 checkpoint_lsn 读取 redo log 到 hash 表中
image.png
1.6检查 crash recovery 所需的表空间, 处理并删除double write buf 中的数据页, 这里会检查double write buf 中页对应的真实数据页的完整性, 如果有问题, 则使用 double write buf 中页进行恢复。同时, 生成后台线程 recv_writer_thread 以清理缓冲池中的脏页。
image.png
1.6.1调用buf_dblwr_process,处理并删除所有表空间的double write buf的数据页
image.png
清除double write buf的数据页
image.png
1.6.2生成后台线程 recv_writer_thread 以清理缓冲池中的脏页
image.png
1.7将日志段从最新的日志组复制到其他日志组,以便它们都包含最新的记录数据。还将有关最新检查点的信息写入组,并将组内存结构中的字段初始化为最新值。
image.png
41.5清除double write buf的数据页
image.png
41.6调用dict_boot函数,初始化数据字典系统,并初始化change buffer
image.png
41.7调用trx_sys_init_at_db_start,创建并初始化事务系统
image.png
41.8调用 recv_apply_hashed_log_recs,应用 redo log
image.png
41.9调用trx_purge_sys_create,创建 trx_purge sys
image.png
41.10调用recv_recovery_from_checkpoint_finish,从一个 checkpoint 位置完成 recovery 操作
image.png
1、log0recv.cc–>recv_recovery_from_checkpoint_finish
image.png
1.1确保 recv_writer 线程已完成
image.png
1.2等待 flush 操作完成
image.png
1.3等待 recv_writer 线程终止
image.png
1.4调用buf_flush_free_flush_rbt,释放 flush 红黑树
image.png
1.5调用trx_rollback_or_clean_recovered,回滚所有的数据字典表的事务,以便数据字典表没有被锁定。数据字典 latch 应保证一次只有一个数据字典事务处于活跃状态。
image.png
41.11调用recv_recovery_rollback_active,回滚未提交的不完整的事务,这是在一个后台线程中进行中
image.png
42、调用 srv_open_tmp_tablespace,打开临时表空间
image.png
43、调用trx_sys_create_rsegs创建回滚段
image.png
44、非只读模式,创建锁等待超时线程、信号量超时监控线程(当信号量等待持续过长的时间时,打印警告信息)、innodb监控线程
image.png
45、调用dict_create_or_check_foreign_constraint_tables函数,创建innodb内部的外键约束系统表。
image.png
46、调用dict_create_or_check_sys_tablespace函数,检查innodb内部的表空间和数据文件格式是否正确,如果不存在,则创建它们。
image.png
47、调用dict_create_or_check_sys_virtual函数,检查innodb内部的虚拟列系统表(SYS_VIRTUAL)格式是否正确,如果不存在,则创建它们。
image.png
48、非只读模式,则创建master线程
image.png
49、非只读模式,则创建purge协调线程;创建purge工作线程,数量由srv_n_purge_threads参数决定;并等待purge线程启动
image.png
50、唤醒page cleaner主循环
image.png
51、非只读模式,则创建buffer pool dump/load线程;创建统计信息收集线程dict_stats_thread;调用函数fts_optimize_init,创建优化线程
image.png
52、创建buffer pool size动态调整线程
image.png

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值