前一段时间研究阿里的分库框架cobar-client,cobar-client是基于ibatis的SqlMapClientTemplate进行了一层薄薄的封装,分装成CobarSqlMapClientTemplate,在用户在CRUD的时候可以透明的进行操作,算是现在大多公司分库的一个成熟解决方案,不过现在面临的一些问题:
①不支持分表
②基于ibatis而且2013年后基本维护了,没有进行升级,所以大多公司都基于该思想进行了自己的重写
来看下当当开源的sharding-jdbc,官方网址:https://github.com/dangdangdotcom/sharding-jdbc
先允许我盗一波图:
好了,看了这么多的介绍,感觉还是很高大上的,注意点有:
①对JDBC API进行了原生态的分装,这是与cobar-client不一样的地方,这就是他可以支持多个第三方ORM框架的关键
②可支持=
,BETWEEN
,IN
等操作,说明,JDBC返回结果后,sharding进行了合并操作,这里面肯定会有性能损耗
③支持分表,这也是cobar-client不支持的地方
好了,先简单的按照官方网址的demo实践一发:
先在mysql中建2个库
分别在这2个库中运行:
CREATE TABLE IF NOT EXISTS `t_order_0` (
`order_id` INT NOT NULL,
`user_id` INT NOT NULL,
PRIMARY KEY (`order_id`)
);
CREATE TABLE IF NOT EXISTS `t_order_item_0` (
`item_id` INT NOT NULL,
`order_id` INT NOT NULL,
`user_id` INT NOT NULL,
PRIMARY KEY (`item_id`)
);
CREATE TABLE IF NOT EXISTS `t_order_1` (
`order_id` INT NOT NULL,
`user_id` INT NOT NULL,
PRIMARY KEY (`order_id`)
);
CREATE TABLE IF NOT EXISTS `t_order_item_1` (
`item_id` INT NOT NULL,
`order_id` INT NOT NULL,
`user_id` INT NOT NULL,
PRIMARY KEY (`item_id`)
);
新建maven项目
Maven依赖的pom.xml
4.0.0
org.study
sharding-jdbc
0.0.1-SNAPSHOT
jar
sharding-jdbc
http://maven.apache.org
UTF-8
3.2.5.RELEASE
3.2.4
junit
junit
4.10
com.dangdang
sharding-jdbc-core
1.0.0
org.springframework
spring-orm
${spring.version}
commons-dbcp
commons-dbcp
1.4
org.mybatis
mybatis-spring
1.2.2
org.mybatis
mybatis
${mybatis.version}
org.springframework
spring-expression
${spring.version}
org.springframework
spring-aop
${spring.version}
org.springframework
spring-beans
${spring.version}
org.springframework
spring-context
${spring.version}
org.springframework
spring-context-support
${spring.version}
org.springframework
spring-test
${spring.version}
org.springframework
spring-tx
${spring.version}
mysql
mysql-connector-java
5.1.28
log4j
log4j
1.2.16
org.slf4j
slf4j-log4j12
1.7.5
package com.study.base;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import com.dangdang.ddframe.rdb.sharding.api.ShardingDataSource;
import com.dangdang.ddframe.rdb.sharding.api.rule.BindingTableRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.DataSourceRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.ShardingRule;
import com.dangdang.ddframe.rdb.sharding.api.rule.TableRule;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.DatabaseShardingStrategy;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.TableShardingStrategy;
public class ShardingJdbc {
public static void main(String[] args) throws SQLException {
//数据源
Map dataSourceMap = new HashMap<>(2);
dataSourceMap.put("sharding_0", createDataSource("sharding_0"));
dataSourceMap.put("sharding_1", createDataSource("sharding_1"));
DataSourceRule dataSourceRule = new DataSourceRule(dataSourceMap);
//分表分库的表,第一个参数是逻辑表名,第二个是实际表名,第三个是实际库
TableRule orderTableRule = new TableRule("t_order", Arrays.asList("t_order_0", "t_order_1"), dataSourceRule);
TableRule orderItemTableRule = new TableRule("t_order_item", Arrays.asList("t_order_item_0", "t_order_item_1"), dataSourceRule);
/**
* DatabaseShardingStrategy 分库策略
* 参数一:根据哪个字段分库
* 参数二:分库路由函数
* TableShardingStrategy 分表策略
* 参数一:根据哪个字段分表
* 参数二:分表路由函数
*
*/
ShardingRule shardingRule = new ShardingRule(dataSourceRule, Arrays.asList(orderTableRule, orderItemTableRule),
Arrays.asList(new BindingTableRule(Arrays.asList(orderTableRule, orderItemTableRule))),
new DatabaseShardingStrategy("user_id", new ModuloDatabaseShardingAlgorithm()),
new TableShardingStrategy("order_id", new ModuloTableShardingAlgorithm()));
DataSource dataSource = new ShardingDataSource(shardingRule);
String sql = "SELECT i.* FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE o.user_id=? AND o.order_id=?";
try (
Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 10);
pstmt.setInt(2, 1001);
try (ResultSet rs = pstmt.executeQuery()) {
while(rs.next()) {
System.out.println(rs.getInt(1));
System.out.println(rs.getInt(2));
System.out.println(rs.getInt(3));
}
}
}
}
/**
* 创建数据源
* @param dataSourceName
* @return
*/
private static DataSource createDataSource(String dataSourceName) {
BasicDataSource result = new BasicDataSource();
result.setDriverClassName(com.mysql.jdbc.Driver.class.getName());
result.setUrl(String.format("jdbc:mysql://localhost:3306/%s", dataSourceName));
result.setUsername("root");
result.setPassword("");
return result;
}
}
package com.study.base;
import java.util.Collection;
import java.util.LinkedHashSet;
import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.SingleKeyDatabaseShardingAlgorithm;
import com.google.common.collect.Range;
/**
*
* @author lyncc
*
*/
public class ModuloDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm{
@Override
public String doEqualSharding(Collection availableTargetNames, ShardingValue shardingValue) {
for (String each : availableTargetNames) {
if (each.endsWith(shardingValue.getValue() % 2 + "")) {
return each;
}
}
throw new IllegalArgumentException();
}
@Override
public Collection doInSharding(Collection availableTargetNames, ShardingValue shardingValue) {
Collection result = new LinkedHashSet<>(availableTargetNames.size());
for (Integer value : shardingValue.getValues()) {
for (String tableName : availableTargetNames) {
if (tableName.endsWith(value % 2 + "")) {
result.add(tableName);
}
}
}
return result;
}
@Override
public Collection doBetweenSharding(Collection availableTargetNames,
ShardingValue shardingValue) {
Collection result = new LinkedHashSet<>(availableTargetNames.size());
Range range = (Range) shardingValue.getValueRange();
for (Integer i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
for (String each : availableTargetNames) {
if (each.endsWith(i % 2 + "")) {
result.add(each);
}
}
}
return result;
}
}
ModuloTableShardingAlgorithm.java
package com.study.base;
import java.util.Collection;
import java.util.LinkedHashSet;
import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.SingleKeyTableShardingAlgorithm;
import com.google.common.collect.Range;
public final class ModuloTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm {
/**
* select * from t_order from t_order where order_id = 11
* └── SELECT * FROM t_order_1 WHERE order_id = 11
* select * from t_order from t_order where order_id = 44
* └── SELECT * FROM t_order_0 WHERE order_id = 44
*/
public String doEqualSharding(final Collection tableNames, final ShardingValue shardingValue) {
for (String each : tableNames) {
if (each.endsWith(shardingValue.getValue() % 2 + "")) {
return each;
}
}
throw new IllegalArgumentException();
}
/**
* select * from t_order from t_order where order_id in (11,44)
* ├── SELECT * FROM t_order_0 WHERE order_id IN (11,44)
* └── SELECT * FROM t_order_1 WHERE order_id IN (11,44)
* select * from t_order from t_order where order_id in (11,13,15)
* └── SELECT * FROM t_order_1 WHERE order_id IN (11,13,15)
* select * from t_order from t_order where order_id in (22,24,26)
* └──SELECT * FROM t_order_0 WHERE order_id IN (22,24,26)
*/
public Collection doInSharding(final Collection tableNames, final ShardingValue shardingValue) {
Collection result = new LinkedHashSet<>(tableNames.size());
for (Integer value : shardingValue.getValues()) {
for (String tableName : tableNames) {
if (tableName.endsWith(value % 2 + "")) {
result.add(tableName);
}
}
}
return result;
}
/**
* select * from t_order from t_order where order_id between 10 and 20
* ├── SELECT * FROM t_order_0 WHERE order_id BETWEEN 10 AND 20
* └── SELECT * FROM t_order_1 WHERE order_id BETWEEN 10 AND 20
*/
public Collection doBetweenSharding(final Collection tableNames, final ShardingValue shardingValue) {
Collection result = new LinkedHashSet<>(tableNames.size());
Range range = (Range) shardingValue.getValueRange();
for (Integer i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
for (String each : tableNames) {
if (each.endsWith(i % 2 + "")) {
result.add(each);
}
}
}
return result;
}
}
log4j.xml
我们现在user_id是10,order_id是1001
我们应该在sharding0库中的t_order_1和t_order_item_1中新建数据:
INSERT INTO `t_order_1` VALUES ('1001', '10');
INSERT INTO `t_order_item_1` VALUES ('4', '1001', '2');
好了,sharding-jdbc正常工作了
官网说sharding-jdbc是支持与orm框架整合的,下一节我们整合一下mybatis END~