MySQL体系结构

0    159    2

Tags:

👉 本文共约12693个字,系统预计阅读时间或需48分钟。

MySQL Server体系结构

MySQL由数据库和数据库实例组成,是单进程多线程架构(Oracle和PG都是多进程架构)。

数据库:物理操作系统文件或者其它文件的集合,在mysql中,数据库文件可以是frm、myd、myi、ibd等结尾的文件,当使用ndb存储引擎时候,不是os文件,是存放于内存中的文件。

数据库实例:由数据库后台进程/线程以及一个共享内存区组成,共享内存可以被运行的后台进程/线程所共享。

MySQL体系结构

MySQL体系结构

MySQL体系结构

MySQL体系结构

https://www.mysql.com/common/images/PSEA_diagram.jpg

•Connectors(连接者):指的是不同语言中与SQL的交互,从图3-1中可以看到目前流行的语言都支持MySQL客户端连接。

•Connection Pool(连接池):管理缓冲用户连接、线程处理等需要缓存的需求。在这里也会进行用户账号、密码和库表权限验证。

•SQL Interface(SQL接口):接收用户执行的SQL语句,并返回查询的结果。

•Parser(查询解析器):SQL语句被传递到解析器时会进行验证和解析(解析成MySQL认识的语法,查询什么表、什么字段)。解析器是由Lex和YACC实现的,是一个很长的脚本。其主要功能是将SQL语句分解成数据结构,并将这个结构传递到后续步骤中,后续SQL语句的传递和处理就是基于这个结构的。如果在分解构成中遇到错误,则说明该SQL语句可能有语法错误或者不合理。

•Optimizer(查询优化器):在查询之前,SQL语句会使用查询优化器对查询进行优化(生成查询路径树,并选举一条最优的查询路径)。它使用“选取—投影—连接”策略进行查询。

•Caches&Buffers(缓存&缓冲):主要包含QC以及表缓存、权限缓存等。对于QC,以往主要用于MyISAM存储引擎,目前在MySQL 8.0中已放弃,对于现在非常流行的InnoDB存储引擎来讲,QC已无任何意义,因为InnoDB存储引擎有自己的且非常完善的缓存功能。除QC之外(记录缓存、key缓存,可使用参数单独关闭),该缓存机制还包括表缓存和权限缓存等,这些是属于Server层的功能,其他存储引擎仍需要使用。

•Pluggable Storage Engines(插件式存储引擎):存储引擎是MySQL中具体的与文件打交道的子系统,也是MySQL最具特色的一个地方。MySQL的存储引擎是插件式的,它根据MySQL AB公司提供的文件访问层的一个抽象接口来定制一种文件访问机制(这种访问机制就叫存储引擎)。目前存储引擎众多,且它们的优势各不相同,现在最常用于OLTP场景的是InnoDB(当然也支持OLAP存储引擎,但MySQL自身的机制并不擅长OLAP场景)。

•Files&Logs(磁盘物理文件):包含MySQL的各个引擎的数据、索引的文件,以及redo log、undo log、binary log、error log、query log、slow log等各种日志文件。

•File System(文件系统):对存储设备的空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取。常见的文件系统包括XFS、NTFS、EXT4、EXT3、NFS等,通常数据库服务器使用的磁盘建议用XFS。

那么,上述各个组件之间是如何协同工作的?下面我们举一个查询例子进行说明。

假如在MySQL中有一个查询会话请求,那么大概流程如下:

(1)MySQL客户端对MySQL Server的监听端口发起请求。

(2)在连接者组件层创建连接、分配线程,并验证用户名、密码和库表权限。

(3)如果打开了query_cache,则检查之,有数据直接返回,没有继续往下执行。

(4)SQL接口组件接收SQL语句,将SQL语句分解成数据结构,并将这个结构传递到后续步骤中(将SQL语句解析成MySQL认识的语法)。

(5)查询优化器组件生成查询路径树,并选举一条最优的查询路径。

