MySQL架构和运行机制解析

先来看看 mysql 技术架构

MySQL 技术架构和运行机制

MySQL架构和运行机制解析_第1张图片
MySQL架构和运行机制解析_第2张图片

连接器(connect):

连接器负责跟客户端建立连接、获取权限、维持和管理连接。有些时候 MySQL 占用内存涨得特别快, 是因为在执行过程中临时使用的内存(join buffer 表join用到、sort buffer 表排序用到、内存临时表和磁盘临时表在分组、去重等时候用到)是管理在连接对象里面的,需要定期断开才能释放,特别是执行过一个占用内存的大查询后,最好断开连接,之后再重连;所以如果连接长时间累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了。

查询缓存(query_cache):

当连接建立成功后,select语句就会来到缓存查询这一块,尝试从缓存中拿到数据。以前执行过的sql语句和结果会以K-V的形式存储,sql是K,查询的结果是V。如果拿到了结果会直接返回,如果没拿到结果则会继续走后面的流程拿到数据返回。但是大多数情况下,该缓存很鸡肋,为什么这样说,假设我们缓存中一张表存在很多缓存,一旦该表执行了update语句,因此所有缓存数据都会失效,对于更新压力大的数据表命中率会很低,在mysql8.0版本已经将缓存功能移除。
MySQL 也提供了按需使用的方式,你可以将my.cnf参数 query_cache_type 设置成 DEMAND

 my.cnf  
 #query_cache_type有3个值 0代表关闭查询缓存OFF,1代表开启ON,2(DEMAND)代表当sql语句中有SQL_CACHE 关键词时才缓存 
解析器(parser):

输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么;根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法,然后从文本中将要查询的表,查询条件都提取出来放到mysql服务器内部使用的一些数据结构上。

优化器(optimizer):

会对我们的语句做一些优化,如外连接转换为内连接、表达式简化、子查询转为连接等,并生成执行计划。

存储引擎层(storage engines):

负责数据的存储和提取;
MySQL架构和运行机制解析_第3张图片

为了方便管理,人们把mysql服务器处理请求的过程简单地划分为server层和存储引擎层。
server层的功能包括连接管理、查询缓存、语法解析、查询优化这些并不涉及真实数据存取的功能,server层涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等;另外只要涉及到多表运算,也都在server层;
存储引擎层实现存取真实数据的功能,各种不同的存储引擎为server层提供统一的调用接口,其中包含了几十个不同用途的底层函数,比如"读取索引第一条记录",“读取索引下一条记录”,"插入记录"等,所以在server层完成了查询优化后,只需按照生成的执行计划调用底层存储引擎提供的接口,获取到数据后返回给客户端就好了。

Innodb 存储结构

MySQL架构和运行机制解析_第4张图片

buffer pool 缓冲池:

为了缓存磁盘中的页,在buffer pool中缓存热的数据页和索引页,减少磁盘读,MySQL 服务器启动时就向操作系统申请了一片连续的内存,默认情况下Buffer_Pool只有128MB,如果嫌弃这个128MB太大或者太小,可以在启动服务器的时候配置innodb_buffer_pool_size。

change buffer 写缓冲池:

change buffer就是在非唯一普通索引页不在buffer pool中时,对页进行了写操作的情况下,先将记录变更缓冲,等未来数据被读取时,再将 change buffer 中的操作merge到原数据页的技术,在MySQL5.5之前,叫插入缓冲(insert buffer),只针对insert做了优化;现在对delete和update也有效,叫做写缓冲(change buffer)。
change buffer 原理:
当需要更新一个数据页时,如果数据页在内存中就直接更新。如果数据页不在内存中。在不影响数据一致性的前提下,InooDB 会将这些更新操作缓存在change buffer 中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行change buffer中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。虽然名字叫作change buffer,实际上它是可以持久化的数据。也就是说change buffer在内存中有拷贝,也会被写入到磁盘上(ibdata)。
将change buffer中的操作合并到原数据页,得到最新结果的过程称为merge。以下情况会触发merge:

  • 访问这个数据页;
  • 后台master线程会定期merge;
  • 数据库缓冲池不够用时;
  • 数据库正常关闭时;
  • redo log写满时;

