Sharding-Jdbc概念与使用技巧
1. sharding-jdbc概念与使用技巧
此讲解版本为
4.0.0-rc1
,目前最新的版本 2019年5月21日发布
1.1. 绑定表
- 指分片规则一致的主表和子表。例如:
t_order
表和t_order_item
表,均按照order_id
分片,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。举例说明,如果sql为:
select i.* from t_order o join t_order_item i on o.order_id=i.order_id where o.order_id in (10, 11);
- 在不配置绑定表关系时,假设分片键
order_id
将数值10路由至第0片,将数值11路由至第1片,那么路由后的sql应该为4条,它们呈现为笛卡尔积:
select i.* from t_order_0 o join t_order_item_0 i on o.order_id=i.order_id where o.order_id in (10, 11); select i.* from t_order_0 o join t_order_item_1 i on o.order_id=i.order_id where o.order_id in (10, 11); select i.* from t_order_1 o join t_order_item_0 i on o.order_id=i.order_id where o.order_id in (10, 11); select i.* from t_order_1 o join t_order_item_1 i on o.order_id=i.order_id where o.order_id in (10, 11);
- 在配置绑定表关系后,路由的sql应该为2条:、
select i.* from t_order_0 o join t_order_item_0 i on o.order_id=i.order_id where o.order_id in (10, 11); select i.* from t_order_1 o join t_order_item_1 i on o.order_id=i.order_id where o.order_id in (10, 11);
- 其中
t_order
在from的最左侧,shardingsphere将会以它作为整个绑定表的主表。 所有路由计算将会只使用主表的策略,那么t_order_item
表的分片计算将会使用t_order
的条件。故绑定表之间的分区键要完全相同。
1.2. 分片算法
通过分片算法将数据分片,支持通过=、between和in分片。分片算法需要应用方开发者自行实现,可实现的灵活度非常高。
目前提供4种分片算法。由于分片算法和业务实现紧密相关,因此并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法。
- 精确分片算法
对应preciseshardingalgorithm,用于处理使用单一键作为分片键的=与in进行分片的场景。需要配合standardshardingstrategy
使用。
- 范围分片算法
对应rangeshardingalgorithm,用于处理使用单一键作为分片键的between and进行分片的场景。需要配合standardshardingstrategy
使用。
- 复合分片算法
对应complexkeysshardingalgorithm,用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合complexshardingstrategy
使用。
- hint分片算法
对应hintshardingalgorithm,用于处理使用hint行分片的场景。需要配合hintshardingstrategy
使用。
1.3.
包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键 + 分片算法,也就是分片策略。目前提供5种分片策略。
- 标准分片策略
- 复合分片策略
- 行表达式分片策略
对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的java代码开发,如: t_user_$->{u_id % 8} 表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0到t_user_7。
- hint分片策略
- 不分片策略
1.4. sql hint
对于分片字段非sql决定,而由其他外置条件决定的场景,可使用sql hint灵活的注入分片字段。例:内部系统,按照员工登录主键分库,而数据库中并无此字段。sql hint支持通过java api和sql注释(待实现)两种方式使用。
1.5. sql支持与不支持
- 看
1.6. 行表达式
- ${begin..end}表示范围区间
- ${[unit1, unit2, unit_x]}表示枚举值
- 行表达式中如果出现连续多个${ expression }或$->{ expression }表达式,整个表达式最终的结果将会根据每个子表达式的结果进行笛卡尔组合。
${['online', 'offline']}_table${1..3}
最终解析为
online_table1, online_table2, online_table3, offline_table1, offline_table2, offline_table3
1.7. 强制分片路由
通过解析sql语句提取分片键列与值并进行分片是shardingsphere对sql零侵入的实现方式。若sql语句中没有分片条件,则无法进行分片,需要全路由。
在一些应用场景中,分片条件并不存在于sql,而存在于外部业务逻辑。因此需要提供一种通过外部指定分片结果的方式,在shardingsphere中叫做hint
。
shardingsphere使用threadlocal管理分片键值。可以通过编程的方式向hintmanager中添加分片条件,该分片条件仅在当前线程内生效。
指定了强制分片路由的sql将会无视原有的分片逻辑,直接路由至指定的真实数据节点。
1.8. 读写分离
- 同一线程且同一数据库连接内,如有写入操作,以后的读操作均从主库读取,用于保证数据一致性。
1.9. 编排治理
- 提供注册中心、配置动态化、数据库熔断禁用、调用链路等治理能力。
1.10. 注意事项
- 分页偏移量过大会使数据库获取数据性能低下,原因看
1.11. 自定义扩展接口
1.11.1. 分库分表为例
- 进入入口函数,通过springboot配置的入口核心就是右边的
datasource
方法,它会把shardingproperties
配置进去,而shardingproperties
的来源就是application.propertes
中的配置属性 - 通过自定义算法进行配置如下
#################################### # 分库分表配置 #################################### #actual-data-nodes:真实数据节点,由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持inline表达式 spring.shardingsphere.sharding.tables.t_order.actual-data-nodes=ds${0..1}.t_order_${0..1} # 自定义分库分表算法 spring.shardingsphere.sharding.tables.t_order.databasestrategy.complex.shardingcolumns=order_id,user_id spring.shardingsphere.sharding.tables.t_order.databasestrategy.complex.algorithmclassname=com.xxx.shardingjdbc\ .cusalgo.algorithm.dbshardingalgorithm ## 自定义分表算法 spring.shardingsphere.sharding.tables.t_order.tablestrategy.complex.shardingcolumns=order_id,user_id spring.shardingsphere.sharding.tables.t_order.tablestrategy.complex.algorithmclassname=com.xxx\ .shardingjdbc.cusalgo.algorithm.tableshardingalgorithm
- 找到配合对应的类如下
你可以看到除了tables,你还可以配置很多其他属性,
bindingtables
,broadcasttables
等等,看名字也知道是绑定表和广播表,绑定表我第一章就讲到了,广播表理解也很简单,默认你不分库的就是广播表,也就是数据在所有分库分表的节点都保存一份这里着重讲自定义配置类,上面配置文件配置了
dbshardingalgorithm
这个类就是自定义类,它实现了complexkeysshardingalgorithm
public class dbshardingalgorithm implements complexkeysshardingalgorithm { private static logger logger = loggerfactory.getlogger(dbshardingalgorithm.class); // 取模因子 public static final integer mode_factor = 1331; @override public collection<string> dosharding(collection<string> availabletargetnames, collection<shardingvalue> shardingvalues) { list<string> shardingresults = new arraylist<>(); long shardingindex = getindex(shardingvalues) % availabletargetnames.size(); // loop and match datasource for (string name : availabletargetnames) { // get logic datasource index suffix string namesuffix = name.substring(2); if (namesuffix.equals(shardingindex.tostring())) { shardingresults.add(name); break; } } logger.info("datasource sharding index : {}", shardingindex); return shardingresults; } /** * get datasource sharding index <p> * sharding algorithm : shardingindex = (orderid + userid.hashcode()) % db.size * @param shardingvalues * @return */ private long getindex(collection<shardingvalue> shardingvalues) { long shardingindex = 0l; listshardingvalue<long> listshardingvalue; list<long> shardingvalue; for (shardingvalue sval : shardingvalues) { listshardingvalue = (listshardingvalue<long>) sval; if ("order_id".equals(listshardingvalue.getcolumnname())) { shardingvalue = (list<long>) listshardingvalue.getvalues(); shardingindex += math.abs(shardingvalue.get(0)) % mode_factor; } else if ("user_id".equals(listshardingvalue.getcolumnname())) { shardingvalue = (list<long>) listshardingvalue.getvalues(); // 这里 % 1313 仅仅只是防止溢出 shardingindex += math.abs(shardingvalue.get(0).hashcode()) % mode_factor; } } return shardingindex; } }
继续追踪进入
可以发现它总共实现了5个接口配置,上面的complexkeysshardingalgorithm
就来自complex
的配置
- 至于该实现哪些接口,看下图
- 上述四个接口,就是我们用户可以自定义实现的接口了,写好实现类把全类名配置上去就可以用了
想要全面了解sharding-jdbc和它相关组件的,移步