默默学Sharding-Sphere(二)

从上篇了解Sharding-Sphere的一个背景、定位以及规划后,我再去看了下官方文档,发现sharding-sphere活跃还是有原因的,文档一个字:详细。这里我大致拆分成两份:说明文档、使用文档。

说明文档

背景

传统的将数据集中存储至单一数据节点的解决方案,在性能、可用性和运维成本这三方面已经难于满足互联网的海量数据场景。
从性能方面来说,由于关系型数据库大多采用B+树类型的索引,在数据量超过阈值的情况下,索引深度的增加也将使得磁盘访问的IO次数增加,进而导致查询性能的下降;同时,高并发访问请求也使得集中式数据库成为系统的最大瓶颈。
从可用性的方面来讲,服务化的无状态型,能够达到较小成本的随意扩容,这必然导致系统的最终压力都落在数据库之上。而单一的数据节点,或者简单的主从架构,已经越来越难以承担。数据库的可用性,已成为整个系统的关键。
数据分片指按照某个维度将存放在单一数据库中的数据分散地存放至多个数据库或表中以达到提升性能瓶颈以及可用性的效果。 数据分片的有效手段是对关系型数据库进行分库和分表。分库和分表均可以有效的避免由数据量超过可承受阈值而产生的查询瓶颈。 除此之外,分库还能够用于有效的分散对数据库单点的访问量;分表虽然无法缓解数据库压力,但却能够提供尽量将分布式事务转化为本地事务的可能,一旦涉及到跨库的更新操作,分布式事务往往会使问题变得复杂。 使用多主多从的分片方式,可以有效的避免数据单点,从而提升数据架构的可用性。
通过分库和分表进行数据的拆分来使得各个表的数据量保持在阈值以下,以及对流量进行疏导应对高访问量,是应对高并发和海量数据系统的有效手段。 数据分片的拆分方式又分为垂直分片和水平分片。

概念

垂直分片

按照业务拆分的方式称为垂直分片,又称为纵向拆分,它的核心理念是专库专用。垂直分片往往需要对架构和设计进行调整。通常来讲,是来不及应对互联网业务需求快速变化的;而且,它也并无法真正的解决单点瓶颈。 垂直拆分可以缓解数据量和访问量带来的问题,但无法根治。如果垂直拆分之后,表中的数据量依然超过单节点所能承载的阈值,则需要水平分片来进一步处理。

水平分片

水平分片又称为横向拆分。通过某个字段(或某几个字段),根据某种规则将数据分散至多个库或表中,每个分片仅包含数据的一部分。水平分片从理论上突破了单机数据量处理的瓶颈,并且扩展相对自由,是分库分表的标准解决方案。

逻辑表

对于水平拆分的数据库(表)的同一类表的总称。如:t_order。

真实表

在分片的数据库中真实存在的物理表。即上个示例中的t_order_0到t_order_9。

数据节点

数据分片的最小单元。由数据源名称和数据表组成,例:ds_0.t_order_0。

绑定表

指分片规则一致的主表和子表。例如:t_order表和t_order_item表,均按照订单ID分片,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。
所有路由计算将会只使用主表的策略,那么t_order_item表的分片计算将会使用t_order的条件。故绑定表之间的分区键要完全相同。

逻辑索引

某些数据库(如:PostgreSQL)不允许同一个库存在名称相同索引,某些数据库(如:MySQL)则允许只要同一个表中不存在名称相同的索引即可。逻辑索引用于同一个库不允许出现相同索引名称的分表场景,需要将同库不同表的索引名称改写为索引名 + 表名,改写之前的索引名称成为逻辑索引。

分片键

用于分片的数据库字段,是将数据库(表)水平拆分的关键字段。SQL中如果无分片字段,将执行全路由,性能较差,支持多分片字段。

分片算法

通过分片算法将数据分片,支持通过=、BETWEEN和IN分片。分片算法需要应用方开发者自行实现,可实现的灵活度非常高。
目前提供4种分片算法:
● 精确分片算法
● 范围分片算法
● 复合分片算法
● Hint分片算法

分片策略

包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 + 分片算法,也就是分片策略。目前提供5种分片策略:
● 标准分片策略
● 复合分片策略
● 行表达式分片策略
● Hint分片策略
● 不分片策略

SQL Hint

对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。例:内部系统,按照员工登录ID分库,而数据库中并无此字段。SQL Hint支持通过Java API和SQL注释(待实现)两种方式使用。

分片规则

分片规则配置的总入口。包含数据源配置、表配置、绑定表配置以及读写分离配置等。

配置

数据源配置

真实数据源列表。

表配置

逻辑表名称、数据节点与分表规则的配置。

数据节点配置

