Sharding-Sphere自动化执行引擎

Q: 什么叫"自动化执行引擎"?数据库

 

A: 一条SQL的生命周期是:从客户端发起、通过Sharding-Sphere处理、再到底层数据库执行消化。而在Sharding-Sphere里过程则是:SQL解析-->SQL优化-->SQL路由-->SQL改写-->SQL执行-->结果归并。自动化执行引擎是为了处理SQL执行问题的,即将路由改写后的真实SQL如何有控制且高效地传送到底层数据库执行。那么直接经过JDBC发送SQL至数据库执行难道行不通吗?还有其余须要考虑吗?答案是:确定有其余考虑,不然我就不用写这篇文章了。这就体如今它的"自动化"上了。所谓"自动化",实际上是为了平衡数据库链接建立与结果归并模式选择问题,为了平衡资源控制与执行效率问题。数据结构

 

 

需求场景多线程

 

Q: 为什么须要自动化执行引擎呢?并发

 

A: 在概念介绍部分,咱们介绍了主角-自动化执行引擎。也谈到它的自动化是为了平衡数据库链接建立以及结果归并模式选择问题。这是它诞生的宿命,历史的选择。下面将为你们介绍这两个须要平衡的问题:分布式

 

1. 数据库链接建立性能

做为一位混娱乐圈的DBA出身的Java coder, 多少仍是会从DBA角度考虑问题。好比从资源控制的角度看,业务方访问数据库的链接数量应当有所限制,这可以有效地防止某一业务操做过多地占用资源,从而将数据库链接的资源耗尽,以至于影响其余业务的正常访问。特别是在一个数据库实例中存在较多分表的状况下,一条不包含分片键的逻辑SQL将产生落在同库不一样表的大量真实SQL,若是每条真实SQL都占用一个独立的链接,那么一次查询确定将会占用过多的资源。Sharding-Sphere做为数据库中间层,若是没有控制好数据库链接数量而致使链接暴增、数据库压力过大的话,极有可能被强行背锅。学习

 

2. 结果归并模式选择优化

可是从执行效率的角度看,为每一个分片查询维持一个独立的数据库链接,能够更加有效地利用多线程来提高执行效率。为每一个数据库链接开启独立的线程,能够并行化IO所产生的消耗。独立的数据库链接,可以保持查询结果集的引用以及游标位置,在须要获取相应数据时移动游标便可,避免了过早将查询结果数据加载至内存。这就涉及到告终果归并模式的选择问题。经过上一篇文章《剖析Sharding-Sphere系列——结果归并》介绍,咱们知道当前有两种结果归并的模式,分别是:线程

 

流式归并:以结果集游标下移进行结果归并的方式,称之为流式归并,它无需将结果数据全数加载至内存,能够有效地节省内存资源,进而减小垃圾回收的频次。code

 

内存归并:以读取内存中加载的结果集进行归并的方式,进行数据对比归并。它须要将结果数据全数加载至内存。

 

相信只要是智商在线的朋友,必定会选择流式归并来处理结果集。但是,若是没法保证每一个分片查询持有一个独立数据库链接的话,那么就须要在复用该数据库链接、获取下一张分表的查询结果集以前,将当前的查询结果集全数加载至内存。所以,即便能够采用流式归并,在此场景下也不得不退化为内存归并。

 

一方面是对数据库链接资源的控制保护,一方面是采用更优的归并模式达到内存资源节省的目的,如何处理好二者之间的关系,是Sharding-Sphere执行引擎需求解决的问题。具体来讲,若是一条SQL在通过Sharding-Sphere的分片后,须要操做某数据库实例下的200张表,那么,是选择建立200个链接并行执行,仍是选择建立一个链接串行执行呢?效率与资源控制又应该如何抉择呢?

 

 

进化论

 

