数据库存储引擎主要解决的问题就是内存与硬盘之间速度不匹配的问题,下面让我们来看下Innodb是如何与文件系统交互的吧。
如下图:
上图的几个核心组件介绍:
-
缓冲池(buffer pool)
Innodb主要操作对象是数据页,而缓冲池就是用来缓存数据页的地方,关于缓冲池的具体操作,可以看这篇文章MySQL Innodb 内存池实现简介 -
日志缓冲(log buffer)
磁盘的随机写速度是远低于磁盘的顺序写的速度的,如果innodb每更新一个页都要将它与磁盘同步,会导致数据库的插入更新会非常的慢,甚至是不可用的状态,所以一般的数据库都会引入redo log的概念。innodb的redo log会记录事务对物理页的所有物理操作,一般来说redo log落盘了就认为当前的事务已经正常提交了。log buffer是redo log在内存中体现,innodb在更改页面后会把所有的操作都写入到log buffer中,后台的日志线程会定时的把log buffer中的数据刷入到磁盘中,当事务最终提交时,可能只需要刷入很少的数据到磁盘中,可以提高事务提交的速度。
innodb中的 innodb_flush_log_at_trx_commit 配置项可以控制InnoDb刷新事务日志的策略,其默认参数为1
刷新的策略值及其含义:
- 0,没一秒钟刷新一次log buffer,再事务提交时不再等待日志刷盘,直接结束,这个选项可能会导致数据丢失。
- 1,每次事务都会把log buffer写入并刷新(调用fsync)到磁盘中,默认操作。
- 2, 每次事务会把log buffer 写入到磁盘中,但是不会调用fsync函数,这样只是mysql服务经常挂了并不会导致数据丢失,但是如果是操作系统断电会导致数据永久丢失。
- double write
当发生数据库宕机时,可能InnoDB正常将某一个页刷入到磁盘中,而InnoDB的页大小正常为16KB,但是磁盘的一个块的大小只有512B,这样就有可能导致物理页只写入了前4KB,数据库就宕机了,造成了数据库的部分写失效。
为了解决部分写失效的问题,innodb引入了doublewrite功能。doublewrite有两个部分组成,一部分时内存中的doublewrite buffer,大小2MB,另外是共享表空间中连续的128个页,大小同样是2MB。在对缓冲池中的脏页进行刷新时,并不是直接将其刷入到磁盘中,而是会把要写入磁盘的脏页都先拷贝到double write缓冲中,之后分两次每次1MB分别刷入到物理磁盘中,最后调用fsync同步到磁盘。在完成double write 页的写入后,再将double write buffer中的页写入到各表文件中。
下图是innodb double write 的结构:
我们已经大致了解了innodb的整体架构,那么innodb插入一条数据会大致做什么操作那?
进入innodb引擎层,handler对应ha_innobase 插入的表信息保存在handler中:
1 | int |
1 | UNIV_INTERN |
1 | UNIV_INTERN |
因为Innodb的存储形式是索引组织表,所以如果mysql没有创建主键或者是唯一键,mysql会隐式创建一个rowid用来组织索引。
1 | static __attribute__((nonnull, warn_unused_result)) |
插入单个索引
1 | static __attribute__((nonnull, warn_unused_result)) |
row_ins_clust_index_entry 和 row_ins_sec_index_entry 函数结构类似,只分析插入聚集索引
1 | UNIV_INTERN |
row_ins_clust_index_entry_low 和 row_ins_sec_index_entry_low 函数结构类似,只分析插入聚集索引
1 | UNIV_INTERN |