Skip to content

DasClient 分库分表策略设计

Shengyuan Lu 卢声远 edited this page Jun 29, 2020 · 10 revisions

简介

分库分表的原理是解析SQL中的表达式来确定分片范围要求。既支持>, >=,<, <=, <>, between[a, b], in (n1, n2, ...nn), not in (...), like, not like, is null, is not null

需要为这种范围表达式提供分库分表的计算支持

适用范围

策略支持所有基于entity的操作,基于SqlBuilder和SegementConstants单个表达式元素构造的Sql语句。

暂时不支持直接使用SQL语句的方式。后期如果支持Sql解析,会再支持原生语句的方式。

策略使用场景

对SqlBuilder的表达式部分做判断,如果是上述范围表达式,则:

  • 按照上述计算方法获取分库范围。
  • 将所有分库范围合并
  • 按照上述方法获取分表范围。
  • 将当前分库下面的所有分表范围合并
  • 按照目前对IN操作的支持办法执行数据库命令

与携程策略的异同

携程现有的策略已经可以针对给定的数值,计算相应的分片,但携程的分库分表策略只支持数值匹配,和按照in数值列表分片。在实际使用中具有较大限制,而且也不方便,直观。

方案

前提

能够按照范围进行分库分表计算,要求分片值,无论是表还是库的分片值,都必须是可比较,可排序的。

分片既可以在数据库层面,也可以在表层面

分片字段可以是一个也可以是多个。因此策略的抽象设计应考虑多个字段的情况

分片的模式是先对数据库分片,再对表分片。因此计算表分片的时候应该提供当前数据库分片信息

在同一个数据库分片中,表分片的方式是一样的。意味着如果表A和表B在同一个逻辑数据库中,并且都进行了分表,则表A的分片数量和分片方式与表B相同。在做连接查询的时候,表A与表B的物理分表按照统一的方式构造

设计

总体设计如下:

strategy design

ShardingStrategy

定义顶层分库分表逻辑,将SQL的表达式,参数和其他必要信息作为参数计算分片范围。该接口设计不假设分片字段是否是一个还是多个,该接口应该可以保证在最宽泛的范围和最抽象的级别适配所有分库分表的情况

API 含义
void initialize(Map<String, String> settings); 初始化
boolean isShardByDb(); 是否支持数据库分片
Set locateDbShards(ShardingContext ctx); 获取当前操作涉及的所有数据库分片
boolean isShardByTable(); 是否支持表分片
Set getAllTableShards(); 获取所有表分片范围
boolean isShardingEnable(String logicTableName); 当前表是否支持表分片
Set locateTableShards(TableShardingContext ctx); 获取当前操作涉及的所有表分片
String getTableName(String logicTableName, String shard); 获取实际表名

AbstractShardingStrategy

提供ShardingStrategy大部分事务性方法的缺省实现,方便扩展:

  • initialize()。通过传入的配置,初始化分表的表名范围,分表时使用的表名与分片值之间的分隔符
  • isShardByDb()
  • isShardByTable()
  • isShardingEnable(String)
  • getAllTableShards()
  • getTableName(String, String)

定义了如下protected方法让子类设置通用属性来支持上述方法:

  • setShardedTables(Set)
  • setSeparator(String)
  • setShardByDb(boolean)
  • setShardByTable(boolean)
  • setAllTableShards(Set)

子类需要实现:

  • locateDbShards(ShardingContext)
  • locateTableShards(TableShardingContext)

AbstractConditionStrategy

通过聚合单个条件所确定的分片范围来确定总体的分片范围的通用逻辑。

分片范围合并规则

表达式可以通过AND, OR和括号()进行自由组合。分片合并规则如下:

  • AND:取每个表达式对应分片的交集
  • OR:取每个表达式对应分片的并集

子类需要实现:

  • locateDbShardsByValue(ShardingContext, Object)
  • locateDbShards(ConditionContext)
  • locateTableShardsByValue(TableShardingContext, Object)
  • locateTableShards(TableConditionContext)

AdvancedModStrategy

预定义的基于取模算法的分库分表策略。支持所有的表达式类型。如果表达式是可以精确确定分片列值的范围的=, in, between,则取范围中所有分片的合集,否则返回所有分片。

属性配置:

属性 说明
shardByDb 是否分库。值为 truefalse
shardByTable 是否分表。值为 truefalse
shardedTables 需分表的表名,以逗号分隔。譬如填写 aTable,bTable 就说明这个库里面只有表 aTable 和表 bTable 需要分表
separator 分表表名和序号的分隔符,譬如aTable_0,aTable_1...那么separator就是下划线_
mod 分库的取模数,譬如配置为 10 ,表示分库以10取模
tableMod 分表的取模数,譬如配置为 10 ,表示分表以10取模
columns 分库的取模的列名字,譬如配置为 userID ,表示根据userID取模分库
tableColumns 分表的取模的列名字,譬如配置为 customerID ,表示根据customerID取模分表

这些属性除了 shardByDbshardByTable 必填,其他需根据分库分表配置特点选择配置。譬如,只分库不分表的话,separatortableModtableColumns 不需要配;只分表不分库的话,modcolumns 不需要配。

HintsStrategy

预定义的基于用户直接指定分片值的方式进行分片操作的策略。目前只支持指定库或表的分片值,暂时不支持同时指定多个分片。

基于表达式的分片策略帮助类

每种表达式对应的计算分片的方式各不相同,为了抽象并方便用户提供自己的表达式分片方法

表达式范围与一般计算方法

IN

按照每个值,计算分片值,返回范围【目前core已经支持,待开放】

BETWEEN