change buffer为什么针对非唯一普通索引页

  • 唯一索引
    所有的更新操作都要先判断这个操作是否违反唯一性约束。而这必须要将数据页读入内存才能判断。如果都已经读入到内存了,那直接更新内存会更快,就没必要使用 change buffer 了。因此,唯一索引的更新就不能使用 change buffer,实际上也只有普通索引可以使用。
  • 普通索引
    不需要判断唯一性,正常使用 change buffer 更新。
mysql> show variables like '%change_buffer%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| innodb_change_buffer_max_size | 25    |
| innodb_change_buffering       | all   |
+-------------------------------+-------+
2 rows in set (0.00 sec)
#innodb_change_buffering:默认是all支持所有DML操作
#innodb_change_buffer_max_size:默认是25,即缓冲池的1/4。最大可设置为50,采用默认即可
log buffer

当在MySQL中对InnoDB表进行更改时,这些更改首先存储在InnoDB日志缓冲区的内存中,然后写入通常称为重做日志(redo logs)的InnoDB日志文件中,日志缓冲区是内存存储区域,用于保存要写入磁盘上的日志文件的数据。日志缓冲区大小由innodb_log_buffer_size 变量定义,默认大小为16MB。
日志缓冲区的内容定期刷新到磁盘。较大的日志缓冲区可以运行大型事务,而无需在事务提交之前将重做日志数据写入磁盘。因此,如果有更新,插入或删除许多行的事务,则增加日志缓冲区的大小可以节省磁盘I/O。

innodb_flush_log_at_trx_commit :控制如何将日志缓冲区的内容写入并刷新到磁盘。
innodb_flush_log_at_timeout :控制日志刷新频率。
  • 如何确定InnoDB日志缓冲区大小是否需要调整?
    如果磁盘I/O导致性能问题,则需要观察事务,例如涉及许多BLOB条目的事务。只要InnoDB日志缓冲区已满,便会将其刷新到磁盘,因此增加缓冲区大小可以减少I/O。
    另外需要观察的是MySQL的InnoDB重做日志(redo logs)。它们是磁盘上的一组文件,其中包含对InnoDB表所做的所有最近更改的记录,并在崩溃恢复期间使用。根据环境的不同,这些文件可能需要根据默认设置进行调整,例如日志的总数和大小。
    日志文件的缺省数量为两个,分别名为 ib_logfile0 和 ib_logfile1 。日志具有固定大小,默认大小取决于MySQL版本。从5.7版本开始,默认值是每个48MB,从MySQL 5.6.3开始,最大总大小为 512GB。
  • 如何确定日志文件的大小是否需要调整?
    如果应用程序是写密集型应用程序,则可以使用48MB,并且鉴于日志以循环方式工作,当日志写满时,有必要对磁盘上的数据文件进行写操作。因此,如果日志文件的大小太小,可能会导致频繁的磁盘写入甚至是等待,从而极大地降低了性能。可以通过查看日志序列号状态变量log_lsn_current和log_lsn_last_checkpoint来观察刷新的频率。通过将两个值相减,并与重做日志空间的总大小进行比较,您可以了解刷新是否比期望的发生更频繁。
    要调整 innodb_log_buffer_size 或 innodb_log_file_size 变量,必须在MySQL的配置文件中显式定义它们。在进行这些更改之前,请关闭实例,以确保MySQL正确无误地停止运行。如果在关闭过程中出现错误,则现有的日志文件可能尚未完全写入数据文件,并且数据可能会丢失。

