简介

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: 查找树的产生

试试折半查找

如果要验证一下这个优化与普通的折半查找的性能差异,需要做一些适当的修改才行。测试中用 perf 之类的工具会发现比较函数会成为热点。在修改代码时需要注意:

symbols、sql_functions 这两个数组不一定是按照顺序排列的,需要认真确认。

查找符号时,字符串并没有以 ‘\0’ 结尾,做比较要注意。

要修改的文件 sql/lex_hash.h 是自动产生的,需要用自己的代码替换其中的 get_hash_symbol 函数。

总结

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

Footnotes:

mysql源码分析_MySQL · 源码分析 · 词法分析及其性能优化相关推荐

  1. mysql udf提权_MySQL日志安全分析技巧

    常见的数据库攻击包括弱口令.SQL注入.提升权限.窃取备份等.对数据库日志进行分析,可以发现攻击行为,进一步还原攻击场景及追溯攻击源. 0x01 Mysql日志分析 general query log ...

  2. linux mysql 释放x锁_MySQL 加锁处理分析-转载

    背景 MySQL/InnoDB的加锁分析,一直是一个比较困难的话题.我在工作过程中,经常会有同事咨询这方面的问题.同时,微博上也经常会收到MySQL锁相关的私信,让我帮助解决一些死锁的问题.本文,准备 ...

  3. 阿里云 mysql日志分析_mysql 慢日志分析-阿里云开发者社区

    启用 slow log 有两种启用方式: 1, 在my.cnf 里 通过 log-slow-queries[=file_name] 2, 在mysqld进程启动时,指定--log-slow-queri ...

  4. mysql group 更新递增_MySQL Group Replication在网易使用和优化实践

    本文由作者授权网易云发布,未经许可,请勿转载 作者:温正湖,网易数据库技术专家 MGR(MySQL Group Replication)是MySQL官方推出的领先的服务高可用和数据高可靠方案,网易从2 ...

  5. 基于英特尔® 优化分析包(OAP)的 Spark 性能优化方案

    简介: Spark SQL 作为 Spark 用来处理结构化数据的一个基本模块,已经成为多数企业构建大数据应用的重要选择.但是,在大规模连接(Join).聚合(Aggregate)等工作负载下,Spa ...

  6. Elasticsearch 技术分析(七): Elasticsearch 的性能优化

    硬件选择 Elasticsearch(后文简称 ES)的基础是 Lucene,所有的索引和文档数据是存储在本地的磁盘中,具体的路径可在 ES 的配置文件../config/elasticsearch. ...

  7. MYSQL POLARDB 学习系列之 拆解 POLARDB 6 Auto-Scaling 与性能优化 (翻译)

    ,最近问 POLARDB 的同学同学是越来越多,准备开一个群,专门和大家一起学习 POLARDB  for MYSQL 的数据库相关知识和PG ,MYSQL等数据库,分享相关的经验,和大家相互学习. ...

  8. mysql获取时间戳_服了!阿里Mysql三位封神专家总结1200多页性能优化的千金良方...

    MYSQL(关系型数据库管理系统) MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品.MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应 ...

  9. mysql源码安装分析_MySQL源码分析(0):编译安装及调试(转)

    编译安装 为了实现MySQL的更高级别的性能调优,我们通常需要理解其内部实现机制,并对其进行优化调试.在下面的系列中,我们会分别介绍MySQL的部分内部实现机制. 首先我们介绍如何从源代码部署一台My ...

  10. mysql 源码 缓存_MySQL源码:MYSQL存储过程/函数的分析原理及缓存机制

    前言:我个人认为,有关MYSQL存储过程/函数在MYSQL中的实现比较粗糙,可扩展性不够好,其实现的耦合性太高,所以主要讲一些它的原理方面的内容,但有可能在某些方面理解不够好或者有些不正确的地方,欢迎 ...

最新文章

  1. oracle 删除用户、表空间
  2. 页面导航的两种方式——声明式导航、编程式导航||vue-router编程式导航||router.push() 方法的参数规则
  3. oracle19c怎么创建Scott,Oracle db-sample-schema-19c安装(scott hr oe pm ix sh bi用户创建部署)...
  4. jooq权限配置_将jOOQ与Spring结合使用:配置
  5. TypeScript中的class声明了什么
  6. C#编程(三十三)----------Array类
  7. A*B Problem II
  8. 用于敏捷开发的免费 UML 工具 2022
  9. Win10系统下怎么将普通账户设置为管理员账户
  10. 小白学python——程序结构
  11. 稳定状态模型 (三):Volterra 模型
  12. win10+hexo+github搭建个人博客
  13. 【华为OD统一考试B卷 | 100分】按身高和体重排队(C++ Java JavaScript Python)
  14. 鸿蒙系统敏感应用,鸿蒙系统特性“揭晓”!一次开发灵活使用,生态构建难题被解决?...
  15. 2018 拼多多 校招真题 最大乘积
  16. Cannot get property '......' on extra properties extension as it does not exist
  17. 《工程电磁场》学习笔记2-恒定电场
  18. 俄罗斯方块的源码实现
  19. 【统计学习方法】朴素贝叶斯
  20. Rust的面向对象(五)——面向对象

热门文章

  1. [IOS开发教程] IOS中用NSJSONSerialization来实现对JSON格式的解析
  2. 带你用Python制作一个经典小游戏:扫雷
  3. VS2019配置SFML环境保姆级教程
  4. 无USB下如何实现PC电脑与手机无线连接
  5. 岁月划过生命线(大二.上)
  6. 矩阵向量求导(Vector derivation)
  7. [转载]蝈蝈学拳笔记-3-6-吴式太极的步法等
  8. 《遥感原理与应用》笔记/期末复习资料
  9. DefaultSerializer requires a Serializable payload but received an object of type [reggie.common.R]
  10. 手把手教你本地如何查看Android源码