MySQL 的解析器以及 MySQL8.0 做出的改进 | StoneDB技术分享 #2

news2024/9/25 13:25:39

 

设计:小艾

审核:丁奇

编辑:宇亭

作者:柳湛宇(花名:乌淄)
浙江大学-软件工程-在读硕士、StoneDB 内核研发实习生

一、MySQL 的解析器

MySQL 所使用的解析器(即 Lexer 和 Parser 的组合)是嵌入了 C/C++语言的 yacc/lex 组合,在 linux/GNU 体系上,这一组合的实现是 GNU Bison/Flex,即 Flex 负责生成 tokens, Bison 负责语法解析。

对于 Bison,请参阅[1]

Bison 本是一个自底向上(Bottom-Up)的解析器,但是由于历史原因,MySQL 语法编写的规则是以自顶向下(Top-Down)的,这将会产生一些问题,我们首先简要介绍这两种解析模式。

二、自底向上与自顶向下解析模式

更多详细讲解,请参阅[2]

当我们在谈论自底向上和自顶向下两种解析模式时,局面是我们手上已经有了编写完成的语法规则和将输入语句词法解析完成后的 token 数组,而之后的任务总体上就是构建语法解析树。

以下 yacc 语法约束和匹配序列(「例 1」)用于展示两种解析模式的不同。

exp1:
 'a' 'b' | 'b' 'c';
exp2:
 'x' 'y' 'z' | 'a' exp3;
exp3:
 'c' 'd' | exp1 'd';

a b c d作为输入序列。

自底向上(Bottom-Up)解析模式

自底向上的解析模式类似于进行「拼图」。对每一个入栈后 token 组成的序列,都尽可能尝试将其规约(reduce)成一个语法规则中规定的表达式并将新的表达式压栈。在达到 token 数组末尾时,栈中的表达式应且仅应匹配一个顶层表达式,如果因为规约顺序不符合实际表达式顺序而无法匹配到顶层表达式,则应当进行回溯并尝试新的规约选择。

对于例 1,自底向上解析模式的解析步骤为:

  • a不能被规约(没有可以匹配a的表达式子项)

  • a b可以被规约:

    • exp1 c d被规约为exp1 exp3

    • exp1 exp3无法被规约

    • 达到序列末尾,需要回溯

    • a b规约为exp1

    • exp1 c无法被规约

    • exp1 c d可以被规约:

    • 因此,exp1 c d无法被规约

    • 达到序列末尾,需要回溯

  • 因此,a b无法被规约

  • a b c可以被规约:

    • a exp1 d可以被规约为a exp3

    • a exp3可以被规约:

    • a exp3可以被规约为exp2

    • 「达到序列末尾,」a b c d「成功匹配表达式」exp2

    • a b c可以被规约为a exp1

    • a exp1 d可以被规约:

自顶向下(Top-Down)解析模式

自顶向下的表达式类似于「多叉树的先序遍历」。对于给定的每一个 token 子序列,都尝试断言(Assertion)其匹配一个表达式,并进一步递归地考察:

1.这个序列是否能通过断言匹配该表达式的子选项;
2.断言匹配子选项后,其对应改规则可归约的子串是否匹配子选项中的表达式。

每当断言失败时,同样进行回溯,来尝试匹配不同的表达式或表达式内不同的子选项,直至构建正确的语法解析树或匹配失败而报错。

对于例 1,自顶向下解析模式的解析步骤为:

  • 假设(此处的原语是断言,Assertion)a b c d匹配exp1的第一个子选项'a' 'b'

  • 断言错误,因此排除这一选项;

  • 同样地,显然可以排除exp1的第二个子选项'b' 'c'exp2的第一个子选项'x' 'y' 'z',此处省略这些步骤;

  • 假设a b c d匹配exp2的第二个子选项'a' exp3

    • 应有b c匹配exp1

    • 假设b c匹配'a' 'b'

    • 断言错误,排除这一选项

    • 假设b c匹配'b' 'c'

    • 「断言正确且无子表达式,匹配成功,」a b c d「匹配」exp2

    • 应有b c d匹配exp3

    • 假设b c d匹配'c' 'd'

    • 断言错误,排除这一选项

    • 假设b c d匹配exp1 'd'

二者的对比与 MySQL 面临的问题

可以看到,自底向上解析模式更符合计算机程序的风格,其将规约操作提前,在后半部分执行匹配和回溯动作。但其缺点在于,每一次匹配和回溯的触发点都仅仅在达到 token 数组末尾时进行,因此如果没有优先级约束,每次有效回溯的代价都较大。

自顶向下的解析模式更符合人类阅读和编写语法文件的习惯,其将断言和回溯动作提前,将实际的匹配动作置于解析的后半段。这样的模式缺点在于,它需要回溯的次数更多,同时语法愈发复杂,如果没有合适的断言顺序(实际上对于不同的 SQL 语句,最优的断言顺序也不尽相同),就会有更多冗余的比较分支和更深的有效回溯长度。

