从上篇了解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决定,而由其他外置条件决定的场景,可使用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完全相同。
通过在客户端生成自增主键替换以数据库原生自增主键的方式,做到分布式主键无重复。
配置分库分表数据源的元数据,可通过调用ConfigMapContext.getInstance()获取ConfigMap中的shardingConfig数据。例:如果机器权重不同则流量可能不同,可通过ConfigMap配置机器权重元数据。
核心由SQL解析 => 执行器优化 => SQL路由 => SQL改写 => SQL执行 => 结果归并的流程组成。
分为词法解析和语法解析。 先通过词法解析器将SQL拆分为一个个不可再分的单词。再使用语法解析器对SQL进行理解,并最终提炼出解析上下文。 解析上下文包括表、选择项、排序项、分组项、聚合函数、分页信息、查询条件以及可能需要修改的占位符的标记。
合并和优化分片条件,如OR等。
根据解析上下文匹配用户配置的分片策略,并生成路由路径。目前支持分片路由和广播路由。
将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解析器主要有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ÿ