许多测试人员都知道如何编写SQL查询以及如何从表中获取数据。但是只有少数人了解内部架构以及MySQL如何与存储引擎交互。该篇文章讨论了MySQL体系结构,MySQL数据中SQL语句的不同层次和旅程。
MySQL采用的是分层体系结构,下图描述了不同组件之间如何相互联系。
MySQL体系结构基本上是一个客户端-服务器系统。MySQL数据库服务器是服务器,连接到MySQL数据库服务器的应用程序或语言都称为客户端(MySQLSQL接口是按照ANSISQL标准构建的,接受与大多数符合ANSI标准的数据库服务器相同的基本SQL语句)。
MySQL体系结构由四个主要子系统组成,这些子系统一起工作以响应对MySQL数据库服务器的请求。
应用层
应用层的主要作用就是负责不同客户端或语言与MySQL服务器的通信服务。
连接器
什么是连接器?
在MySQL文档中,术语连接器是指允许您的应用程序连接到MySQL数据库服务器的软件。MySQL提供了多种语言的连接器。比如PHP。
如果您的PHP应用程序需要与数据库服务器通信,则需要编写PHP代码来执行诸如连接数据库服务器,查询数据库以及其他与数据库相关的功能之类的活动。需要软件来提供PHP应用程序将使用的API,并且还需要处理应用程序与数据库服务器之间的通信,可能需要使用其他中间库。该软件通常称为连接器,因为它允许您的应用程序连接到数据库服务器。
MySQL从网络通信路径接收到客户端与数据库服务器的连接,并为每个连接创建一个线程。MySQL是一个真正的多线程应用程序,线程是MySQL服务器中的核心,每个线程之间基本都是相互独立的(除了某些帮助线程)。连接成功后每个线程都会被存储在MySQL连接池中。
连接层的工作主要包括客户端与服务端建立连接的服务,比如连接处理、身份验证、安全校验。
连接处理
客户端会通过各种接口与实例与MySQL通信,当服务端接收到客户端发来的连接请求时,服务端会为这个客户端分配一个单独的线程来专门处理它们之间的交互。该线程由服务器缓存,因此服务端不需要为每个新连接创建和销毁它们,每次客户端断开连接后,服务端不会立刻将此链接销毁掉,而是先缓存起来,等有新的客户端在进行连接时在把这个线程分配给它,这样就大大减少了MySQL创建线程的开销。
身份验证
每当客户端连接到MySQL服务器时,服务器都会在服务器端执行身份验证。身份验证基于用户名,客户端主机和客户端用户密码。
权限安全校验
客户端与服务端成功连接后,服务器端会检查当前客户端用户的所有操作权限。
逻辑层
逻辑层从应用程序层获取数据,逻辑层包含了MySQL服务器所有的逻辑操作功能。MySQL逻辑层就类似于人的大脑,电脑的cpu,MySQL的逻辑层分为多个子组件,MySQL服务以及一些实用程序、SQL分类器、SQL解析器、SQL优化器、数据缓存器。
MySQL服务以及实用程序
MySQL提供了广泛的服务和实用程序,这也正是MySQL普及的主要原因之一。该层提供了对MySQL服务器的管理和维护,具体可分为事务管理、备份和恢复、数据库安全、复制、集群、分区等。
SQL分类器
该层提供的主要服务就是对客户端发来是SQL进行分类,没啥好说的。它呢是一系列SQL接口组件,根据这些组件的定义来区分你传入的语句到底的什么类型的语句。比如数据操作语言(DML)、数据定义语言(DDL)、权限控制语言(DCL)、存储过程、函数、触发器。
事务管理器
事务管理负责确保自动记录和执行事务。它是通过日志管理器和并发控制管理器来完成的。它还负责发出COMMIT和ROLLBACKSQL命令
日志管理器
此处的日志管理器记录了数据库中执行的每个操作,并将操作日志存储为MySQL命令。在系统崩溃的情况下,执行这些命令将使数据库回到最后的稳定阶段。恢复管理器负责将数据库还原到其最后的稳定状态。通过使用数据库的日志来做到这一点,该日志是从缓冲区管理器获取的,并执行日志中的每个操作。由于日志管理器记录了对数据库执行的所有操作(从数据库生命周期的开始),因此在日志文件中执行每个命令会将数据库恢复到其最后的稳定状态。
SQL解析器
了解一下东西我们首先得知道它是干嘛用的,对吧。对于MySQL解析器来说,主要作用就是对SQL语句进行语法验证。MySQL解析器是使用由Bison编译的大型Lex-YACC脚本实现的。解析器构造一个查询结构,该查询结构用于表示内存中的查询语句(SQL)为可用于执行查询的树结构(也称为抽象语法树)。
根据MySQL内部,解析器结构如下:
在解析常规语句时,首先进行词法分析(从字符流中生成单词或标记)。语法分析(分析SQL语句是否正确可执行),语义分析(确保使这些句子确实有意义)和代码生成(对于编译器而言)在所有代码阶段都一次完成。
优化器
在经过对SQL分类、创建内部解析树后,紧接着就是对SQL语句执行内部优化处理。MySQL应用了各种优化技术,这些技术可能包括重写查询,表扫描顺序和选择要使用的正确索引。
根据MySQL内部结构,优化器执行大致如下:
优化器的第一步是检查表是否存在以及用户的访问控制。如果存在错误,则返回适当的错误消息,并且控制权返回到线程管理器或侦听器。一旦识别出正确的表,便会打开它们并将适当的锁应用于并发控制。完成所有维护和设置任务后,优化器将使用内部查询结构并评估查询的WHERE条件(限制操作)。结果将作为临时表返回以为下一步做准备。如果存在UNION运算符,则优化器将在继续之前循环执行所有语句的SELECT部分。优化器的下一步是执行投影。这些以与限制部分相似的方式执行,再次将中间结果存储为临时表,并仅将列规范中指定的那些属性保存在SELECT语句中。最后,针对使用join类构建的任何JOIN条件对结构进行分析,然后调用join::optimize()方法。在这一阶段,通过评估表达式并消除导致死分支或始终为真或始终为假的条件的条件(以及许多其他类似的优化)来优化查询。优化程序正在尝试在执行联接之前消除查询中的任何已知不良条件。这样做是因为联接是所有关系运算符中最昂贵和最耗时的。同样重要的是要注意,对所有具有WHERE或HAVING子句的查询都执行联接优化步骤,而不管是否存在任何联接条件。这使开发人员可以将所有表达式评估代码集中在一个地方。
在MySQL源码中可以看到,执行插入或者执行查询都是由特定库去处理的。例如,mysql_insert()方法旨在插入数据。同样,有一个mysql_select()方法设计为查找和返回与WHERE子句匹配的数据。该执行方法库位于相似名称的文件(例如sql_insert.cc或sql_select.cc)下的各种源代码文件中。所有这些方法都有一个线程对象作为参数,该对象允许该方法访问内部查询结构并简化执行。使用网络通信路径库返回每种执行方法的结果。使用查询执行的解释模型可以清楚地实现查询执行库方法。
缓存器
查询缓存是一项了不起的发明,它不仅缓存查询结构,还缓存查询结果本身。这使系统能够检查频繁使用的查询,并缩短整个查询优化和执行阶段的总和。这是MySQL独有的另一项技术。
MySQL缓存(查询缓存)存储SELECT语句的完整结果集。甚至在解析查询之前,MySQL服务器都会查询查询缓存。如果任何客户端发出的查询与高速缓存中已有的查询相同,则服务器仅跳过解析,优化甚至执行,它仅显示高速缓存的输出。
MySQL查询缓存会考虑到结果的纯净层度,在对基表运行INSERT,UPDATE或DELETE后,MySQL会根据需要重新生成一份查询缓存。
缓冲区管理器/缓存和缓冲区
缓存和缓冲区子系统负责确保以最有效的方式获得最常用的数据或结构,换句话说,数据必须始终驻留或准备随时读取。高速缓存极大地增加了对该数据请求的响应时间,因为该数据位于内存中,因此无需额外的磁盘访问即可检索它。创建缓存子系统是为了将所有缓存和缓冲封装到一组松散耦合的库函数中。尽管你会发现在几个不同的源代码文件中实现的缓存,但是它们被视为同一子系统的一部分。
在该子系统中实现了许多缓存。大多数缓存机制使用相同或相似的概念将数据存储为链表中的结构。缓存在代码的不同部分中实现,以使实现适合正在缓存的数据类型。下面让我们看一下每个缓存。
表缓存
创建表缓存是为了最大程度地减少打开,读取和关闭表(磁盘上的.frm文件)的开销。表高速缓存旨在将有关表的元数据存储在内存中。这使线程读取表的架构更快,而不必每次都重新打开文件。每个线程都有自己的表缓存结构列表。这允许线程维护它们自己的表视图,以便如果一个线程正在更改表的架构(但尚未提交更改),则另一个线程可以将该表与原始架构一起使用。所使用的结构是一种简单的结构,其中包含表的所有元数据信息。这些结构存储在内存中的链接列表中,并与每个线程关联。
记录缓存
创建记录缓存是为了增强从存储引擎的顺序读取。因此,记录缓存通常仅在表扫描期间使用。它一次读取一个数据块,就像预读缓冲区一样工作,因此在扫描过程中减少了磁盘访问。较少的磁盘访问通常等于提高了性能。有趣的是,记录高速缓存还用于顺序写入数据,方法是先将新的(或更改的)数据写入高速缓存,然后在高速缓存已满时将高速缓存写入磁盘。这样,写入性能也得到了改善。尽管不限于MyISAM,但这种顺序行为(称为引用局部性)是记录缓存最常与MyISAM存储引擎一起使用的主要原因。记录缓存以不可知的方式实现,不会干扰用于访问存储引擎API的代码。由于记录缓存是在API层内实现的,因此开发人员无需采取任何措施即可利用它。
密钥缓存
密钥缓存是常用索引数据的缓冲区。在这种情况下,它是索引文件(B树)的数据块,仅用于MyISAM表(磁盘上的.MYI文件)。索引本身存储为键高速缓存结构中的链接列表。首次打开MyISAM表时,将创建一个密钥缓存。每次读取索引都会访问键高速缓存。如果在缓存中找到索引,则从那里读取索引;否则,必须从磁盘读取新的索引块并将其放入高速缓存中。但是,缓存的大小有限,并且可以通过更改key_cache_block_size配置变量进行调整。因此,并非索引文件的所有块都可以放入内存。
那么系统如何跟踪已使用的块
高速缓存实现了一个监视系统,以跟踪索引块的使用频率。密钥缓存已实现以跟踪索引块的“温暖”程度。在这种情况下,“暖”是指随着时间的推移已访问索引块的次数。温暖的值包括BLOCK_COLD,BLOCK_WARM和BLOCK_HOT。随着块的冷却和新块的变热,将清除冷块,并添加温暖的块。此策略是最近最少使用(LRU)的页面替换策略(与用于操作系统中的虚拟内存管理和磁盘缓冲的算法相同),即使面对更为复杂的页面替换算法,该策略也被证明是非常有效的。以类似的方式,键高速缓存会跟踪已更改的索引块(称为“变脏”)。清除脏块后,将其数据写回磁盘上的索引文件中,然后再进行替换。相反,清除干净块后,只需将其从内存中删除即可。
特权缓存
特权缓存用于将授权数据存储在用户帐户上。此数据的存储方式与访问控制列表(ACL)相同,访问控制列表列出了用户对系统中的对象拥有的所有特权。特权缓存被实现为存储在先进先出(FILO)哈希表中的结构。在用户身份验证和初始化期间读取授权表时,将收集高速缓存的数据。将这些数据存储在内存中非常重要,因为它可以节省大量时间读取授权表。
主机名缓存
主机名缓存是辅助缓存中的另一个,例如特权缓存。它也被实现为结构的堆栈。它包含与服务器的所有连接的主机名。似乎令人惊讶,但是经常需要此数据,因此需求量很高,因此它是专用高速缓存的候选者。
杂项
整个MySQL源代码中都实现了许多其他小型缓存机制。一个示例是复杂连接操作期间使用的连接缓冲区缓存。例如,某些联接操作需要将一个元组与第二个表中的所有元组进行比较。在这种情况下,高速缓存可以存储读取的元组,从而可以实现联接,而不必多次将第二个表重新读入内存。
物理层(存储引擎层)
MySQL中的物理层也可以叫做是存储引擎层,在MySQL中提供了许多存储引擎供用户选择,这也是MySQL受广大用户喜欢的原因之一。
他们主要负责存储和检索存储在MySQL中的所有数据。MySQL的物理层与其他RDBMS略有不同。此处,物理系统由可插拔存储引擎体系结构组成,该体系结构使存储引擎可以加载到正在运行的MySQL服务器中或从中运行。
在MySQL中,存储引擎都是对表使用的,一个数据库可以包含具有多个存储引擎的表。
可用的各种引擎是MyISAM,InnoDB,CSV,Archives等。从MySQL5.5.5开始,新表的默认存储引擎是InnoDB(MySQL中的CREATETABLE语句默认创建InnoDB表)。每个存储引擎都有不同的特性,根据应用程序的需求,我们可以选择合适的存储引擎。
例如,如果我们有一个CSV数据,该数据仅接受用逗号分隔的文本,则可以使用CSV存储引擎,它使用逗号分隔值格式将数据存储在文本文件中。同样,您可以使用存档存储引擎来存储大量没有索引的数据。
数据存储层
存储管理器与操作系统连接,以将数据有效地写入磁盘。由于存储功能驻留在单独的子系统中,因此MYSQL存储引擎在远离操作系统的抽象级别上运行。存储管理器将用户表中的所有数据写入磁盘。索引,日志以及内部系统数据。
MySQL将每个数据库(也称为模式)存储为基础文件系统中其数据目录的子目录。每个数据库都有一个对应的数据目录。
创建表时,如果是MyIsAm引擎,MySQL将表定义存储在与表同名的.frm文件中。如果是innodb引擎的话,数据索引表结构这些信息则统一存储在.idb文件中。因此,当你创建一个名为MyIsAm引擎的Orders的表时,MySQL将表定义存储在Orders.frm中。这些.frm文件不存储数据,而仅具有包含表结构描述的格式。
注意:
frm结尾的文件在MySQL8.0版本后将不在存在,MyIsAm表创建后结尾文件为sdi(序列化字典信息)、MYI、MYD。
mysql8.0以前MyISAM引擎的表是由以下三个文件组成的:
tablaName.frm表结构文件
tablaName.MYD数据文件
tablaName.MYI索引文件
只要一张表的这三个文件存在,直接复制到msql的数据目录中,mysql是可以直接读取到这张表的,但是到了8.0,.FRM文件变成了.sdi文件,这种方法已经无法恢复数据了。