由于 MySQL 因历史原因选择了易读的自顶向下的解析模式,其在语法解析时,会产生二义性带来的两种冲突(conflict):移位/规约(shift/reduce)冲突和规约/规约(reduce/reduce)冲突,而使用自底向上解析模式的 posgres[3]则不会产生这两种冲突。

三、移位/规约冲突与规约/规约冲突

两种操作

首先简要介绍自底向上分析方法的移位(shift)和规约(reduce)操作。按自底向上的解析模式,解析器对输入符号串从左到右扫描,读取输入并与语法规则比较,其中:

  • 移位操作是将符号从输入流转入分析栈中的操作。如果当前输入与语法规则匹配,解析器就将当前输入移入(shift)语法栈中,并继续尝试处理下一个符号。简单演示见下例 2:

对于如下语法定义:

simpleStrSeq:
 'a' 'b' 'c' | 'e' 'f' 'g';

处理输入串a b c时,处理前两个 token 时都会将其直接放入语法栈,因为它们匹配simpleStrSeq表达式。

  • 规约操作是将语法栈上的一部分内容替换为相应的非终结符的操作。当解析器发现输入与语法规则的右侧匹配时,它可以执行归约操作,将右侧的符号替换为对应的非终结符。简单演示见下例 3:

对于如下语法定义:

%type<int> num
%%
product:
num '*' num;
plus:
product '*' product;
%%

处理输入串1 * 2 + 3 * 4时,在处理到符号2时,会将语法栈中现有的1 * 2规约(reduce)为product,进一步地,会在处理到4时将3 * 4规约为product,将product + product规约为plus

两种冲突

上述的移位和规约操作是针对自底向上范式提出的,因此使用自顶向下顺序编写语法约束,就会产生移位/规约冲突与规约/规约冲突:

  • 移位/规约冲突:移位/规约冲突指当解析器处理一个符号时,它既可以进行移位(shift)操作,将符号部分或完全匹配一个表达式,同时也可以进行规约(reduce)操作,将当前语法栈内的内容联合输入替换成表达式。简单演示见下例 4:

对于如下语法定义:

%type<int> num
%%
numToken:
 numToken '+' numToken | num;
%%

当处理输入1 + 2 + 3时,处理到符号2时,解析器既可以仅仅将其视作numToken的第二个子选项,移入(shift)语法栈,也可以将其与语法栈中部分内容结合组成1 + 2,匹配成为一个numToken表达式。因此,这个输入合法语法树(指最终结果只有一个顶层表达式)就有 2 个:

  • 规约/规约冲突:规约/规约冲突是在解析器在遇到一个输入符号时,存在多个可以进行归约操作的情况。这种冲突通常在文法规则中存在二义性或相似的产生式时发生。简单演示见下例 5:

对于如下语法定义:

%type<int> num
%%
numToken:
 numToken '+' numToken |  numToken '*' numToken | num;
%%

当处理输入1 + 2 * 3时,解析器既可以将2其视作numToken的第 1 个子选项的后半部分规约为加法,也可以将其视作numToken第 2 个与子项的前半副本,规约为乘法。因此,这个输入合法语法树(指最终结果只有一个顶层表达式)就有 2 个:

MySQL 中的语法冲突

我们之前提到,由于历史原因和可读性考虑,MySQL 的 yacc 语法文件采用自顶向下的编写方式,它引入了上述两种语法冲突。产生冲突的原因是,自顶向下的解析方法需要层层进行断言与子表达式的匹配,而在更顶层的子表达式无法在实际上以自底向上执行的 Bison 解析器中直接确定匹配选项。

这意味着语法冲突并不总是意味着语句的二义性而导致解析失败(对于确实需要指定关联性和优先级的操作符,MySQL 也对它们进行了%left%right%nonassoc),事实上 MySQL 的问题是广泛存在的 shift/reduce 冲突引起的断言失败数量增加,进而使得解析时间变长。

正如我们从上图中看到的,MySQL 各个版本中都有相当数量的 shift/reduce 冲突,但除了图中显示的 MySQL 4.0 中存在的 4 个会导致解析二义性的 reduce/reduce 冲突[4],shift/reduce 冲突不会使得解析器最终得到正确的结果,因此 MySQL8.0 的态度是:

1.We do not accept any reduce/reduce conflicts
2.We should not introduce new shift/reduce conflicts any more.

四、MySQL 8.0 对语法约束的改进

从上图中可以看到,MySQL 8.0 版本降低了语法文件中的 shift/reduce 冲突数量,且随着版本不断更新,目前这一冲突数量已下降到了 63[5‍](通过语法文件中的%expect语句显式声明)。

