文章目录
sharingsphere 入门实例
sharingsphere简介
这里原谅我直接copy了一段官方网站上简介了(目前已经进入Apache孵化器)
Apache ShardingSphere(Incubator) is an open-source ecosystem consisted of a set of distributed database middleware solutions, including 3 independent products, Sharding-JDBC, Sharding-Proxy & Sharding-Sidecar (Planning). They all provide functions of data sharding, distributed transaction and database orchestration, applicable in a variety of situations such as Java isomorphism, heterogeneous language and cloud native.
Apache ShardingSphere(Incubator) 是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(规划中)这3款相互独立,却又能够混合部署配合使用的产品组成。它们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、云原生等各种多样化的应用场景。
简单解释一下,就是一个分库分表的中间件。更多详情请见sharingsphere官方文档
郑重声明
我这里的代码(配置文件demo),之前是在网上看的,但是我已经找不到原文章在哪里的了,请原谅,请原谅请原谅请原谅请原谅请原谅请原谅请原谅请原谅请原谅请原谅请原谅请原谅请原谅
结合java使用实例
工程结构(单个整体结构)
这是去年年底的demo了,就是因为时间久远,导致找不到网上的那篇原文了,见谅/侵删
spring20191227sharingsphere
│
└─src.main 包
| │
| ├─java 微服务接口和传输对象定义
| │
| ├─resource 资源类
| │ ├─application.properties 服务配置相关
| │
| └─Spring20191227sharingsphereApplication 服务启动类
|
└─src.test 测试类
SQL脚本
//SQL脚本
create table user_0
(
id int not null
primary key,
name varchar(255) null,
age int null
);
表结构:
db0
├── user_0
└── user_1
db1
├── user_0
└── user_1
配置文件
# 数据源 ds0,ds1
sharding.jdbc.datasource.names=ds0,ds1
# 第一个数据库
sharding.jdbc.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
#sharding.jdbc.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
sharding.jdbc.datasource.ds0.driver-class-name=com.mysql.cj.jdbc.Driver
sharding.jdbc.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/ds0?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
sharding.jdbc.datasource.ds0.username=root
sharding.jdbc.datasource.ds0.password=password
# 第二个数据库
sharding.jdbc.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
sharding.jdbc.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
sharding.jdbc.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/ds1?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
sharding.jdbc.datasource.ds1.username=root
sharding.jdbc.datasource.ds1.password=password
# 水平拆分的数据库(表) 配置分库 + 分表策略 行表达式分片策略
# 分库策略
sharding.jdbc.config.sharding.default-database-strategy.inline.sharding-column=id
sharding.jdbc.config.sharding.default-database-strategy.inline.algorithm-expression=ds$->{id % 2}
# 分表策略 其中user为逻辑表 分表主要取决于age行
sharding.jdbc.config.sharding.tables.user.actual-data-nodes=ds$->{0..1}.user_$->{0..1}
sharding.jdbc.config.sharding.tables.user.table-strategy.inline.sharding-column=age
# 分片算法表达式
sharding.jdbc.config.sharding.tables.user.table-strategy.inline.algorithm-expression=user_$->{age % 2}
# 主键 UUID 18位数 如果是分布式还要进行一个设置 防止主键重复
#sharding.jdbc.config.sharding.tables.user.key-generator-column-name=id
# 打印执行的数据库以及语句
sharding.jdbc.config.props..sql.show=true
spring.main.allow-bean-definition-overriding=true
这里稍微解释一下这上面的配置:
首先是分库,分为两个库,分别是ds0和ds1,每一个库中有两个表(这里因为是除2取余的,所以分了两个表),然后每个表中有三个字段。
id:我们按照id分库,不同的id除2取余后,分别落到ds0和ds1中;
name:无分库分表,正常字段;
age:分表,根据age除2取余后,分别落到表user_0或者表user_1中。
这里的配置使用的是Groovy的语法:
行表达式均能够支持。例如:
${begin..end}
表示范围区间
${[unit1, unit2, unit_x]}
表示枚举值
行表达式中如果出现连续多个${ expression }
或$->{ expression }
表达式,整个表达式最终的结果将会根据每个子表达式的结果进行笛卡尔组合。
更多操作请查看官方文档行表达式
更多操作请查看官方文档配置文件
代码
maven 配置
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<!-- for spring namespace -->
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-namespace</artifactId>
<version>3.1.0</version>
</dependency>
//这里使用了mybatis plus
public interface UserService extends IService<User> {
@Override
boolean save(User entity);
List<User> getUserList();
List<User> selectUserByName(String name);
List<User> selectUserByAge(Integer age);
User selectUserById(Long Id);
}
//实现类,新增,查询操作
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public boolean save(User entity) {
return super.save(entity);
}
@Override
public List<User> getUserList() {
return baseMapper.selectList(Wrappers.<User>lambdaQuery());
}
@Override
public List<User> selectUserByName(String name) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", name);
return baseMapper.selectList(queryWrapper);
}
@Override
public List<User> selectUserByAge(Integer age) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", age);
return baseMapper.selectList(queryWrapper);
}
@Override
public User selectUserById(Long id) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id", id);
return baseMapper.selectOne(queryWrapper);
}
}
//User实体类
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("user")
public class User extends Model<User> {
private Long id;
private String name;
private Integer age;
}
测试
@Test
public void insert() {
User user = new User();
//这里我们假设ID是唯一的;这里我们可以认为是用户ID
user.setId(1L);
user.setName("zhangsan");
user.setAge(20);
userService.save(user);
System.out.println("-----------名称查询分割线-------------------");
System.out.println(userService.selectUserByName(user.getName()).toString());
System.out.println("-----------编码查询分割线-------------------");
System.out.println(userService.selectUserById(user.getId()).toString());
System.out.println("-----------年龄查询分割线-------------------");
System.out.println(userService.selectUserByAge(user.getAge()).toString());
}
新增数据
我们查看控制台日志:
新增数据的时候日志如下:
2020-01-16 13:04:08.127 INFO 16000 --- [ main] ShardingSphere-SQL : Rule Type: sharding
2020-01-16 13:04:08.127 INFO 16000 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO user ( id,name,age ) VALUES ( ?,?,? )
2020-01-16 13:04:08.127 INFO 16000 --- [ main] ShardingSphere-SQL : SQLStatement: InsertStatement(super=DMLStatement(super=io.shardingsphere.core.parsing.parser.sql.dml.insert.InsertStatement@1fb2eec), columns=[Column(name=id, tableName=user), Column(name=name, tableName=user), Column(name=age, tableName=user)], generatedKeyConditions=[], insertValues=InsertValues(insertValues=[InsertValue(type=VALUES, expression=( ?,?,? ), parametersCount=3)]), columnsListLastPosition=34, generateKeyColumnIndex=-1, insertValuesListLastPosition=56)
2020-01-16 13:04:08.127 INFO 16000 --- [ main] ShardingSphere-SQL : Actual SQL: ds1 ::: INSERT INTO user_0 ( id,name,age ) VALUES ( ?,?,? ) ::: [[1, zhangsan, 20]]
抱歉日志内容有点乱
我们大致可以看到 Logic SQL,指逻辑SQL,这里我们可以把数据库当成一个库一个表,逻辑SQL就是针对这个来说的;
再看Actual SQL,这里指实际操作的SQL数据,我们可以看到实际数据是插入了ds1库user_0表中的
查询数据库可以看到,ds1库下user_1插入了一条数据,其他表没有数据;
我们再插入几条数据观察一下:
2020-01-16 13:54:02.333 INFO 12124 --- [ main] ShardingSphere-SQL : Rule Type: sharding
2020-01-16 13:54:02.333 INFO 12124 --- [ main] ShardingSphere-SQL : Logic SQL: INSERT INTO user ( id,name,age ) VALUES ( ?,?,? )
2020-01-16 13:54:02.333 INFO 12124 --- [ main] ShardingSphere-SQL : SQLStatement: InsertStatement(super=DMLStatement(super=io.shardingsphere.core.parsing.parser.sql.dml.insert.InsertStatement@4c18516), columns=[Column(name=id, tableName=user), Column(name=name, tableName=user), Column(name=age, tableName=user)], generatedKeyConditions=[], insertValues=InsertValues(insertValues=[InsertValue(type=VALUES, expression=( ?,?,? ), parametersCount=3)]), columnsListLastPosition=34, generateKeyColumnIndex=-1, insertValuesListLastPosition=56)
2020-01-16 13:54:02.333 INFO 12124 --- [ main] ShardingSphere-SQL : Actual SQL: ds0 ::: INSERT INTO user_1 ( id,name,age ) VALUES ( ?,?,? ) ::: [[2, 李四, 21]]
可以看到这条编码为2,年龄为21的李四插入了ds0库下的user_1中。
按照分库规则查询
我们先看按照编码查询
2020-01-16 13:04:08.704 INFO 16000 --- [ main] ShardingSphere-SQL : Rule Type: sharding
2020-01-16 13:04:08.704 INFO 16000 --- [ main] ShardingSphere-SQL : Logic SQL: SELECT id,name,age FROM user
WHERE id = ?
2020-01-16 13:04:08.704 INFO 16000 --- [ main] ShardingSphere-SQL : SQLStatement: SelectStatement(super=DQLStatement(super=io.shardingsphere.core.parsing.parser.sql.dql.select.SelectStatement@11abd6c), containStar=false, firstSelectItemStartPosition=8, selectListLastPosition=20, groupByLastPosition=0, items=[CommonSelectItem(expression=id, alias=Optional.absent()), CommonSelectItem(expression=name, alias=Optional.absent()), CommonSelectItem(expression=age, alias=Optional.absent())], groupByItems=[], orderByItems=[], limit=null, subQueryStatement=null, subQueryStatements=[], subQueryConditions=[])
2020-01-16 13:04:08.704 INFO 16000 --- [ main] ShardingSphere-SQL : Actual SQL: ds1 ::: SELECT id,name,age FROM user_0 WHERE id = ? ::: [[1]]
2020-01-16 13:04:08.704 INFO 16000 --- [ main] ShardingSphere-SQL : Actual SQL: ds1 ::: SELECT id,name,age FROM user_1 WHERE id = ? ::: [[1]]
//查询结果
User(id=1, name=zhangsan, age=20)
这里我们可以看到,实际上根据ID查询的时候,ShardingSphere去ds1库查询了,因为我们按照id分库,id1,可以很清楚的知道它存在ds1库中,所以,ShardingSphere只需要查询ds1库即可。
按照分表规则查询
我们按照年龄查询
2020-01-16 13:04:08.711 INFO 16000 --- [ main] ShardingSphere-SQL : Rule Type: sharding
2020-01-16 13:04:08.711 INFO 16000 --- [ main] ShardingSphere-SQL : Logic SQL: SELECT id,name,age FROM user WHERE age = ?
2020-01-16 13:04:08.711 INFO 16000 --- [ main] ShardingSphere-SQL : SQLStatement: SelectStatement(super=DQLStatement(super=io.shardingsphere.core.parsing.parser.sql.dql.select.SelectStatement@794c5f5e), containStar=false, firstSelectItemStartPosition=8, selectListLastPosition=20, groupByLastPosition=0, items=[CommonSelectItem(expression=id, alias=Optional.absent()), CommonSelectItem(expression=name, alias=Optional.absent()), CommonSelectItem(expression=age, alias=Optional.absent())], groupByItems=[], orderByItems=[], limit=null, subQueryStatement=null, subQueryStatements=[], subQueryConditions=[])
2020-01-16 13:04:08.711 INFO 16000 --- [ main] ShardingSphere-SQL : Actual SQL: ds0 ::: SELECT id,name,age FROM user_0 WHERE age = ? ::: [[20]]
2020-01-16 13:04:08.711 INFO 16000 --- [ main] ShardingSphere-SQL : Actual SQL: ds1 ::: SELECT id,name,age FROM user_0 WHERE age = ? ::: [[20]]
//查询结果
[User(id=1, name=zhangsan, age=20)]
这里我们可以看到,实际上查询了ds0库和ds1库,因为我们的查询条件没有带分库字段(即id),所以,它需要到每一个库去查询,但是我们是按照age分表的,所以ShardingSphere可以明确的知道我们的age=20的数据应该再user_0表中,所以ShardingSphere没有查询user_1表。
按照普通字段查询
我们按照名称查询
2020-01-16 13:04:08.539 INFO 16000 --- [ main] ShardingSphere-SQL : Rule Type: sharding
2020-01-16 13:04:08.540 INFO 16000 --- [ main] ShardingSphere-SQL : Logic SQL: SELECT id,name,age FROM user WHERE name = ?
2020-01-16 13:04:08.540 INFO 16000 --- [ main] ShardingSphere-SQL : SQLStatement: SelectStatement(super=DQLStatement(super=io.shardingsphere.core.parsing.parser.sql.dql.select.SelectStatement@561d88ee), containStar=false, firstSelectItemStartPosition=8, selectListLastPosition=20, groupByLastPosition=0, items=[CommonSelectItem(expression=id, alias=Optional.absent()), CommonSelectItem(expression=name, alias=Optional.absent()), CommonSelectItem(expression=age, alias=Optional.absent())], groupByItems=[], orderByItems=[], limit=null, subQueryStatement=null, subQueryStatements=[], subQueryConditions=[])
2020-01-16 13:04:08.540 INFO 16000 --- [ main] ShardingSphere-SQL : Actual SQL: ds0 ::: SELECT id,name,age FROM user_0 WHERE name = ? ::: [[zhangsan]]
2020-01-16 13:04:08.540 INFO 16000 --- [ main] ShardingSphere-SQL : Actual SQL: ds0 ::: SELECT id,name,age FROM user_1 WHERE name = ? ::: [[zhangsan]]
2020-01-16 13:04:08.540 INFO 16000 --- [ main] ShardingSphere-SQL : Actual SQL: ds1 ::: SELECT id,name,age FROM user_0 WHERE name = ? ::: [[zhangsan]]
2020-01-16 13:04:08.540 INFO 16000 --- [ main] ShardingSphere-SQL : Actual SQL: ds1 ::: SELECT id,name,age FROM user_1 WHERE name = ? ::: [[zhangsan]]
[User(id=1, name=zhangsan, age=20)]
这里我们可以看到,按照名称查询的时候(名称不是分库/分表字段),ShardingSphere不知道名称为zhangsan的数据在哪一个库哪一个表中,所以ShardingSphere必须查询全部库表(即全路由)。
按照分片规则查询
//这里我们按照条件查询
public List<User> selectUserByCondition(User user) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
if (user.getId()!= null){
queryWrapper.eq("id", user.getId());
}
if (user.getAge()!= null){
queryWrapper.eq("age", user.getAge());
}
if (user.getName()!= null){
queryWrapper.eq("name", user.getName());
}
return baseMapper.selectList(queryWrapper);
}
//日志
2020-01-16 13:51:01.214 INFO 9368 --- [ main] ShardingSphere-SQL : Rule Type: sharding
2020-01-16 13:51:01.215 INFO 9368 --- [ main] ShardingSphere-SQL : Logic SQL: SELECT id,name,age FROM user WHERE id = ? AND age = ?
2020-01-16 13:51:01.215 INFO 9368 --- [ main] ShardingSphere-SQL : SQLStatement: SelectStatement(super=DQLStatement(super=io.shardingsphere.core.parsing.parser.sql.dql.select.SelectStatement@64f32e9e), containStar=false, firstSelectItemStartPosition=8, selectListLastPosition=20, groupByLastPosition=0, items=[CommonSelectItem(expression=id, alias=Optional.absent()), CommonSelectItem(expression=name, alias=Optional.absent()), CommonSelectItem(expression=age, alias=Optional.absent())], groupByItems=[], orderByItems=[], limit=null, subQueryStatement=null, subQueryStatements=[], subQueryConditions=[])
2020-01-16 13:51:01.215 INFO 9368 --- [ main] ShardingSphere-SQL : Actual SQL: ds1 ::: SELECT id,name,age FROM user_0 WHERE id = ? AND age = ? ::: [[1, 20]]
这里我们按照分库+分表字段进行查询,可以发现ShardingSphere只进行了一次查询操作,ShardingSphere精准的查询了ds1库下的user_0表。所以如果我们采用分库分表的策略,查询的时候尽量带上分库分表规则字段,否则效率很低。
总结
这里我们学习了分库分表的基本操作,按照字段分库分表,把大量数据拆分到多个数据库表中,减轻了对数据库的压力,同时提升了数据库的瓶颈,但是同时也引入了一些需要注重的点,以后的文章将会介绍更多的点。