(6)调用存储引擎接口,打开表,执行查询,检查存储引擎缓存中是否有对应的缓存记录,如果没有就继续往下执行。

(7)到磁盘物理文件中寻找数据。

(8)当查询到所需要的数据之后,先写入存储引擎缓存中,如果打开了query_cache,也会同时写进去。

(9)返回数据给客户端。

(10)关闭表。

(11)关闭线程。

(12)关闭连接。

MySQL体系结构

MySQL 使用典型的客户端/服务器(Client/Server)结构,结构图如下所示:

本人提供Oracle(OCP、OCM)、MySQL(OCP)、PostgreSQL(PGCA、PGCE、PGCM)等数据库的培训和考证业务,私聊QQ646634621或微信db_bao,谢谢!

MySQL体系结构

查询缓存,用于将执行过的SELECT语句和结果缓存在内存中。每次执行查询之前判断是否命中缓存,如果命中直接返回缓存的结果。缓存命中需要满足许多条件,SQL语句完全相同,上下文环境相同等。实际上除非是只读应用,查询缓存的失效频率非常高,任何对表的修改都会导致缓存失效;因此,查询缓存在MySQL 8.0中已经被删除。

SQL接口,接收客户端发送的各种DML和DDL命令,并且返回用户查询的结果。另外还包括所有的内置函数(日期、时间、数学以及加密函数)和跨存储引擎的功能,例如存储过程、触发器、视图等。

解析器,对SQL语句进行解析,例如语义和语法的分析和检查,以及对象访问权限检查等。

优化器,利用数据库的统计信息决定SQL语句的最佳执行方式。使用索引还是全表扫描的方式访问单个表,多表连接的实现方式等。优化器是决定查询性能的关键组件,而数据库的统计信息是优化器判断的基础。

缓存与缓冲,由一系列缓存组成的,例如数据缓存、索引缓存以及对象权限缓存等。对于已经访问过的磁盘数据,在缓冲区中进行缓存;下次访问时可以直接读取内存中的数据,从而减少磁盘IO。

存储引擎,存储引擎是对底层物理数据执行实际操作的组件,为服务器层提供各种操作数据的API。MySQL支持插件式的存储引擎,包括InnoDB、MyISAM、Memory等。

InnoDB存储引擎体系结构

