亿级并发丝毫不虚,TDSQL-SQL引擎是如何炼成的

2021-12-21 00:00:00 查询 执行 模型 下推 引擎

今天分享分为四章,分别是:TDSQL简介、SQL引擎简介、SQL引擎查询处理和佳实践。

PartⅠ TDSQL分库分表策略

前面的课程中,我们已经对TDSQL的架构做了比较详细的介绍,这里我们简单回顾一下TDSQL。TDSQL是腾讯针对金融行业推出的一款自主可控、高一致、分布式、HTAP数据库产品,目前已为超过500+金融政企提供数据库服务,行业涵盖了银行、保险、证券信托、互联网金融等等行业。

TDSQL是Shared-Nothing架构的分布式数据库,这张图是TDSQL的核心架构图,在这个架构图中包含了三个重要组件,分别是SQL引擎、MetaCluster和后端SET。其中每个SET是由一主多备构成的高可用复制单元,在一个TDSQL中往往包含多个这样的SET。每一个SET负责存储分布式表的部分分片信息。SQL引擎负责处理业务发过来的SQL请求,然后对SQL进行拆分,发送给各个SET进行处理,并对每个SET返回的结果进行聚合,得到终的结果。

在TDSQL中,每个SET都会负责一段连续的哈希;在TDSQL中建立的每一个分布式表,SQL引擎都要求用户指定其中的一个列为分区键,SQL引擎通过计算这个分区键的哈希值,将这个表的每一行数据都映射到这个哈希空间,由对应的SET进行存储。哈希空间与SET之间的对应关系,我们叫做路由表。这个路由信息会存储在MetaCluster里。当对整个集群进行缩容或扩容时,每个SET对应的哈希空间也会发生变化,对应的数据也会进行搬迁。SQL引擎通过监听MC来感知集群的变化。

在建表的时候,SQL引擎通过关键字让用户在建表的时候指定其中的一个列为分区键。在这个例子中,用户建立一张表,其中指定ID为shardkey,也就是说SQL引擎通过计算分区键ID的哈希值,将这个表的数据进行打散,均匀的分布在各个SET上。

PartⅡ TDSQL-SQL引擎如何优雅处理海量SQL逻辑

前面我们回顾了一下TDSQL的整体架构,以及Sharding策略。这里我们再介绍一下SQL引擎。

目前TDSQL-SQL引擎已经能够支持绝大多数的MySQL语法,分布式查询、事务和死锁检测。应用程序端可以像使用单机数据库一样,通过SQL引擎来使用整个TDSQL。除了这些比较基本的功能,SQL引擎还支持一些比较的功能,例如SQL防火墙、读写分离等等。

我们这里通过一条SQL的大体执行路径来看一下这些功能之间的关系:

应用程序通过MySQL客户端向SQL引擎发送了一条SQL,SQL引擎通过协议解析,从数据包中得到这条SQL,并对这条SQL进行语法解析,语法解析以后我们就得到一棵抽象的语法树,如果应用配置了SQL防火墙,我们还会匹配防火墙的规则,判断这条SQL能否执行。如果能够执行的话,接着我们会构建这条SQL的分布式执行计划——在这个计划里面,我们描述了由哪些SET参与查询的执行,每个SET应该执行什么样的逻辑,SQL引擎又进行怎样的聚合,两者之间怎么进行协调。得到分布式执行计划以后,我们将这个计划再转换成SQL的形式发送给对应的SET执行。接着我们执行读写分离算法,从每个SET中挑选出适合访问的实例,然后就可以向这个实例发送SQL了。我们收到每个SET反馈的响应以后,对返回的结果进行聚合。这里的聚合逻辑主要是执行分布式执行计划分配给SQL引擎的一些任务。如果是一条简单的查询,我们通过流式聚合就可以得出终的结果。并将结果反馈给应用;如果是一条比较复杂的查询,往往需要拆分成多个阶段来执行。这个时候就需要构建下一个阶段的分布式执行计划,然后再执行这个计划。经过多次执行,所有的阶段全部执行完毕之后,终在本地的执行器中进行终的计算和聚合,从而得到一个终的结果。

PartⅢ 如何实现高性能SQL引擎查询