MySQL 8.0 做出了很多努力来达到这一成果。其中最关键一点在于对 query 语句整体格式的重构。MySQL 8.0 以前,相同的语法结构(如 create select 和 select 语句都是用的参数列表,select、update 和 delete 语句中都需要使用的 table 列表等)会直接被不同类型的语句直接引用,而没有做额外的约束。

在 MySQL 8.0 中,它同意了所有语句的语法结构,将共用的子结构段进行了进一步的约束和封装,这使得自顶向下的断言可以更快地匹配到对应的语法,同时也能体现于结构上的简洁性。

以下是 MySQL 5.7 到 MySQL 8.0 上层语法结构对比一览:

可以看出 MySQL 8.0 使得整体架构更加清晰有序。

同时,8.0 将部分只有一处定义的语法结构展开到上层结构的子选项中,这样的操作以增加边缘功能的代码行数、降低可读性为代价减少了 shift/reduce 冲突。此外,MySQL 8.0 通过显示定义两个伪 token:%left KEYWORD_USED_AS_IDENT%left KEYWORD_USED_AS_KEYWORD来显式地声明对以关键字作为标识符的行为,减少了解析过程中二义性因其的断言失败。

结论

从整体上看,关系数据库系统对于典型的 SQL 语句在语法解析阶段的耗时很短,几乎可以忽略不计,因此 MySQL 维持其自顶向下解析结构以获得语法文件的可读性和可扩展性是可以理解的。我们可以看到 MySQL 8.0 并没有对将语法解析模块更改成类似 Posgres 那样 LALR 的模式以消除语法冲突,而是尽可能地将语法树表达的更加简洁,进而使其对基于 MySQL 语法进行扩展和兼容的开发者更加友好。

参考资料


  1. https://www.gnu.org/software/bison/manual/bison.html

  2. https://qntm.org/top

  3. https://github.com/postgres/postgres/blob/47556a0013fa64d44add2760577d49cf2eca4cd0/src/pl/plpgsql/src/pl_gram.y#L4

  4. MySQL Bugs: #2690: bison -y -d sql_yacc.yy && mv y.tab.c sq y.tab.c - No such file or directory

  5. https://github.com/mysql/mysql-server/blob/trunk/sql/sql_yacc.yy

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/822478.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

⛳ Java多线程 一,线程基础

线程基础 ⛳ Java多线程 一&#xff0c;线程基础&#x1f43e; 一&#xff0c;线程基础&#x1f4ad; 1.1&#xff0c;什么是程序&#xff0c;进程&#xff0c;线程&#x1f3ed; 1.2&#xff0c;什么是并行和并发&#x1f463; 1.3&#xff0c;线程使用的场景&#x1f3a8; 1.…

Nginx 15分钟入门

1、反向代理和负载均衡 Nginx 反向代理 负载均衡 网站初期用户量较少的时候&#xff0c;一台服务器就够用&#xff0c;但是当大量用户注册&#xff0c;那么显然一台机器就不够了。如下图&#xff0c;我们把同一个项目部署在3台服务器上。那么问题又来了&#xff0c;用户A的请…

连锁门店新零售管理系统服务商,提供新零售商城一体化解决方案|亿发

新零售时代&#xff0c;客户需求和购物方式正在发生翻天覆地的变化&#xff0c;数字化运营服务成为连锁门店增强竞争力的有效工具。那么&#xff0c;我们该如何借助数字化力量&#xff0c;升级连锁门店的新零售运营服务&#xff0c;迎接未来的商业挑战呢&#xff1f;一、智慧新…

emWin - BMP图片显示

BmpCvt.exe 用途 利用BMP图片&#xff0c;进行GUI显示&#xff1b;ICON等图标都是小BMP图片&#xff0c;核心是将BMP图片&#xff0c;转成emWin支持的方式&#xff0c;最终显示到TFT屏上 使用BmpCvt.exe工具&#xff0c;将各个图片转成相应的C文件. emWin有关的工具&#xff…

P4780 Phi的反函数

题目 思路 φ(x)n 当指数均为1时n最小 证明&#xff1a;容斥原理 代码 #include<bits/stdc.h> using namespace std; #define int long long const int maxn1e9; int ansINT_MAX,n; bool f; map<int,bool> mp; bool is_prime(int n){if(n<1) return false;fo…

Web功能测试之表单、搜索测试

初入职场接触功能测试老是碰到以下情况不知道怎么写测试用例&#xff1a; 一个界面很多搜索条件怎么写用例&#xff1f; 下拉框测试如何考虑测试点&#xff1f; 上传要考虑哪些验证点&#xff1f;...... 所以这篇主要是整理关于web测试之表单、搜索测试的相关要点。 一、表…

个性定义轻松掌控,更适合PC玩家的游戏鼠标,雷柏VT350S体验