用于配置逻辑表与真实表的映射关系。可分为均匀分布和自定义分布两种形式。
● 均匀分布(指数据表在每个数据源内呈现均匀分布的态势)
db0
├── t_order0
└── t_order1
db1
├── t_order0
└── t_order1
● 自定义分布(指数据表呈现有特定规则的分布)
db0
├── t_order0
└── t_order1
db1
├── t_order2
├── t_order3
└── t_order4

分片策略配置

● 数据源分片策略
对应于DatabaseShardingStrategy。用于配置数据被分配的目标数据源。
● 表分片策略
对应于TableShardingStrategy。用于配置数据被分配的目标表,该目标表存在与该数据的目标数据源内。故表分片策略是依赖与数据源分片策略的结果的。
两种策略的API完全相同。

自增主键生成策略

通过在客户端生成自增主键替换以数据库原生自增主键的方式,做到分布式主键无重复。

Config Map

配置分库分表数据源的元数据,可通过调用ConfigMapContext.getInstance()获取ConfigMap中的shardingConfig数据。例:如果机器权重不同则流量可能不同,可通过ConfigMap配置机器权重元数据。

数据分片主要流程

核心由SQL解析 => 执行器优化 => SQL路由 => SQL改写 => SQL执行 => 结果归并的流程组成。
在这里插入图片描述

SQL解析

分为词法解析和语法解析。 先通过词法解析器将SQL拆分为一个个不可再分的单词。再使用语法解析器对SQL进行理解,并最终提炼出解析上下文。 解析上下文包括表、选择项、排序项、分组项、聚合函数、分页信息、查询条件以及可能需要修改的占位符的标记。

执行器优化

合并和优化分片条件,如OR等。

SQL路由

根据解析上下文匹配用户配置的分片策略,并生成路由路径。目前支持分片路由和广播路由。

SQL改写

将SQL改写为在真实数据库中可以正确执行的语句。SQL改写分为正确性改写和优化改写。

SQL执行

通过多线程执行器异步执行。

结果归并

将多个执行结果集归并以便于通过统一的JDBC接口输出。结果归并包括流式归并、内存归并和使用装饰者模式的追加归并这几种方式。

解析引擎

抽象语法树

为了便于理解,抽象语法树中的关键字的Token用绿色表示,变量的Token用红色表示,灰色表示需要进一步拆分。
最后,通过对抽象语法树的遍历去提炼分片所需的上下文,并标记有可能需要改写的位置。 供分片使用的解析上下文包含查询选择项(Select Items)、表信息(Table)、分片条件(Sharding Condition)、自增主键信息(Auto increment Primary Key)、排序信息(Order By)、分组信息(Group By)以及分页信息(Limit、Rownum、Top)。 SQL的一次解析过程是不可逆的,一个个Token的按SQL原本的顺序依次进行解析,性能很高。 考虑到各种数据库SQL方言的异同,在解析模块提供了各类数据库的SQL方言字典。
在这里插入图片描述

SQL解析引擎

SQL解析作为分库分表类产品的核心,其性能和兼容性是最重要的衡量指标。目前常见的SQL解析器主要有fdb,jsqlparser和Druid。 Sharding-Sphere的前身,Sharding-Sphere在1.4.x之前的版本使用Druid作为SQL解析器。经实际测试,它的性能远超其它解析器。
从1.5.x版本开始,Sharding-Sphere采用完全自研的SQL解析引擎。 由于目的不同,Sharding-Sphere并不需要将SQL转为一颗完全的抽象语法树,也无需通过访问器模式进行二次遍历。 它采用对SQL半理解的方式,仅提炼数据分片需要关注的上下文,因此SQL解析的性能和兼容性得到了进一步的提高。
在最新的3.x版本中,Sharding-Sphere尝试使用ANTLR作为SQL解析的引擎,并计划根据DDL -> TCL -> DAL –> DCL -> DML –>DQL这个顺序,依次替换原有的解析引擎。 使用ANTLR的原因是希望Sharding-Sphere的解析引擎能够更好的对SQL进行兼容。对于复杂的表达式、递归、子查询等语句,虽然Sharding-Sphere的分片核心并不关注,但是会影响对于SQL理解的友好度。 经过实例测试,ANTLR解析SQL的性能比自研的SQL解析引擎慢3倍左右。为了弥补这一差距,Sharding-Sphere将使用PreparedStatement的SQL解析的语法树放入缓存。因此建议采用PreparedStatement这种SQL预编译的方式提升性能。
Sharding-Sphere会提供配置项,将两种解析引擎共存,交由用户抉择SQL解析的兼容性与性能。

路由引擎

