ShardingSphere (一)
ShardingSphere-JDBC 与 SpringBoot 集成
ShardingSphere-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。
适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC;
支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, HikariCP 等;
支持任意实现 JDBC 规范的数据库,目前支持 MySQL,PostgreSQL,Oracle,SQLServer 以及任何可使用 JDBC 访问的数据库。
分库
通过配置user_id将不同的数据存入不同的库表中
依赖配置
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.1</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
如果出现以下错误,则需引入
snakeyaml
版本为1.33
Caused by: java.lang.NoSuchMethodError: 'void org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1.setCodePointLimit(int)'
at org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor$1.<init>(ShardingSphereYamlConstructor.java:44)
at org.apache.shardingsphere.infra.util.yaml.constructor.ShardingSphereYamlConstructor.<init>(ShardingSphereYamlConstructor.java:41)
at org.apache.shardingsphere.infra.util.yaml.YamlEngine.unmarshal(YamlEngine.java:83)
application.yml 配置
spring:
shardingsphere:
datasource:
names: ds-0, ds-1
ds-0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.79.177:14306/datas_0?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
ds-1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.79.177:14306/datas_1?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
rules:
sharding:
default-database-strategy:
standard:
# 以字段分片
sharding-column: user_id
# 分片算法名称,可自定义
sharding-algorithm-name: db-inline
# 绑定广播表,多库存在此表且数据一致
broadcast-tables: t_subject
# 绑定表规则列表
binding-tables[0]: t_student, t_student_subject
tables:
t_student:
actual-data-nodes: ds-$->{0..1}.t_student
key-generate-strategy:
column: id
key-generator-name: snowflake
t_student_subject:
actual-data-nodes: ds-$->{0..1}.t_student_subject
key-generate-strategy:
column: id
key-generator-name: snowflake
key-generators:
snowflake:
type: SNOWFLAKE
sharding-algorithms:
db-inline:
type: INLINE
props:
algorithm-expression: ds-$->{user_id % 2}
props:
sql-show: true
jpa:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.MySQL5Dialect
# show_sql: true
database: mysql
创建数据库
创建database datas_0
, datas_1
drop database if exists datas_0;
drop database if exists datas_1;
create database datas_0;
create database datas_1;
编写代码
创建对象结构
- Student: id, name, userId
- Subject: id, name
- StudentSubject: id, userId, subjectId
public interface IdObj extends Serializable {
Long getId();
}
public class Student implements IdObj {
private Long id;
private Long userId;
private String name;
// ommit getter and setter
}
public class StudentSubject implements IdObj {
private Long id;
private Long subjectId;
private Long userId;
// ommit getter and setter
}
public class Subject implements IdObj {
private Long id;
private String name;
// ommit getter and setter
}
@Entity
@Table(name = "t_student")
public class StudentEntity extends Student {
@javax.persistence.Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Override
public Long getId() {
return super.getId();
}
@Column(name = "user_id")
@Override
public Long getUserId() {
return super.getUserId();
}
@Column(name = "name")
@Override
public String getName() {
return super.getName();
}
}
@Entity
@Table(name = "t_student_subject")
public class StudentSubjectEntity extends StudentSubject {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Override
public Long getId() {
return super.getId();
}
@Column(name = "subject_id")
@Override
public Long getSubjectId() {
return super.getSubjectId();
}
@Column(name = "user_id")
@Override
public Long getUserId() {
return super.getUserId();
}
}
@Entity
@Table(name = "t_subject")
public class SubjectEntity extends Subject {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Override
public Long getId() {
return super.getId();
}
@Column(name = "name")
@Override
public String getName() {
return super.getName();
}
}
创建持久接口
@Repository
public interface StudentRepository extends CrudRepository<StudentEntity, Long> {
}
@Repository
public interface StudentSubjectRepository extends CrudRepository<StudentSubjectEntity, Long> {
}
@Repository
public interface SubjectRepository extends CrudRepository<SubjectEntity, Long> {
}
创建服务类
@Service
public class StudentService {
@Resource
StudentRepository studentRepository;
public void save(StudentEntity studentEntity) {
studentRepository.save(studentEntity);
}
}
@Service
public class StudentSubjectService {
@Resource
StudentSubjectRepository studentSubjectRepository;
public void save(StudentSubjectEntity studentSubjectEntity) {
studentSubjectRepository.save(studentSubjectEntity);
}
}
@Service
public class SubjectService {
@Resource
SubjectRepository subjectRepository;
public SubjectEntity save(SubjectEntity subjectEntity) {
return subjectRepository.save(subjectEntity);
}
}
测试用例
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ShardingSphereMain.class)
@Rollback(value = false)
public class TestApp {
@Resource
StudentService studentService;
@Resource
StudentSubjectService studentSubjectService;
@Resource
SubjectService subjectService;
public void init() {
for (long i = 1; i <= 10; i++) {
StudentEntity studentEntity = new StudentEntity();
studentEntity.setUserId(i);
studentService.save(studentEntity);
SubjectEntity subjectEntity = new SubjectEntity();
subjectEntity.setName("computer_" + i);
subjectEntity = subjectService.save(subjectEntity);
StudentSubjectEntity studentSubjectEntity = new StudentSubjectEntity();
studentSubjectEntity.setSubjectId(10 + subjectEntity.getId());
studentSubjectEntity.setUserId(studentEntity.getUserId());
studentSubjectService.save(studentSubjectEntity);
}
}
@Test
public void testData() {
init();
}
}
执行testcase 生成基础数据
Student
实体类数据会按user_id % 2
的方式入 datas_0
, datas_1
对应的t_student
表中
StudentSubject
实体类数据会按user_id % 2
的方式入 datas_0
, datas_1
对应的t_student_subject
表中
Subject
实体类数据会广播到datas_0
, datas_1
对应的t_subject
表中
datas_0.t_student
id | name | user_id |
---|---|---|
802927542603350016 | named_2 | 2 |
802927542930505728 | named_4 | 4 |
802927543253467136 | named_6 | 6 |
802927543551262720 | named_8 | 8 |
802927543844864000 | named_10 | 10 |
datas_0.t_subject
id | name |
---|---|
1 | subject_1 |
2 | subject_2 |
3 | subject_3 |
4 | subject_4 |
5 | subject_5 |
6 | subject_6 |
7 | subject_7 |
8 | subject_8 |
9 | subject_9 |
10 | subject_10 |
datas_0.t_student_subject
id | subject_id | user_id |
---|---|---|
802927542720790529 | 12 | 2 |
802927543039557633 | 14 | 4 |
802927543366713345 | 16 | 6 |
802927543664508929 | 18 | 8 |
802927543953915905 | 20 | 10 |
datas_1.t_student
id | name | user_id |
---|---|---|
802927541458305024 | named_1 | 1 |
802927542771122176 | named_3 | 3 |
802927543085694976 | named_5 | 5 |
802927543412850688 | named_7 | 7 |
802927543706451968 | named_9 | 9 |
datas_1.t_subject
id | name |
---|---|
1 | subject_1 |
2 | subject_2 |
3 | subject_3 |
4 | subject_4 |
5 | subject_5 |
6 | subject_6 |
7 | subject_7 |
8 | subject_8 |
9 | subject_9 |
10 | subject_10 |
datas_1.t_student_subject
id | subject_id | user_id |
---|---|---|
802927542532046849 | 11 | 1 |
802927542888562689 | 13 | 3 |
802927543207329793 | 15 | 5 |
802927543513513985 | 17 | 7 |
802927543815503873 | 19 | 9 |
分库分表
分库分表只需对上面的文件稍加改动
application.yml 配置
spring:
shardingsphere:
datasource:
names: ds-0, ds-1
ds-0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.79.177:14306/datas_0?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
ds-1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.79.177:14306/datas_1?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
username: root
password: 123456
rules:
sharding:
default-database-strategy:
standard:
# 以字段分片
sharding-column: user_id
# 分片算法名称,可自定义
sharding-algorithm-name: db-inline
# 绑定广播表,多库存在此表且数据一致
broadcast-tables: t_subject
# 绑定表规则列表
binding-tables[0]: t_student, t_student_subject
tables:
t_student:
actual-data-nodes: ds-$->{0..1}.t_student
key-generate-strategy:
column: id
key-generator-name: snowflake
t_student_subject:
actual-data-nodes: ds-$->{0..1}.t_student_subject
key-generate-strategy:
column: id
key-generator-name: snowflake
key-generators:
snowflake:
type: SNOWFLAKE
sharding-algorithms:
db-inline:
type: INLINE
props:
algorithm-expression: ds-$->{user_id % 2}
props:
sql-show: true
jpa:
properties:
hibernate:
hbm2ddl:
auto: update
dialect: org.hibernate.dialect.MySQL5Dialect
# show_sql: true
database: mysql