



我们已对Server层的功能有了初步的了解,但需要明确的是,Server层还包含诸多组件,它们协同工作以确保整个系统顺畅运行。Server层根植于MySQL的早期设计之中,其初衷在于与MyISAM表引擎保持兼容性。尽管Server层不直接涉及数据的组织和存储,但它承担着与客户端进行交互、优化查询处理、管理主从复制以及与存储引擎进行交互等关键任务。这些模块的实现均较为复杂。MySQL Server层的整体架构如图2-2所示。
下面简单介绍Server层中每个组件的作用。
连接层级的核心职责在于与客户端交互。此层级集成了多个关键模块,包括专注于网络监听的实现以及数据包的发送与接收的vio模块,用于确保数据交互的安全性与合规性的登录认证与权限控制模块,以及相应的线程缓存与表缓存机制。
当客户端发起连接请求时,服务器端先通过vio模块与客户端建立稳定的连接通道,随后利用认证模块执行登录认证及权限审核流程,以确保客户端身份的合法性与操作权限的适宜性。
一旦验证通过,系统将为客户端创建一个用户线程(THD),并将其存储在线程缓存中,以便后续快速访问与管理。同时,用户线程所打开的表也将被记录并存储在表缓存中,以提高数据访问的效率与响应速度。
图2-2 MySQL Server层的整体架构
1. vio模块
vio模块封装着套接字操作的相关方法,例如Linux网络编程中的send方法向网络文件描述符(fd)发送数据包,recv方法监听网络文件描述符接收数据包等,下面介绍vio提供的一些方法:
❑ vio_delete
❑ vio_read
❑ vio_write
❑ vio_keepalive
❑ vio_io_wait
❑ vio_shutdown
vio也结合了epoll io多路复用技术来支撑高并发场景,这些内容在第3章会详细介绍。
2.登录认证与权限控制
MySQL的登录认证过程是在客户端与服务端之间完成三次握手及一系列校验之后进行的。在此过程中,客户端会向服务端发送一个认证数据包,该数据包包含通过特定认证协议加密的用户名、密码、请求访问的数据库名称等关键信息。目前,MySQL支持的常用认证协议包括mysql_native_password、sha256_password、caching_sha2_password以及ed25519等。服务器在接收到这个认证数据包后,会根据所使用的协议进行解密操作,并将解密后的信息与系统中维护的用户信息进行比对,以确认用户身份是否合法,从而决定认证是否成功。这一过程的详细技术细节将在第3章中深入阐述。
此外,MySQL还提供了对数据库、表以及列级别的精细权限控制机制。在用户尝试访问任何数据库资源之前,系统会首先验证该用户是否具备相应的权限。具体而言,系统会根据用户的请求以及所请求资源的权限设置,进行严格的权限校验。如果用户未获得相应的访问权限,系统将直接返回错误信息,阻止未经授权的访问操作。
3.线程缓存
在MySQL系统中,存在一个精心设计的线程缓存机制,其核心策略是高效复用连接资源。具体而言,该机制通过将创建的用户线程管理对象存储于内存缓存之中,实现对象的快速复用。当系统需要新的用户线程对象时,会直接从缓存中取出已存在的对象再利用,并根据需要执行必要的数据初始化操作。而当用户线程对象不再需要时,系统会将其相关的维护数据清空,并重新放回线程缓存中,以备后续使用。
注意 MySQL 的线程缓存机制在某些特定场景下可能面临内存泄漏的风险。特别是当系统同时开启大量线程,并在这些线程中频繁申请小量内存时,若未能妥善管理这些内存资源的释放,就可能导致在连接关闭后,部分内存仍然被占用而未得到释放。这一现象可能表现为 MySQL 进程的内存占用持续且缓慢地增长,进而可能对系统的稳定性和性能产生不利影响。
4.表缓存
MySQL会在Server层维护一个表缓存,主要缓存表结构信息。表缓存又分为全局级别和会话级别,表缓存属于数据字典的一部分,数据字典在MySQL 8.0之后进行了重构优化,详细介绍请参考第4章。
一条SQL语句在查询优化模块需要经历如下四个流程:
1)通过解析器完成词法及语法解析,解析完成后生成一个执行树。
2)查询缓存中查看是否有满足条件的执行计划,前提是打开了查询缓存。
3)通过优化器生成逻辑执行计划和物理执行计划。逻辑执行计划主要是关系代数基础上的优化,通过对关系代数表达式进行逻辑上的等价变换,包含一些SQL改写、列裁剪、谓词下推、聚合消除等;物理执行计划是对具体的执行路径进行优化,在统计信息的基础上,通过选择最佳的索引、合适的关联方式(Join)等来估算最低的执行代价。
4)执行器会负责完成优化器生成的整个执行计划的执行,最终将结果返回给客户端。
MySQL的参数模块,用于平常的参数设置。参数又分为静态参数和动态参数,动态参数可以在MySQL运行期间通过set variables命令修改。MySQL内部针对每个参数实现了具体的修改方法,修改之后会在内存中更新全局的参数。不过有时候虽然实现了动态修改,在内部也需要重新读取参数才能生效,比如修改binlog_format参数时,需要断开客户端连接并重连,参数才会生效。
MySQL的状态模块,用于收集各项指标状态。MySQL几乎在每个模块都有埋点记录相关的指标信息,可以通过show global status来查看全部状态指标信息。这些指标信息几乎都是递增的,并且只存储在内存中,一旦重启将会重新计数。实际上几乎所有的MySQL监控系统都是从这里采集的数据。
performance_schema 是MySQL的一个数据库,里面采集了很多相关的指标数据。上述的status在performance_schema中就有对应的表,除此之外还有内存使用情况、线程使用情况、io、锁、事件等待等信息。可以通过performance_schema中的表来分析线上相关问题,比如内存的使用情况。
Server层设计并维护了多种类型的缓冲区,这些缓冲区的核心目标在于优化SQL语句的执行效率。以下是对每种缓冲区作用的简要概述:
❑join buffer。 在涉及join操作的场景中,若join的表未建立相应的索引,系统将采取优化措施,即将这些表的数据暂存于join buffer之中,以此提升join操作的执行效率。join buffer的具体容量由系统参数join_buffer_size进行调控,其作用域限定于每一次独立的join查询过程。因此,在配置该参数时,务必充分考虑业务实际需求及特点,以做出合理的设定。
❑net buffer。 MySQL客户端和服务端会维护相应的缓冲区,用以缓存发送和接收的数据。具体地讲,每个客户端线程均会被分配一个连接缓冲区以及一个结果集缓冲区,以便高效地处理数据传输和存储。注意这里说的连接buffer虽然区分客户端和服务端,不过都是维护在服务端的。上述连接缓冲区与结果集缓冲区的容量主要受net_buffer_length和max_allowed_packet两个系统参数的控制。其中,初始容量设置net_buffer_length的默认值,即16KB。在任何情况下,这两个缓冲区的最大容量均不得超过max_allowed_packet参数所设定的上限值。
❑myisam sort buffer。 MyISAM维护的排序缓冲区主要用于在repair table、create index、alter table等场景中对MyISAM索引进行排序操作,其大小由myisam_sort_buffer_size参数进行精确控制。
❑read buffer。 主要用于缓存MyISAM存储引擎顺序扫描表的结果,它是线程级别的。同时针对所有存储引擎,在Order By排序、批量插入分区、缓存嵌套循环数据等场景下使用。由read_buffer_size参数控制。
❑key buffer。 其主要功能为缓存MyISAM引擎的索引块,旨在通过此种方式加速查询操作。该缓存具有全局粒度,意味着其资源为所有线程所共享,并受到key_buffer_size参数的直接控制与管理。
❑read rnd buffer。 该特性主要应用于MyISAM引擎中的Order by语句场景。在此场景下,系统直接从read rnd buffer中读取数据,以此来提升Order by语句的执行性能。此功能是以线程为单位进行管理的,其大小由参数read_rnd_buffer_size进行控制。
❑sort buffer。 该机制主要被设计用于在所有存储引擎中处理数据查询排序的场景,其以线程为单位进行运作,并由sort_buffer_size参数进行精确控制。
❑sql buffer。 由sql_buffer_result参数控制,它会强制MySQL将查询结果集放入一个临时表中。这样做的主要目的是将客户端与实际的表查询操作分离开来,使得客户端可以更快地获取到部分结果,而MySQL可以在后台继续处理查询结果的其他部分,例如进行排序、分组等操作。
Server层主要维护如下四种日志:
❑全量日志 ,主要用于问题定位、审计等场景,其启用与否由参数general_log控制。在默认情况下,此功能处于关闭状态,以确保系统性能和资源利用率的优化。
❑慢查询日志 ,用于记录执行时间超过预设阈值的SQL语句。其启用与否由slow_query_log参数控制。具体而言,当SQL语句的执行时间超过long_query_time参数所设定的阈值时,这些SQL语句将被记录在慢查询日志中,以供后续分析和优化。
❑错误日志, 用于在MySQL启动过程中或遇到错误时记录相关信息的工具。观察和分析这些错误日志,可以诊断问题、定位错误原因,并据此采取相应的解决措施。
❑binlog, MySQL主从同步的媒介是binlog文件,该文件在事务被提交时,负责将SQL语句记录其中。
Server层主要维护两种类型的锁: 元数据锁 和 表锁 。
元数据锁的主要功能是确保表在并发访问过程中的安全性,能够锁定表结构信息以及表数据,从而避免在并发环境下发生数据冲突或不一致。具体而言,当表被打开时,系统会自动加上元数据读锁,以保护表结构的稳定;而当需要更改表结构时,则会加上元数据写锁,以确保修改操作的原子性和一致性。
注意 元数据锁的作用范围不仅限于表对象,它还能锁定schema、存储过程以及全局级别的对象,为数据库的整体并发控制和数据一致性提供强有力的支持。从实现层面来看,元数据锁主要是在MySQL Server层进行管理和控制的。
表锁指的是针对表对象的锁定机制,起初仅存在表锁,随后引入了元数据锁,目前采用表锁与元数据锁相结合的方式来实现。需要特别注意的是,表锁主要在Server层实现,而部分存储引擎也提供了自身的表锁实现方式。
存储过程相关主要包括:存储过程、函数、触发器和事件。
1.存储过程
这是一组可编程的函数集合,旨在完成特定的功能,它们以SQL语句集的形式存在。这些函数集经过编译后创建并存储在数据库中。用户可以通过指定存储过程的名称并提供必要的参数来调用并执行这些存储过程。存储过程的定义及其相关信息被保存在information_schema.ROUTINES表中,该表采用InnoDB作为存储引擎。因此,存储过程的定义实质上是存储在InnoDB存储引擎之下的。然而,存储过程的实际实现则位于Server层。
2.函数
MySQL具备自定义函数的能力,这些函数能够执行特定的SQL语句,并允许用户传入参数。当这些函数被执行时,其结果将直接反馈给客户端。此外,所有自定义函数的定义均存储在information_schema.ROUTINES表中,以便后续管理与查询。
3.触发器
可以通过创建触发器来监测数据库表的数据操作语言(Data Manipulation Language, DML)操作,例如,利用触发器实现表数据的同步处理。触发器的定义及其相关属性均被系统地存储在information_schema.TRIGGERS表中,以便用户查询和管理。
4.事件
MySQL中的定时器功能,类似于Linux系统中的crontab工具,允许用户在事件中编写SQL语句,并设置定时触发机制。这些事件的定义及其相关信息均被存储在information_schema.EVENTS表中,以便进行管理和查询。
MySQL支持多种内置函数,如sum和count等,这些函数在数据处理和分析中扮演着重要角色。然而,在实际业务场景中,有时需要扩展这些内置函数的功能以满足特定需求。MySQL在Server层定义了一套UDF(User Define Function,用户定义函数)的实现接口,该接口允许开发者根据需求自定义函数的逻辑。开发者只需遵循这套接口规范,并在接口内部实现自定义函数的具体逻辑。当MySQL服务启动时,就会自动加载这些自定义函数,使得它们可以在数据库操作中被调用。
在使用自定义函数之前,还需要通过CREATE FUNCTION语句在MySQL中正式创建这些函数。通过这种方式,开发者可以在MySQL中定制特定的特性和功能,并通过自定义函数来管理这些功能,从而提高数据库操作的灵活性和效率。
MySQL的复制主要分为 主从复制 和 组复制 两个大的阶段。
主从复制 是在Server层实现的,主要有以下模块:
❑binlog dump线程 。在主库侧启动,用于给所有的从库发送binlog日志。
❑slave io线程 。在从库测启动,用于接受主库侧发送过来的binlog日志。
❑slave sql线程 。在从库侧启动,用于解析binlog日志并应用到从库。
❑binlog cache 。用于缓冲binlog日志,在写入binlog文件前会先写入binlog cache中,然后再统一触发刷盘到binlog文件中。
❑semi-sync 。半同步插件,主要通过在主库事务提交时写binlog的流程中控制等待从库接收binlog完成从而实现半同步,semi-sync通过hook技术,在主库侧会开启一个ack receiver的线程。
组复制 作为一种分布式数据强一致同步方案,其核心基于类Paxos协议构建。它的实现机制与semi-sync相似,均通过插件形式实现。具体而言,在事务提交的流程中,利用hook机制来确保binlog已被成功发送至多数派的跟随者节点之后,领导者节点上的事务方能得以提交。这一设计确保了数据在分布式系统中的高度一致性与可靠性。
API层是指MySQL Server所定义的与存储引擎相关的接口集合。为了成功实现一个存储引擎,必须遵循Server层所抽象出的这些接口规范,包括但不限于以下示例接口:
❑prepare。 由于MySQL架构中明确区分了Server层与存储引擎层,因此事务的提交过程被设计为包含两个阶段:首先是准备(prepare)阶段,其次是存储引擎层的提交(commit)阶段。在准备阶段,主要进行的是Server层的操作,而存储引擎层在这一阶段也可以执行相关的操作,这些操作的具体逻辑可以在prepare阶段得到实现。这样的设计确保了事务的完整性和一致性。
❑commit。 事务的最终提交过程实际上发生在存储引擎层面。在提交逻辑中,包括事务状态的修改,例如修改成事务提交状态,然后可以释放事务中一些锁定的资源等。实际上在InnoDB存储引擎中,提交操作就包含前面描述的这些步骤。
❑rollback。 在进行事务回滚操作时,其核心环节在于存储引擎层的有效执行。具体而言,此过程旨在将已写入数据文件的数据恢复到先前的状态版本,相应的逻辑实现需被严谨地封装于指定方法之内。值得注意的是,Server层生成的binlog一旦完成磁盘写入操作,便无法再行回滚,原因在于Server层本身并未设计包含对binlog进行回滚的逻辑机制。因此,存储引擎层须具备兼容此逻辑的能力,以应对可能出现的异常情况。以InnoDB存储引擎为例,若在系统完成binlog写入并发生崩溃后重启,系统需自动执行扫描binlog的操作,以确保将相关事务的更改准确地补写回InnoDB存储引擎中,从而保证数据的一致性和完整性。
❑create。 在数据库系统中,实现创建表逻辑是一个严谨且关键的过程。以InnoDB存储引擎为例,这一过程涉及对数据字典信息的详尽记录。具体而言,表结构信息被精确无误地存储在数据字典中,以确保数据的完整性和可访问性。随后,为了高效地存储和检索数据,会创建索引结构,这些索引结构专门用于存储具体的数据记录,从而优化数据库的性能和响应速度。整个过程体现了数据库管理系统在数据处理上的严谨性、稳定性和高效性。
上述仅为几个常用的接口示例,对于更广泛的接口需求,建议感兴趣的读者查阅sql/handler.h文件中关于handlerto结构体的详细定义。此外,MySQL的内部文档也是获取此类信息的重要资源,可供参考。