根据解析上下文匹配数据库和表的分片策略,并生成路由路径。 对于携带分片键的SQL,根据分片键的不同可以划分为单片路由(分片键的操作符是等号)、多片路由(分片键的操作符是IN)和范围路由(分片键的操作符是BETWEEN)。 不携带分片键的SQL则采用广播路由。
分片策略通常可以采用由数据库内置或由用户方配置。 数据库内置的方案较为简单,内置的分片策略大致可分为尾数取模、哈希、范围、标签、时间等。 由用户方配置的分片策略则更加灵活,可以根据使用方需求定制复合分片策略。

分片路由

用于根据分片键进行路由的场景,又细分为直接路由、标准路由和笛卡尔积路由这3种类型。
● 满足直接路由的条件相对苛刻,它需要通过Hint(使用HintAPI直接指定路由至库表)方式分片,并且是只分库不分表的前提下,则可以避免SQL解析和之后的结果归并。
● 标准路由是Sharding-Sphere最为推荐使用的分片方式,它的适用范围是不包含关联查询或仅包含绑定表之间关联查询的SQL。
● 笛卡尔积路由是最复杂的情况,它无法根据绑定表的关系定位分片规则,因此非绑定表之间的关联查询需要拆解为笛卡尔积组合执行。

广播路由

对于不携带分片键的SQL,则采取广播路由的方式。根据SQL类型又可以划分为全库广播路由、全库表广播路由、全实例广播路由、单播路由和阻断路由这5种类型。
● 全库广播路由用于处理对数据库的操作,包括用于库设置的SET类型的数据库管理命令,以及TCL这样的事务控制语句。
● 全库表广播路由用于处理对数据库中与其逻辑表相关的所有真实表的操作,主要包括不带分片键的DQL和DML,以及DDL等。
● 全实例广播路由用于DCL操作,授权语句针对的是数据库的实例。无论一个实例中包含多少个Schema,每个数据库的实例只执行一次。
● 单播路由用于获取某一真实表信息的场景,它仅需要从任意库中的任意真实表中获取数据即可。
● 阻断路由用于屏蔽SQL对数据库的操作。
在这里插入图片描述

改写引擎

工程师面向逻辑库与逻辑表书写的SQL,并不能够直接在真实的数据库中执行,SQL改写用于将逻辑SQL改写为在真实数据库中可以正确执行的SQL。

正确性改写

在包含分表的场景中,需要将分表配置中的逻辑表名称改写为路由之后所获取的真实表名称。除此之外,还包括补列和分页信息修正等内容。

标识符改写

需要改写的标识符包括表名称、索引名称以及Schema名称。

补列

需要在查询语句中补列通常由两种情况导致。 第一种情况是Sharding-Sphere需要在结果归并时获取相应数据,但该数据并能未通过查询的SQL返回。 这种情况主要是针对GROUP BY和ORDER BY。结果归并时,需要根据GROUP BY和ORDER BY的字段项进行分组和排序,但如果原始SQL的选择项中若并未包含分组项或排序项,则需要对原始SQL进行改写。
最后的一种补列是在INSERT的SQL时,如果使用数据库自增主键,是无需写入主键字段的。 但数据库的自增主键是无法满足分布式场景下的主键唯一的,因此Sharding-Sphere提供了分布式自增主键的生成策略,并且可以通过补列,让使用方无需改动现有代码,即可将分布式自增主键透明的替换数据库现有的自增主键。

分页修正

从多个数据库获取分页数据与单数据库的场景是不同的。 假设每10条数据为一页,取第2页数据。在分片环境下获取LIMIT 10, 10,归并之后再根据排序条件取出前10条数据是不正确的。

批量拆分

在使用批量插入的SQL时,如果插入的数据是跨分片的,那么需要对SQL进行改写来防止将多余的数据写入到数据库中。

优化改写

优化改写的目的是在不影响查询正确性的情况下,对性能进行提升的有效手段。它分为单节点优化和流式归并优化。

单节点优化

它是指将路由至单节点的SQL停止改写的优化。

流式归并优化

它仅为包含GROUP BY的SQL增加ORDER BY以及和分组项相同的排序项和排序顺序,用于将内存归并转化为流式归并。
在这里插入图片描述

执行引擎

Sharding-Sphere采用一套自动化的执行引擎,负责将路由和改写完成之后的真实SQL安全且高效发送到底层数据源执行。 它不是简单地将SQL通过JDBC直接发送至数据源执行;也并非直接将执行请求放入线程池去并发执行。它更关注平衡数据源连接创建以及内存占用所产生的消耗,以及最大限度地合理利用并发等问题。 执行引擎的目标是自动化的平衡资源控制与执行效率。

连接模式

