这本书的内容写的很好,排版也很好,很值得人花时间去细细品味。
以前我只会sql语法,我觉得数据库还是很简单,基本逻辑我都能写呀。
直到遇到这本书,我才发现数据库的水还是很深的
目录
- mysql体系结构框图
- innodb结构图
-
后台线程
- Master Thread
- IO Thread
- Purge Thread
- Page Cleaner Thread
-
内存池
- 缓冲池
- 重做日志缓冲
- 额外的内存池
- LRU List + Free List + Flush List
- Check Point
- Master Thread工作内容
-
innodb 几大特性
- 插入缓冲
- 两次写
- 自适应哈希
- 命令集合
- 磁盘基本概念
- 性能优化相关
mysql体系结构
由于要讲innodb,所以咱们先看下innodb在mysql中的位置。
详细的大家可以参考这篇文章:体系结构
我们会在另一篇文章里专门讲mysql的体系结构的。这里只讲Innodb存储引擎部分。
说明:Innodb是支持事务的插件式存储引擎,而且也是大家最常用的一款。
innodb结构
innodb体系结构.jpg咱们从这里可以看出,innodb存储引擎是直接跟磁盘文件打交道的一层。
下面咱们看看Innodb具体做了什么
后台线程
-
Master Thread
主线程算是Innodb的一个中流砥柱了吧。会做很多事情。
主要负责将缓冲的数据同步到磁盘,包括刷新脏页,回收undo页,合并插入缓冲等。
后面会专门讲 -
IO Thread
输入输出线程也是个伟大的存在,就像小蜜蜂一样,是个勤劳的搬运工。
是咱们与磁盘IO交换数据的基石。
主要负责刷新缓冲数据到磁盘和从磁盘读取数据。
Read IO Thread
Write IO Thread
Insert Buffer IO Thread
Log IO Thread
在1.0.x版本开始,默认情况下Read IO Thread 和 Write IO Thread的个数是4个。
不过在Windows下可以通过参数来改变。
Innodb_read_io_threads和Innodb_write_io_threads来控制。(之前版本用这个参数Innodb_file_io_threads控制IO数量)
关于IO和磁盘的内容下面还是会专门讲的。 -
Purge Thread
undo页回收线程
可以通过参数innodb_purge_threads=N来设置线程个数 -
Page Cleaner Thread
脏页处理线程
内存池
innodb 内存数据对象.jpg从上图咱们可以看出,内存池被划分为缓冲池、日志缓冲、额外的内存池。
1. 缓冲池
既然这里出现了缓冲池,基本也算是生产者和消费者模型了,不过这里面的操作是相当复杂的。
咱们操作的数据也不是单一的某种类型,而是数据页、索引页、插入缓冲、自适应哈希索引、锁信息、数字字典信息等。是不是觉得很精彩呀。
由于缓冲池也算是临界区了吧,咱们一口气配了那么多IO线程来操作它,要花很多代价在等待上。所以设计人员就把缓冲池实例数量改成可配置的。允许我们配置多个。这样就减少多个线程的竞争,提高并发的能力。
那么既然缓冲池这么重要,咱们是不是要把它配大一点呀,如果太小了,我们里面的线程可是要忙死的,万一忙不过来可是会出大问题的。
innodb_buffer_pool_size可以配置缓冲池的大小。
innodb_buffer_pool_instances可以配置缓冲池实例的数量。
LRU List、Free List、Flush List
既然是个缓冲池,如果咱们不定期清理的话,很可能就会满。
从上面咱们也说过,innodb的缓冲不能配置的太小了,不然如何应对百万级、千万级的数据呢。
所以里面肯定有管理机构啦,不然咱们计算机动不动就要遍历这么个超级数据团,还不死翘翘。
第一个机构叫做Free List,管理着缓冲池中未使用的页。其实有点类似于失业人口管理所。
第二个机构叫做LRU List,管理着已经读取的页。相当于咱们每个在职人员都登记在册一样。
第三个机构叫做Flush List,管理着数据不一致的页,也即是脏页。相当于职位变更的记录。
那么这三个机构刚好构成了咱们工作的状态信息。
-
Free List
当咱们需要找人来干活,会发布信息给Free List,希望它能够帮我们找到一个合适的人来给我们干活。 -
LRU List
这个机构其实是一个比较实在的机构,平时可以帮我们做很多事情。
当咱们找到人来帮我们干活时,一定会登记到这个LRU里面的。
假如在试用期过了之后,这个人的表现还不是令人很满意,他就不能够成为热点数据。
只能被扔到冷门的部门,这时候就可能会被优先解雇掉。
假如这次咱们要找一批兼职人员做某个活动,结果发现几个兼职人员合作能够干过一个老员工,这是老板就疯狂的招聘兼职人员,结果老员工都被顶替了。当兼职人员干完活的时候,公司就没有人可以干活了。
为了避免发生这种事情,公司就出了新的策略。
第一 为了保证员工的利益,兼职人员除非转正,且成绩优秀,才能够做重要的任务。
第二 由于公司资金有限,所以只能招聘一定数量的人员,不干活的人员要及时解雇。
第三 到底要解雇多少员工,这个要由老板说了算,不过默认是从垫底的5/8员工里挑选解雇人员的。
这个挑选范围是innodb_old_blocks_pct参数来控制的。
试用期由innodb_old_blocks_time来决定的。
当现在社会就业情况不是很好的时候,LRU管理机构决定解雇很多不干活的人,如果解雇的人中有人已经换了工作,那么它会及时把信息存档的,然后把他们都给解雇了。
这里咱们要表明一点,LRU最少要预留100个页的空位。
-
Flush List
这个机构主要负责记录那些工作变更的人。
2. 重做日志缓冲
重做日志这东西比较重要的。还记的之前事务的时候咱们有讲过持久性吗?其实重做日志就是用来恢复数据的。所以重做日志要及时的写到磁盘上(假如重做日志设置的太小或者太大会怎么样呢)
重做日志会在以下三个情况下写入到磁盘:
- Master Thread每秒都会进行一次刷新
- 每个事务提交的时候都会进行一次磁盘写
- 当缓存池空间小于一半时,进行写操作
所以按照上面的情况来讲,咱们只要保证重做日志缓冲区的大小,满足每秒并发的事务量就好了。
日志缓冲区的大小由innodb_log_buffer_size决定。
3. 额外内存池
//todo
checkpoint
明眼人一听这个名字,就知道这肯定时一个比较强势、权威的机构了。
checkpoint 检查点,我觉得可以理解为巡逻点。
目的是为了维护咱们周边环境的治安,降低各种天灾人祸带来的损失。
专业来说是,为了缩短数据库宕机后的恢复时间。
刷新脏页到磁盘。
那么问题来了,这个机构啥时候要发发威,做做事呢。
其实他们是很懒的,也是比较被动的。
-
上级发现很多脏页,让他们去处理一下。这时他们才会去找Flush List要脏页信息,然后将其刷新到磁盘。
innodb_max_dirty_pages_pct控制最大脏页的比例。 -
下面出场的这个机构其实也不能叫啥机构,不过比较喜欢管事情,而且还管杂七杂八的事情。我觉得应该叫他社会舆论吧。这群家伙闲的很,经常会去做各种调查,然后报告给checkpoint,checkpoint无奈之下只能去Flush List找相应的脏页信息然后刷新。
忘记说了,这个东西叫做Master Thread -
LRU List这个机构,上面说了相当于一个人事啦,它发现现在社会上大部分的工作都已经被人应聘了。可能只剩下100个岗位空缺了,这时候他就很着急,就会去解雇一些不干活的人,解雇的同时如果他发现脏页,也会叫check point来清理。
当然这个空闲岗位的数量也是可以由我们自己来定的。
innodb_lru_scan_depth来控制。 -
最后一个可能会比较难理解,咱们知道,为了保证数据的可恢复性,必须要先把日志写到日志文件,然后才会把数据更新到磁盘。
因为咱们日志文件的大小不是无限大的,所以会导致一些历史悠久的日志要被删除。那么假如在删除这部分日志文件之前,发现相应的数据还是没有同步到磁盘,那么就要赶紧发起check point,让他去脏页列表中,把我们要同步的数据给刷到磁盘。
到底什么时候要刷新我们要的脏页呢,看下面的规矩。
大体如下:
假如咱们还没有同步的数据<日志文件大小的百分之70,那么不着急找Check point.
假如大于百分之70小于百分之90,那么就异步刷新咱们要的数据。
如果大于百分之90,那么要全力刷新咱们要的数据了。
这个刷新操作在Page Cleaner Thread中执行。
Master Thread的工作内容
上面咱们说过,Master Thread相当于社会舆论。他会做很多事情,比较杂乱。
分为4个主要部分:
- Loop
由每秒循环和10秒循环构成。
重要的东西会每秒去执行,比如重做日志刷新。
其他的每秒可能执行内容包括:插入缓冲合并,脏页刷新
当脏页的比例超过设置的阈值时,比如默认是百分之75,则会刷新100个脏页到磁盘。
当前一秒的IO执行不超过5次,那么innodb会认为当前io压力不是很重,将会执行插入缓冲合并。
每十秒执行内容:重做日志刷新、合并5条插入缓冲、假设脏页的数量小于百分之70,刷新百分之10的脏页到磁盘,如果脏页的数量大于百分之70,刷新100个脏页到磁盘。还会判断过去10秒内的IO操作是否小于200次,如果小于的话,还会刷新100个脏页到磁盘。会回收最多20个undo页。
-
backgroup Loop
若当前没有用户活动 或者数据库关闭,那么会切换到这个后台循环。
后台循环总是会去删除无用的undo页。
总是会合并20条插入缓冲。
总是会跳到主循环。
总是会刷新100个脏页直到符合条件----可能会在Flush Loop中执行。 -
flush loop
-
suspend loop
当上面的循环都没有事情做了,那么就会跳到暂停循环了。
上面咱们讲的是1.0.x之前的版本。
下面来讲下1.0.x对Master Thread所做的改变。
由于之前刷新多少脏页,合并多少插入缓冲,回收多少无用Undo页,都是写死的。
随着咱们现在硬盘技术的提升,IO的处理能力已经大大的提升了。所以咱们就提供了参数,可以让用户根据相应的硬件设备来修改这些值,以达到最大的吞吐量。
innodb_io_capacity这个值会影响合并插入缓冲和刷新脏页的数量。
合并插入缓冲数量=innodb_io_capacity * 5%;
脏页刷新数量=innodb_io_capacity;
innodb_adaptive_flushing 会影响每秒脏页刷新的数量。叫做自适应刷新,他的判断依据是通过判断重做日志产生的速度,来决定每秒要刷新多少脏页。
默认是开启的。
innodb_purge_batch_size这个参数是决定一次回收多少undo页。
默认是20
几大特性
-
插入缓冲
由于不知道索引在磁盘中的存储结构,及表的机构,所以这个点先不讲
//todo -
两次写
在写这个点之前,我们先了解下日志文件记录的内容。
mysql日志文件是逻辑型的。
逻辑性日志文件:即我要向某个表空间写入一条数据。
而不是记录我正在想磁盘上某个地方写一个值,还有多少值没写之类的吧。
那么正当我从内存池把一个页的数据写到磁盘的中途,突然断电了。那么这时候假设就只有半个页写进去。这就叫做部分写失效。
当发生部分写失效的时候,下次上电咱们去进行数据恢复,发现有个页数据不一致,也就是我们那个被多写半个页的家伙。这个不上不下的家伙,就找不到自己日志的相应点了,因为日志里并没有标记我已经执行了多少,然后还有多少没执行。
所以innodb就搞个两次写来。也就是我写数据页之前,先备份一份数据页。
-
自适应哈希
这个点也到后面索引之后再讲
//todo
命令集合
线程相关:
innodb_read_io_threads
innodb_write_io_threads
innodb_purge_threads
缓冲相关:
innodb_buffer_pool_size
innodb_buffer_pool_instances
LRU相关:
innodb_old_blocks_pct
innodb_old_blocks_time
innodb_lru_scan_depth
脏页相关:
innodb_max_dirty_pages_pct
重做日志:
innodb_log_buffer_size
Master Thread刷新量相关:
innodb_io_capacity
innodb_adaptive_flushing
innodb_purge_batch_size
网友评论