目录
一. MySQL体系结构
1.1 MySQL物理架构
1.2 MySQL逻辑架构
- Client :
提供连接MySQL服务器功能的常用工具集 - Server :
MySQL实例,真正提供数据存储和数据处理功能的MySQL服务器进程 - mysqld:
MySQL服务器守护程序,在后台运行。它管理着客户端请求。mysqld是一个多线程的进程,允许多个会话连接,端口监听连接,管理MySQL实例 - MySQL memory allocation:
MySQL的要求的内存空间是动态的,比如 innodb_buffer_pool_size (from 5.7.5), key_buffer_size。每个会话都有独一无二的执行计划,我们只能共享同一会话域内的数据集。 - SESSION
为每个客户端连接分配一个会话,动态分配和回收。用于查询处理,每个会话同时具备一个缓冲区。每个会话是作为一个线程执行的 - Parser
检测SQL语句语法,为每条SQL语句生成SQL_ID,用户认证也发生在这个阶段 - Optimizer
创造一个有效率的执行计划(根据具体的存储引擎)。它将会重写查询语句。比如:InnoDB有共享缓冲区,所以,优化器会首先从预先缓存的数据中提取。使用 table statistics optimizer将会为SQL查询生成一个执行计划。用户权限检查也发生在这个阶段。 - Metadata cache
缓存对象元信息和统计信息 - Query cache
共享在内存中的完全一样的查询语句。如果完全相同的查询在缓存命中,MySQL服务器会直接从缓存中去检索结果。缓存是会话间共享的,所以为一个客户生成的结果集也能为另一个客户所用。查询缓存基于SQL_ID。将SELECT语句写入视图就是查询缓存最好的例子。 - key cache
缓存表索引。MySQL keys是索引。如果索引数据量小,它将缓存索引结构和叶子节点(存储索引数据)。如果索引很大,它只会缓存索引结构,通常供MyISAM存储引擎使用
第二章 InnoDB存储引擎
2.1 innodb 架构
InnoDB存储引擎是多线程的模型,因此其后台有多个不同的后台线程,负责处理不同的任务。
后台线程的主要负责:
- 刷新内存池中的数据,保证缓冲池的内存缓存的是最近的数据,
- 已经修改的数据文件刷新到磁盘文件
- 保证在数据库发生的异常情况下,Inno能恢复到正常的运行状态。‘
主要的后台线程:
- Master Thread
负责将缓冲池的数据异步刷回到磁盘,包括,脏页的刷新,合并插入缓存,undo日志回收 - IO Thread
在InnoDB存储引擎中大量使用了AIO(Async IO)来处理写IO请求,这样可以极大提高数据库的性能。而IO Thread 的工作主要负责这些IO请求的回调(call back)处理。
- Purge(净化) Thread
事务被提交后,其所使用的undolog可能不在需要,因此需要PurgeThread来回收并分配的undo页面。Purge操作可以独立到单独的线程中,以此来减轻Master Thread的工作,从而提高CPU的使用率以及提升存储引擎的性能。用户可以再MySQL 数据库的配置文件中添加如下命令来启动独立的Purge Thread
innodb_Purge_threads=1
若申请数量 > 1 那么也还是会给一个线程。然后错误日志会有一个Warning
2.2 内存
mysql 是按照磁盘存储,并且按照页的方式管理,但是cpu与磁盘之间的速度有很大差距,所以搞了一个缓冲池来优化。简单来说就是在内存中开辟一块区域。
- 对于读取操作
首先把磁盘中读到的页放在缓冲池中,这个过程成为将页“FIX"在缓冲池中,下一次读取相同的页时,首先判断该页是否被命中,命中直接读取页,否则读取磁盘的页。 - 对于修改操作
首先修改缓冲池中的页,然后以一定的频率刷回磁盘中,并不是每次更新就刷回磁盘,而是通过checkpoint机制刷回磁盘。为了提高数据库整体性能。
综上所述,缓冲池大小影响性能,32位机器最大是3G内存,可以打开操作系统提供的PAE选项获取32位系统下最大64GB内存的支持。
对于innodb 可以 innodb_buffer_pool_size 来设置
具体来看,缓冲池中缓存的数据页类型有:索引页,数据页、undo页、插入缓冲(insert buffer)、自适应哈希索引(adaptive hash index)、InnoDB存储的锁信息(lock info)、数据字典信息(data dictionary)等,InnoDB存储引擎中内存的结构情况。不能简单认为缓冲池只有数据页与索引页,他们只是占据很大而已。
2.4 LRU List、Free List和Flush List
数据库采用了LRU缓存方式管理页面,最近使用频繁的放在链表前端,不常使用的放到尾部。当缓冲池不能读取到新的页时,首先释放LUR中尾部的的页。
在innodb 一个页的大小默认时16kb,innodb对LRU做了一个优化,新来的数据会被放到链表的5/8,官方叫做midpoint位置。midpoint前面的时new,后面的时old区域。这样做的目的时防止抖动。因为一些操作只是对于当前操作需要,而不是热点数据。如果放到了首部,那么很可能回直接导致热点数据被页面置换出去,下一次需要的时候又要重新读取。
还有一个参数控制mid位置的页合适被加到列表首部。innodb_old_blocks_time
LRU列表用来管理已经读取的页,但当数据库刚启动时,LRU
列表是空的,即没有任何页,这是页都存放在free 列表中,当需要从缓冲池中分页时,首先从Free
列表中查找是否有可用的空闲页面,若有则将该页从free列表中删除,放入到LRU列表中,否则根据LRU算法,淘汰LRU列表末尾的页面,将该内部空间分配给新的页面。当页从LRU列表的old部分加入到new部分时候,称此时操作为page made young
,而因为inno_old_blocks_time
的设置导致页从old部分移动new部分操作称为page not made young
。可用通过命令SHOW ENGINE INNODB STATUS
来观察LRU列表及Free列表的使用情况和运行状态。
在LRU列表中的页被修改后,称该页为脏页(dirty page),即缓冲池中的页和磁盘上的页数据产生的不一致。这时数据库会通过CHECKPOINT
机制将脏页刷新回磁盘,而Flush列表中,LRU列表用来管理缓冲池中的页的可用性,Fluash列表用来管理将页刷新回磁盘,二者互不影响。
2.5 重做日志缓冲
InnoDB存储引擎的内存区域除了有缓冲池外,还有重做日志缓冲(redo log buffer)。InnoDB存储引擎首先将重做日志信息放入到这个缓冲区,然后按一定的频率将其刷新到重做日志文件。重做日志缓冲一般不需要社会得很大,因为一般情况下每秒钟会将重做日志缓冲刷新到新的日志文件,因此用户只需要保证每秒产生的事务在这个缓冲大小之内即可,该值可由配置参数innodb_log_buffer_size
控制,默认为8MB
在通常情况下,8MB的重做日志缓冲池足以满足绝大部分的应用,因为重做日志在系列三种情况下会将重做日志缓冲中的内容刷新到外挂磁盘的重做日志文件中。
1、Master Thread每一秒将重做日志缓冲刷新到重做日志文件;
2、每个事务提交时会将重做日志缓冲刷新到重做日志文件;
3、当重做日志缓冲池剩余空间小于1/2,重做日志日志缓冲刷新到重做日志文件。
2.6 额外的内存池
在InnoDB存储引擎中,对内存的管理是通过一种称为内存堆(heap)的方式进行的。在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中进行分配。