针对上述的场景,Sharding-Sphere在3.0.0.M4以前提供了一种解决思路,即提出了链接模式(Connection Mode)的概念,并划分了两种模式:内存限制模式(MEMORY_STRICTLY)和链接限制模式(CONNECTION_STRICTLY)这两种类型。

  • 内存限制模式

    使用此模式的前提是数据库对其一次操做所耗费的链接数量不作限制。若是实际执行的SQL须要对某数据库实例中的200张表作操做,则对每张表建立一个新的数据库链接,并经过多线程的方式并发处理,以达成执行效率最大化。而且在SQL知足条件状况下,优先选择流式归并,以防止出现内存溢出或避免频繁垃圾回收状况。

 

  • 链接限制模式

    使用此模式的前提是数据库严格控制对其一次操做所耗费的链接数量。若是实际执行的SQL须要对某数据库实例中的200张表作操做,那么只会建立惟一的数据库链接,并对其200张表串行处理。若是分片在不一样的数据库,仍然是多线程处理不一样库,但每一个库的每次操做仍然只建立一个惟一的数据库链接。这样便可以防止对一次请求对数据库链接占用过多所带来的问题。该模式始终选择内存归并。

 

内存限制模式适用于OLAP操做,能够经过放宽对数据库链接的限制提高系统吞吐量;链接限制模式适用于OLTP操做,OLTP一般带有分片键,会路由到单一的分片,所以严格控制数据库链接,以保证在线系统数据库资源可以被更多的应用所使用,是明智的选择。

 

而Sharding-Sphere最终使用何种模式的决定权就交由用户。Sharding-Sphere提供对链接模式的配置,让开发者依据本身业务的实际场景需求选择使用内存限制模式或链接限制模式。

 

但是,将两难的选择的决定权甩锅给用户,使得用户必需要了解这两种模式的利弊,并依据业务场景需求进行选择。这显然增长了用户对Sharding-Sphere的学习和使用的成本,这并非一种最优的解决方案。

 

此外,这种一分为二的处理方案,将两种模式的切换交由静态的初始化配置,缺少灵活应性。在实际的使用场景中,面对不一样SQL以及占位符参数,每次的路由结果是不一样的。这就意味着某些操做可能须要使用内存归并,而某些操做则可能选择流式归并更优,它们不该该由用户在Sharding-Sphere启动以前配置好,而更应该根据SQL和占位符参数的场景,来动态的决定链接模式。

 

像Sharding-Sphere这样,老是站在用户角度考虑问题而且不断优化精进的七道杠青年是必定要进行相关优化调整的,因而自动化执行引擎就进化出来了。

 

为了下降用户的使用成本以及链接模式动态化这两个问题,Sharding-Sphere提炼出自动化执行引擎的思路,在其内部消化了链接模式的概念。用户无需了解所谓的内存限制模式和链接限制模式是什么,而是交由执行引擎根据当前场景自动选择最优的执行方案。

 

同时,自动化执行引擎将链接模式的选择粒度细化至每一次SQL的操做。针对每次SQL请求,自动化执行引擎都将根据其路由结果,进行实时的演算和权衡,并自主地采用恰当的链接模式执行,以达到资源控制和效率的最优平衡。针对自动化的执行引擎,用户只需配置maxConnectionSizePerQuery便可,该参数表示一次查询时每一个数据库所容许使用的最大链接数,剩余的处理逻辑将由自动化执行引擎为您负责。

 

 

实现解析

 

整个自动化执行引擎的执行流程以下图所示。

 

在路由改写完成后,咱们会获得路由结果(SQLRouteResult),这个结果集主要包含了SQL、SQL的参数集、数据库等信息。其数据结构以下图所示:

执行引擎的执行过程分为准备、执行两个阶段。

 

  • 准备阶段

顾名思义,此阶段用于准备执行的数据。它分为结果集分组和执行单元建立两个步骤。

 

   a. 结果集分组

该步骤是实现内化链接模式概念的关键。执行引擎根据maxConnectionSizePerQuery配置项,结合当前路由结果,自动选择恰当的链接模式。具体步骤以下:

 

1. 将SQL的路由结果按照数据库的名称进行分组。