我们这里已经介绍了SQL引擎的一个大体的功能,这里我们再具体介绍SQL引擎是怎么处理各类查询的。我们将查询分成两类,一类是Select查询,一类是更新操作,例如Delete、Update、Insert等等。对Select查询的话,我们有两种处理方式:一种是流式处理模型,一种是通用处理模型。流式处理模型主要是处理单表上的查询,而通用处理模型是对流式处理模型的弥补,负责处理分布式的跨节点查询。

3.1 流式处理模型原理及典型案例详解

我们先介绍一下流式处理模型。这里有一个简单的例子

在这个例子里面用户访问的是一个日志表,这个日志表里面有三个列:其中ID是日志的ID,name是用户名,score是用户的历史游戏得分。一个用户会有多个游戏得分,并且他的游戏得分的记录会分散在各个SET上。业务想要获取每个用户的平均分,SQL引擎不能直接把SQL发送给各个SET,所以他需要计算出每个用户的总得分和这个用户在这张表上的一个总记录数,然后用两者的商来求得每个用户的平均分——具体来说SQL引擎要求每个SET返回,每个用户在每个SET上的一个局部的总得分和总记录数。接着SQL引擎将每个SET上返回的结果再进行聚合,得到每个用户的总得分和总记录数,然后再用两者相除。

我们发现,SET执行的SQL里面有一个order by,这个是做什么的呢。我们假设没有这个order by,那么某一个用户张三,SET1反馈的条记录是张三的局部得分,SQL引擎拿到这个张三的结果以后,无法直接计算出张三这个用户的平均分,他还需要知道张三在SET2上的得分。但是SET2可能在后一条记录才返回张三在SET2的局部的得分,所以SQL引擎不得不进行等待操作,等待SET2返回所有的结果,而这个等待操作会非常影响性能。如果我们增加了这个order by,SET1和SET2返回的结果都是按名字有序地进行,SQL引擎进行一个简单的Merge就可以得到一个全局有序的结果。对张三这个用户来说,这个有序的结果里面,他在SET1和SET2上的局部得分就是相邻的,所以SQL引擎不需要等待SET1和SET2反馈的所有结果,他直接就能计算出张三的平均分。这个引擎计算出张三的平均分以后,立马就可以将张三的得分再反馈给业务。

在这个例子中可以看到,SET1和SET2不停地向SQL引擎返回数据,SQL引擎随即对返回的结果进行聚合,并将聚合结果立即返回给这里。整个过程就像流水一样,所以我们将这个过程称之为流式处理模型。流式处理模型核心的地方就是对SQL进行拆分——拆分成两部分,一个是SET执行的部分,以及在SQL引擎执行的部分。就拿刚刚的例子来说,SET进行一个局部的聚合,计算出每个用户在SET上的局部的总得分和总记录数。SQL引擎进行一个全区的累加和聚合,得到每个用户的总得分和总记录数。进行拆分以后,流式处理模型还会添加一些额外的操作,将两者衔接起来——例如刚刚例子中的order by,这样一来我们就得到了一个流式处理的执行计划。当然除了这个以外,我们还会进行一些优化,将原本分配给SQL引擎的一些操作进行下推,例如模型中的Limit和distinct,其实它应该在SQL引擎上执行的。我们通过把这些下推,能够较大地减少SET返回给SQL引擎的数据量,从而也就提升了整个执行的性能。

刚才我们一直强调流式处理模型是针对单表处理的,事实上它还能够处理其他的场景。

例如这个例子1中的表,两张表的shardkey相等,因此我们可以把t1、t2当成一个整体,当成一个整体之后我们就可以用刚刚介绍的查询拆分构建执行计划,并用流式处理模型进行处理了。对子查询的话,类似这里的IN,a是T1和B1的shardkey,这个子查询同样暗含着T1的shardkey等于B的shardkey,我们同样将T1和这个子查询当成一个整体,套用刚刚介绍的查询拆分和流式处理模型进行处理。

当然,并不是所有的查询都能够满足这样的一个情况,如果两个表不是shardkey相等的话,这个时候我们就需要用后面的通用处理模型进行处理了。但是通用处理模型的性能比不上流式处理模型,那怎样才能够将流式处理模型进行进一步推广呢?TDSQL就引入了一个广播表——广播表的意思就是说将一些表的数据全量备份在每个节点上,这样一来原本T1、T2的关联查询,他们必须用通用处理模型进行处理,这个时候我们也可以使用流式处理模型进行处理了。

