Sharding-JDBC
Perform operations such as sharding the database in accordance with the rules in the form of configuration
Hello World
Implement horizontal table splitting through Sharding-JDBC: use user_1 and user_2 in the database to split horizontally through the primary key, the id is an odd number into the user_1 table, and the id is an even number into the user_2 table
1 Create a database and table
create database sharding character set = utf8;
use sharding;
create table user_1(
id int primary key auto_increment,
name varchar(20),
age int,
birthday datetime,
cmd varchar(200)
);
create table user_2(
id int primary key auto_increment,
name varchar(20),
age int,
birthday datetime,
cmd varchar(200)
);
2 Create a SpringBoot project
Sharding-jdbc provides us with a startup class that integrates springBoot, which is very simple to configure
<!-- 数据库连接驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!-- sharding-jdbc 数据库分库分表 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>
<!-- 数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
<!-- reids 依赖(这里主要用于生成主键) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3 configure the application.properties file
##基本配置(正常操作)
server.port=8998
# mybatis-plus 配置
mybatis-plus.mapper-locations=classpath*:/mapper/**/*.xml
mybatis-plus.type-aliases-package=com.mt.bean
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
spring.redis.host=120.25.227.88
spring.redis.password=123456
#sharding-jdbc的配置 ps:官网有详细的配置文件介绍:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/configuration/config-spring-boot/
#声明一个数据库(虚拟的)
spring.shardingsphere.datasource.names=db1
#声明虚拟数据库对应的连接,驱动,用户名,密码,连接池等信息
spring.shardingsphere.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db1.url=jdbc:mysql://localhost:3306/sharding?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
spring.shardingsphere.datasource.db1.username=root
spring.shardingsphere.datasource.db1.password=root
spring.shardingsphere.datasource.db1.type=com.alibaba.druid.pool.DruidDataSource
#声明 表存放在对应的数据库 $->{0..1} 就是行内表达式
#这里的意思是db1库内有user虚拟表指向 user_1和user_2
spring.shardingsphere.sharding.tables.user.actual-data-nodes=db1.user_$->{1..2}
#设置主键字段
spring.shardingsphere.sharding.tables.user.key-generator.column=id
# 设置主键生成策略 可选内置的 SNOWFLAKE(雪花算法)/UUID
#也可以自定义(实现ShardingKeyGenerator,并配置META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator)
spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE
#设置 根据哪个字段进行分片
spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column=id
#分片规则奇数存入user_1 偶数存入user_2
spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression=user_$->{id % 2 != 0 ? 1:2}
spring.shardingsphere.props.sql.show=true
4 write the corresponding object
bean:
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private Date birthday;
private String cmd;
}
mapper:
public interface UserMapper extends BaseMapper<User> {
}
Test class:
@SpringBootTest
class ShardingSphereApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
User user1 = new User();
user1.setAge(10);
user1.setName("张三");
user1.setBirthday(new Date());
user1.setCmd("张三今年10岁");
User user2 = new User();
user2.setAge(20);
user2.setName("李四");
user2.setBirthday( new Date() );
user2.setCmd("李四今年20岁");
userMapper.insert( user2 );
}
}
After testing, user Zhang San was added to user_1, and Li Si was added to user_2
Custom primary key generation strategy
Sharding-jdbc provides us with 2 default generation strategies:
- SNOWFLAKE: Snowflake algorithm (corresponding to the database bigint type and java Long type)
- UUID: uuid generation strategy (varchar and String types)
These two types alone cannot meet our needs, so sharding-jdbc provides us with an ShardingKeyGenerator
interface to allow us to customize the primary key generation strategy.
Implementation: Here is an auto-incremented primary key generated by redis
1 Implement the ShardingKeyGenerator interface
@Component
@Slf4j
public class KeyGenerator implements ShardingKeyGenerator, ApplicationContextAware {
@Getter
@Setter
private Properties properties;
//必须设置为静态,否则为null
public static RedisTemplate redisTemplate;
@Override
public Comparable<?> generateKey() {
ValueOperations valueOp = redisTemplate.opsForValue();
return valueOp.increment("id");
}
//设置在yaml内的名字
@Override
public String getType() {
return "auto_increment";
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
redisTemplate = applicationContext.getBean(StringRedisTemplate.class);
}
}
2 Configure the class just created
We need to create files under resource
META-INF\services\org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator
And configure the host generator we just wrote
com.mt.config.Sharding.KeyGenerator
3 use
Just specify the name of the generator just in the configuration file
spring.shardingsphere.sharding.tables.user.key-generator.type=auto_increment
Custom table fragmentation strategy
Demonstrated above sub-table strategy is sharding-jdbc provides us with the inline
fragmentation of the rules, you can write 行表达式
simple table partitioning strategy, for example, according to the modulo id, according to gender classification. But for some more complex partitioning strategies, row expressions may not meet our requirements, so we need to customize the table fragmentation strategy
Case: According to the current year, month and minute, insert data into different tables. For example, there are order_202001, order_202002, etc. in the database. We need to dynamically store the date generated by the order into different tables:
1 Create a database table
create table `order_202001`(
id int primary key auto_increment,
date datetime,
price decimal(10,2),
cmd varchar(200)
);
create table `order_202002`(
id int primary key auto_increment,
date datetime,
price decimal(10,2),
cmd varchar(200)
);
create table `order_202003`(
id int primary key auto_increment,
date datetime,
price decimal(10,2),
cmd varchar(200)
);
2 Create an entity object
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("`order`")
public class Order {
@TableId(type = IdType.AUTO)
private Integer id;
private Date date;
private BigDecimal price;
private String cmd;
}
3 Create a sharding logic class
@Component
public class OrderTableShardingAlgorithm implements PreciseShardingAlgorithm<Date> {
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<Date> preciseShardingValue) {
//preciseShardingValue就是当前插入的字段值
//collection 内就是所有的逻辑表
//获取字段值
Date time = preciseShardingValue.getValue();
if(time == null){
throw new UnsupportedOperationException("prec is null");
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
String year =sdf.format(time);
for (String tableName : collection) {
//循环表名已确定使用哪张表
String name = tableName.substring(tableName.length() - 6);
if(year.equals(name)){
return tableName;//返回要插入的逻辑表
}
}
return null;
}
}
#声明虚拟表
spring.shardingsphere.sharding.tables.order.actual-data-nodes=db1.order_$->{2000..2099}0$->{1..9},db1.order_$->{2000..2099}1$->{0..2}
#声明表内的主键
spring.shardingsphere.sharding.tables.order.key-generator.column=id
#声明主键生成策略
spring.shardingsphere.sharding.tables.order.key-generator.type=order_auto_increment
#声明根据哪个字段进行分片
spring.shardingsphere.sharding.tables.order.table-strategy.standard.sharding-column=date
#自定义分片规则类
spring.shardingsphere.sharding.tables.order.table-strategy.standard.precise-algorithm-class-name=com.mt.config.Sharding.OrderTableShardingAlgorithm
4 Add operation through mybatis-plus
@RestController
@RequestMapping("order")
public class OrderController {
@Autowired
private OrderMapper orderMapper;
@GetMapping("add")
public String add(Order order){
orderMapper.insert(order);
return "success";
}
}
Reference blog: https://www.freesion.com/article/3959674242
Sub-library
Splitting the table horizontally through sharding-jdbc has been able to solve most of the problems. However, as the amount of data continues to increase, the performance of the table alone cannot be improved too much, and it must be solved by the database.
Scenario: There are 2 databases: sharding1, sharding2, each database has two tables user_1, user_2, sharding1 stores users younger than 18 years old, sharding2 puts users greater than or equal to 18, and then user_1 stores an odd-numbered id, and user_2 stores an even-numbered id
1 Create a database and table
create table user_1(
id int primary key auto_increment,
name varchar(200),
age int,
birthday datetime,
cmd varchar(200),
sex char(1)
);
create table user_2(
id int primary key auto_increment,
name varchar(200),
age int,
birthday datetime,
cmd varchar(200),
sex char(1)
);
2 Configure application.properties
# 配置逻辑库
spring.shardingsphere.datasource.names=db1,db2
# 配置第 1 个数据源
spring.shardingsphere.datasource.db1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db1.url=jdbc:mysql://localhost:3306/sharding1?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
spring.shardingsphere.datasource.db1.username=root
spring.shardingsphere.datasource.db1.password=root
# 配置第 2 个数据源
spring.shardingsphere.datasource.db2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db2.url=jdbc:mysql://localhost:3306/sharding2?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
spring.shardingsphere.datasource.db2.username=root
spring.shardingsphere.datasource.db2.password=root
# 配置 user 表规则
spring.shardingsphere.sharding.tables.user.actual-data-nodes=db$->{1..2}.user_$->{1..2}
# 配置 user 主键生成策略
spring.shardingsphere.sharding.tables.user.key-generator.column=id
spring.shardingsphere.sharding.tables.user.key-generator.type=user_auto
#配置 表分片策略
spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression=user_$->{ (id % 2)+1 }
#配置 库分片策略(主要就是增加了这个配置)
spring.shardingsphere.sharding.tables.user.database-strategy.inline.sharding-column=age
spring.shardingsphere.sharding.tables.user.database-strategy.inline.algorithm-expression=db$->{ (age>18 ? 2:1) }
The rest of the operation remains the same
Customized sub-library strategy
The custom sharding strategy is the same as the custom sharding, you only need to implement the PreciseShardingAlgorithm interface, and then configure the interface
public class UserDatabaseAlgorithm implements PreciseShardingAlgorithm<Integer> {
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<Integer> preciseShardingValue) {
//collection 内是所有的数据库名称
Integer value = preciseShardingValue.getValue();
if(value.compareTo(18) >=0){
return "db2";
}else{
return "db1";
}
}
}
Configuration:
#配置 库分片策略
spring.shardingsphere.sharding.tables.user.database-strategy.standard.sharding-column=age
#这里指向刚刚自定义的类
spring.shardingsphere.sharding.tables.user.database-strategy.standard.precise-algorithm-class-name=com.mt.config.UserDatabaseAlgorithm
Public table
In some cases, we may return a table with the same name in two databases, and then the data must be consistent. When inserting data, insert data into the two databases at the same time, or both succeed or fail.
Scenario: Create a common table in two databases, sharding1 and sharding2
create table common(
id int primary key,
name varchar(20)
);
Configuration:
# 配置真实数据源
spring.shardingsphere.datasource.names=db1,db2
# 配置第 1 个数据源
spring.shardingsphere.datasource.db1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db1.url=jdbc:mysql://localhost:3306/sharding1?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
spring.shardingsphere.datasource.db1.username=root
spring.shardingsphere.datasource.db1.password=root
# 配置第 2 个数据源
spring.shardingsphere.datasource.db2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db2.url=jdbc:mysql://localhost:3306/sharding2?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
spring.shardingsphere.datasource.db2.username=root
spring.shardingsphere.datasource.db2.password=root
#配置2个数据库的公共表(重点)
spring.shardingsphere.sharding.broadcast-tables=common
#配置表主键
spring.shardingsphere.sharding.tables.common.key-generator.column=id
#配置主键生成策略
spring.shardingsphere.sharding.tables.common.key-generator.type=user_auto
use:
Create entity, mapper and service:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Common {
private Integer id;
private String name;
}
public interface CommonMapper extends BaseMapper<Common> {
}
@Service
public class CommonService {
@Autowired
private CommonMapper commonMapper;
//这里必须加事务,否则可能出现一个数据库插入成功另一个不成功的情况
@Transactional(rollbackFor = Exception.class)
public void insert(Common common){
commonMapper.insert(common);
}
}
test:
@SpringBootTest
class ShardingJdbcApplicationTests {
@Autowired
private CommonService commonService;
@Test
void contextLoads() {
commonService.insert(new Common(1,"abcdadfa"));
}
}
Master-slave replication
Sharding-jdbc provides us with the dynamic switching function of Mysql master-slave replication database, only need to carry out the corresponding configuration to realize the dynamic switching data source under the master-slave replication environment
Scenario: mysql is configured with master-slave replication. The master library is sharding1, and the slave libraries are sharding2 and sharding3. Sharding1 is responsible for adding, deleting and modifying operations, sharding2 and sharding3 only accept query operations
1. Create a database table
create table student(
id int primary key auto_increment,
name varchar(200)
);
2. Configuration
# 配置真实数据源
spring.shardingsphere.datasource.names=db1,db2,db3
# 配置第 1 个数据源
spring.shardingsphere.datasource.db1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db1.url=jdbc:mysql://localhost:3306/sharding1?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
spring.shardingsphere.datasource.db1.username=root
spring.shardingsphere.datasource.db1.password=root
# 配置第 2 个数据源
spring.shardingsphere.datasource.db2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db2.url=jdbc:mysql://localhost:3306/sharding2?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
spring.shardingsphere.datasource.db2.username=root
spring.shardingsphere.datasource.db2.password=root
# 配置第 2 个数据源
spring.shardingsphere.datasource.db3.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.db3.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.db3.url=jdbc:mysql://localhost:3306/sharding3?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
spring.shardingsphere.datasource.db2.username=root
spring.shardingsphere.datasource.db2.password=root
#重点
#配置 虚拟数据库ds, 真实的主库为db1
spring.shardingsphere.sharding.master-slave-rules.ds.master-data-source-name=db1
#配置 虚拟数据库ds, 真实的从库为db2
spring.shardingsphere.sharding.master-slave-rules.ds.slave-data-source-names=db2,db3
#配置student表的真实数据库(注意配置的是ds数据库)
spring.shardingsphere.sharding.tables.student.actual-data-nodes=ds.student
3 test
//编写bean mapper
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private Integer id;
private String name;
}
public interface StudentMapper extends BaseMapper<Student> {
}
//测试:
@Test
public void tet2(){
System.out.println("=====================插入数据库======================");
studentMapper.insert(new Student(1,"abc"));
System.out.println("=====================查询数据库======================");
studentMapper.selectList(null);
System.out.println("=====================查询数据库======================");
studentMapper.selectList(null);
System.out.println("=====================查询数据库======================");
studentMapper.selectList(null);
}