为了更好的理解Mysql,我们需要对Mysql的体系结构有一个大致的了解,下面是Mysql体系结构图以及各部分说明.
结构 | 说明 |
---|---|
Connectors | 不同语言中与SQL的交互 |
Management Services & Utilities | 管理服务和工具组件,例如备份恢复、MySQL复制、集群等 |
Connection Pool | 连接池组件,管理缓冲用户连接、用户名、密码、权限校验、线程处理等需要缓存的需求 |
SQL Interface | SQL接口组件,接收用户的SQL命令,并且返回用户需要查询的结果 |
Parser | 查询分析器组件,SQL命令传递到解析器的时候会被解析器验证和解析 |
Optimizer | 优化器组件,对查询进行优化 |
Cache & Buffer | 缓冲组件,查询缓存,如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据,MySQL 8被取消 |
Engine | 插件式存储引擎,是MySQL中具体与文件打交道的子系统,是MySQL中最具特色的一个地方 |
File System | 物理文件 |
存储引擎是基于表的,而不是数据库.
InnoDB存储引擎是多线程的,因此后台有多个后台线程负责不同的任务.
Master Thread是一个核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据一致性,包括脏页刷新,合并插入缓冲等.
在InnoDB存储引擎中大量使用AIO来处理写IO请求,可以极大的提高数据库的性能.
IO Thread主要负责这些IO请求的回调处理,共有4种IO Thread:wirte,read,insert buffer,log IO Thread.
事务被提交后,其所使用的undo log(回滚日志)可能不再需要,因此需要Purge Thread来回收已经使用并分配的undo页.
在 1.1版本之前,purge操作仅在InnoDB的Master Thread中完成.而从1.1开始,purge操作可以独立到单独的线程中进行.
Page Cleaner Thread是在InnoDB 1.2版本中引入的,作用是将之前版本中脏页刷新的操作都放到单独的线程中来完成.
目的是为了减轻Master Thread的工作及用户查询线程的阻塞,进一步提高InnoDB存储引擎的性能.
InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。由于CPU速度与磁盘速度之间的鸿沟,基于磁盘的系统通常使用缓存池技术来提供数据库的整体性能。
在数据库中进行读取页的操作,首先将从磁盘读到的页放在缓冲池中,这个过程称为将页“FIX”在缓冲池中。下一次再读相同的页时,首页判断该页是否在缓存池中。若在缓冲池中,称该页在缓冲池中被命中,直接读取该页。否则,读取磁盘上的页。
对于数据库中页的修改操作,首先修改在缓冲池中的页,然后再以一定的频率(checkpoint机制)刷新到磁盘上。
缓冲池的大小直接影响着数据库的整体性能。
通过命令show variables like 'innodb_buffer_pool_size'
可以查看缓冲池的大小.
从InnoDB 1.0.x版本开始允许有多个缓冲池实例,每个页根据哈希值平均分配到不同缓冲池实例中,增加并发处理能力.
页是InnoDB存储引擎磁盘管理的最小单位,每个页默认16KB;InnoDB存储引擎从1.2.x版本碍事,可以通过参数innodb_page_size将页的大小设置为4K、8K、16K。若设置完成,则所有表中页的大小都为innodb_page_size,不可以再次对其进行修改,除非通过mysqldump导入和导出操作来产生新的库。
可以理解为数据库的数据文件分配在磁盘空间上的逻辑划分?
LRU List(Latest Recent Used)
InnoDB中缓冲池是一个很大的内存区域,其中存放着各种类型的页.通常来说,数据库中的缓冲池是通过LRU(最近最少使用)算法进行管理的.最频繁使用的页在LRU列表的前端,最少使用的页在LRU列表的尾端.
在InnoDB中缓冲池页大小默认是16KB,同样使用LRU算法进行管理,只不过在InnoDB中对传统LRU算法进行了优化.在LRU列表中加入了midpoint(中点)位置,即新读取到的页并不直接放到LRU列表首部,而是放到了列表的midpoint位置,这个算法称为midpoint insertion strategy
,默认配置下midpoint位置在LRU列表长度的5/8处,通过参数innodb_old_blocks_pct
控制.
通过命令看到默认值是37,表示新读取的也查到LRU列表尾端37%的位置.在InnoDB中把midpoint之后的列表称为old列表,之前的称为new列表.
同时InnoDB又引入参数innodb_old_blocks_time
,表示页读取到midpoint位置后需要等待多久才会被加入到LRU列表的热端(new列表).
如果预估活跃的热点数据不止63%,可以通过sql语句来减少热点数据被刷出的概率.
SET GLOBAL innodb_old_blocks_pct=20;
通过表INNODB_BUFFER_PAGE_LRU来观察每个LRU列表中每个页的具体信息:
use information_schema;
SELECT TABLE_NAME,SPACE,PAGE_NUMBER,PAGE_TYPE FROM INNODB_BUFFER_PAGE_LRU;
Free List
当数据库刚启动时LRU列表是空的,没有任何页,这时的页都会放到Free列表中.当需要从缓冲池中分页时,首先从Free列表中查找是否有空闲页,如果有则将该页从Free列表中删除,放到LRU列表中.否则根据LRU算法淘汰LRU列表末尾页,将内存分配给新的页.
当页从LRU列表的old部分加入到new部分时,称此操作为page made young
,因innodb_old_blocks_time设置而导致也没有从old移动到new的操作称为page not made young
.可以通过命令来查看LRU列表和Free列表的使用和运行情况.
SHOW ENGINE INNODB STATUS
=====================================
2020-08-04 15:30:18 0x47f8 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 2 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 5 srv_active, 0 srv_shutdown, 25346 srv_idle
srv_master_thread log flush and writes: 25351
......
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137297920
Dictionary memory allocated 138904
Buffer pool size 8192
Free buffers 7718
Database pages 472
Old database pages 0
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 423, created 49, written 87
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 472, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
.....
可以看到Buffer pool size共有8192个页,及8192*16k的缓冲池.Free buffers表示当前Free列表中页的数量,Database pages表示LRU列表中页的数量.
Flush list
在LRU列表中的页被修改后,称该页为脏页,即缓冲池中的页和磁盘上的页的数据产生了不一致.这时会通过CHECKPOINT
机制将脏页刷新回磁盘,而Flush列表中的页即为脏页列表.
在上面命令查看到的信息中,Modified db pages
就显示了脏页的数量.
Mysql默认情况下会有两个文件:ib_logfile0和ib_logfile1,这两个文件就是重做日志文件,或者事务日志.
重做日志的目的:万一实例或者介质失败,重做日志文件就能派上用场.
每个InnoDB存储引擎至少有一个重做日志文件组,每个文件组下至少有2个重做日志文件,如默认的ib_logfile0、ib_logfile1.InnoDB存储引擎先写重做日志文件1,当达到文件的最后时,会切换至重做日志文件2,当重做日志文件2也被写满时,会再被切换到重做日志文件1中.
InnoDB首先将重做日志信息放入这个缓冲区,然后按一定的频率将其刷新到重做日志文件.
重做日志缓冲一般不需要设置的很大,只需要保证每秒产生的事务量在这个缓冲大小之间即可.
通过innodb_log_buffer_size
来控制,默认为8MB
SHOW VARIABLES LIKE 'innodb_log_buffer_size'