从刚刚的介绍,我们可以看到流式处理模型是以pipeline的方式执行,SET不断返回数据给SQL引擎,SQL引擎不停地对返回的结果进行聚合,并将聚合的结果立即返回给应用。所以说在这种处理方式下,SQL引擎的内存开销是非常低的。同时在执行的过程中,各个SET是并发执行,这也保证了流式处理模型的性能、并行性。流式处理模型核心的一方就是拆分和下推——通过智能的拆分和下推,我们将大量的计算下推给SET执行,这样就大大减少了SQL引擎需要从SET获取的数据。流式处理模型虽然要求比较严苛,但是我们通过合理的选择shardkey以及引入广播表,事实证明流式处理模型其实能够满足绝大多数的OLTP业务。

3.2 通用处理模型:跨节点分布式查询优化的利器

接下来我们介绍一下SQL引擎的通用处理模型。

通用处理模型主要是针对分布式的跨节点查询。说到分布式的跨节点查询,大家首先想到的肯定是Spark、Greenplum这样的大数据处理引擎。在这些引擎中,他们执行查询的时候往往需要在节点之间进行大规模的数据搬迁,因为TDSQL主要针对OLTP业务,所以在生产上,TDSQL往往运行着大量的交易SQL,进行大规模数据搬迁肯定是不现实的。所以,TDSQL基于常见的OLTP场景而设计了自己的分布式处理查询框架。在这个模型下,SQL引擎对SQL进行语法解析以后,就将实际参与查询的数据加载到SQL引擎,然后在本地执行这个查询。在我们所设想的OLTP场景下,用户常常为一个表的索引指定一个常量或者常量的范围,表之间的连接也基本上为等值连接。在这样的一个场景下,SQL引擎通过查询下推和条件下推大大降低了需要加载的数据量,从而使得这个方案变得切实可行。

这里我们以一个例子来讲解通用数据模型是怎样处理一条SQL的。

在这个例子中一共有三张表,每个表中的shardkey我们都设置为A。SQL先将T1、T2进行一个等值连接,然后where条件指定了T1.A是一个常量;在这里还有一个子查询——这个子查询是一个相关子查询,它引用了外层的T1.C。在执行这条查询的时候,SQL引擎首先选择一个需要加载的表,在这里我们选择了T1.A,因为它包含了一个可以下推条件,而且条件是一个主键。我们加载完T1以后,就可以知道T1.B和T1.C是两个列的范围了,根据这个连接的连接条件,我们可以推算出T2.A的范围。根据推算出来的T2.A的范围我们继续加载T2。这样一来,就可以将上层涉及的两个表T1、T2都加载到本地。接着我们处理内层的子查询,这个子查询是一个相关子查询,如果我们在加载T2的时候发现T1.C其实是一个常量,这个时候就可以将这个相关子查询转换成一个非相关子查询,并将这个子查询提取出来进行独立计算。计算以后我们将它的结果来替换这个子查询;如果它不是一个常量的话,我们就利用这个条件推断出T3.A的范围,再用推算出来的这个条件去加载T3,这样一来,这条SQL所设计的所有的表的数据,我们都加载到了SQL引擎,接下来就像MySQL一样执行这条查询就可以了。

通过这个例子我们可以看到,SQL引擎通过条件下推,并利用加载的数据推算出新的条件,然后利用新的条件再去过滤我们需要加载的表,这样可以大大降低需要加载的数据量。对于子查询的话,如果我们发现它能够转换成非相关词查询,这个时候我们还会进行子查询的下推。当然具体优化的技术还有很多,这里我们就不再一一列举。

我们将优化和工程实践中所使用的一些技术进行一个分类,主要包括逻辑优化、条件下推和隔离。

逻辑优化:主要是对SQL的结构进行优化,使它变得更加容易处理。例如我们将左/右连接转换成内连接。如果它能够转换——在原来的逻辑下需要先加载左连接的左表,然后才能加载左连接的右表。后转换以后,我们就可以消除这种限制,能够生成多种加载方式,这样的话我们就可以有更好的一个优化的空间。

条件下推:是指用一些手段将SQL中的条件提取出来,或者说我们利用已知的一些信息推断出新的条件,用这些推断出来的条件来过滤需要加载的数据。同时我们还可以通调整加载的顺序,例如刚刚例子中我们优先加载了T1,利用优先加载小表的策略同样能够起到降低需要加载的数据量的作用。

