相比于spring基于abstractroutingdatasource实现的分库分表功能,sharding jdbc在单库单表扩展到多库多表时,兼容性方面表现的更好一点。例如,spring实现的分库分表sql写法如下:
select id, name, price, publish, intro
from book${tableindex}
where id = #{id,jdbctype=integer}
sql中的表名book需要加一个分表的后缀tableindex,也就是需要在sql注入的参数中指定插入哪个表。相比,sharding jdbc在这一块封装的更好一点。其sql中,根本不需要指定tableindex,而是根据分库分表策略自动路由。
select id, name, price, publish, intro
from book
where id = #{id,jdbctype=integer}
sharding jdbc的这种特性,在水平扩展的时候无疑更具有吸引力。试想一下,一个项目开发一段时间后,单库单表数据量急剧上升,需要分库分表解决数据库的访问压力。而现有sql配置都是基于单库单表实现的,如果基于spring的abstractroutingdatasource实现,需要修改每一个相关表的sql,修改涉及较多地方,出错概率较大。而基于sharding jdbc实现时,sql无需修改,只需要在spring中添加sharding jdbc的相关配置即可,减少了修改面,大大简化分库分表的实现难度。
那么,sharding jdbc是如何实现这种分库分表的逻辑呢?下面我们用一段简单、易懂的代码描述sharding jdbc的原理。
通常我们在写一段访问数据库的数据时,逻辑是这样的:
classpathxmlapplicationcontext ctx = new classpathxmlapplicationcontext("application.xml");
datasource datasource = ctx.getbean("datasource", datasource.class);
connection connection = datasource.getconnection();
string sql = "select id, name, price, publish, intro from book where id = 111";
preparedstatement ps = connection.preparestatement(sql);
resultset rs = ps.executequery();
// handle resultset...
sharding jdbc是基于jdbc协议实现的,当我们获得datasource时,这个datasource是sharding jdbc自己定义的一个springshardingdatasource类型的数据源,该数据源在返回getconnection()及preparestatement()时,分别返回shardingconnection和shardingpreparedstatement的实例对象。然后在executequery()时,shardingpreparedstatement做了这样的一件事:
- 根据逻辑sql,经过分库分表策略逻辑计算,获得分库分表的路由结果sqlrouteresult;
- sqlrouteresult中包含真实的数据源以及转换后的真正sql,利用真实的数据源去执行获得resultset;
- 将resultset列表封装成一个可以顺序读的resultset对象iteratorreducerresultset。
class shardingpreparedstatement implements preparedstatement {
@override
public resultset executequery() throws sqlexception {
list routeresults = routesql(logicsql);
list resultsets = new arraylist<>(routeresults.size());
for (sqlrouteresult routeresult : routeresults) {
preparedstatement ps = routeresult.getdatasource().getconnection.preparestatement(routeresult.getparsedsql());
resultset rs = ps.executequery();
resultsets.add(rs);
}
return new iteratorreducerresultset(resultsets);
}
.....
}
其中,分库分表策略的sql路由过程,我们将sharding jdbc中的相关代码全部抽出来,放到一起来观看这个过程的实现:
// 环境准备
@suppresswarnings("resource")
classpathxmlapplicationcontext ctx = new classpathxmlapplicationcontext("application.xml");
springshardingdatasource datasource = ctx.getbean(springshardingdatasource.class);
field field = springshardingdatasource.class.getsuperclass().getdeclaredfield("shardingcontext");
field.setaccessible(true);
shardingcontext sctx = (shardingcontext)field.get(datasource);
shardingrule shardingrule = sctx.getshardingrule();
string logicsql = "select id, name, price, publish, intro from book where id = ?";
list