喜欢玩PC游戏的玩家都知道&#xff0c;一款好的鼠标可以在游戏中更加游刃有余&#xff0c;甚至扭转乾坤。但是&#xff0c;有线鼠标总是让人觉得不够灵活&#xff0c;无线鼠标又担心延迟和续航。那么&#xff0c;有没有一款无线鼠标既能满足游戏需求&#xff0c;操作又能随心所…

话费充值系话费直充系统源码支付快充慢充系统运营商接口

话费充值系话费直充系统源码支付快充慢充系统运营商接口

小程序学习(五):WXSS模板语法

1.什么是WXSS WXSS是一套样式语言,用于美化WXML的组件样式,类似于网页开发中的CSS 2.WXSS和CSS的关系 WXSS模板样式-rpx 3.什么是rpx尺寸单位 4.rpx的实现原理 5.rpx与px之间的单位换算* WXSS模板样式-样式导入 6.什么是样式导入 使用WXSS提供的import语法,可以导入外联的样式…

Spring-BeanFactory

Spring Spring是整个Java体系最核心的框架&#xff0c;没有之一。 核心类图结构 ApplicationContext ApplicationEventPublisher&#xff1a;提供了一种机制&#xff0c;用于通知应用程序中感兴趣的部分有关其执行过程中发生的特定事件。ListableBeanFactory&#xff1a;是S…

P3855 [TJOI2008] Binary Land(BFS)(内附封面)

[TJOI2008] Binary Land 题目背景 Binary Land是一款任天堂红白机上的经典游戏&#xff0c;讲述的是两只相爱的企鹅Gurin和Malon的故事。两只企鹅在一个封闭的迷宫中&#xff0c;你可以控制他们向上下左右四个方向移动。但是他们的移动有一个奇怪的规则&#xff0c;即如果你按…

cloudstack之basic network

本章则主要尝试basic network模式的使用。 基础环境搭建见cloudstack测试环境搭建 1、概念介绍 cloudstack的网络模式主要分为两种&#xff1a; basic network&#xff1a;一个zone中仅有一个guest network来承载客户虚拟机的流量&#xff0c;可以理解为一个简单的二层网络。…

【EI/SCOPUS征稿】2023年算法、图像处理与机器视觉国际学术会议(AIPMV2023)

2023年算法、图像处理与机器视觉国际学术会议&#xff08;AIPMV2023&#xff09; 2023 International Conference on Algorithm, Image Processing and Machine Vision&#xff08;AIPMV2023&#xff09; 2023年算法、图像处理与机器视觉国际学术会议&#xff08;AIPMV2023&am…

Django学习记录:使用ORM操作MySQL数据库并完成数据的增删改查

Django学习记录&#xff1a;使用ORM操作MySQL数据库并完成数据的增删改查 数据库操作 MySQL数据库pymysql Django开发操作数据库更简单&#xff0c;内部提供了ORM框架。 安装第三方模块 pip install mysqlclientORM可以做的事&#xff1a; 1、创建、修改、删除数据库中的…

maven如何打包你会吗?

1.新建一个maven项目&#xff0c;在main/java中建立Main类 public class Main {public static void main(String[] args) {System.out.println("hello java ...");} } 2.添加依赖&#xff0c;使其成为可执行包 <build><plugins><!--打包成为可执行包-…

uniapp app端 echarts 设置tooltip的formatter不生效问题以及解决办法

需求一&#xff1a; y轴数据处理不同数据增加不同单位 需求二&#xff1a; 自定义图表悬浮显示的内容 需求一&#xff1a;实现方式 在yAxis里面添加formatter yAxis: [{//y轴显示value的设置axisLabel: {show: true,formatter (value, index) > {var valueif (value > 1…

Uboot实现PSCI

快速链接: . &#x1f449;&#x1f449;&#x1f449; 【目录】ARM/TEE/ATF/SOC微信群问题记录 &#x1f448;&#x1f448;&#x1f448; 付费专栏-付费课程 【购买须知】: 问 &#xff1a; 8核的A72 多核启动 目前用的spin-table的方式&#xff0c;想尝试一下psci方式&…

Redis事务、管道

一.Redis事务 1.概念 可以一次执行多个命令&#xff0c;本质是一组命令的集合。一个事务中的所有命令都会序列化&#xff0c;按顺序地串行化执行而不会被其它命令插入&#xff0c;不许加塞 2.Redis事务与数据库事物的区别 3.常用命令 4.事务执行情况 正常执行 即整个过程…

全国高校招投标信息在哪里看?

很多投标人在查询招标信息的时候常常没有找到合适的&#xff0c;但是现在网上查询投标信息的网站是很多的。而学校招标信息获取的渠道是比较少的&#xff0c;企业的反而更多一些&#xff0c;那么我们能在那些渠道获取这些信息&#xff1f; 1.教育部网站 教育部提供了招标信息…