MySQL · 源码分析 · 词法分析及其性能优化

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介: Table of Contents1. 简介2. 背景知识3. 查找树的实现3.1. 树的查找3.2. 树的产生4. 试试折半查找5. 总结简介MySQL 支持标准的 SQL 语言,具体实现的时候必然要涉及到词法分析和语法分析。早期的程序可能会优先考虑手工实现词法分析和语法分析,现在大多数场合下都会采用工具来简化实现。MySQL、PostgreSQL 等

简介

MySQL 支持标准的 SQL 语言,具体实现的时候必然要涉及到词法分析和语法分析。早期的程序可能会优先考虑手工实现词法分析和语法分析,现在大多数场合下都会采用工具来简化实现。MySQL、PostgreSQL 等采用 C/C++ 实现的开源数据库采用的是现代的 yacc/lex 组合,也就是 GNU bison/flex。其他比较流行的工具还有 ANTLR、JavaCC 等等。这些工具大多采用扩展的 BNF 语法,并支持很多定制化选项,使得语法比较容易维护和实现。MySQL 语法分析器的入口函数是 MYSQLparse(),词法分析器的入口函数为 MYSQLlex()。不过, MySQL 的词法分析器是手工打造的,并且为了提高关键字的查找效率做了针对性的优化。这个博客上有点介绍,建议在阅读代码之前先了解一下。

背景知识

MySQL 的语法分析器采用的工具是 bison,对应的语法文件是 sql/sql_yacc.yy。bison 处理语法文件的输出是 sql/sql_yacc.cc 和 sql/sql_yacc.h。对应的 sql/CMakeLists.txt 中有相关的 make 规则:

INCLUDE(${CMAKE_SOURCE_DIR}/cmake/bison.cmake)
RUN_BISON(
  ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy 
  ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc
  ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h
)

实际在 make 的时候,这个过程比较复杂。也可以单独 make 词法语法分析的部分,例如:

$ make -C sql gen_lex_token

阅读代码的时候,可以查找 MYSQLparse,以找到语法分析的代码路径。下面是清除掉生成的 yacc 代码再查找的结果:

$ make -C sql clean
$ grep --color=auto -rwIn MYSQLparse sql/
sql/sql_parse.cc:6748:extern int MYSQLparse(class THD *thd); // from sql_yacc.cc
sql/sql_parse.cc:6752:  This is a wrapper of MYSQLparse(). All the code should call parse_sql()
sql/sql_parse.cc:6753:  instead of MYSQLparse().
sql/sql_parse.cc:6858:  bool mysql_parse_status= MYSQLparse(thd) != 0;
sql/sql_parse.cc:6917:    Check that if MYSQLparse() failed either thd->is_error() is set, or an
sql/sql_lex.cc:3442:  parser before returning an error from MYSQLparse. If your

MySQL 手工打造的词法分析器对应的源代码文件是 sql/sql_lex.h 和 sql/sql_lex.cc。词法分析的入口函数是 MYSQLlex()。解析出一个 token 的函数为 lex_one_token()。词法分析出来的每个 token 都会对应一个语法分析器中的终结符,它们的字符串表示在 sql/lex.h 中。这些符号被分为两组,SQL 关键字以及 SQL 函数,在代码中对应数组 symbols[] 和 sql_functions[]。通常而言,在语法/词法分析过程中为了判断某个 token 是否为 SQL 的关键字,可以直接二分查找字符串数组。考虑到关键字列表是固定的一个集合,MySQL 对此作了专门的优化,用 Trie 树来进一步提高效率。下一节介绍这部分代码的实现。

查找树的实现

查找树的产生用的是一个独立的小程序 gen_lex_hash[.cc]。CMake 产生的 Makefile 规则为在文件 sql/CMakeFiles/sql.dir/build.make 中:

sql/lex_hash.h: sql/gen_lex_hash
  $(CMAKE_COMMAND) -E cmake_progress_report /home/x/mysql/CMakeFiles $(CMAKE_PROGRESS_153)
  @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --blue --bold "Generating lex_hash.h"
  cd /home/x/mysql/sql && ./gen_lex_hash > lex_hash.h

可以看到,它产生的代码在 sql/lex_hash.h 中。里头包含了两个大数组:sql_functions_map[] 和 symbols_map[],以及一个函数 get_hash_symbol()。

具体的实现自然分为两个部分,一个是产生树,另一个是查找产生的树。

树的查找

最主要的函数就是 get_hash_symbol(),它的调用和被调用关系为:

调用关系

注:上图是使用 Graphviz 产生的。

文件 gen_lex_hash.cc 的代码注释中有一个树的示例:

查找树的示
例

可以看出,根节点是按照字符串长度从小到大排序组织的。对于每种长度的字符串,要记录首字母和尾字母以及下一层节点的指针。中间节点除了是按照字符从小到大排序外,其它部分与根节点相同。叶子节点就是 symbols 数组的成员。树的查找就是一个自然的遍历过程。

树的产生