隔离:TDSQL主要针对OLTP业务,所以它线上会运行着大量的交易SQL,这个时候如果应用发送过来的是一个复杂的查询、是一个OLAP类的查询的话,往往需要加载很大的数据量。而且执行的时候也会有很大的CPU开销,为了不影响这些正在运行的OLTP业务,我们需要进行隔离。比如说我们将比较耗时的操作,例如把数据加载过来,然后缓存在本地的临时表里,以及终要执行查询的时候,也将这个比较耗时的操作放在后台异步执行,这样就可以对OLTP业务进行隔离,避免影响在线的一个交易。

3.2.1 通用处理模型总结

通过前面我们可以看到通用处理模型的SQL兼容性是非常好的,因为我们只需要将数据加载到本地,然后用传统的方法执行这个查询就可以了。然后我们对OLTP场景有很多的优化策略,通过这些策略,我们大大降低了需要加载的数据量。同时,这个通用处理模型也可以作为OLAP来用,如果当做OLAP来用的话,我们会将数据加载和执行这两个比较耗时的动作在后台异步执行。

3.3 TDSQL-SQL引擎更新操作原理

前面我们讲解了SQL引擎如何处理Select的查询。这里我们再简单介绍一下SQL引擎是如何处理更新操作的。

3.3.1 简单更新操作

对于一些简单的更新操作,例如这里的Insert、Update语句,我们根据SQL中的shardkey对这些SQL进行拆分。例如INSERT,它插入这些数据,我们根据它的shardkey计算出2、3是需要插入到SET1的;1和4是需要插入到SET2的。我们将这些需要插入的数据进行拆分以后,再构建对应的Insert语句分别发送给SET1和SET2进行执行。如果一条更新语句没有带shardkey,例如这里的Delete语句,这个时候我们就需要将这条更新操作广播给所有的SET执行。如果一条更新语句更新了多个SET,我们就会使用分布式事务来保证这个更新操作的原子性。

3.3.2 复杂更新操作

而对于一个比较复杂的更新操作,例如这个例子里面的联合更新SQL。对于这样一个复杂的SQL,SQL引擎会将它拆分成两个部分:一部分是Select,一部分是对应的更新部分。对于这个语句来说就是一条Update。而Select的话,它主要的目的是提取出那些需要被更新行的主键,以及我们需要设置的新值。获取这两个关键信息以后,我们会构建对应的更新操作,因为这个例子里面就是一条Update语句——在这个语句里面我们指定了需要被更新行的主键,同时我们也指定了需要被更新的列和它的新值;然后将这条Update语句发送给对应的SET执行。对于这条Select语句的话,我们就看它的类型,看它是否shardkey相等——如果shardkey相等的话,我们就将由流式处理模型先处理;如果不相等,我们就用通用处理模型先处理。

3.3.3 更新操作中的分布式事务

前面我们多次提到分布式事务,在前面的课程中,我的同事也对分布式事务做了一些讲解。这里我们再简要回顾一下。SQL引擎分布式事务所使用的算法是两阶段提交,对应用来说其实不需要关心具体的实现细节,对应用来说就是简单的Commit——当业务发送Commit到SQL引擎的时候,SQL引擎就会向所有的参与者发送prepare,接下来每个收到prepare的主机将这个事务的状态设置成prepare状态,并将这个事务所产生的binlog全部发送给备机。备机收到以后就会再向主返回一个应答,主再向SQL引擎返回一个应答,SQL引擎收到所有参与者的应答之后就会做一个决策。当所有的参与者都应答成功,它就会做一个提交的决策,并将这个决策写入到分布式的事务日志中。一旦写成功的话,我们就可以直接将这个事务提交;而当发生任何一种故障,我们都可以根据这个全局的分布式事务日志进行提交或者回滚。

3.3.4 更新操作自增列优化原理

使用数据库的过程中,我们经常会使用自增列。SQL引擎也提供了这个功能,当应用创建表的时候,如果指定了一个自增列,SQL引擎就向一个全局的元数据表中插入一个记录,这个记录包含了shardkey的名称,以及下一个可以使用的值。当应用后续再发送INSERT语句过来的时候,SQL引擎就会对记录中这个值进行加1,然后再用原来的值来填充这条INSERT语句,再将这条填充后的Insert语句进行拆分并发送到对应的SET执行。如果对每一条Insert语句我们都去访问这一条记录,这一条记录必然会成为一个热点,继而对整个系统的稳定性和性能都会产生很大的影响。所以SQL引擎丢弃了自增的属性,只保留了它全区的属性。SQL引擎为了做到这一点,在每次获取自增列值的时候会一次性获取多个,然后再缓存到本地。当应用发送Insert 语句过来,就直接从本地的cache中拿出一个值来填充这个Insert ,避免热点的产生,从而也提升了Insert语句的性能。当然,如果用户非得要使用递增值,SQL引擎还提供序列功能,通过序列来满足单调递增的属性。

