当前位置:   article > 正文

ShardingSphere实践(1)水平分表_dynamic-datasource shardingsphere postgresql水平分表

dynamic-datasource shardingsphere postgresql水平分表

水平分表

水平分表,是一种数据库分表策略,它将一个逻辑表按照某个规则拆分成多个物理表,将数据水平分散存储在这些物理表中。每个物理表通常存储一部分数据,使得数据库的负载可以均匀地分布在多个表中,提高数据库的性能和扩展性。

水平分表的主要思想是根据某个列或一组列的值将数据进行分割,并将相同分割规则的数据存储到不同的物理表中。这样,当执行查询时,可以只针对特定的物理表进行操作,避免全表扫描,提高查询效率。

常见的水平分表策略包括:

1.范围分表:按照某个范围将数据进行划分,例如按照时间范围将订单数据分散到不同的表中,如user_2023、user_2024等。

2.散列分表:根据某个列的散列值将数据分布到多个表中,例如使用用户ID的散列值将用户数据分散到不同的表中。

3.列分表:根据某个列的特定值将数据分散到不同的表中,例如按照地区将用户数据分散到不同的表中,如user_shanghai、user_beijing等。

下面我们进行基于散列分表策略的实践

一.加入依赖

参考我的pom文件

  1. <properties>
  2. <mysql.version>5.1.20</mysql.version>
  3. <mybatis.plus.boot.starter.version>3.4.2</mybatis.plus.boot.starter.version>
  4. <lombok.version>1.18.24</lombok.version>
  5. <druid.version>1.1.22</druid.version>
  6. <shardingsphere.version>4.0.0-RC1</shardingsphere.version>
  7. </properties>
  8. <dependencies>
  9. <dependency>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-test</artifactId>
  12. <scope>test</scope>
  13. </dependency>
  14. <dependency>
  15. <groupId>org.springframework.boot</groupId>
  16. <artifactId>spring-boot-starter-web</artifactId>
  17. </dependency>
  18. <dependency>
  19. <groupId>mysql</groupId>
  20. <artifactId>mysql-connector-java</artifactId>
  21. <version>${mysql.version}</version>
  22. </dependency>
  23. <dependency>
  24. <groupId>org.projectlombok</groupId>
  25. <artifactId>lombok</artifactId>
  26. <optional>true</optional>
  27. <scope>provided</scope>
  28. <version>${lombok.version}</version>
  29. </dependency>
  30. <dependency>
  31. <groupId>com.baomidou</groupId>
  32. <artifactId>mybatis-plus-boot-starter</artifactId>
  33. <version>${mybatis.plus.boot.starter.version}</version>
  34. </dependency>
  35. <dependency>
  36. <groupId>org.apache.shardingsphere</groupId>
  37. <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
  38. <version>${shardingsphere.version}</version>
  39. </dependency>
  40. <dependency>
  41. <groupId>com.alibaba</groupId>
  42. <artifactId>druid-spring-boot-starter</artifactId>
  43. <version>${druid.version}</version>
  44. </dependency>
  45. </dependencies>

二.配置ShardingSphere

application.properties

  1. # 单数据库分表,指定数据源名称
  2. spring.shardingsphere.datasource.names=master0
  3. # 分表时一个实体类对应多张表,设置为true覆盖
  4. spring.main.allow-bean-definition-overriding=true
  5. #配置第一个数据源,包括连接池、驱动类、数据库地址、用户名、密码
  6. spring.shardingsphere.datasource.master0.type=com.alibaba.druid.pool.DruidDataSource
  7. spring.shardingsphere.datasource.master0.driver-class-name=com.mysql.jdbc.Driver
  8. spring.shardingsphere.datasource.master0.url=jdbc:mysql://localhost:3306/shardingsphere-demo?serverTimezone=GMT%2B8
  9. spring.shardingsphere.datasource.master0.username=root
  10. spring.shardingsphere.datasource.master0.password=123456
  11. #指定数据库分布情况,数据库里面表分布情况,如下user表,对应user_0,user_1,user_2三张表
  12. spring.shardingsphere.sharding.tables.user.actual-data-nodes=master0.user_$->{0..2}
  13. # 指定表的主键id,生成策略,SNOWFLAKE(雪花算法)
  14. spring.shardingsphere.sharding.tables.user.key-generator.column=id
  15. spring.shardingsphere.sharding.tables.user.key-generator.type=SNOWFLAKE
  16. # 指定表分片策略,分片列为id字段,根据id字段的值进行分片,分片策略为id的值 % 3(表的个数),余数为0的放入user_0表,为1的放入user_1表,为2的放入user_2表
  17. spring.shardingsphere.sharding.tables.user.table-strategy.inline.sharding-column=id
  18. spring.shardingsphere.sharding.tables.user.table-strategy.inline.algorithm-expression=user_$->{id % 3}
  19. # 打开sql输出日志
  20. spring.shardingsphere.props.sql.show=true