理解了上面的树的结构,就很好理解树的产生逻辑了。它的做法是读取关键字数组,产生一个原始的查找树(参看函数 generate_find_structs);然后,调整这个树,产生一个数组,也就是不用链表表示的树(参看函数 print_find_structs)。主要的函数和调用关系如下:

调用关系

其中:insert_symbols 处理的是 SQL 关键字,insert_sql_functions 处理的是函数名。get_hash_struct_by_len 处理的是树的根节点,insert_into_hash 处理的是树的内节点,递归执行。

为了更好的理解,可以在处理到输入数组不同位置时,查看当时对应的树。例如:

Table 1: 查找树的产生

img

试试折半查找

如果要验证一下这个优化与普通的折半查找的性能差异,需要做一些适当的修改才行。测试中用 perf 之类的工具会发现比较函数会成为热点。在修改代码时需要注意:
1. symbols、sql_functions 这两个数组不一定是按照顺序排列的,需要认真确认。
2. 查找符号时,字符串并没有以 ‘\0’ 结尾,做比较要注意。
3. 要修改的文件 sql/lex_hash.h 是自动产生的,需要用自己的代码替换其中的 get_hash_symbol 函数。

总结

本文是基于 MySQL 5.6 做的分析。可以看到 MySQL 对词法分析中的关键字查找热点做了性能改进。也可以发现代码的结构不是特别清晰,存在一些代码冗余和明显的可改进之处。 WL#8016: Parser for optimizer hints 在重构的过程中顺便将其改掉了。

Footnotes:

1 MySQL: Query Parsing <https://blog.imaginea.com/mysql-query-parsing/>
2 MySQL Download <http://dev.mysql.com/downloads/mysql/#downloads>
3 Graphviz <http://www.graphviz.org/Gallery.php>
4 WL#8016: Parser for optimizer hints <https://dev.mysql.com/worklog/task/?id=8016>
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
SQL 关系型数据库 MySQL
MySQL死锁及源码分析!
MySQL死锁及源码分析!
MySQL死锁及源码分析!
|
7天前
|
存储 缓存 负载均衡
mysql的性能优化
在数据库设计中,应选择合适的存储引擎(如MyISAM或InnoDB)、字段类型(如char、varchar、tinyint),并遵循范式(1NF、2NF、3NF)。功能上,可以通过索引优化、缓存和分库分表来提升性能。架构上,采用主从复制、读写分离和负载均衡可进一步提高系统稳定性和扩展性。
26 9
|
5月前
|
存储 关系型数据库 MySQL
MySQL数据库进阶第三篇(MySQL性能优化)
MySQL数据库进阶第三篇(MySQL性能优化)
|
2月前
|
存储 SQL 关系型数据库
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
MySQL调优主要分为三个步骤:监控报警、排查慢SQL、MySQL调优。 排查慢SQL:开启慢查询日志 、找出最慢的几条SQL、分析查询计划 。 MySQL调优: 基础优化:缓存优化、硬件优化、参数优化、定期清理垃圾、使用合适的存储引擎、读写分离、分库分表; 表设计优化:数据类型优化、冷热数据分表等。 索引优化:考虑索引失效的11个场景、遵循索引设计原则、连接查询优化、排序优化、深分页查询优化、覆盖索引、索引下推、用普通索引等。 SQL优化。
536 15
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
|
25天前
|
存储 关系型数据库 MySQL
MySQL性能优化实践指南
【10月更文挑战第16天】MySQL性能优化实践指南
39 0
|
25天前
|
存储 关系型数据库 MySQL
MySQL性能优化指南
【10月更文挑战第16天】MySQL性能优化指南
35 0
|
2月前
|
存储 关系型数据库 MySQL
mysql-性能优化(一)
mysql-性能优化(一)
|
2月前
|
关系型数据库 MySQL 数据处理
针对MySQL亿级数据的高效插入策略与性能优化技巧
在处理MySQL亿级数据的高效插入和性能优化时,以上提到的策略和技巧可以显著提升数据处理速度,减少系统负担,并保持数据的稳定性和一致性。正确实施这些策略需要深入理解MySQL的工作原理和业务需求,以便做出最适合的配置调整。
333 6
|
2月前
|
SQL 存储 关系型数据库
深入 MySQL 的执行计划与性能优化
深入 MySQL 的执行计划与性能优化
39 0
|
3月前
|
存储 关系型数据库 MySQL
"深入探索MySQL临时表:性能优化利器,数据处理的灵活之选"
【8月更文挑战第9天】MySQL临时表专为存储临时数据设计,自动创建与删除,仅在当前会话中存在,有助于性能优化。它分为本地临时表和全局临时表(通过特定逻辑模拟)。创建语法类似于普通表,但加TEMPORARY或TEMP关键字。适用于性能优化、数据预处理和复杂查询,需注意内存占用和事务支持问题。合理使用可大幅提升查询效率。
207 2

相关产品

  • 云数据库 RDS MySQL 版