-
Notifications
You must be signed in to change notification settings - Fork 55
DasClient 分库分表策略设计
分库分表的原理是解析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的物理分表按照统一的方式构造
总体设计如下:
定义顶层分库分表逻辑,将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); | 获取实际表名 |
提供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)
通过聚合单个条件所确定的分片范围来确定总体的分片范围的通用逻辑。
表达式可以通过AND, OR和括号()进行自由组合。分片合并规则如下:
- AND:取每个表达式对应分片的交集
- OR:取每个表达式对应分片的并集
子类需要实现:
- locateDbShardsByValue(ShardingContext, Object)
- locateDbShards(ConditionContext)
- locateTableShardsByValue(TableShardingContext, Object)
- locateTableShards(TableConditionContext)
预定义的基于取模算法的分库分表策略。支持所有的表达式类型。如果表达式是可以精确确定分片列值的范围的=, in, between,则取范围中所有分片的合集,否则返回所有分片。
属性 | 说明 |
---|---|
shardByDb | 是否分库。值为 true 或 false
|
shardByTable | 是否分表。值为 true 或 false
|
shardedTables | 需分表的表名,以逗号分隔。譬如填写 aTable,bTable 就说明这个库里面只有表 aTable 和表 bTable 需要分表 |
separator | 分表表名和序号的分隔符,譬如aTable_0,aTable_1...那么separator就是下划线_
|
mod | 分库的取模数,譬如配置为 10 ,表示分库以10取模 |
tableMod | 分表的取模数,譬如配置为 10 ,表示分表以10取模 |
columns | 分库的取模的列名字,譬如配置为 userID ,表示根据userID取模分库 |
tableColumns | 分表的取模的列名字,譬如配置为 customerID ,表示根据customerID取模分表 |
这些属性除了 shardByDb 和 shardByTable 必填,其他需根据分库分表配置特点选择配置。譬如,只分库不分表的话,separator,tableMod 和 tableColumns 不需要配;只分表不分库的话,mod 和 columns 不需要配。
预定义的基于用户直接指定分片值的方式进行分片操作的策略。目前只支持指定库或表的分片值,暂时不支持同时指定多个分片。
每种表达式对应的计算分片的方式各不相同,为了抽象并方便用户提供自己的表达式分片方法
按照每个值,计算分片值,返回范围【目前core已经支持,待开放】
按照上界和下界数值计算出的分片值,在给定的分片值全集里面选取落在上下界分片值之间的所有分片值。【包括上下界的值?】
按照参数计算出的分片值,在给定的分片值全集里面按照操作符选取符合要求的所有分片值。
目前看来用例基本没有,暂时不考虑。需要的话再扩展
在不考虑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) |
DAS在生成表达式的时候,如果遇到操作符前面包含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 (A AND B)–> NOT A OR NOT B
- NOT (A OR B)–> NOT A AND NOT B
上述规则可用直接用于表达式嵌套的情况。
利用抽象类来实现基于表达式的分片范围获取的从一般到特殊的过程
该类提供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 |
扩展自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); | 所有分片 |
基于取模算法的locator,实现了父类的:
- locateForEqual(ConditionContext)
- locateForGreaterThan(CTX)
- locateForLessThan(CTX)
- locateForBetween(ConditionContext)
这是最常见的场景,推荐自定义策略继承AbstractConditionStrategy,将对每个条件的判断delegate给AbstractConditionShardLocator的实现
需用户自己实现ShardingStrategy接口里面的逻辑