Files
rikako-note/mysql/mysql文档/innodb体系结构.md
2024-11-22 13:01:06 +08:00

148 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# innodb体系结构
## innodb体系结构
innodb体系结构由如下部分构成
- innodb存储引擎内存池
- 后台线程
- 磁盘上的文件数据
innodb内存池由多个内存块构成内存快负责如下功能
- 维护进程、线程访问的内部数据结构
- 对磁盘上的文件数据进行缓存(加快读取速度),同时缓存对磁盘数据的修改
- redo log缓冲
在innodb存储引擎中后台线程主要负责刷新内存池中的数据保证内存缓存为最新状态。此时后台线程还负责将内存池中的修改刷新到磁盘中。
## 后台线程
innodb采用多线程模型存在多个后台线程每种后台线程负责不同的后台任务。
### Master Thread
Master Thread主要负责将内存池中的缓存数据异步刷新到磁盘中包括脏页的刷新、合并`插入缓冲`insert bufferundo页的回收等。
### IO Thread
innodb中使用AIO来处理IO请求IO Thread主要则用来处理异步IO的回调其中`innodb_read_io_threads``innodb_write_io_threads`默认均为4个。
```sql
show variables like 'innodb_%_io_threads'
```
|Variable_name|Value|
|-------------|-----|
|innodb_read_io_threads|4|
|innodb_write_io_threads|4|
### Purge Thread
当事务被提交之后其对应的undo log不再会被需要需要Purge Thread来回收已经使用的undo页。
可以设置多个purge threads默认情况下mysql 8的purge threads为4个
```sql
show variables like 'innodb_purge_threads'
```
|Variable_name|Value|
|-------------|-----|
|innodb_purge_threads|4|
### Page Cleaner Thread
page cleaner thread将脏页刷新的任务放到单线程中来完成从而减轻原Master Thread的工作以及减少对用户查询线程的阻塞。
## 内存
### 缓冲池
innodb存储引擎是基于磁盘存储的并将记录基于页的方式进行管理。为了提升数据库系统的读写性能通常采用缓冲池来提升数据库的整体性能。
> ### 缓冲池原理
> #### 读缓冲
> 在数据库读取磁盘上的页面时,会将丛磁盘上读取到的页存放到缓冲池中,后续再读取相同的页数据时,先丛缓冲池中查找。如果缓冲池中存在该页,直接从缓冲池中读取。
> #### 写缓冲
> 在数据库针对磁盘上的页数据进行修改时,首先会尝试修改缓冲池中的页数据,并且,缓冲池中的页数据会定期刷新到磁盘中。
>
> 缓冲池刷新页到磁盘中的操作由checkpoint机制进行触发并不会在每次更新缓冲池中的页数据后立马触发。
### 缓冲池参数配置
对于innodb其缓冲池大小通过`innodb_buffer_pool_size`来配置。默认情况下,`innodb_buffer_pool_size`大小为128M.
```sql
show variables like 'innodb_buffer_pool_size'
```
|Variable_name|Value|
|-------------|-----|
|innodb_buffer_pool_size|134217728|
缓冲池中缓存的数据页类型如下:
- 索引页
- 数据页
- 插入缓冲insert buffer
- 自适应哈希索引
- innodb存储的锁信息
- 数据字典信息
在mysql 8中innodb可以支持多个缓冲池实例每个页根据hash值不同被散列到不同缓冲池实例中这样可以提高应用的并发能力。
缓冲池实例数量可以通过`innodb_buffer_pool_instances`变量来进行设置该变量默认值为1.
#### innodb_buffer_pool_size
当修改`innodb_buffer_pool_size`操作将会在chunk上执行。chunk size通过`innodb_buffer_pool_chunk_size`来配置。
`M = innodb_buffer_pool_chunk size * innodb_buffer_pool_instances`
`innodb_buffer_pool_size`必须等于`M`或是`M`的整数倍。如果`innodb_buffer_pool_size`不等于M且不是M的整数倍那么`innodb_buffer_pool_size`将会被自动调整到等于M或是M的整数倍。
> #### 设置innodb_buffer_pool_size示例
> `innodb_buffer_pool_chunk_size`其默认大小为128M如果将`innodb_buffer_pool_instances`调整为16那么`M`值为`128M * 16 = 2G`.
>
> ##### 将innodb_buffer_pool_size设置为8G
> 由于`8G = 2G * 4`那么8G是2G的整数倍此时该innodb_buffer_pool_size有效
>
> ##### 将innodb_buffer_pool_size设置为9G
> 由于9G不是2G的整数倍那么innodb_buffer_pool_size将会被自动调整到10G10G是2G的整数倍
### LRU List, Free List, Flush List
缓冲池是一块由页构成的内存区域。
innodb中缓冲池通过LRU算法来进行管理LRU中最频繁使用的页放在最前端而较少使用的页放在最尾端。当缓冲池中内存已满不能存放新读取到的页时会释放LRU尾端较少使用的页。
innodb中`页大小默认为16KB`.
#### midpoint
在LRU中新读取的页`并不放在LRU的首部而是放在midpoint的位置该算法被称为midpoint insertion strategy`
默认情况下midpoint位于`5/8`的位置离首部5/8 离尾部3/8。在innodb中`首部 -> midpoint`部分的页称之为new列表`midpoint -> 尾部`部分称之为old列表。
midpoint位置可以通过`innodb_old_blocks_pct`来进行控制,默认情况下该值为`37`
```sql
show variables like 'innodb_old_blocks_pct'
```
结果为
|Variable_name|Value|
|-------------|-----|
|innodb_old_blocks_pct|37|
引入midpoint的原因是防止在进行数据扫描等操作时热点数据被淘汰。
此外innodb还引入了`innodb_old_blocks_time`变量来管理LRU列表代表LRU列表在被读取到midpoint位置后需要经过多久时间才能被加入到`LRU的new部分`
默认情况下,`innodb_old_blocks_time`该值为1000单位为ms如果增加该值会使新页面更快的从缓冲区中淘汰。
#### page made young
在innodb LRU中使用了midpoint insertion的方法来对LRU列表进行管理。当需要将一个新的页添加到缓冲池时最近最少被使用的页将会从缓冲池中淘汰并且新的页将会被插入到midpoint位置。
midpoint将LRU列表分为了两部分
- young部分该部分为head到midpoint的部分默认占列表长度的5/8用于存放访问频繁的页
- old部分用于存放访问频率较少的页
默认情况下LRU通过如下算法管理缓冲池
- 3/8部分属于old sublist部分
- 当innodb读取页到缓冲池中时新读取的页将会被插入到midpoint位置。页可能因为如下原因被读取
- 由用户发起的操作例如sql查询
- 由innodb执行的read ahead操作
- 当访问old部分的页时`made young`即将页从old部分移动到young部分的头部。如果对old部分页的访问是由用户发起的操作那么那么该页面会被移动到young部分的头部
- 随着数据库的运行LRU中的young部分和old部分页面都会向LRU列表尾部移动这被称为`老化`。当其他页面触发`made young`操作时:
- 如果发生`made young`操作那么young部分和old部分的节点都会向后移动发生老化
- 如果由新页面被插入到midpoint那么只有old部分的节点会发生老化
#### FreeList
当数据库实例刚启动时LRU里列表中并没有任何页此时页都存放在Free List中。当要从缓冲池中获取页时首先查看Free List中是否有空闲的页`如果有则从FreeList中获取并将该页添加到LRU的midpoint位置``若Free List中没有空闲的页那么将根据LRU算法淘汰LRU尾部的页将淘汰页的内存空间分配给新的页。`