总结:在考虑MySQL数据库性能时,Innodb_log_buffer_size和Innodb_log_file_size是要分析的两个重要变量。如果大型事务导致过多的磁盘I/O,则可以增加InnoDB日志缓冲区。出于相同的原因,InnoDB日志文件通常会基于默认值增加。如果它变得太快,则检查点刷新活动会增加,并且可能会导致性能降低。

sys-tablespace 系统表空间

innodb系统表空间包含innodb数据字典(innodb相关对象的元数据),双写缓冲 (doublewrite buffer)、改变缓冲 (change buffer) 和 undo 日志(undo logs)等也存储于系统表空间中。此外,系统表空间也包含用户在改表空间创建的表和索引等数据。由于系统表空间可以存储多张表,因此,其为一个共享表空间。系统表空间由一个或多个数据文件组成,默认情况下,其包含一个叫ibdata1的系统数据文件,位于mysql数据目录下。系统表空间数据文件的大小和数目由innodb_data_file_path启动选项控制。

file-per-table-tablespace 独立表空间

表文件表空间是一个单表表空间,该表创建于自己的数据文件中,而非创建于系统表空间中。当innodb_file_per_table选项开启时,表将被创建于表文件表空间中。否则,innodb将被创建于系统表空间中。每个表文件表空间由一个.ibd数据文件代表,该文件默认被创建于数据库目录中。表文件表空间支持动态(DYNAMIC)和压缩(commpressed)行格式。

general tablespace 通用表空间

通用表空间为通过create tablespace语法创建的共享表空间。通用表空间可以创建于mysql数据目录外的其他表空间,其可以容纳多张表,且其支持所有的行格式。
通过create table tab_name … tablespace [=] tablespace_name或alter table tab_name tablespace [=] tablespace_name语法将其添加与通用表空间内。

undo tablespace undo表空间

undo表空间由一个或多个包含undo日志的文件组成。innodb_undo_tablespace配置选项控制undo表空间的数目。undo表空间创建于innodb_undo_directory配置选项确定的位置,该选项典型被用于将undo日志放于不同的存储设备上。如果该选项没有确定任何路径,undo表空间则备创建于mysql通过datadir确定的数据目录下。

temporary tablespace 临时表空间

用户创建的临时表和磁盘内部临时表创建于共享临时表空间中。innodb_temp_data_file选项确定临时表空间数据文件的相对路径、名字、大小和属性等。如果该选项未确定任何值,默认情况下,系统将在innodb_data_home_dir确定的目录下创建一个叫ibtmp1的自动扩展的数据文件,该文件将稍大于12m。
mysql服务器正常关闭或异常终止初始化时,临时表空间将被移除,并且,mysql服务器每次启动时会被重新创建。当临时表空间被创建时,其被赋予一个动态产生的空间ID(space ID)。如果不能创建临时表空间,mysql服务器启动将被拒绝。mysql服务器异常终止的情况下,临时表空间将不被移除。这种情况下,DBA能手工移除临时表空间或重启mysql服务器,重启服务器过程中,将自动移除和重新创建临时表间。
临时表空间并不能存储于裸设备。
这里既然说到了innodb_data_home_dir,那么,就说说这个选项,该选项确定innodb系统表空间数据文件目录路径的共同部分。innodb_file_per_table开启时,该选项设置并不影响表文件表空间的位置。该选项默认值为mysql数据目录。如果你将该选项设置为空串,那么,你可以为innodb_data_file_path设置一个绝对路径值。此外,当为innodb_data_home_dir指定一个值时,需要在尾部添加一个斜杠。

后台线程
  • Master Thread:
    是innodb的主线程,负责调度其他各线程,优先级最高,主要负责将缓冲池中的数据异步刷新(AIO)到磁盘,保证数据一致性。包括脏页的刷新、合并插入缓存(merge insert buffer) 、undo页的回收和redo日志的刷新,内部有两个主处理,分别是每隔1秒和10秒处理。