从资源控制的角度看,业务方访问数据库的连接数量应当有所限制。 它能够有效地防止某一业务操作过多的占用资源,从而将数据库连接的资源耗尽,以致于影响其他业务的正常访问。
针对上述场景,Sharding-Sphere提供了一种解决思路。 它提出了连接模式(Connection Mode)的概念,将其划分为内存限制模式(MEMORY_STRICTLY)和连接限制模式(CONNECTION_STRICTLY)这两种类型。

内存限制模式

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

连接限制模式

使用此模式的前提是数据库严格控制对其一次操作所耗费的连接数量。 如果实际执行的SQL需要对某数据库实例中的200张表做操作,那么只会创建唯一的数据库连接,并对其200张表串行处理。 如果分片在不同的数据库,仍然是多线程处理不同库,但每个库的每次操作仍然只创建一个唯一的数据库连接。 这样即可以防止对一次请求对数据库连接占用过多所带来的问题。该模式始终选择内存归并。
内存限制模式适用于OLAP操作,可以通过放宽对数据库连接的限制提升系统吞吐量; 连接限制模式适用于OLTP操作,OLTP通常带有分片键,会路由到单一的分片,因此严格控制数据库连接,以保证在线系统数据库资源能够被更多的应用所使用,是明智的选择。

自动化执行引擎

Sharding-Sphere最初将使用何种模式的决定权交由用户配置,让开发者依据自己业务的实际场景需求选择使用内存限制模式或连接限制模式。
这种一分为二的处理方案,将两种模式的切换交由静态的初始化配置,是缺乏灵活应对能力的。在实际的使用场景中,面对不同SQL以及占位符参数,每次的路由结果是不同的。 这就意味着某些操作可能需要使用内存归并,而某些操作则可能选择流式归并更优,它们不应该由用户在Sharding-Sphere启动之前配置好,而是应该根据SQL和占位符参数的场景,来动态的决定连接模式。
结果集分组是实现内化连接模式概念的关键。执行引擎根据maxConnectionSizePerQuery配置项,结合当前路由结果,选择恰当的连接模式。 具体步骤如下:

  1. 将SQL的路由结果按照数据源的名称进行分组。
  2. 通过下图的公式获得每个数据库实例在maxConnectionSizePerQuery的允许范围内,每个数据库连接需要执行的SQL路由结果组,并演算出本次请求最优的连接模式。
    在这里插入图片描述
    当数据源使用数据库连接池等控制数据库连接数量的技术时,在获取数据库连接时,如果不妥善处理并发,则有一定几率发送死锁。 在多个请求相互等待对方释放数据库连接资源时,将会产生饥饿等待,造成交叉的死锁问题。
    在这里插入图片描述
    Sharding-Sphere为了避免死锁的出现,在获取数据库连接时进行了同步处理。 它在创建执行单元时,以原子性的方式一次性获取本次SQL请求所需的全部数据库连接,杜绝了每次查询请求获取到部分资源的可能。 由于对数据库的操作非常频繁,每次获取数据库连接时时都进行锁定,会降低Sharding-Sphere的并发。因此,Sharding-Sphere在这里进行了2点优化:
  3. 避免锁定一次性只需要获取1个数据库连接的操作。因为每次仅需要获取1个连接,则不会发生两个请求相互等待的场景,无需锁定。 对于大部分OLTP的操作,都是使用分片键路由至唯一的数据节点,这会使得系统变为完全无锁的状态,进一步提升了并发效率。 除了路由至单分片的情况,读写分离也在此范畴之内。
  4. 仅针对内存限制模式时才进行资源锁定。在使用连接限制模式时,所有的查询结果集将在装载至内存之后释放掉数据库连接资源,因此不会产生死锁等待的问题。
    在这里插入图片描述

归并引擎

将从各个数据节点获取的多数据结果集,组合成为一个结果集并正确的返回至请求客户端,称为结果归并。
Sharding-Sphere支持的结果归并从功能上分为遍历、排序、分组、分页和聚合5种类型,它们是组合而非互斥的关系。 从结构划分,可分为流式归并、内存归并和装饰者归并。流式归并和内存归并是互斥的,装饰者归并可以在流式归并和内存归并之上做进一步的处理。
流式归并是指每一次从结果集中获取到的数据,都能够通过逐条获取的方式返回正确的单条数据,它与数据库原生的返回结果集的方式最为契合。遍历、排序以及流式分组都属于流式归并的一种。
内存归并则是需要将结果集的所有数据都遍历并存储在内存中,再通过统一的分组、排序以及聚合等计算之后,再将其封装成为逐条访问的数据结果集返回。
装饰者归并是对所有的结果集归并进行统一的功能增强,目前装饰者归并有分页归并和聚合归并这2种类型。