三.建表

建立user_0、user_1、user_2三张表

  1. CREATE TABLE `user_0` (
  2. `id` bigint(20) NOT NULL,
  3. `name` varchar(255) DEFAULT NULL,
  4. PRIMARY KEY (`id`)
  5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  6. CREATE TABLE `user_1` (
  7. `id` bigint(20) NOT NULL,
  8. `name` varchar(255) DEFAULT NULL,
  9. PRIMARY KEY (`id`)
  10. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  11. CREATE TABLE `user_2` (
  12. `id` bigint(20) NOT NULL,
  13. `name` varchar(255) DEFAULT NULL,
  14. PRIMARY KEY (`id`)
  15. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

四.编写业务代码

实体类

  1. @Data
  2. public class User {
  3. @TableId
  4. private Long id;
  5. private String name;
  6. }

UserMapper

  1. public interface UserMapper extends BaseMapper<User> {
  2. }

UserService 

  1. public interface UserService extends IService<User> {
  2. }

UserServiceImpl

  1. @Service
  2. public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
  3. }

五.测试

  1. @SpringBootTest(classes = ShardingsphereDemoApplication.class)
  2. public class UserServiceTest {
  3. @Autowired
  4. private UserService userService;
  5. @Test
  6. public void testUser() {
  7. for (int i = 0; i < 10; i++) {
  8. User user = new User();
  9. user.setName("user_" + i);
  10. userService.save(user);
  11. }
  12. List<User> list = userService.list();
  13. for (User user: list) {
  14. System.out.println(user.toString());
  15. }
  16. }
  17. }

我们往user表中插入10条数据,结果如下

数据根据id取余后的数分别分配到三张表中,同时通过

userService.list() 

 查询出三张表的所有数据,日志中,可以看到实际上执行了3个SQL,从三张表中查出所有数据

自定义分片策略

范围分表

上面实践了如何通过散列策略进行水平分表,这里通过自定义分片策略,实现根据数据的创建时间来分配到不同的表上

1.自定义日期分片算法类

  1. /**
  2. * 日期分片算法
  3. */
  4. @Slf4j
  5. public class DateShardingAlgorithm implements PreciseShardingAlgorithm<LocalDateTime> {
  6. @Override
  7. public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<LocalDateTime> shardingValue) {
  8. // 将传进来的createTime拼接为表名
  9. String table = shardingValue.getLogicTableName() + "_" + shardingValue.getValue().getYear();
  10. // 匹配分表
  11. for (String targetName : availableTargetNames) {
  12. if (targetName.equals(table)) {
  13. return targetName;
  14. }
  15. }
  16. throw new IllegalArgumentException();
  17. }
  18. }

2.User实体类上增加一个createTime字段

  1. @Data
  2. public class User {
  3. @TableId
  4. private Long id;
  5. private String name;
  6. @TableField(value = "create_time")
  7. private LocalDateTime createTime;
  8. }

3.分片策略配置修改

  1. spring.shardingsphere.sharding.tables.user.actual-data-nodes=master0.user_$->{2023..2024}
  2. spring.shardingsphere.sharding.tables.user.table-strategy.standard.sharding-column=create_time
  3. spring.shardingsphere.sharding.tables.user.table-strategy.standard.precise-algorithm-class-name=com.demo.config.DateShardingAlgorithm

4.创建user_2023、user_2024两张表

  1. CREATE TABLE `user_2023` (
  2. `id` bigint(20) NOT NULL,
  3. `name` varchar(255) DEFAULT NULL,
  4. `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  5. PRIMARY KEY (`id`)
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  7. CREATE TABLE `user_2024` (
  8. `id` bigint(20) NOT NULL,
  9. `name` varchar(255) DEFAULT NULL,
  10. `create_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  11. PRIMARY KEY (`id`)
  12. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

5.测试

  1. @SpringBootTest(classes = ShardingsphereDemoApplication.class)
  2. public class UserServiceTest {
  3. @Autowired
  4. private UserService userService;
  5. @Test
  6. public void testUser() {
  7. for (int i = 0; i < 10; i++) {
  8. User user = new User();
  9. user.setName("user_" + i);
  10. LocalDateTime now = LocalDateTime.now();
  11. if (i > 6) {
  12. now = now.minusYears(1);
  13. }
  14. user.setCreateTime(now);
  15. userService.save(user);
  16. }
  17. }
  18. }

列分表

通过自定义策略,我们还可以根据用户所在区域进行分片

1.自定义区域分片算法类

  1. /**
  2. * 区域分片算法
  3. */
  4. @Slf4j
  5. public class AreaShardingAlgorithm implements PreciseShardingAlgorithm<String> {
  6. @Override
  7. public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) {
  8. // 将传进来的area拼接为表名
  9. String table = shardingValue.getLogicTableName() + "_" + shardingValue.getValue();
  10. // 匹配分表
  11. for (String targetName : availableTargetNames) {
  12. if (targetName.equals(table)) {
  13. return targetName;
  14. }
  15. }
  16. throw new IllegalArgumentException();
  17. }
  18. }

2.User实体类上增加一个area字段

  1. @Data
  2. public class User {
  3. @TableId
  4. private Long id;
  5. private String name;
  6. private String area;
  7. }

3.分片策略配置修改

  1. spring.shardingsphere.sharding.tables.user.actual-data-nodes=master0.user_shanghai,master0.user_beijing
  2. spring.shardingsphere.sharding.tables.user.table-strategy.standard.sharding-column=area
  3. spring.shardingsphere.sharding.tables.user.table-strategy.standard.precise-algorithm-class-name=com.demo.config.AreaShardingAlgorithm

4.创建user_shanghai、user_beijing两张表

  1. CREATE TABLE `user_shanghai` (
  2. `id` bigint(20) NOT NULL,
  3. `name` varchar(255) DEFAULT NULL,
  4. `area` varchar(255) DEFAULT NULL,
  5. PRIMARY KEY (`id`)
  6. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  7. CREATE TABLE `user_beijing` (
  8. `id` bigint(20) NOT NULL,
  9. `name` varchar(255) DEFAULT NULL,
  10. `area` varchar(255) DEFAULT NULL,
  11. PRIMARY KEY (`id`)
  12. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

5.测试

  1. @SpringBootTest(classes = ShardingsphereDemoApplication.class)
  2. public class UserServiceTest {
  3. @Autowired
  4. private UserService userService;
  5. @Test
  6. public void testUser() {
  7. for (int i = 0; i < 10; i++) {
  8. User user = new User();
  9. user.setName("user_" + i);
  10. String area = "shanghai";
  11. if (i > 6) {
  12. area = "beijing";
  13. }
  14. user.setArea(area);
  15. userService.save(user);
  16. }
  17. }
  18. }

  

水平分表的优缺点

优点

水平分表将大量的数据分配到不同的表上,解决了单表过大,检索性能降低的问题,为后期数据迁移、扩展、备份提供了很大的便利,当我们根据id进行查询时,速度非常快,ShardingSphere将查询的id根据分片策略找到数据存放的分片,在分片表上查询

执行下面的语句进行测试

User byId = userService.getById(1780139288814874625L);

可见此语句直接在user_2表中进行查找

缺点

分页查询问题

在分页查询上,我们希望像在单表中一样,按照顺序进行分页,而在水平分表时,数据被分配到多张表上,ShardingSphere会将三张表的数据拼接为一张全表,再进行分页,由于分片的原因,查询出来的数据并不能像我们预想的那样垂直分页,而是乱序的分页

执行下面SQL进行测试

  1. Page page = new Page();
  2. page.setCurrent(1);
  3. page.setSize(3);
  4. Page<User> data = userService.page(page);

最终查询出来的结果不是name等于user_0、user_1、user_2的数据,而是user_0表上的user_2、user_3、user_6

其他条件查询

在单表的情况下,如果我们执行以下SQL

  1. User byId = userService.getOne(Wrappers.<User>lambdaQuery()
  2. .eq(User::getName, "user_6")
  3. .last(" limit 1"));

那么在检索到user_6时就会停止了,而如果通过ShardingSphere进行分表查询

可以发现,在三张表上都执行了同样的SQL

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号