2. 经过下图的公式得到每一个数据库实例在maxConnectionSizePerQuery的容许范围内,每一个数据库链接须要执行的SQL路由结果组,并演算出本次请求最优的链接模式。

 

 

在maxConnectionSizePerQuery容许的范围内,当一个链接须要执行的请求数量大于1时,意味着当前的数据库链接没法持有相应的数据结果集,则必须采用内存归并;反之,当一个链接须要执行的请求数量等于1时,意味着当前的数据库链接能够持有相应的数据结果集,则能够采用流式归并。

 

每一次的链接模式的选择,是针对每个物理数据库的。也就是说,在同一次查询中,若是路由至一个以上的数据库,每一个数据库的链接模式不必定同样,它们多是混合存在的形态。

 

   b. 执行单元建立

该步骤经过上一步骤得到的路由分组结果建立用于执行的单元。执行单元是指为每一个路由分组结果建立相应的数据库链接。

 

当数据库被限制了链接资源数量且线上业务出现大量并发操做时,若是不妥善处理并发获取数据库链接的问题,则颇有可能会发送死锁。在多个请求相互等待对方释放数据库链接资源时,就会产生饥饿等待,形成交叉死锁。

 

举个栗子,假设一次查询须要在某一数据库上获取2个数据库链接,用于路由至一库的2个分表查询。有可能出现查询A已获取到该数据库的1个数据库链接,并等待获取另外一个数据库链接;而查询B则也已经得到了该数据库上的1个数据库链接,并一样等待另外一个数据库链接的获取。若是数据库链接池的容许最大链接数是2,那么这2个查询请求将永远孤独地等待着彼此,图绘版的解释可能会更便于你们理解:

为了不死锁的出现,Sharding-Sphere在获取数据库链接时进行了同步处理。它在建立执行单元时,以原子性的方式一次性获取本次SQL请求所需的所有数据库链接,杜绝了每次查询请求获取到部分资源的可能。这种加锁作法确实能够解决死锁问题,只是,同时会带来必定程度并发性能的损失。为了展现咱们不同!有啥不同呢?

咱们针对此问题还进行了如下两方面优化:

 

1.    避免锁定一次性只需获取一个数据库链接的操做。由于每次仅须要获取一个链接,就不会发生两个请求相互等待的场景,无需锁定。对于大部分OLTP的操做,都是使用分片键路由至惟一的数据节点,此时无需担忧交叉死锁问题,也无需考虑加锁问题,从而减小对并发效率的影响。除了路由至单分片的状况,读写分离也属于此范畴以内的场景。

2.    仅针对内存限制模式进行连接资源的锁定。在使用链接限制模式时,数据库链接资源在全部查询结果集装载至内存以后被释放掉,所以没必要考虑死锁等待、加锁处理的问题。

 

  • 执行阶段



  •  

该阶段用于真正的执行SQL,它分为分组执行和归并结果集生成两个步骤。

   a. 分组执行

该步骤将准备执行阶段生成的执行单元分组下发至底层并发执行引擎,并针对执行过程当中的每一个关键步骤发送事件。如:执行开始事件、执行成功事件以及执行失败事件。执行引擎仅关注事件的发送,它并不关心事件的订阅者。Sharding-Sphere的其余模块,如:分布式事务、调用链路追踪等,会订阅感兴趣的事件,并进行相应的处理。

   b. 归并结果集生成

Sharding-Sphere经过在执行准备阶段的获取的链接模式,生成内存归并结果集或流式归并结果集,并将其传递至结果归并引擎,以进行下一步的工做。内存归并结果集或流式归并结果集的核心区别是:流式归并结果集会经过游标方式获取结果集的数据,而内存归并结果集则是从内存里获取数据。这也是内存归并和流式归并的数据基础。

 

经过上述全部步骤就完成了自动化执行引擎的执行流程。其核心目的是自动化平衡数据库链接建立以及结果归并模式选择问题,实现细粒度地平衡把控每一次查询的资源控制与执行效率,从而减小用户的使用学习成本和业务场景变化的担心。

相关文章
相关标签/搜索