PartⅣ TDSQL-SQL引擎查询佳实践

前面我们介绍了SQL引擎是如何处理各类查询的,接下来是我们的佳实践。在这一章节我们会介绍选择广播表的基本原则;SQL引擎提供的基本命令等,通过这些命令我们可以看到一条SQL具体的执行细节。

4.1 广播表

使用广播表可以让一条SQL使用流式处理的方式进行处理,流式处理的性能往往是比较高的。那么什么样的一个表适合作为广播表呢?

对于广播表,SQL引擎需要将这个表全量备份到所有的SET上,这会产生空间上的消耗;同时对广播表的每一次更新,都需要使用分布式事务,从而保证这个更新操作的一致性。因此,我们优先要选取的表,会希望它的数据量比较小,来减少对空间的占用;同时我们希望它的更新频率比较低,这样能够降低对更新操作性能的影响。我们使用广播表主要是为了优化连接查询,所以说我们选择的广播表本身,它应该经常被访问,当满足这两个条件,我们就可以建议用户将这样的一个表设置成广播表。

4.2 explain信息解读

在使用MySQL的时候,我们经常使用的命令就是explain,通过explain我们可以看到查询的执行计划,看到这条SQL是否使用了某个索引,或者说多表连接的顺序是否符合预期,SQL引擎也提供了这样一个功能。刚才我们说过一条select语句的执行方式有两种:一种是流式处理模型,一种是通用处理模型。对流式处理模型来说,SQL引擎会拆分成两部分,一个是SET执行部分和SQL引擎执行部分。如果SQL引擎执行的逻辑比较简单,这个时候explain所产生的结果就是SET上的一个实际执行计划,例如对这条SQL来说,其实SQL引擎并没有做什么比较大的计算,所以它产生的结果其实是MySQL上的执行计划。SQL引擎还会追加一些额外的信息,例如可以看到提示用户这条SQL是发给哪个SET的,实际发给SET的是怎样的一条SQL。

如果SQL引擎对SQL在进行流式处理的过程中进行了比较复杂的聚合,例如——对这条SQL来说,SQL引擎需要计算出count(distinct b),这个时候explain产生的结果就跟前面不一样,explain会告诉用户DB上执行的是什么样的查询,SQL引擎进行怎样的聚合。这个例子里面,我们可以看到DB上执行的是扫描查询,并且下推了distinct,在DB部分进行了初步去重。如果想要知道下推部分的执行计划,我们还提供了透传命令——通过透传命令我们可以看到这条SQL在每个SET上的具体执行计划。这个例子里面,我们可以看到,为了执行这个计划,每个SET使用的临时表,并且进行了一个排序。

对于使用通用处理模型处理的查询,explain展示的信息又会不一样。前面我们说过在通用处理模型下,我们需要对表进行加载,因此explain输出的结果就是告诉我们,SQL引擎到底使用了什么条件去加载数据。对于推导出来条件,explain会添加一个前缀/esteimated/加以提示。如果子查询可以直接下推,explain同样也会展示出来。在展示怎么样加载数据和怎么样下推查询之后,explain会展示终SQL引擎要执行的查询。

4.3 trace:展示SQL各个阶段的耗时

当然光知道一条SQL是怎么执行,有时候还是不够的的。因为影响一条SQL的执行效率可能还跟当前的环境、负载有关系。所以我们想知道一条SQL在实际执行过程中各个阶段的时耗,SQL引擎就提供了这样一个强大的功能——trace。当你在SQL的前面加一个trace前缀的时候,SQL引擎会执行这条查询并记录在执行过程中各个阶段的时耗。例如在这个例子里面,这条SQL是以使用通用处理模型进行处理的,所以在展示的结果里面,我们可以清晰地看到,加载T1时,从SET1和SET2加载数据的时耗,以及SET1和SET2返回的数据量。通过这些信息,我们就可以看到在数据加载过程中的具体细节。如果时耗比较大的话,我们就再去翻看前面的EXPLAIN信息,看看它的下推是否正常,下推之后是否能够使用到索引。通过这些信息我们就可以清楚的看到一条SQL在各个阶段所占用的时间,结合前面介绍的的explain命令,就可以很方便地定位到性能上的瓶颈了。