遍历归并

它是最为简单的归并方式。只需将多个数据结果集合并为一个单向链表即可。

排序归并

由于在SQL中存在ORDER BY语句,因此每个数据结果集自身是有序的,因此只需要将数据结果集当前游标指向的数据值进行排序即可。

分组归并

分组归并的情况最为复杂,它分为流式分组归并和内存分组归并。 流式分组归并要求SQL的排序项与分组项的字段以及排序类型(ASC或DESC)必须保持一致,否则只能通过内存归并才能保证其数据的正确性。

聚合归并

无论是流式分组归并还是内存分组归并,对聚合函数的处理都是一致的。 除了分组的SQL之外,不进行分组的SQL也可以使用聚合函数。 因此,聚合归并是在之前介绍的归并类的之上追加的归并能力,即装饰者模式。聚合函数可以归类为比较、累加和求平均值这3种类型。

分页归并

上文所述的所有归并类型都可能进行分页。 分页也是追加在其他归并类型之上的装饰器,Sharding-Sphere通过装饰者模式来增加对数据结果集进行分页的能力。 分页归并负责将无需获取的数据过滤掉。
在这里插入图片描述

SQL支持项

全面支持DQL、DML和DDL。支持分页、去重、排序、分组、聚合、关联查询(不支持跨库关联)。

SQL不支持项

不支持冗余括号、CASE WHEN、HAVING、UNION (ALL),有限支持子查询。

支持的SQL

SQL 必要条件
SELECT * FROM tbl_name
SELECT * FROM tbl_name WHERE (col1 = ? or col2 = ?) and col3 = ?
SELECT * FROM tbl_name WHERE col1 = ? ORDER BY col2 DESC LIMIT ?
SELECT COUNT(*), SUM(col1), MIN(col1), MAX(col1), AVG(col1) FROM tbl_name WHERE col1 = ?
SELECT COUNT(col1) FROM tbl_name WHERE col2 = ? GROUP BY col1 ORDER BY col3 DESC LIMIT ?, ?
INSERT INTO tbl_name (col1, col2,…) VALUES (?, ?, ….)
INSERT INTO tbl_name VALUES (?, ?,….)
INSERT INTO tbl_name (col1, col2, …) VALUES (?, ?, ….), (?, ?, ….)
UPDATE tbl_name SET col1 = ? WHERE col2 = ?
DELETE FROM tbl_name WHERE col1 = ?
CREATE TABLE tbl_name (col1 int, …)
ALTER TABLE tbl_name ADD col1 varchar(10)
DROP TABLE tbl_name
TRUNCATE TABLE tbl_name
CREATE INDEX idx_name ON tbl_name
DROP INDEX idx_name ON tbl_name
DROP INDEX idx_name TableRule中配置logic-index
SELECT DISTINCT * FROM tbl_name WHERE col1 = ? 在3.1.0支持,目前还未发布正式版本
SELECT COUNT(DISTINCT col1) FROM tbl_name 在3.1.0支持,目前还未发布正式版本

不支持的SQL

SQL 不支持原因
INSERT INTO tbl_name (col1, col2, …) SELECT col1, col2, … FROM tbl_name WHERE col3 = ? INSERT … SEL
INSERT INTO tbl_name SET col1 = ? INSERT … SET
SELECT COUNT(col1) as count_alias FROM tbl_name GROUP BY col1 HAVING count_alias > ? HAVING
SELECT * FROM tbl_name1 UNION SELECT * FROM tbl_name2 UNION
SELECT * FROM tbl_name1 UNION ALL SELECT * FROM tbl_name2 UNION ALL
SELECT * FROM tbl_name1 WHERE (val1=?) AND (val1=?) 冗余括号
SELECT * FROM ds.tbl_name1 包含schema
SELECT SUM(DISTINCT col1), SUM(col1) FROM tbl_name

DISTINCT支持情况(3.1.0发布)

支持的SQL

SQL Required condition
SELECT DISTINCT * FROM tbl_name WHERE col1 = ?
SELECT DISTINCT col1 FROM tbl_name
SELECT DISTINCT col1, col2, col3 FROM tbl_name
SELECT COUNT(DISTINCT col1) FROM tbl_name
SELECT SUM(DISTINCT col1) FROM tbl_name
SELECT DISTINCT col1 FROM tbl_name ORDER BY col1
SELECT DISTINCT col1 FROM tbl_name ORDER BY col2
SELECT COUNT(DISTINCT col1) FROM tbl_name GROUP BY col1
SELECT COUNT(DISTINCT col1), col1 FROM tbl_name GROUP BY col1

不支持的SQL