InnoDB存储引擎体系结构如图所示(该图来自Percona Database Performance Blog,参考链接:https://www.percona.com/blog/2010/04/26/xtradb-innodb-internals-in-drawing/)。

https://myslide.cn/slides/21572#

MySQL体系结构

从MySQL 5.5版本开始默认使用InnoDB作为引擎,它擅长处理事务,具有自动崩溃恢复的特性,在日常开发中使用非常广泛。下面是官方的InnoDB引擎架构图,主要分为内存结构和磁盘结构两大部分。

MySQL体系结构

内存结构

InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。

缓冲池简单来说就是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响。在数据库中进行读取页的操作,首先将从磁盘读到的页存放在缓冲池中,这个过程为将页"FIX"在缓冲池中。下一次再读相同的页时,首先判断该页是否在缓冲池中。若在缓冲池中,称该页在缓冲池中被命中,直接读取该页。否则读取磁盘上的页。

对于数据库中页的修改操作,则首先修改在缓冲池中的页,然后再以一定的频率刷新到磁盘上。这里需要注意的是,页从缓冲池刷新回磁盘的操作并不是在每次页发生更新时触发,而是通过一种称为Checkpoint的机制刷新回磁盘

允许有多个缓冲池实例,每个页根据哈希值平均分配到不同缓冲池实例中。这样做的好处是减少数据库内部的资源竞争增加数据库的并发处能力。可以通过Innodb_buffer_pool_instances来进行配置,默认是1

InnoDB缓冲池包括了数据页、索引页、插入缓冲、锁信息、自适应Hash和数据字典信息等。InnoDB存储引擎基于磁盘文件存储,访问物理硬盘和在内存中进行访问,速度相差很大,为了尽可能弥补这两者之间I/O效率的差值,我们就需要把经常使用的数据加载到缓冲池中,避免每次访问都进行磁盘I/O。

MySQL体系结构

内存结构主要包括Buffer Pool、Change Buffer、Adaptive Hash Index和Log Buffer四大组件。

Buffer Pool:缓冲池是InnoDB在启动时分配的一块内存区域,用于InnoDB在访问数据时缓存表和索引数据。利用缓冲池,可以合并一些对经常访问的数据的操作,直接从内存中处理,加快了处理速度。通常,在专用数据库服务器上,可以将80%的物理内存分配给InnoDB缓冲池。为了提高缓存管理的效率,使用页面链表的方式+LRU(最近最少使用)算法进行管理。该区域大小由参数innodb_buffer_pool_size来定义,默认128MB。

Buffer Pool以Page页为单位,默认大小16K,BP的底层采用链表数据结构管理Page。在InnoDB访问表记录和索引时会在Page页中缓存,以后使用可以减少磁 盘IO操作,提升效率。

Change Buffer(Insert buffer part of buffer pool):这是一种特殊的数据结构(早期只支持INSERT操作的缓冲,所以也叫作Insert Buffer),当受影响的页面不在缓冲池中时,将会缓存对辅助索引页的更改。这些更改可能是由INSERT、UPDATE、DELETE(DML)语句执行时所导致的,当其他读取操作从磁盘中加载数据页时,如果这些数据页包含Change Buffer中缓存的更改操作页,那么将进行合并操作。

Adaptive Hash Index:自适应哈希索引(AHI),用于管理缓冲池中的内部数据结构,并对缓冲池中的相关工作负载和内存操作组合进行自动调节,且不会牺牲任何事务功能、性能和可靠性。

Log Buffer(Redo Log Buffer):重做日志缓冲区是用于保存将要写入重做日志磁盘文件中的数据的内存缓冲区域。重做日志缓冲区的大小由innodb_log_buffer_size配置参数定义。重做日志缓冲区中的内容会定期刷新到磁盘上的日志文件中。更大的重做日志缓冲区允许运行更大的事务,这在一定程度上避免提交大事务之前需要将重做日志写入磁盘中。因此,如果在应用场景中经常有大事务,则可以考虑增大重做日志缓冲区以减少磁盘I/O操作。innodb_flush_log_at_trx_commit参数控制如何将重做日志缓冲区的内容写入日志文件中(例如,设置为1时,每个事务提交时都需要执行一次将重做日志缓冲区的内容写入日志文件中)。innodb_flush_log_at_timeout参数控制重做日志的刷新频率。

innodb-buffer-pool-list:

MySQL体系结构

innodb-change-buffer:

MySQL体系结构

Insert Buffer(插入缓存)?

影响数据库最主要的性能问题就是I/O,而插入缓冲的作用就是把普通索引上的DML操作从随机I/O变成顺序I/O、提高I/O效率。它的工作原理也很简单,就是先判断插入的普通索引页是否在缓冲池中,如果在就可以直接插入,如果不在就要先放到change buffer中 ,然后进行change buffer和普通索引的合并操作,可以将多个插入合并到一个操作中,一下子就提高了普通索引插入性能,

innodb_change_buffer_max 的含义: 占innodb_buffer_pool的最大比例,默认是25%,最大占比buffer pool 1/4的大小,建议调整为50。

innodb_change_buffering: change buffer 的类型

有如下几种类型:

all:buffer inserts,delete-marking operations,and purges

缓冲全部inserts、delete标记操作和purges操作

none:Do not buffer any operations

关闭 insert buffer

inserts:Buffer insert operations

insert 标记操作

deletes:Buffer delete-marking operations

delete 标记操作

changes: Buffer both inserts and delete-marking

未进行实际insert 和 delete,只是标记,等待后续purge

purges:Buffer the physical deletion operations that happen in the background

缓冲后台进程purges(物理删除)操作。建议选择默认的all就可以了

刷脏页的时机:

l redo log写满时,没有看见了,此时需要将checkpoint向前推进,推进的这部分日志对应的脏页刷入到磁盘,此时所有的更新全部阻塞,此时写的性能变为0,必须待刷一部分脏页后才能更新。

l 系统内存不足时,需要将一部分数据页淘汰掉,如果淘汰的是脏页,需要先将脏页同步到磁盘。

l MySQL认为空闲的时间,这种没有性能问题。

l mysql正常关闭之前,会把所有脏页刷入磁盘,不存在性能问题。

Buffer状态及其链表结构

page是innodb磁盘I/O的最小单位,数据是存放在page中的,那么对应到内存中就是一个个的buffer。每个buffer又分为三种状态

l free buffer:此状态下的buffer从未被使用,像一张白纸,但在实际生产中,数据库很繁忙的情况下,free buffer的状态基本是不存在的

l clean buffer:内存中buffer里面的数据和磁盘page的数据一致

l dirty buffer:内存中新写入的数据还没有刷新到磁盘,跟磁盘中数据不一致

buffer在内存中是需要被组织起来的,由chain来管理,也就是链。

之前介绍过InnoDB是双向链表结构,由三种不同的buffer状态生出了三条链表

n free list:把那些free状态的buffer都串联起来。在数据库真正跑起来的时候,每次把page调到内存中,都先会判断free buffer的使用情况。如果不够用了,就会从lru list和flush list链表中释放free buffer,以获得新的空闲buffer

n lru list:会把那些与磁盘数据一致,并且最近最少被使用的buffer串联起来,释放出free buffer,page调到内存中便于使用新的可用buffer

n flush list:把那些dirty buffer串联起来,为了方便刷新线程把脏数据刷到磁盘。推进checkpoint Lsn,使实例崩溃之后,可以快速恢复。其实flush list中也隐藏这一个lru的规则。举个例子,假如目前有条数据是19,下一秒变成了29,再下一秒又变成了39,这样的数据情况就是经常被访问、被使用到,暂时不能把它串起来。我们要把那些最近少被"弄脏"的数据串起来,刷新到磁盘之后,释放出更多free buffer供我们使用

磁盘结构

System Tablespace:InnoDB系统表空间包含InnoDB数据字典(InnoDB相关对象的元数据)、Doublewrite Buffer、Change Buffer磁盘部分和Undo Logs,还包含在系统表空间中创建的任何表和索引数据。它之所以被称为系统表空间,是因为它可以被多个用户表共享。系统表空间可以由一个或多个数据文件构成。在默认情况下,只会创建一个名为ibdata1的共享表空间文件。但可以使用innodb_data_file_path启动选项控制共享表空间的数量和大小。

Data Dictionary(InnoDB Data Dictionary):InnoDB数据字典由内部系统表组成,这些系统表包含用于跟踪对象(如表、索引和表列)的元数据。元数据存放在InnoDB系统表空间中。由于历史原因,数据字典元数据在一定程度上与存储在InnoDB表的.frm文件中的信息重叠。

Doublewrite Buffer:双写缓冲区是一个位于磁盘的存储区域,InnoDB在进行刷脏操作时,在将脏数据写入数据文件中的正确位置之前,先把脏页从InnoDB缓冲池写入双写缓冲区(8.0.20之前位于系统表空间,从8.0.20开始位于双写文件中)中。只有将脏页成功写入并落盘到双写缓冲区之后,InnoDB才能将脏页从缓冲池中写入数据文件中的正确位置。如果操作系统、存储子系统或mysqld进程在刷新脏页过程中发生崩溃,那么可能会发生部分写(InnoDB默认的页大小为16KB,而文件系统默认的块大小为4KB,如果InnoDB的一个页在写入磁盘过程中发生异常,则可能导致数据页只写入了一部分到磁盘中),InnoDB在重新启动时的崩溃恢复期间从双写缓冲区中找到正确的页面副本进行覆盖恢复。虽然双写会导致脏页被两次写入磁盘中,但双写缓冲区不需要两倍的I/O开销或两倍的I/O操作。因为脏页在双写时是以一次1MB,作为一个大的顺序块被写入双写缓冲区中,并执行一次fsync()调用的。另外,如果系统表空间文件(ibdata文件)被存放在支持原子写入的Fusion-io设备上,则会自动禁用双写缓冲区功能,并将Fusion-io原子写入功能用于所有的数据文件。注意:由于双写缓冲区设置参数(innodb_doublewrite)是全局的,因此对于存放在非Fusion-io设备上的数据文件,也会禁用双写缓冲区功能(对于这部分数据文件可能造成部分写)。所以,原子写功能仅在Fusion-io设备上且在Linux中启用了Fusion-io NVMFS时生效。要充分使用该功能,建议将innodb_flush_method参数设置为O_DIRECT。

•Undo Logs:用于存放事务修改之前的旧数据(undo log记录了有关如何撤销事务对聚集索引记录的最新更改的信息),基于undo实现了MVCC和一致性非锁定读。InnoDB总共支持128个回滚段,每个回滚段有1023个事务槽位,在并行事务场景中一个事务槽位对应一个事务。其中32个回滚段位于临时表空间(Temporary Tablespace),也就是说,对临时表操作的最大并行事务数大约为32×1023个;96个回滚段位于非临时表空间(系统表空间至少一个,因为MySQL 5.7新增的在线undo truncate功能需要,Undo Tablespace最多95个),也就是说,对非临时表操作的最大并行事务数大约为96×1023个。

•File-Per-Table Tablespaces:设置参数innodb_file_per_table=1启用独立表空间时,每个表都会对应生成一个.ibd文件用于存放自己的索引和数据等;否则,在创建表时数据和索引将被存放在ibdata系统表空间文件中(系统表空间)。

•General Tablespaces:常规表空间,在datadir路径下使用CREATE TABLESPACE语法创建的InnoDB共享表空间(tablespace_name.ibd)。可以在MySQL datadir之外创建,能够保存多个表,并支持所有行格式的表。使用CREATE TABLE tbl_name...TABLESPACE[=]tablespace_name或ALTER TABLE tbl_name TABLESPACE[=]tablespace_name语法将表添加到常规表空间中,该功能是MySQL 5.7新增的。

•Undo Tablespace:undo表空间,包含一个或多个undo log文件,文件个数由配置参数innodb_undo_tablespaces控制。关于回滚段的分配详见“Undo Logs”解释。

•Temporary Tablespace:临时表空间用于存放非压缩的InnoDB临时表和相关对象。配置参数innodb_temp_data_file_path为临时表空间的数据文件定义了相对路径和初始大小等。如果未设置innodb_temp_data_file_path参数,则会在数据目录中创建一个名为ibtmp1的自动扩展的12MB初始大小的文件。临时表空间文件在服务器每次重启时都会重新创建(正常停止或终止初始化时会自动删除,但发生崩溃时不会自动删除),并使用动态生成的空间标识ID来避免与现有空间标识ID冲突。另外,在INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO表中可以查看有关InnoDB临时表的元数据(可以看到InnoDB实例中处于活动状态的所有用户和系统创建的临时表)。注意:如果无法创建临时表空间,则Server将启动失败。该功能是MySQL 5.7新增的。

•Redo Logs:重做日志是在崩溃恢复期间使用的基于磁盘的数据结构文件,用于恢复不完整提交事务写入的数据。在MySQL实例正常运行期间,重做日志对事务产生的数据变更部分进行编码并持久化到磁盘中(重做日志中的数据就是对受影响的行记录进行编码,利用这些编码数据把事务进行前滚的操作就叫作重做)。在默认情况下,重做日志在磁盘中创建一组名为ib_logfile0和ib_logfile1的文件。MySQL以循环滚动方式写入重做日志文件,并使用一个不断增加的LSN值表示重做日志的写入量,以及标记写入重做日志文件中的位置。根据WAL(Write-Ahead Logging,日志先行)原则,在提交事务时会先使用redo log持久化事务发生修改的部分数据(只要redo log落盘并打上commit标记就表示事务已经持久化)。

双写???:

插入缓冲带来的是针对普通索引插入性能上的提升,而double write就是保证写入的安全性防止在Mysql实例发生宕机时,InnoDB发生数据页部分页写(partial page write)的问题。数据库实例崩溃,我们可以通过redo log 进行恢复,不会有任何问题,但redo log文件记录的是页的物理操作,如果页都损坏了,是无法进行任何恢复操作的巧妇难为无米之炊就是这个道理。所以我们需要页的一个副本,如果实例宕机了,可以先通过副本把原来的页还原出来,再通过redo log 进行恢复,重做。这就是double write的作用

双写缓冲是一个位于系统空间中的存储区域(从 8.0.20开始独立出来了),InnoDB缓冲池中刷出的脏页在被写入数据文件之前,都先会写入double write buffer,然后从两次写缓冲区分两次,每次将1MB大小的数据写入磁盘共享表空间(double write),最后再从double write buffer写入数据文件

InnoDB各个组件如何协同工作

那么,InnoDB存储引擎体系结构中的各个组件是如何协同工作的?我们列举一个UPDATE场景加以说明。

假设有一个UPDATE语句正在执行:UPDATE test SET idx=2 WHERE id=10,执行流程如下(这里主要以InnoDB存储引擎体系结构中的组件为主):

(1)在Server层进行词法解析,解析成MySQL认识的语法,查询什么表、什么字段,并生成查询路径树,选择最优查询路径。

(2)到了InnoDB存储引擎这里,先判断id=10这行数据对应的页是否在缓冲池中,如果不在,则将id=10记录对应的页从datafile中读入InnoDB缓冲池中(如果该页已经在缓冲池中,就省去了读入这一步),并对相关记录加独占锁。

(3)将idx修改之前的值和对应的主键、事务ID原来的信息写入Undo Tablespace的回滚段中。

(4)更改缓存页中的数据,并将更新记录和新生成的LSN值(日志序列号)写入Log Buffer中,更新完之后在缓冲池中这个页就是脏页了。

(5)在提交事务时,根据innodb_flush_log_at_trx_commit的设置,用不同的方式将Log Buffer中的更新记录刷新到redo log中,然后写binlog(二进制日志文件),写完binlog就开始commit(这里的commit是指binlog的commit,就是同步到磁盘),binlog同步之后就把binlog文件名和position(binlog文件内的位置)也写到redo log中。然后在redo log中写入一个commit标记,那么此时就完成了这个事务的提交。接下来释放独占锁。

(6)后台I/O线程根据需要择机将缓存中合适的脏页刷新到磁盘数据文件中。当然,在刷新脏页时要先拷贝一份到双写缓冲区中(如果开启了双写缓冲区功能的话),当双写缓冲区中的数据落盘之后,再从缓冲池中把脏页刷新到各个数据文件中。

存储结构

MySQL体系结构

InnoDB逻辑存储单元主要分为表空间、段、区、页、行。

表空间

表空间用于存储表结构和数据。Innodb存储引擎表中所有数据都是存储在表空间中的,表空间又分为系统表空间、独立表空间、通用表空间、临时表空间、Undo表空间等多种类型

系统表空间(The System Tablespace)

包含InnoDB数据字典,Doublewrite Buffer,Change Buffer,Undo Logs的存储区域。系统表空间也默认包含任何用户在系统表空间创建的表数据和索引数据。系统表空 间是一个共享的表空间因为它是被多个表共享的。该空间的数据文件通过参数 innodb_data_file_path控制,默认值是ibdata1:12M:autoextend(文件名为ibdata1、 12MB、自动扩展)。

独立表空间(File-Per-Table Tablespaces)

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

通用表空间(General Tablespaces)

通用表空间为通过create tablespace语法创建的共享表空间。通用表空间可以创建于mysql数据目录外的其他表空间,其可以容纳多张表,且其支持所有的行格式。

CREATE TABLESPACE ts1 ADD DATAFILE ts1.ibd Engine=InnoDB; //创建表空 间ts1

CREATE TABLE t1 (c1 INT PRIMARY KEY) TABLESPACE ts1; //将表添加到ts1 表空间

撤销表空间(Undo Tablespaces)

撤销表空间由一个或多个包含Undo日志文件组成。在MySQL 5.7版本之前Undo占用的是System Tablespace共享区,从5.7开始将Undo从System Tablespace分离了出来。InnoDB使用的undo表空间由innodb_undo_tablespaces配置选项控制,默认为0。参数值为0表示使用系统表空间ibdata1;大于0表示使用undo表空间undo_001、undo_002等。

临时表空间(Temporary Tablespaces)

分为session temporary tablespaces 和global temporary tablespace两种。session temporary tablespaces存储的是用户创建的临时表和磁盘内部的临时表。global temporary tablespace储存用户临时表的回滚段(rollback segments )。mysql服务器正常关闭或异常终止时,临时表空间将被移除,每次启动时会被重新创建。

表空间是由段组成的,也可以把一个表理解为一个段,通常有数据段、回滚段、索引段等。每个段由N个区间和32个零散的页组成,段空间扩展是以区为单位进行扩展的。通常情况下,创建一个索引的同时就会创建两个段,分别为非叶子节点和叶子节点段。那一个表有几个段呢?

答案是4个,是索引个数的2倍

区是由连续的页组成的,是物理上连续分配的一段空间,每个区的大小固定是1MB

nnoDB的最小物理存储分配单位是page,有数据页回滚页等。一般情况下,一个区由64个连接的页组成,页默认大小是16KB

区就等于64*16KB=1MB。但Mysql数据库从5.6开始可以自定义调低page的大小,可以默认的16KB调整为8KB或4KB。而mysql5.7开始可以调高page的大小,可以从默认的16KB调整为32KB或者64KB。一般情况下,一个page页会默认预留1/16的空间用于更新数据。真正使用的是15/16的空间。一个页最少可以存两行数据,虚拟最小行(infimum records)和虚拟最大行(infimum records).用来限定行记录的范围,以此来保证B+tree节点是双向链表结构

页里面又记录着行记录的信息,InnoDB存储引擎是面向行的,也就是数据是按照行存储的

Innodb存储引擎有两种文件格式

  • Antelope文件格式:compact和redundant两种行记录格式

  • Brracuda文件格式:compressed和dynamic**两种行记录格式

线程结构

InnoDB存储引擎后台线程

MySQL是一个单进程多线程架构的数据库管理系统,所以当MySQL实例启动之后,会存在众多的线程来做各种各样的事情。我们可以通过performance_schema.threads表查询到所有线程,包括后台线程和前台线程,这里主要介绍后台线程。

我们可以看到总共有16种后台线程,这些后台线程的主要功能如下。

srv_master_thread(主线程):InnoDB存储引擎主线程,由4个循环组成,即主循环(loop)、后台循环(background loop)、刷新循环(flush loop)、暂停循环(suspend loop),其中大多数工作都在主循环中完成,主循环主要负责将脏缓存页刷新到数据文件中,执行undo purge操作,触发检查点,合并插入缓冲区等。

io_ibuf_thread(插入缓冲线程):主要负责插入缓冲区的合并操作。将对辅助索引页的修改操作从随机变成顺序I/O,大幅度提升了效率(会先判断发生修改的辅助索引页是否在缓冲池中,如果在则直接修改;如果不在,则先存放在一个Change Buffer对象中。当其他读取操作把该修改对应的页从磁盘读取到缓冲池中时,就会合并该Change Buffer对象中保存的记录到辅助索引页中)。

io_read_thread(读I/O操作线程):负责数据库的AIO(异步I/O)读取操作,可以配置多个读线程,由参数innodb_read_io_threads设置,默认值为4。

io_write_thread(写I/O操作线程):负责数据库的AIO(异步I/O)写操作,可配置多个写线程,由参数innodb_write_io_threads设置,默认值为4。

io_log_thread(日志线程)用于将重做日志刷新到日志文件中。

srv_purge_thread(undo清理线程):主要负责undo页清理操作,在MySQL 5.6之后可设置独立的线程执行undo purge操作,以减少主线程负载。通过innodb_purge_thread参数来控制purge的线程个数。默认是1个

srv_lock_timeout_thread(锁线程):负责锁控制和死锁检测等。

srv_error_monitor_thread(错误监控线程),主要负责错误控制和错误处理。

page_cleaner_thread(脏页清理线程):负责刷脏操作。在MySQL 5.6之后可设置独立的线程执行刷脏操作,以减少主线程负载。刷脏分为两种方式,其中一种是基于LRU list(最近最少访问的列表)的最近访问时间顺序刷新的;另一种是基于flush list(刷新列表)的最近修改时间和LSN值的顺序刷新的。

thread_timer_notifier(计时器过期通知线程):超过计时器时间,通知线程处理。一般在超过MAX_EXECUTION_TIME时自动中止SQL语句的执行。

main(主线程):MySQL服务的主线程(请与InnoDB存储引擎主线程区别开),包括初始化、读取配置文件等功能。

srv_monitor_thread(InnoDB监控打印线程):如果开启了InnoDB监控器,那么每隔5秒打印一次InnoDB监视器采集的信息。

srv_worker_thread(InnoDB工作线程):InnoDB的实际工作线程,轮询从任务队列中取出任务(row select、row insert等)并执行。

buf_dump_thread(InnoDB缓冲池导入/导出线程):InnoDB缓冲池热点数据页导入/导出的线程。

dict_stats_thread(InnoDB后台统计线程):负责InnoDB表统计信息更新的后台线程。

signal_handler(信号处理线程):负责SIGTERM、SIGQUIT、SIGHUP信号处理的线程。

MySQL前台线程

前台线程其实就是通过TCP/IP协议或客户端程序创建的线程,所以包括了与主备复制相关的线程用户创建的连接线程

跟MySQL后台线程类似,我们也可以通过performance_schema.threads表来查询MySQL中有哪些前台线程。

以下为双主复制架构数据库实例中的查询结果:

以下为MySQL 5.7单库查询:

我们可以看到,有5种前台线程,这些前台线程的主要功能如下。

compress_gtid_table(GTID压缩线程):用于压缩MySQL 5.7新增的mysql.gtid_executed表中的GTID记录数量。在MySQL 5.7版本中,当从库关闭log-bin或者log_slave_updates参数之后,SQL线程每应用一个事务就会实时更新一次mysql.gtid_executed表(在MySQL 5.7中启用GTID复制时可以关闭log_slave_updates参数,使用该表来记录GTID。但在MySQL 5.6中由于没有此表,所以不能关闭log_slave_updates参数),时间一长,该表中就会存在大量的GTID记录(每个事务一行),使用该线程可以把多行记录压缩成一行。

one_connection(用户连接线程):用于处理用户请求的线程。

slave_io(I/O线程):用于拉取主库binlog日志的线程。

slave_sql(SQL线程):用于应用从主库拉取的binlog日志的线程。注意:在多线程复制中,该线程为协调器线程,用于分发binlog日志给工作线程(slave_worker)应用,并对多个工作线程进行协调。

slave_worker(工作线程):在多线程复制场景中,接收并应用SQL线程(slave_sql)分发的主库binlog日志,多个工作线程之间的一致性依靠SQL线程(slave_sql)进行协调。

标签:

头像

小麦苗

学习或考证,均可联系麦老师,请加微信db_bao或QQ646634621

您可能还喜欢...

发表回复

嘿,我是小麦,需要帮助随时找我哦
  • 18509239930
  • 个人微信

  • 麦老师QQ聊天
  • 个人邮箱
  • 点击加入QQ群
  • 个人微店

  • 回到顶部
返回顶部