每个数据库都有自己的SQL执行流程,SQL语句分为DML语句和DDL语句,DML语句又分为读操作或是写操作。本文概要介绍一下TiDB数据库中DML和DDL语句执行流程。
一. DML语句读流程
DML语句的读流程如上图所示,具体可以描述为:
接收SQL请求。客户端发起SQL请求,由TiDB Server中的协议层(Protocol Layer)接收。
获取TSO。当接收到SQL请求时,TiDB Server首先向PD节点申请TSO,标识SQL的开始执行时间(不管是读操作还是写操作,都必须获取这样的TSO)。
解析。SQL解析,通过Parser模块对SQL进行词法解析及语法解析,将SQL解析为AST语法树。
编译。将AST语法树发送到Compiler模块进行编译。Compiler判断是点查还是非点查,如果是点查,比如根据主键或索引的精确查询,则直接发送到下面执行;如果是非点查,则进入一个优化流程,包括逻辑优化和物理优化,最终生成一个物理执行计划。
执行。无论是点查还是非点查,都会发送到Executor模块执行,并从TiKV中读取想要的数据。更具体一点,当Executor接收到执行计划后,首先需要做两件事:(1)获取表的元数据信息(节点启动时从TiKV读取并缓存到TiDB的内存,即图中的information schema);(2)获取数据所在的region及region所属的TiKV(第一次从PD获取,之后从TiKV Client缓存中获取),如果发现TiKV Client中缓存的是错误的region,TiDB Server会重新从PD中读取正确的信息并重新缓存一份到TiKV Client。
之后Executor便可以开始从下面读取数据,根据前面Compile步骤中判断是点查还是非点查,Executor也会选择两条不同的路径来读取数据,第1个路径是走KV模块(通过TiKV Client直接从TiKV中读取相应的键值数据),第2个路径是走DistSQL(针对复杂的SQL语句如多表关联,DistSQL会将复杂的SQL语句转换为对单表的简单查询再下发到TiKV节点,相当于把TiKV和复杂的SQL做了一个解耦)。
返回结果。当数据从TiKV中读取到后返回到Executor模块,然后进一步返回到客户端。
二.DML语句写流程
DML语句的写流程在一开始的时候和读流程几乎一致,因为数据修改之前需要先将数据读取出来。所以写流程的开始阶段就是经过Protocol Layer、Parser、Compiler、Executor之后通过Executor将RocksDB中的KV数据读取到memBuffer之中,之后的步骤完全不同,具体描述为:
读取数据到缓存。与读流程类似,将数据读取到TiDB Server的memBuffer中。
修改数据。在memBuffer中修改数据。
提交修改数据。进入事务两阶段提交逻辑。第一步为prewrite,即将修改的数据和锁信息写到TiKV中。第二步为commit,此时会向PD再申请一个事务提交时间戳并写入提交信息,同时释放锁信息。关于TiDB中事务两阶段提交相关内容,可以参考聊聊TiKV中的分布式事务 (qq.com)TiKV中负责数据写入的有三个模块:Scheduler、Raftstore及Apply。Scheduler负责协调事务并发写入的冲突并将收到的事务操作向下写入,它使用latch来管理并发冲突,谁拿到latch的可以继续向下写入,未拿到latch的处于等待状态。Raftstore是将写请求转化为Raft日志,Raft日志一方面需要持久到本地的RocksDB,另一方面需要复制到其他Follower节点同步。Apply模块负责读取Raft日志并应用到本地的RocksDB KV中。有关RocksDB的写入流程以及Raft复制流程,请参考浅谈TiKV中的数据持久化存储 (qq.com) 以及 了解TiKV中的Raft (qq.com)
返回。当两阶段commit完成后,返回客户端执行完成。
三.DDL语句执行流程
DDL主要是对表的定义进行修改,或是对表添加索引等操作。DDL执行流程为:
SQL执行编译。与DML语句执行类似,当TiDB Server接收到DDL请求时,会先进行解析和编译。当Compile后发现是DDL语句,则将DDL交给TiDB Server中的start job组件进一步处理。
添加到队列。Start job组件将接收到的DDL放到TiKV中的队列(注意:实际上,start job会先判断本节点的workers是否为owner,如果是owner则直接交给owner执行,如果不是则写到TiKV队列)。TiKV中关于DDL的队列有三种,第1个队列是除add index以外的DDL、第2个队列是add index队列、第3个队列是存储历史执行完成的DDL队列。
执行DDL。执行DDL由TiDB Server中的workers组件负责。需要注意的是,每个TiDB Server中都有一个worker,但只有一个TiDB Server中的worker是owner角色,只有处于owner角色的worker才能执行DDL操作。具有owner角色的worker采用先进先出的策略从TiKV中的job队列中取出一个DDL并执行,执行完成后放到history队列。(注意:owner角色是会轮询的,当一个TiDB Server中的worker处于owner一段时间会后重新选举并切换到另一台)