SQL 不支持原因
SELECT DISTINCT(col1) FROM tbl_name DISTINCT()
SELECT COUNT(DISTINCT col1 + col2) FROM tbl_name PLUS FUNCTION + DISTINCT
SELECT AVG(DISTINCT col1) FROM tbl_name AVG(DISTINCT)
SELECT COUNT(DISTINCT col1), SUM(DISTINCT col1) FROM tbl_name 同时使用2种DISTINCT的聚合函数
SELECT SUM(DISTINCT col1), SUM(col1) FROM tbl_name 同时使用普通聚合函数和DISTINCT聚合函数
SELECT col1, COUNT(DISTINCT col1) FROM tbl_name GROUP BY col1 COUNT(DISTINCT)和普通查询项的顺序问题

分页性能

性能瓶颈

查询偏移量过大的分页会导致数据库获取数据性能低下

分页方案优化

由于LIMIT并不能通过索引查询数据,因此如果可以保证ID的连续性,通过ID进行分页是比较好的解决方案

分页子查询

Oracle和SQLServer的分页都需要通过子查询来处理,Sharding-Sphere支持分页相关的子查询。
● Oracle
支持使用rownum进行分页,但目前不支持rownum + BETWEEN的分页方式。
● SQLServer
支持使用TOP + ROW_NUMBER() OVER配合进行分页,支持SQLServer 2012之后的OFFSET FETCH的分页方式。目前不支持使用WITH xxx AS (SELECT …)的方式进行分页。由于Hibernate自动生成的SQLServer分页语句使用了WITH语句,因此目前并不支持基于Hibernate的SQLServer分页。 目前也不支持使用两个TOP + 子查询的方式实现分页。
● MySQL, PostgreSQL
MySQL和PostgreSQL都支持LIMIT分页,无需子查询。

行表达式

语法说明

行表达式的使用非常直观,只需要在配置中使用 e x p r e s s i o n { expression }或 ->{ expression }标识行表达式即可。 目前支持数据节点和分片算法这两个部分的配置。行表达式的内容使用的是Groovy的语法,Groovy能够支持的所有操作,行表达式均能够支持。
${begin…end}表示范围区间
[ u n i t 1 , u n i t 2 , u n i t x ] {[unit1, unit2, unit_x]}表示枚举值 行表达式中如果出现连续多个 { expression }或$->{ expression }表达式,整个表达式最终的结果将会根据每个子表达式的结果进行笛卡尔组合。

配置分片算法

对于只有一个分片键的使用=和IN进行分片的SQL,可以使用行表达式代替编码方式配置。
行表达式内部的表达式本质上是一段Groovy代码,可以根据分片键进行计算的方式,返回相应的真实数据源或真实表名称。
例如:分为10个库,尾数为0的路由到后缀为0的数据源, 尾数为1的路由到后缀为1的数据源,以此类推。用于表示分片算法的行表达式为:
ds${id % 10}

分布式主键生成器

Sharding-Sphere提供灵活的配置分布式主键生成策略方式。 在分片规则配置模块可配置每个表的主键生成策略,默认使用雪花算法(snowflake)生成64bit的长整型数据。
雪花算法是由Twitter公布的分布式主键生成算法,它能够保证不同进程主键的不重复性,以及相同进程主键的有序性。
在同一个进程中,它首先是通过时间位保证不重复,如果时间相同则是通过序列位保证。 同时由于时间位是单调递增的,且各个服务器如果大体做了时间同步,那么生成的主键在分布式环境可以认为是总体有序的,这就保证了对索引字段的插入的高效性。例如MySQL的Innodb存储引擎的主键。
在这里插入图片描述

强制分片路由

Sharding-Sphere使用ThreadLocal管理分片键值。可以通过编程的方式向HintManager中添加分片条件,该分片条件仅在当前线程内生效。
除了通过编程的方式使用强制分片路由,Sharding-Sphere还计划通过SQL中的特殊注释的方式引用Hint,使开发者可以采用更加透明的方式使用该功能。
指定了强制分片路由的SQL将会无视原有的分片逻辑,直接路由至指定的真实数据节点。

分布式读写分离

在这里插入图片描述

主库

添加、更新以及删除数据操作所使用的数据库,目前仅支持单主库。

从库

查询数据操作所使用的数据库,可支持多从库。

主从同步

将主库的数据异步的同步到从库的操作。由于主从同步的异步性,从库与主库的数据会短时间内不一致。

负载均衡策略

通过负载均衡策略将查询请求疏导至不同从库。

Config Map

配置读写分离数据源的元数据,可通过调用ConfigMapContext.getInstance()获取ConfigMap中的masterSlaveConfig数据。例:如果机器权重不同则流量可能不同,可通过ConfigMap配置机器权重元数据。