按照上界和下界数值计算出的分片值,在给定的分片值全集里面选取落在上下界分片值之间的所有分片值。【包括上下界的值?】

>, >=,<, <=,<>

按照参数计算出的分片值,在给定的分片值全集里面按照操作符选取符合要求的所有分片值。

like,not like,is null,is not null

目前看来用例基本没有,暂时不考虑。需要的话再扩展

最少支持方法

在不考虑like,not like,is null, is not null的情况下,仅需=,>,<,between即可支持所有的运算。具体规则如下:

操作符 实现方式
EQUAL locateForEqual
NOT_EQUAL locateForLessThan + locateForGreaterThan
GREATER_THAN locateForGreaterThan
GREATER_THAN_OR_EQUAL locateForEqual + locateForGreaterThan
LESS_THAN locateForLessThan
LESS_THAN_OR_EQUAL locateForEqual + locateForLessThan
IN For each value execute locateForEqual and combine
NOT_IN For each value execute NOT_EQUAL and combine
BEWTEEN locateForBetween
NOT_BETWEEN locateForLessThan(lower value) + locateForGreaterThan(upper value)

NOT操作

DAS在生成表达式的时候,如果遇到操作符前面包含NOT运算,会将NOT运算通过表达式表换来消除掉。这样做的目的是为了减少用户实际需要处理的情况。

单一表达式NOT消除规则:

原操作符 反向操作符
EQUAL NOT_EQUAL
GREATER_THAN LESS_THAN_OR_EQUAL
GREATER_THAN_OR_EQUAL LESS_THAN
IN NOT_IN
BEWTEEN NOT_BETWEEN
LIKE NOT_LIKE
IS_NULL IS_NOT_NULL

组合表达式的NOT消除规则

  • NOT (A AND B)–> NOT A OR NOT B
  • NOT (A OR B)–> NOT A AND NOT B

上述规则可用直接用于表达式嵌套的情况。

总体设计方案

利用抽象类来实现基于表达式的分片范围获取的从一般到特殊的过程

shard locator design

AbstractConditionShardLocator

该类提供locateShards方法,根据传入CTX中的操作符调用下列抽象方法:

其中CTX是ConditionContext或其子类

表达式 方法
= public abstract Set locateForEqual(CTX context);
<> public abstract Set locateForNotEqual(CTX context);
> public abstract Set locateForGreaterThan(CTX context);
>= public abstract Set locateForGreaterThanOrEqual(CTX context);
< public abstract Set locateForLessThan(CTX context);
<= public abstract Set locateForLessThanOrEqual(CTX context);
BETWEEN public abstract Set locateForBetween(CTX context);
NOT BEWEEN public abstract Set locateForNotBetween(CTX context);
IN public abstract Set locateForIn(CTX context);
NOT IN public abstract Set locateForNotIn(CTX context);
LIEK public abstract Set locateForLike(CTX context);
NOT LIKE public abstract Set locateForNotLike(CTX context);
IS NULL public abstract Set locateForIsNull(CTX context);
IS NOT NULL public abstract Set locateForIsNotNull(CTX context);

该类还提供了下列帮助方法:

方法 说明
getAllShards(CTX) 获取当前CTX的所有分片值
createConditionContext(CTX, OperatorEnum, Object) 基于CTX创建指定操作符的表达式上下文
locateForCombination(CTX, OperatorEnum, Object, OperatorEnum, Object) 取两个表达式所确定分片范围的并集
locateForIntersection(CTX, OperatorEnum, Object, OperatorEnum, Object) 取两个表达式所确定分片范围的交集
exclude(CTX, Set) 返回CTX所有分片范围去掉传入的分片范围
isAlreadyAllShards(Set, Set) 判断两个Set是否相等
toSet(String) 将单个字符串放入Set

AbstractCommonShardLocator

扩展自AbstractConditionShardLocator,基于最基本的几个条件表达式的数据库分片策略,包括:

API 含义
locateForEqual 确定"="操作涉及的分片范围
locateForGreaterThan 确定">"操作涉及的分片范围
locateForLessThan 确定"<"操作涉及的分片范围
locateForBetween 确定BETWEEN操作涉及的分片范围

其他表达式求分片方法逻辑如下:

表达式 方法 逻辑
<> public abstract Set locateForNotEqual(CTX context); >和<的并集
>= public abstract Set locateForGreaterThanOrEqual(CTX context); >和=的并集
<= public abstract Set locateForLessThanOrEqual(CTX context); =和<的并集
NOT BEWEEN public abstract Set locateForNotBetween(CTX context); <下界和>上界的并集
IN public abstract Set locateForIn(CTX context); 每个元素=的并集
NOT IN public abstract Set locateForNotIn(CTX context); 每个元素<>的并集
LIEK public abstract Set locateForLike(CTX context); 所有分片
NOT LIKE public abstract Set locateForNotLike(CTX context); 所有分片
IS NULL public abstract Set locateForIsNull(CTX context); 所有分片
IS NOT NULL public abstract Set locateForIsNotNull(CTX context); 所有分片

ModShardLocator

基于取模算法的locator,实现了父类的:

  • locateForEqual(ConditionContext)
  • locateForGreaterThan(CTX)
  • locateForLessThan(CTX)
  • locateForBetween(ConditionContext)

用户自定义策略建议

分片字段为一个的情况

这是最常见的场景,推荐自定义策略继承AbstractConditionStrategy,将对每个条件的判断delegate给AbstractConditionShardLocator的实现

分片字段为多个或者依赖其他条件的情况

需用户自己实现ShardingStrategy接口里面的逻辑

Clone this wiki locally