1.前言
在当今数据驱动的时代,数据库系统作为信息存储和管理的核心组件,其性能和可靠性直接影响着应用的稳定性和用户体验。MySQL,作为最流行的开源关系型数据库管理系统之一,被广泛应用于各类互联网应用中。然而,许多开发者和数据库管理员对其内部机制知之甚少,特别是在事务处理和日志管理方面。
事务的ACID特性(原子性 Atomicity、一致性 Consistency、隔离性 Isolation 和持久性 Durability)是保障数据库操作可靠性和数据一致性的关键。为了实现这些特性,MySQL引入了多种日志机制,其中Undo Log、Redo Log和 Binlog 扮演着至关重要的角色。这些日志不仅确保了事务的正确执行和系统的高可用性,还为数据恢复和复制提供了坚实的基础。
本文将带您深入探讨MySQL中Undo Log、Redo Log和Binlog的核心机制,全面解析它们如何协同工作以保障事务的ACID特性。从查询优化到数据修改,我们将揭示这些日志在实际应用中的具体实现方式和作用机制,帮助您全面掌握MySQL事务管理的内部原理,提升数据库设计和运维的能力。
2.MySQL的架构
图片
从图中可以看出,MySQL 的架构可以大致划分为四个层次:连接层、服务层、存储引擎层和文件系统层。
- 连接层:负责对来自客户端的连接进行权限验证,并将连接信息存入连接池中,方便后续的连接复用。
- 服务层:主要负责 SQL 语句的解析与优化,还包括查询缓存和 MySQL 内置函数的实现。
- 存储引擎层:提供多种可插拔的存储引擎,允许我们通过不同的引擎进行数据的存取操作。存储引擎使得 MySQL 能够直接与硬盘上的数据和日志进行交互,用户可以根据需求选择合适的引擎。
- 文件系统层:这一层主要包括日志文件、数据文件及与 MySQL 相关的其他程序。在这四个层次中,服务层和存储引擎层构成了架构的核心。服务层负责处理 MySQL 的核心逻辑,而存储引擎层则直接负责数据的存取操作。
3.Server服务层的功能
3.1 查询解析与优化
- 语法解析:将客户端发送的 SQL 查询转化为可理解的内部结构,检查 SQL 语句是否符合 MySQL 语法规则。后续步骤的传递和处理就是基于这个结构的。
- 查询重写:服务层会将一些查询优化为等效的、执行效率更高的形式。
- 查询优化:MySQL 服务层有一个查询优化器,会基于数据统计信息,选择最优的执行计划。包括表的连接顺序、索引的选择等。
3.2 查询缓存
MySQL 服务层可以将一些查询的结果缓存起来,尤其是频繁执行的查询。如果客户端请求的查询已经存在于缓存中,MySQL 可以直接从缓存中返回结果,而无需重新执行查询。但是缓存在生产环境一般是不开启的,除非经常有SQL一模一样的查询。缓存严格要求2次SQL请求要完全一样。SQL语句,连接的数据库,协议版本,字符集等因素不一样,都会导致缓存失效。从8.0版本开始,MySQL不在使用缓存。
3.3 SQL 执行
在查询解析和优化之后,服务层负责将 SQL 语句执行。服务层会将解析后的查询计划传递给存储引擎层,存储引擎负责实际的数据操作。
4.Server服务层的核心组件
MySQL服务层有三个核心组件,分别是解析器,优化器,执行器。每个组件在查询的过程中分别代表着不同的角色,下面将分别介绍这三者的作用:
4.1 解析器
解析器是 SQL 查询执行的第一步,它的主要职责是将用户发送的 SQL 语句解析成数据库能够理解和处理的内部结构。这个过程包括以下几个子阶段:
- 词法分析:解析器首先对 SQL 语句进行词法分析。词法分析的任务是将 SQL 语句拆分成一系列有意义的“单词”或“标记”(Tokens),例如:表名,列名,操作符,关键字等。这个阶段的输出是一个由标记组成的列表,为后续的语法分析提供基础。
- 语法分析:语法分析的任务是根据 SQL 语法规则将标记组合成一个层次化的结构,通常是 解析树(Parse Tree) 或 抽象语法树(Abstract Syntax Tree)。解析树的每个节点代表了 SQL 语句的一个语法结构单元。在这一阶段,MySQL 会检查 SQL 语句的语法是否正确,并生成语法树。如果 SQL 语法不正确,解析器会报错并拒绝执行。
- 语义分析:语义分析主要是检查 SQL 语句中的每个对象是否符合数据库的实际情况。如表名、字段名是否存在,用户是否对相关表和列拥有执行权限,数据类型是否匹配等。
解析器将 SQL 语句从文本形式转化为数据库可以理解的内部表示。它完成了从词法、语法到语义的分析,并确保 SQL 语句符合语法和语义规则。
4.2 优化器
优化器的主要任务是对 SQL 查询进行优化,生成一个最优的执行计划,从而提高查询性能。优化器的工作基于查询的解析树和元数据,它会尝试在不同的查询执行策略中选择效率最高的一个。
4.2.1 逻辑优化
在这一阶段,优化器会进行一些逻辑层面的优化,主要目的是通过调整 SQL 语句的结构来提高查询效率。这些优化包括:
- 消除冗余的子查询:将某些子查询转换为连接或合并查询。
- 重写查询:比如将 OR 条件转换为 UNION 操作。
- 查询合并:将多个查询合并成一个查询。
- 移除不必要的操作:例如消除不需要的 ORDER BY 或 DISTINCT。
4.2.2 物理优化
优化器根据数据库的具体执行引擎、索引、统计信息等做出的决策。这个阶段会根据优化器评估的成本模型选择合适的执行计划。具体的优化措施包括:
- 选择合适的连接方式:比如选择 Nested Loop Join、Hash Join 或 Sort Merge Join。
- 选择索引:通过选择合适的索引来加速数据访问。
- 选择合适的排序方式:通过使用索引扫描或临时表来避免全表扫描。
优化器会使用基于成本的模型(Cost-Based Optimization)来评估每种查询执行计划的成本,选择成本最低的执行计划。其核心是通过计算不同执行计划的资源消耗(如 CPU 时间、I/O 操作等),并选出最优的执行策略。
优化器的目标是通过多种优化策略来降低查询的执行成本,生成一个尽可能高效的执行计划。它在逻辑层面和物理层面对 SQL 查询进行优化,以减少查询执行所需的资源。
4.3 执行器
执行器是 SQL 查询执行过程的最后一步,负责根据优化器生成的执行计划来实际执行 SQL 查询,返回查询结果或修改数据库的状态。
4.3.1 权限检查
在执行之前,执行器会首先检查用户是否有权限执行相应的操作。如果没有权限,则返回错误信息。
4.3.2 执行计划的执行
执行器根据优化器选择的执行计划一步步执行 SQL 操作。执行器的主要工作包括:
- 表扫描:根据查询条件决定是否使用索引、是否全表扫描。
- 连接操作:根据优化器选择的连接方式(如嵌套循环连接、哈希连接等)执行表之间的数据合并。
- 排序和聚合:执行查询中的 ORDER BY、GROUP BY 等操作。
- 数据返回:查询结果被返回给用户,修改操作则会提交事务。
4.3.3 事务管理
对于涉及数据修改的 SQL(如 INSERT、UPDATE、DELETE 等),执行器还需要管理事务的提交和回滚操作,确保数据的一致性和持久性。这些操作会与 MySQL 的日志系统(Undo Log、Redo Log、Binlog) 密切交互,确保事务的 ACID 属性。
执行器根据优化器生成的执行计划实际执行 SQL 查询,完成数据操作,返回查询结果或更新数据库状态。它是查询执行的最后环节,直接与 MySQL 的存储引擎进行交互。
5.MySQL查询语句的执行过程
MySQL一条SQL语句的执行过程可以大致分为以下几个步骤:
- 首先,通过连接器,客户端与MySQL服务器建立连接,并完成身份认证和权限验证过程。在此过程中,客户端需要提供用户名和密码以证明其合法性,服务器则会对这些信息进行核对。
- 检查是否开启缓存。MySQL 8.0之前,Query Cache 确实会缓存完全相同的查询结果,以便重复执行相同查询时直接返回缓存数据。然而,MySQL 8.0及以后版本已经完全弃用Query Cache,因此在MySQL 8.0及更高版本中这一步骤不在适用。
- MySQL的解析器会对查询语句进行解析,检查语法是否正确,并将查询语句转换为内部数据结构。预处理器则会根据MySQL的规则进一步检查解析树是否合法,如检查数据表或数据列是否存在等。
- 优化器会根据查询语句的结构、表的统计信息等因素,生成多个可能的执行计划,并通过成本估算器选出最优的执行计划。这一步旨在提高查询效率,降低资源消耗。
- 执行器按照优化器选择的执行计划,调用存储引擎的API来执行查询。存储引擎负责实际的数据存储和检索,根据执行器的请求,读取或写入数据。
- 存储引擎负责实际的数据存储和检索工作,根据执行器的请求,读取或写入数据。
- 如果开启了Query Cache且查询结果能够命中缓存,查询结果会从缓存中直接返回。而如果没有开启Query Cache或缓存没有命中,MySQL会直接返回查询结果。
6.MySQL修改语句的执行过程
我们简单列举一条SQL为例 update table set name=“张三” where id = 10。具体的还行流程如下图:
图片
- 找存储引擎取到 id = 10 这一行记录。
- 根据主键索引树找到这一行,如果 id = 10 这一行所在的数据页本来就在内存池(Buffer Pool)中,就直接返回给执行器;否则,需要先从磁盘读入内存池,然后再返回。
- 记录Undo Log日志,对数据进行备份,便于回滚。
- 拿到存储引擎返回的行记录,把 name 字段设置为 “张三”,得到一行新的记录,然后再调用存储引擎的接口写入这行新记录。
- 将这行新数据更新到内存中,同时将这个更新操作记录到 Redo Log 里面,为 Redo Log 中的事务打上 prepare 标识。然后告知执行器执行完成了,随时可以提交事务。
- 生成这个操作的 Binlog,并把 Binlog 写入磁盘。
- 提交事务。
- 把刚刚写入的 Redo Log 状态改成提交(commit)状态,更新完成。
以上只是一个简单的case,方便我们能够简单的熟悉流程。接下来,我们对update过程中的全流程进行梳理,具体的流程如下图:
图片
- 首先客户端发送一条 SQL 语句到 Server 层的 SQL interface。
- SQL interface 接到该请求后,先对该条语句进行解析,验证权限是否匹配,也就是在我们上文中讲到的执行器中在执行。
- 验证通过以后,分析器会对该语句分析,是否语法有错误等。
- 接下来是优化器生成相应的执行计划,选择最优的执行计划,然后是执行器根据执行计划执行这条语句。
- 执行器从Buffer Pool中获取数据页的数据,如果数据页没有,需要从磁盘中进行加载。
- 开启事务,修改数据之前先记录Undo Log,写入Buffer Pool的Undo Page。
- 开始更新数据页中的记录,被修改的数据页称为脏页,修改会被记录到内存中的 Redo Log Buffer中,再刷盘到磁盘的Redo Log文件,此时事务是 perpare阶段。
- 这个时候更新就完成了,当时脏页不会立即写入磁盘,而是由后台线程完成,这里会用double write来保证脏页刷盘的可靠性。
- 通知Server层,可以正式提交数据了, 执行器记录Binlog cache,事务提交时才会将该事务中的Binlog刷新到磁盘中。
- 这个时候Update语句完成了Buffer Pool中数据页的修改、Undo Log、Redo Log缓存记录,以及记录Binlog cache缓存。
- commit阶段,这个阶段是将Redo Log中事务状态标记为commit。
- 此时Binlog和Redo Log都已经写入磁盘,如果触发了刷新脏页的操作,先把脏页copy到double write buffer里,double write buffer 的内存数据刷到磁盘中的共享表空间 ibdata,再刷到数据磁盘上数据文件 ibd。
以上就是修改语句的全部流程,可以看到,上图中涉及刷盘的操作本初没有详细讲解,接下来,将结合三大日志,详细讲解刷盘相关操作。
7.Undo Log、Redo Log和Binlog日志实现事务的ACID特性
7.1 事务的ACID特性概述
在深入理解Undo Log、Redo Log和Binlog之前,首先需要明确事务的ACID特性,这些特性是确保数据库操作可靠性和一致性的基石。
- 原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失败,不会出现部分完成的状态。
- 一致性(Consistency):事务的执行必须使数据库从一个一致性状态转变到另一个一致性状态,确保数据的完整性。
- 隔离性(Isolation):指在多事务并发执行时,一个事务的操作对其他事务的影响程度。它确保事务之间的操作是相互独立的,避免并发带来的数据不一致问题。
- 持久性(Durability):一旦事务提交,其对数据库的修改是永久性的,即使系统发生故障也不会丢失。
7.2 Undo Log(回滚日志)
Undo Log记录了事务在修改数据之前的原始状态,用于在事务回滚时撤销未完成的修改,确保事务的原子性和隔离性。
Undo Log主要的功能有两个:事务回滚和MVCC。
首先来说事务回滚,事务如何通过Undo Log进行回滚操作呢?其实很简单,只需要在Undo Log日志中记录事务中的反向操作即可,发生回滚时直接通过Undo Log中记录的反向操作进行恢复。例如:
- 事务进行insert操作,Undo Log记录delete操作。
- 事务进行delete操作,Undo Log记录insert操作。
- 事务进行update操作(value1 改为value2 ),Undo Log记录update操作(value2 改为value1 )。
Undo Log 保存的是一个版本的链路,使用roll_pointer这个字段来连接的。多个事务的Undo Log 日志组成了一个版本链,如图:
图片
在上图中:
- trx_id代表事务id,记录了这一系列事务操作是基于哪个事务。
- roll_pointer代表回滚指针,就是当要发生rollback回滚操作时,就通过roll_pointer进行回滚,这个链表称为版本链。在事务执行过程中,如果发生错误或主动回滚,Undo Log 会将数据恢复到事务开始前的状态,确保事务的所有操作要么全部完成,要么全部撤销,从而实现原子性。
为了 提升 Undo Log 读写性能, Undo页还存在于Buffer Pool中,因为Buffer Pool 是 InnoDB 的内存缓存,用于存储数据页和索引页,以便快速访问。它通过减少磁盘 I/O,提高数据库的整体性能。将Undo页放在缓存中,可以加速事务的回滚和数据恢复。
当事务commit之后,不会立即删除,会保留至所有快照读完成。后续会通过后台线程中的Master Thread或Purge Thread进行Undo Page的回收工作。
再说MVCC,实现了自己 Copy-On-Write思想提升并发能力的时候, 也需要数据的副本,如上图,既然已经存在了这么多Undo Log的副本,那么MVCC可以直接复用这些副本数据。
所以,Undo Log中的副本,可以用于实现多版本并发控制(MVCC),提升事务的并发性能,同时每一个事务操作自己的副本,实现事务的隔离性。
实现MVCC主要通过三个元素,一个是我们上面已经提到的Undo Log版本链,一个是readView,最后就是我们上面已经提到的这些字段。因为整个课题比较大,在这里就不在过多的赘述。
7.3 Redo Log(重做日志)
Redo Log记录了事务对数据的修改操作,用于在系统崩溃后恢复已提交事务的修改,确保事务的持久性。
7.3.1 Redo Log 日志执行过程
当一个更新事务到达时,Redo Log的处理过程如下:
- 首先,从磁盘读取原始数据到Buffer Pool(内存中),然后在内存中对数据进行修改,修改后的数据将被标记为脏页。
- 接着,系统会生成一系列重做日志,并将其写入Redo Log Buffer,日志内容记录的是数据修改后的新值。
- 当事务提交(commit)时,Redo Log Buffer中的内容会被刷新到Redo Log File中,并采用追加写的方式将日志记录写入文件。
- 定期会将内存中修改过的数据刷新回磁盘,以保证数据持久性。
Redo Log利用WAL(Write-Ahead Logging)机制来保证故障恢复的安全性(crash-safe)。
WAL的核心思想是先写日志,再写磁盘。根本原因是机械磁盘的性能,日志是顺序写,而数据页是随机写。顺序写的性能更高,所以先把日志归档。具体来说,当缓存页被修改(即变成脏页)后,相关的操作会先记录到Redo Log Buffer中。在事务提交(commit)时,后台线程会将Redo Log Buffer中的内容刷新到磁盘上(事务提交是Redo Log默认刷盘的时机)。此时,虽然脏页还没有写回磁盘,但只要Redo Log成功写入磁盘,就可以认为此次修改操作已完成。这是因为,即使发生故障导致脏页丢失,我们也可以通过磁盘上的Redo Log来恢复数据。因此,Redo Log与Undo Log的配合作用如下:
- 若事务在提交前发生崩溃,则可以通过Undo Log回滚事务。
- 若事务在提交后发生崩溃,则可以通过Redo Log来恢复事务。
7.3.2 Redo Log日志文件写入机制
Redo Log采用固定大小并循环写入的方式,类似环形缓冲区。当日志文件写满时,会从头开始覆盖之前的内容。这样的设计是因为Redo Log记录的是数据页的修改,而一旦Buffer Pool中的数据页被刷写到磁盘,之前的Redo Log记录就不在有效。新的日志会覆盖这些过时的记录。此外,硬盘上的Redo Log文件并非单一存在,而是以文件组的形式存储,每个文件的大小都相同。
图片
在写入数据的同时,也需要执行擦除操作。Redo Log成功刷盘到磁盘后,才可以进行擦除。因此,我们使用两个指针来管理这一过程:
- write pos:表示当前日志记录写入的位置,即当前Redo Log文件已写到哪里。
- checkpoint:表示当前可以擦除的位置,即Redo Log文件中哪些记录已不在需要,可以被新的日志覆盖。
图片
在图示中,黄色部分表示已写入完成的区域,而绿色部分则代表空闲区域。
write pos和checkpoint指针之间的绿色区域,表示剩余的可写入空间,即Redo Log文件的空闲/可用部分。
当进行数据页刷盘操作(checkpoint)时,checkpoint指针会顺时针移动,覆盖掉已写入的黄色区域,使其变为绿色。
当write pos追赶上checkpoint时,意味着Redo Log文件已满,此时必须强制执行checkpoint操作,刷新Buffer Pool中的脏页并将其写入磁盘。随后,checkpoint指针会被移动,这样就可以继续向Redo Log文件中写入新的数据。
7.3.3 Redo Log Buffer刷盘策略
Redo Log Buffer刷盘策略主分为三种,由核心参数innodb_flush_log_at_trx_commit来控制。具体的流程如同下图:
图片
- innodb_flush_log_at_trx_commit=0
刷盘时机:InnoDB每秒钟会将日志缓存(Redo Log Buffer)刷新到磁盘,而不是在每次事务提交时进行刷新。即便事务已提交,日志仅会写入内存中的日志缓冲区,1秒钟后由后台线程将其写入磁盘。
持久性:如果发生数据库崩溃,可能会丢失最近1秒内的事务数据。
性能:这种方式提供最高的性能,因为磁盘写入的频率最小,但相应的持久性较弱。
图片
- innodb_flush_log_at_trx_commit=1(默认设置)
当设置为1时,表示每次事务提交时,都会执行刷盘操作。这意味着在默认配置下,系统提供高可靠性,但性能较低。
刷盘时机:每当事务提交时,InnoDB会将日志缓冲区的内容写入到文件系统缓存中,并立即使用fsync将其刷新到磁盘。
持久性:这种设置提供了最高级别的持久性,确保每次事务提交后日志已持久化到磁盘。如果发生崩溃,最多会丢失尚未提交的事务。
性能:性能较差,因为每次事务提交都需要进行磁盘写入操作。在高并发写入的环境中,频繁的磁盘I/O可能会成为系统的性能瓶颈。
- innodb_flush_log_at_trx_commit=2
当设置为2时,表示每次事务提交时,InnoDB仅将Redo Log缓冲区的内容写入操作系统的文件系统缓存(Page Cache),而实际的磁盘刷盘操作由操作系统来负责。
刷盘时机:在每次事务提交时,InnoDB会将日志缓冲区的内容写入操作系统的文件系统缓存(Page Cache),此时并不会执行fsync操作,日志的实际写入磁盘由操作系统决定。
持久性:如果数据库发生崩溃,但服务器没有崩溃,数据不会丢失。如果服务器也发生崩溃,Page Cache默认保留最近5秒的数据,最多丢失最近5秒内的事务。但与innodb_flush_log_at_trx_commit=0不同,日志至少会写入文件系统缓存,这为数据安全性提供了一定保障。
性能:性能较高,因为每次事务提交时,只需将日志写入操作系统的内存缓存,而不需要立即执行磁盘I/O操作,从而减少了磁盘操作的频率。
以上三种策略中:
- 如果是对数据安全性要求比较高的场景,则需要将参数设置为1,因为1的安全性最高。
- 如果是在一些可以容忍数据库崩溃时丢失 1s 数据的场景,我们可以将该值设置为 0,这样可以明显地减少日志同步到磁盘的 I/O 操作。
- 如果是需要安全性和性能折中的方案,可以将参数设置为2,虽然参数 2 没有参数 0 的性能高,但是数据安全性方面比参数 0 强,因为参数 2 只要操作系统不宕机,即使数据库崩溃了,也不会丢失数据,同时性能方便比参数 1 高。
7.4 Binlog(二进制日志)
Binlog是MySQL特有的一种日志机制,记录了所有导致数据库状态变化的操作(如INSERT、UPDATE、DELETE)。Binlog主要有两个功能:
- 备份恢复,实现崩溃一致性(Crash Consistency)。原理是每次事务进行提交时,都会将增、删、改操作以追加的方式记录到Binlog文件中。
- 主从复制,实现 主从复制的一致性。原理是主从复制常用于MySQL主从集群搭建,MySQL从节点通过监听主节点Binlog日志进行同步即可。
7.4.1 Binlog的刷盘时机
事务执行过程中,先把日志写到 Binlog Cache(Server 层的 Cache),事务提交的时候,再把 Binlog Cache 写到 Binlog 文件中。由于一个事务的Binlog日志必须作为一个整体写入,不能拆分,不论事务多大,都会确保日志在一次操作中完整写入。因此,系统为每个线程分配了一个块内存用于存储Binlog Cache。尽管每个线程有自己的Binlog Cache,最终这些日志都会写入到同一个Binlog文件中。
图片
- 图中的 write,指的就是指把日志写入到 Page Cache ,但是并没有把数据持久化到磁盘,因为数据还缓存在文件系统的 Page Cache 里,write 的写入速度还是比较快的,因为不涉及磁盘 I/O。
- 图中的 fsync,才是将数据持久化到磁盘的操作,这里就会涉及磁盘 I/O,所以频繁的 fsync 会导致磁盘的 I/O 升高。
MySQL提供了一个sync_binlog参数,用于控制Binlog日志写入磁盘的频率:
- sync_binlog = 0:每次事务提交时,只进行写操作(write),不执行fsync,具体何时将数据持久化到磁盘由操作系统决定。
- sync_binlog = 1:每次事务提交时,先进行写操作(write),然后立即执行fsync,确保日志被持久化到磁盘。
- sync_binlog = N(N > 1):每次事务提交时只执行写操作(write),但累积N个事务后才会执行fsync,将日志持久化到磁盘。在MySQL中,默认的sync_binlog设置为0,意味着没有强制性的磁盘刷新操作,这样可以获得最佳的性能,但也伴随较高的风险。如果操作系统发生异常重启,尚未持久化到磁盘的Binlog数据将会丢失。
当sync_binlog设置为1时,系统提供最强的安全性,确保即使发生异常重启,也最多丢失一个事务的Binlog,而已经持久化的数据不会受到影响。然而,这种设置对性能的影响非常大。
如果能够接受少量事务Binlog丢失的风险,并希望提高写入性能,一般可以将sync_binlog设置为100到1000之间的某个值,从而在性能和安全性之间找到平衡。
8 总结
通过本文的深入解析,我们全面了解了MySQL中Undo Log、Redo Log和Binlog三大日志机制,以及它们在保障事务ACID特性中的关键作用。
- Undo Log:通过记录数据修改前的状态,确保事务在发生错误或回滚时能够恢复到初始状态,保障了原子性和隔离性。
- Redo Log:通过记录事务的修改操作,确保即使在系统崩溃后,已提交的事务也能被恢复,保障了持久性。
- Binlog:作为MySQL特有的二进制日志,支持数据复制和恢复,进一步增强了数据的持久性和系统的可扩展性。理解和掌握这些日志机制,不仅有助于优化数据库性能,提升事务处理效率,还能在面对系统故障时快速进行数据恢复,确保业务的连续性和数据的安全性。同时,这些知识也为构建高可用、高性能的数据库架构提供了坚实的理论基础。
在实际应用中,合理配置和管理这些日志机制,结合具体业务需求进行优化,是每位数据库管理员和开发者需要掌握的重要技能。希望通过本文,您能够对MySQL的核心机制有更深刻的理解,并在实际工作中灵活运用,为构建稳定可靠的数据库系统贡献力量。
关于作者:朱洪旭 侠客汇Java开发工程师
参考资料
[1] 《MySQL实战45讲》
[2] https://zhuanlan.zhihu.com/p/451007506
[3] https://zhuanlan.zhihu.com/p/667283776