核心功能

  1. 提供一主多从的读写分离配置,可独立使用,也可配合分库分表使用。
  2. 独立使用读写分离支持SQL透传。
  3. 同一线程且同一数据库连接内,如有写入操作,以后的读操作均从主库读取,用于保证数据一致性。
  4. 基于Hint的强制主库路由。

不支持项

  1. 主库和从库的数据同步。
  2. 主库和从库的数据同步延迟导致的数据不一致。
  3. 主库双写或多写。

配置中心

● 配置集中化:越来越多的运行时实例,使得散落的配置难于管理,配置不同步导致的问题十分严重。将配置集中于配置中心,可以更加有效进行管理。
● 配置动态化:配置修改后的分发,是配置中心可以提供的另一个重要能力。它可支持数据源、表与分片及读写分离策略的动态切换。

配置中心数据结构

配置中心在定义的命名空间的config下,以YAML格式存储,包括数据源,分库分表,读写分离、ConfigMap及Properties配置,可通过修改节点来实现对于配置的动态管理。
config
├──datasource # 数据源配置
├──sharding # 分库分表(包括分库分表+读写分离)配置根节点
├ ├──rule # 分库分表(包括分库分表+读写分离)规则
├ ├──configmap # 分库分表ConfigMap配置,以K/V形式存储
├ ├──props # 属性配置
├──masterslave # 读写分离独立使用配置
├ ├──rule # 读写分离规则
├ ├──configmap # 读写分离ConfigMap配置,以K/V形式存储
├ ├──props # 属性配置

config/datasource

多个数据库连接池的集合,不同数据库连接池属性自适配(例如:DBCP,C3P0,Druid, HikariCP)。

ds_0: !!org.apache.commons.dbcp.BasicDataSource
  defaultAutoCommit: true
  defaultCatalog:
  defaultReadOnly: false
  defaultTransactionIsolation: -1
  driverClassName: com.mysql.jdbc.Driver
  initialSize: 0
  logAbandoned: false
  maxActive: 8
  maxIdle: 8
  maxOpenPreparedStatements: -1
  maxWait: -1
  minEvictableIdleTimeMillis: 1800000
  minIdle: 0
  numTestsPerEvictionRun: 3
  password: ''
  removeAbandoned: false
  removeAbandonedTimeout: 300
  testOnBorrow: false
  testOnReturn: false
  testWhileIdle: false
  timeBetweenEvictionRunsMillis: -1
  url: jdbc:mysql://localhost:3306/demo_ds_0
  username: root
  validationQuery:
  validationQueryTimeout: -1
ds_1: !!org.apache.commons.dbcp.BasicDataSource
  defaultAutoCommit: true
  defaultCatalog:
  defaultReadOnly: false
  defaultTransactionIsolation: -1
  driverClassName: com.mysql.jdbc.Driver
  initialSize: 0
  logAbandoned: false
  maxActive: 8
  maxIdle: 8
  maxOpenPreparedStatements: -1
  maxWait: -1
  minEvictableIdleTimeMillis: 1800000
  minIdle: 0
  numTestsPerEvictionRun: 3
  password: ''
  removeAbandoned: false
  removeAbandonedTimeout: 300
  testOnBorrow: false
  testOnReturn: false
  testWhileIdle: false
  timeBetweenEvictionRunsMillis: -1
  url: jdbc:mysql://localhost:3306/demo_ds_1
  username: root
  validationQuery:
  validationQueryTimeout: -1

config/sharding/rule

分库分表配置,包括分库分表 + 读写分离配置。

tables:
  t_order:
    actualDataNodes: ds_$->{0..1}.t_order_$->{0..1}
    keyGeneratorColumnName: order_id
    logicTable: t_order
    tableStrategy:
      inline:
        algorithmExpression: t_order_$->{order_id % 2}
        shardingColumn: order_id
  t_order_item:
    actualDataNodes: ds_$->{0..1}.t_order_item_$->{0..1}
    keyGeneratorColumnName: order_item_id
    logicTable: t_order_item
    tableStrategy:
      inline:
        algorithmExpression: t_order_item_$->{order_id % 2}
        shardingColumn: order_id
bindingTables:
  - t_order,t_order_item
broadcastTables:
  - t_config
  
defaultDataSourceName: ds_0
defaultDatabaseStrategy:
  inline:
    algorithmExpression: ds_$->{user_id % 2}
    shardingColumn: user_id
    
masterSlaveRules: {}

config/sharding/configmap

分库分表ConfigMap配置,以K/V形式存储。
key1: value1

config/sharding/props

