Sharding-JDBC是当当网研发的开源分布式数据库中间件,从 3.0 开始Sharding-JDBC被包含在 Sharding-Sphere 中,之后该项目进入进入Apache孵化器,4.0版本之后的版本为Apache版本。 ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding- Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品组成。 他们均提供标准化的数据分片、分布式事务和 数据库治理功能,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景
分库分表概念
垂直分表:可以把一个宽表的字段按访问频次、是否是大字段的原则拆分为多个表,这样既能使业务清晰,还能提 升部分性能。拆分后,尽量从业务角度避免联查,否则性能方面将得不偿失。
垂直分库:可以把多个表按业务耦合松紧归类,分别存放在不同的库,这些库可以分布在不同服务器,从而使访问 压力被多服务器负载,大大提升性能,同时能提高整体架构的业务清晰度,不同的业务库可根据自身情况定制优化 方案。但是它需要解决跨库带来的所有复杂问题。
水平分库:可以把一个表的数据(按数据行)分到多个不同的库,每个库只有这个表的部分数据,这些库可以分布在 不同服务器,从而使访问压力被多服务器负载,大大提升性能。它不仅需要解决跨库带来的所有复杂问题,还要解 决数据路由的问题(数据路由问题后边介绍)。
水平分表:可以把一个表的数据(按数据行)分到多个同一个数据库的多张表中,每个表只有这个表的部分数据,这 样做能小幅提升性能,它仅仅作为水平分库的一个补充优化。 一般来说,在系统设计阶段就应该根据业务耦合松紧来确定垂直分库,垂直分表方案,在数据量及访问压力不是特 别大的情况,首先考虑缓存、读写分离、索引技术等方案。若数据量极大,且持续增长,再考虑水平分库水平分表 方案。
入门程序(采用水平分表加水平分库)
1.创建数据库
创建订单 order_db_1,order_db_2数据库
CREATE DATABASE `order_db_1` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
CREATE DATABASE `order_db_2` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';
在 order_db_1和 order_db_2 库中创建t_order_1,t_order_2两张表
DROP TABLE IF EXISTS `t_order_1`;
CREATE TABLE `t_order_1` (
`order_id` bigint(20) NOT NULL COMMENT '订单id',
`price` decimal(10, 2) NOT NULL COMMENT '订单价格',
`user_id` bigint(20) NOT NULL COMMENT '下单用户id',
`status` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态',
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
DROP TABLE IF EXISTS `t_order_2`;
CREATE TABLE `t_order_2` (
`order_id` bigint(20) NOT NULL COMMENT '订单id',
`price` decimal(10, 2) NOT NULL COMMENT '订单价格',
`user_id` bigint(20) NOT NULL COMMENT '下单用户id', `
status` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态', PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
2.引入sharding-jdbc 和 springboot整合 maven依赖
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding‐jdbc‐spring‐boot‐starter</artifactId>
<version>4.0.0‐RC1</version>
</dependency>
3.编写分片规则配置
server.port=8080
spring.application.name=sharding-jdbc-demo
#覆盖 bean
spring.main.allow-bean-definition-overriding=true
#mybatis 驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true
#以下是分片规则
#配置分几个库
spring.shardingsphere.datasource.names=m1,m2
#数据库的连接参数
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver‐class‐name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3307/order_db_1?useUnicode=true
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root
spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver‐class‐name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m2.url=jdbc:mysql://localhost:3307/order_db_2?useUnicode=true
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=root
#分库策略 以user_id为分片键,分片策略为user_id % 2 + 1,user_id为偶数操作m1数据源,否则操作m2。
spring.shardingsphere.sharding.tables.t_order.databaseStrategy.inline.shardingColumn=user_id
spring.shardingsphere.sharding.tables.t_order.databaseStrategy.inline.algorithmExpression=m$->{user_id % 2 + 1}
#指定t_order 表的数据分布情况
spring.shardingsphere.sharding.tables.t_order.actualDataNodes=m$->{1..2}.t_order_$->{1..2}
# 指定t_order表的主键生成策略为SNOWFLAKE
spring.shardingsphere.sharding.tables.t_order.keyGenerator.column=order_id
spring.shardingsphere.sharding.tables.t_order.keyGenerator.type=SNOWFLAKE
#分表策略 以 order_id 为分片键,分片策略为order_id % 2 + 1,order_id为偶数操作t_order_1表,否则操作t_order_2表
spring.shardingsphere.sharding.tables.t_order.tableStrategy.inline.shardingColumn=order_id
spring.shardingsphere.sharding.tables.t_order.tableStrategy.inline.algorithmExpression=t_order_$->{order_id % 2 +1}
# 打开sql输出日志
spring.shardingsphere.props.sql.show=true
logging.level.root = info
logging.level.org.springframework.web=info
logging.level.com.itheima.dbsharding=debug
logging.level.druid.sql = debug
1.首先定义数据源 m1,m2 (这个名称可以随便定义 但是这里定义什么,下面的配置就需要使用什么)
2.配置各个数据源的连接配置参数
3.配置水平分库策略.
4.指定t_order 表的数据分部情况,实际分部情况为 m1.t_order_1,m1.t_order_2,m2.t_order_1,m2.t_order_2,使用的Groovy的表达式语法.
5.使用程序控制主键生成,这里采用雪花生成器算法
6.配置水平分表策略
4.代码
@Component
@Mapper
public interface OrderDao {
@Insert("insert into t_order(price,user_id,status) values(#{price},#{userId},#{status})")
int insertOrder(@Param("price")BigDecimal price,@Param("userId")Long userId,@Param("status")String status);
@Select({"<script>" +
"select " +
" * " +
" from t_order t" +
" where t.order_id in " +
"<foreach collection='orderIds' item='id' open='(' separator=',' close=')'>" +
" #{id} " +
"</foreach>"+
"</script>"})
List<Map> selectOrderByIds(@Param("orderIds")List<Long> orderIds);
@Select({"<script>"+
" select"+
" * "+
" from t_order t "+
"where t.order_id in"+
"<foreach collection='orderIds' item='id' open='(' separator=',' close=')'>"+
"#{id}"+
"</foreach>"+
" and t.user_id = #{userId} "+
"</script>" })
List<Map> selectOrderbyUserAndIds(@Param("userId") Long userId,@Param("orderIds")List<Long> orderIds);
}
@Test
void testInsertOrder() {
for (int i = 0; i < 10; i++) {
orderDao.insertOrder(new BigDecimal((i + 1) * 5), 1L, "WAIT_PAY");
}
for (int i = 0; i < 10; i++) {
orderDao.insertOrder(new BigDecimal((i + 1) * 10), 2L, "WAIT_PAY");
}
}
@Test
void testSelectOrderbyIds() {
List<Long> ids = new ArrayList<>();
ids.add(487266182827606016L);
ids.add(487266182856966145L);
List<Map> maps = orderDao.selectOrderByIds(ids);
System.out.println(maps);
}
//根据指定 userId测试分库以及 order_id分表
@Test
void testSelectOrderbyUserAndIds() {
List<Long> orderIds = new ArrayList<>();
orderIds.add(373422416644276224L);
orderIds.add(373422415830581248L);
//查询条件中包括分库的键user_id
Long user_id = 1L;
List<Map> orders = orderDao.selectOrderbyUserAndIds(user_id, orderIds);
JSONArray jsonOrders = new JSONArray(orders);
System.out.println(jsonOrders);
}
Sharding-JDBC支持以下几种分片策略
不管理分库还是分表,策略基本一样。
standard:标准分片策略,对应StandardShardingStrategy。提供对SQL语句中的=, IN和BETWEEN AND的 分片操作支持。StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和 RangeShardingAlgorithm两个分片算法。PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。 RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片,如果不配置 RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。
complex:符合分片策略,对应ComplexShardingStrategy。复合分片策略。提供对SQL语句中的=, IN和 BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复 杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发 者实现,提供最大的灵活度。
inline:行表达式分片策略,对应InlineShardingStrategy。使用Groovy的表达式,提供对SQL语句中的=和 IN的分片操作支持,只支持单分片键。对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java 代码开发,如: t_user_$->{u_id % 8} 表示t_user表根据u_id模8,而分成8张表,表名称为 t_user_0 到 t_user_7 。
hint:Hint分片策略,对应HintShardingStrategy。通过Hint而非SQL解析的方式分片的策略。对于分片字段 非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。例:内部系统,按照员工 登录主键分库,而数据库中并无此字段。SQL Hint支持通过Java API和SQL注释(待实现)两种方式使用。 none:不分片策略,对应NoneShardingStrategy。不分片的策略。
上面程序中采用的是 inline:行表达式分片策略,适用于比较简单的分片算法,复杂的分片算法可以使用 standard分片策略来实现
分库策略
1.分库策略配置修改为
inline 策略配置:
spring.shardingsphere.sharding.tables.t_order.databaseStrategy.inline.shardingColumn=user_id
spring.shardingsphere.sharding.tables.t_order.databaseStrategy.inline.algorithmExpression=m$->{user_id % 2 + 1}
standard 策略配置:(需要自定义实现类实现分片算法)
spring.shardingsphere.sharding.tables.t_order.databaseStrategy.standard.shardingColumn=user_id
spring.shardingsphere.sharding.tables.t_order.databaseStrategy.standard.preciseAlgorithmClassName=com.study.sharding.config.DatabaseOrderShardingAlgofithm
2.standard分库策略算法实现类
@Configuration
public class DatabaseOrderShardingAlgofithm implements PreciseShardingAlgorithm {
/**
*
* 根据 userid进行分库
* @Author
* @Description //TODO
* @Date
* @Param
* @return
**/
@Override
public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
//可以进行复杂的分片算法
Long userId = (Long)preciseShardingValue.getValue();
String l = userId % 2 + 1 + "";
System.out.println("得到的 userId 为:"+userId);
System.out.println("分表算法后数据库序号为:"+l);
Iterator iterator = collection.iterator();
while (iterator.hasNext()){
String next = (String) iterator.next();
if(next.endsWith(l)){
System.out.println("返回的实际数据库名:"+next);
return next;
}
}
throw new IllegalArgumentException();
}
}
分表策略
1.分表策略配置修改为
inline 分表策略配置:
spring.shardingsphere.sharding.tables.t_order.tableStrategy.inline.shardingColumn=order_id
spring.shardingsphere.sharding.tables.t_order.tableStrategy.inline.algorithmExpression=t_order_$->{order_id % 2 +1}
standard 分表策略配置:(需要自定义实现类实现分表算法)
spring.shardingsphere.sharding.tables.t_order.tableStrategy.standard.shardingColumn=order_id
spring.shardingsphere.sharding.tables.t_order.tableStrategy.standard.preciseAlgorithmClassName=com.study.sharding.config.TablesOrderShardingAlgofithm
2. standard分表策略算法实现类
@Configuration
public class TablesOrderShardingAlgofithm implements PreciseShardingAlgorithm {
@Override
public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
Long id = (Long) preciseShardingValue.getValue();
System.out.println("得到生成的 id为:" + id);
long i = id % 2 + 1;
System.out.println("经过自定义分表算法后:" + i);
Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
String echo = (String) iterator.next();
if (echo.endsWith(i+"")) {
System.out.println("返回的实际表名为:" + echo);
return echo;
}
}
throw new IllegalArgumentException();
}
}
若对其他分片策略细节若感兴趣,请查阅官方文档: sharding 官方文档