暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

innodb存储引擎读书笔记:

架构思考 2020-07-23
371

mysql是单进程多线程架构的。


在mysql安装目录下的bin目录中执行如下命令,可以查看mysql会从哪些地方读取配置文件,以及读取的顺序:


可以看到,mysql是按照先/etc/my.cnf,再/etc/mysql/my.cnf,再/usr/local/mysql/etc/my.cnf,最后~/.my.cnf的顺序读取配置文件的,后面的文件中的参数会覆盖前面文件中的参数。配置文件中的datadir参数指定了数据库的路径。


下图是mysql的体系结构:


innodb存储引擎的特点:

  1. 支持事务、行级锁、外键、非锁定读(读取情况下读操作不加锁)等。

  2. mysql4.1及之后的版本支持将innodb的表独立存放到ibd文件中。

  3. 通过next-key locking策略避免幻读(phantom),通过mvcc(多版本并发控制)实现高并发性,通过插入缓冲(insert buffer)、二次写(double write)、自适应哈希索引(adaptive hash index)、预读(read ahead)实现高可用和高性能。

  4. innodb中主键索引是聚簇索引,其他的索引都是非聚簇索引。

  5. 如果没有为表定义主键,innodb会自动生成一个6字节的ROWID作为主键。



MYISAM存储引擎的特点:

  1. 不支持事务和行锁,支持表锁和全文索引;

  2. MYD文件存放数据,MYI文件存放索引.


我的5.7.22版本的mysql支持如下存储引擎:


innodb的体系结构如下图:


执行show innodb status,可以查看innodb参数。在我的5.7.22 mysql中,innodb的后台线程如下:

10个io线程:

    1个insert buffer线程

    1个log线程

    4个read thread

    4个write thread

1个master线程、一个锁监控线程、一个错误监控线程。


innodb的内存由如下几部分组成:

  1. buffer pool(由参数innodb_buffer_pool_size配置)

  2. redo log buffer(innodb_log_buffer_size)

  3. addtional memory pool(innodb_additional_mem_pool_size)


innodb的基本工作原理:

将数据库文件按页(16KB)读到buffer pool中,按LRU算法淘汰缓存的数据;修改数据库文件时,总是先修改buffer pool中的页(修改后成为脏页),然后按一定的频率flush到文件中。


show engine innodb status的输出中,BUFFER POOL AND MEMORY部分是buffer pool的使用情况:


图中buffer pool size = 8192,表示buffer pool中有8192个buffer frame,一个buffer frame为16KB。

free buffer表示当前空闲的frame数。

database pages;已使用的frame数

modified db pages:脏页数



上图显示了buffer pool的部分page类型,除图中的类型外,还有undo页。


redo log会先存到log_buffer中,然后按一定的频率flush到redo log文件中。



master thread内部由几个循环组成:

主循环、backgroud loop、flush loop、suspend loop(暂停循环)。

master thread会根据数据库的运行状态在这几个loop中切换。


主循环的操作分为每秒的操作和每10秒的操作,伪代码如下:

每秒的操作包括:

  1. flush log_buffer到磁盘,即使事务还未提交。(总是执行)

  2. 合并insert buffer(可能执行)

  3. 至多flush 100个脏页到磁盘(可能执行)

  4. 如果当前没有用户操作,切到backgroud loop(可能执行)


innodb会判断,如果当前一秒内的io次数小于5,就会合并insert buffer。

innodb会判断,如果当前buffer pool中脏页比例超过了配置文件中的innodb_max_dirty_pages_pct参数(默认90,表示90%),就将100个脏页flush到磁盘上。


每10秒的操作:

  1. flush100个脏页到磁盘(当过去10秒内的io次数小于200时执行)

  2. 合并至多5个insert buffer(总是执行)

  3. 将log_buffer刷新到磁盘(总是执行)

  4. 删除无用的undo page(总是)

  5. 刷新100个或10%的脏页到磁盘(总是)

  6. 产生一个checkpoint(总是)


在检查点上,会将最老日志序列号对应的日志写入磁盘。



background loop中执行的操作:

  1. 删除无用的undo page(总是)

  2. 合并20个insert buffer(总是执行)

  3. 如果还是空闲状态,则跳到flush loop,否则会到主loop


flush loop中执行的操作:

  1. flush 100个脏页到磁盘

  2. 如果脏页比例大于配置的参数,则继续flush loop,否则跳到suspend loop


suspend loop中执行的操作:

  1. 挂起线程

  2. 等待事件

  3. 回到主loop


show engine innodb status的输出中,BACKGROUND THREAD部分描述了线程状态:




innodb的doublewrite架构如下:


内存脏页首先会被拷贝到doublewrite buffer中,接着进行第一次写:将doublewrite buffer写到磁盘上的共享表空间中(顺序写,速度快),再进行第二次写:将doublewrite buffer写到磁盘上的.ibd文件中(随机写,速度慢)。如果在写入.ibd文件的过程中发生了崩溃,.ibd文件中的数据页就是不完整的,当mysql进程恢复后,可以将共享表空间中的数据写到.ibd文件中,实现数据页的恢复。




insert buffer:

如果索引类型是辅助索引,同时不是唯一索引,当发生索引插入或更新时,innodb首先会判断非聚集索引页是否已经读到了buffer pool中。如果是,则直接将索引插入到非聚集索引页中,否则,先将索引放到insert buffer中,再以一定的频率将insert buffer合并到非聚集索引页中。(初步理解,可能不是很正确)


文章转载自架构思考,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论