相对于sharding-sphere配置里面的Sharding Properties。
executor.size: 20
sql.show: true

config/masterslave/rule

读写分离独立使用时使用该配置。
name: ds_ms
masterDataSourceName: ds_master
slaveDataSourceNames:

  • ds_slave0
  • ds_slave1
    loadBalanceAlgorithmType: ROUND_ROBIN

config/masterslave/configmap

读写分离ConfigMap配置,以K/V形式存储。
key2: value2

编排治理

通过注册中心,提供熔断数据库访问程序对数据库的访问和禁用从库的访问的能力。数据治理仍然有大量未完成的功能。

注册中心数据结构

注册中心在定义的命名空间的state下,创建数据库访问对象运行节点,用于区分不同数据库访问实例。包括instances和datasources节点。
instances
├──your_instance_ip_a@-@your_instance_pid_x
├──your_instance_ip_b@-@your_instance_pid_y
├──…
datasources
├──ds0
├──ds1
├──…

Sharding-Proxy支持多逻辑数据源,因此datasources子节点的名称采用schema_name.data_source_name的形式。
instances
├──your_instance_ip_a@-@your_instance_pid_x
├──your_instance_ip_b@-@your_instance_pid_y
├──…
datasources
├──sharding_db.ds0
├──sharding_db.ds1
├──…

state/instances

数据库访问对象运行实例信息,子节点是当前运行实例的标识。运行实例标识由运行服务器的IP地址和PID构成。运行实例标识均为临时节点,当实例上线时注册,下线时自动清理。注册中心监控这些节点的变化来治理运行中实例对数据库的访问等。

state/datasources

可以治理读写分离从库,可动态添加删除以及禁用。

操作指南

熔断实例

可在IP地址@-@PID节点写入DISABLED(忽略大小写)表示禁用该实例,删除DISABLED表示启用。
Zookeeper命令如下:
[zk: localhost:2181(CONNECTED) 0] set /your_zk_namespace/your_app_name/state/instances/your_instance_ip_a@-@your_instance_pid_x DISABLED

Etcd命令如下:
etcdctl set /your_app_name/state/instances/your_instance_ip_a@-@your_instance_pid_x DISABLED

禁用从库

在读写分离(或分库分表+读写分离)场景下,可在数据源名称子节点中写入DISABLED表示禁用从库数据源,删除DISABLED或节点表示启用。(2.0.0.M3及以上版本支持)。
Zookeeper命令如下:
[zk: localhost:2181(CONNECTED) 0] set /your_zk_namespace/your_app_name/state/datasources/your_slave_datasource_name DISABLED

Etcd命令如下:
etcdctl set /your_app_name/state/datasources/your_slave_datasource_name DISABLED

支持的注册中心

SPI

Sharding-Sphere在数据库治理模块使用SPI方式载入注册中心,进行实例熔断和数据库禁用。 目前,Sharding-Sphere内部支持Zookeeper和Etcd两种常用的注册中心。

应用性能监控

Sharding-Sphere可以通过两种方式对接应用性能监控系统。第一种方式是使用OpenTracing API发送性能追踪数据。第二种方式是使用SkyWalking的自动探针。

使用OpenTracing协议

● 通过读取系统参数注入APM系统提供的Tracer实现类
启动时添加参数:-Dio.shardingsphere.opentracing.tracer.class=org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer
调用初始化方法:ShardingTracer.init()

● 通过参数注入APM系统提供的Tracer实现类
shardingTracer.init(new SkywalkingTracer())

注意:使用SkyWalking的OpenTracing探针时,应将原Sharding-Sphere探针插件禁用,以防止两种插件互相冲突

分布式事务

本地事务 XA强一致事务 柔性事务
业务改造 实现相关接口
一致性 不支持 支持 最终一致
隔离性 不支持 支持 业务方保证
并发性能 无影响 严重衰退 略微衰退
适合场景 业务方处理不一致 短事务 & 低并发 长事务 & 高并发

本地事务

支持情况
● Sharding-JDBC可以支持由用户自行配置不使用XA数据源
● Sharding-Proxy无需支持,使用XA或柔性事务即可

XA事务

支持情况
● Sharding-JDBC可以支持由用户自行配置XA数据源
● Sharding-Proxy支持

BASE事务

支持情况
● Sharding-JDBC预计3.1.0支持
● Sharding-Proxy预计3.1.0支持

总结

通篇我们需要了解sharding-sphere的概念,包括:分片、表、数据节点、策略等,同时我们需要知道如何配置。数据分片的主要流程,以及各步骤的主要内容。目前SQL支持情况、事务支持情况、主从支持情况等。

猜你喜欢

转载自blog.csdn.net/caohao1210/article/details/85108118
今日推荐