4.4 show processlist

另外我们在MySQL中经常使用的一个命令还有show processlist,这个命令可以看到当前性能中正在运行的一些查询,SQL引擎也同样提供了这个命令。通过这条命令可以看到系统中当前正在运行的查询,通过观察查询执行时从后端DB加载的数据量,以及时耗,我们可以方便地定位到这个系统中哪条查询是资源的消耗大户。show processlist输出中还有一个关键的字段,extra,因为SQL引擎会对用户发送过来的SQL进行拆解,实际发送给DB的SQL跟原生的SQL已经不太一样,extra会展示这些被拆分以后得到的SQL及其实际执行的时间。这样我们就可以将DB上的SQL和当前正在运行的业务SQL结合起来,如果我们发现DB上被某条SQL占用了大部分资源,我们就可以通过这些信息将其与一条业务的SQL进行一个关联。

当然,如果是DB出现慢查询的时候,你刚好在电脑旁边的话,就可以用刚刚说的show pracesslist进行查看。如果你不在电脑旁边,当回到电脑旁边的时候,这个高峰期已经过去了,那怎么办?SQL引擎会将应用发过来的SQL,以及拆分过的SQL记录到日志中。通过日志分析工具,例如这个例子,可以看到当时SQL引擎对这条SQL的拆分,以及每条被拆分的SQL在哪个时间点发送到了哪一个SET,同时还展示每条SQL的影响行数及返回的数据量。通过日志分析工具,我们可以还原出现问题的时候当时的现场,从而进一步对业务的SQL进行分析和优化。

PartⅤ Q&A

Q:SQL引擎是自己开发的还是用的MySQL的引擎?

A:SQL引擎是我们自己开发的。

Q:如果做数据库从Oracle换成TDSQL,对应用来说需要的变更很大吗?

A:TDSQL与Oracle在语法上会有一些差异,我们的一些客户也是从Oracle迁移到TDSQL来的,实践证明,只需要一点点改造,将部分Oracle 的SQL替换成与之相对应的TDSQL语法即可。

Q:如果业务单表数量小的情况是不是不推荐使用这种分库分表的方式?请问随着业务的发展,当数量扩展到多少时,数据库推荐分库分表?

A:数据量比较小的时候自然不需要使用分库分表。当数据量在可预期的时间范围内,它可能会超过单机能够承受的范围,比如说当可能有上T的数据时,就需要考虑分库分表了。另外,当一台机器的计算能力满足不了业务的话,也可以使用分库分表,SQL引擎通过将计算进行下推,使得计算能力随着节点数而线性增长。所以说我们当出现容量或者计算能力上单机无法满足的情况时,我们就可以使用分库分表。

Q:这个SQL引擎类似于Oracle 优化器或sharding?开发者不用过度关注吧?

A:你用SQL引擎的话,使用TDSQL可以像使用单机MySQL一样使用SQL引擎,一般来说你是不需要过度关注的。

Q:参数在每个SET上执行的时候也是走事务吗?不然每个SET的原子性没法保证吧?

A:是的,通过SQL引擎开启一个事务的时候,SQL引擎也会在每个SET上开启相应的子事务。

Q:分布式事务是基于MySQL原生的两阶段提交,不是基于BASE,两阶段提交性能会不会很差?

A:分布式事务的话,我们是基于原生的MySQL所提供的XA功能,但是在真正上线之前,其实我们对它做了很大的一个改造,就是说其实跟开源差别比较大。在实际使用过程中,两阶段提交会有一些性能影响,但是性能影响不是特别大,基本上影响可能在20%左右。

Q:Squence的实现只是保证递增,不保证连续性吗?如果可以实现单调递增,具体的实现逻辑是怎么样的?

A:Squence的话,是可以保证连递增的,每次获取新值都需要访问元数据表。

Q:开发者应该需要关注TDSQL的语法吗?比如说建表的时候需要指定shardkey之类的。

A:前面的课程已经讲解过。如果建表的时候没有指定shardkey——TDSQL会自己选择一个shardkey,但是该功能默认是关闭的。为什么呢?如果你没有参与到选择shardkey的过程,TDSQL没法保证这个shardkey的选择是合适的,因为TDSQL不知道业务逻辑是怎么访问这张表的。

相关文章