其内部包含几个循环:主循环(loop),后台循环(background loop),刷新循环(flushloop),暂停循环(suspend loop)。
master thread会根据其内部运行的相关状态在前述各循环间中进行切换,大部分操作在主循环(loop)中完成,其包含有1s和10s两种操作:
1s操作主要包括:日志缓冲刷新到磁盘(总是,即使事务还没有提交);最多刷100个新脏页到磁盘(可能);执行和并改变缓冲的操作(可能);若当前没有用户活动,可能切换到background loop等;
10s操作主要包括:刷新100个脏页到磁盘(可能);合并至多5个改变缓冲(总是);日志缓冲刷新到磁盘(总是);删除无用的undo页(总是);刷新100个或者10个脏页到磁盘(总是)产生一个检查点(总是)等;

  • IO Thread
    InnoDB使用AIO(Async IO)来处理写IO请求,IO Thread的主要工作是负责这些IO请求的回调处理。
    在InnoDB1.0之前,一共有4个IO Thread,分别是:write、read、insert buffer和log IO thread。从InnoDB1.0.x开始,write和read在线程个数分别调到了4个,可通过配置项innodb_write_io_threads和innodb_read_io_threads来设置,
    IO Thread 0是insert buffer thread, 1是log thread,2-5是read thread,6-9是write thread 读线程的ID总是小于写线程ID
    通过show engine innodb status 查看InnoDB中的IO Thread相关信息
  • Purge Thread
    事务被提交后,不再需要undo log,此时Purge Thread会回收已经使用并分配的undo页。通过配置项innodb_purge_threads来设置是否启用独立的Purge Thread。目前的InnoDB版本页支持多个Purge Thread,可以加速undo页的回收,也是通过配置参数innodb_purge_threads
  • Page Cleaner Thread
    负责将脏页的刷新操作放入到单独的线程中,目的为了减轻Master Thread的工作以及对用户查询线程的阻塞,脏数据刷盘后,相应的redo log 也就可以覆盖,即可以同步到数据,又能达到redo log 循环使用的目的,会调用wright thread 线程处理。

Innodb数据存储结构

MySQL架构和运行机制解析_第5张图片
Innodb存储结构,存在着页(page)、区(extent)、段(segment)和表空间(tablespace)的概念。

  • 页(page):lnnoDB将数据划分为若干个页,InnoDB中页的大小默认为16KB。
    以页作为磁盘和内存之间交互的基本单位,也就是一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。也就是说,在数据库中,不论读一行,还是读多行,都是将这些行所在的页进行加载。也就是说,数据库管理存储空间的基本单位是页(Page),数据库I/O操作的最小单位是页。一个页中可以存储多个行记录。
  • 区(Extent):是比页大一级的存储结构,在InnoDB存储引擎中,一个区会分配64个连续的页。因为InnoDB中的页大小默认是16KB,所以一个区的大小是64*16KB=1MB。
  • 段(Segment):由一个或多个区组成,区在文件系统是一个连续分配的空间(在InnoDB 中是连续的64个页),不过在段中不要求区与区之间是相邻的。段是数据库中的分配单位,不同类型的数据库对象以不同的段形式存在。当我们创建数据表、索引的时候,就会相应创建对应的段,比如创建一张表时会创建一个表段,创建一个索引时会创建一个索引段。
  • 表空间(Tablespace):是一个逻辑容器,表空间存储的对象是段,在一个表空间中可以有一个或多个段,但是一个段只能属于一个表空间。数据库由一个或多个表空间组成,表空间从管理上可以划分为系统表空间、用户表空间、撤销表空间、临时表空间等。

总结:表空间表示一本书,段表示书中的章节,区表示每章节的小节,页表示书的每一页,行就是每页的每行数据。表空间里有多个段,一个段包含256个区,一个区包含64个页,一个页为16K。

你可能感兴趣的:(mysql,架构,数据库)