SpringBoot中4种数据水平分片策略

2025-06-22 17:50

本文主要是介绍SpringBoot中4种数据水平分片策略,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《SpringBoot中4种数据水平分片策略》数据水平分片作为一种水平扩展策略,通过将数据分散到多个物理节点上,有效解决了存储容量和性能瓶颈问题,下面小编就来和大家分享4种数据分片策略吧...

一、前言

数据水平分片作为一种水平扩展策略,通过将数据分散到多个物理节点上,有效解决了存储容量和性能瓶颈问题。

而分片键(Sharding Key)作为数据分片的核心,决定了数据如何在各个分片中分布,直接影响到分片系统的性能、数据分布均衡性以及查询效率。

本文将分享4种数据分片策略。

二、哈希分片

2.1 原理

哈希分片通过对分片键值应用哈希函数,然后对分片数量取模,将数据均匀分布到各个分片。

分片索引 = hash(分片键值) % 分片数量

2.2 SpringBoot实现

在SpringBoot中,我们可以使用ShardingSphere-JDBC实现哈希分片。首先添加依赖:

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>5.1.2</version>
</dependency>

然后在application.yml中配置哈希分片策略:

spring:
  shardingsphere:
    datasource:
      names: ds0,ds1,ds2,ds3
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/order_db_0
        username: root
        password: password
      # 其他数据源配置...
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds${0..3}.t_order
            database-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: order-id-hash
        sharding-algorithms:
          order-id-hash:
            type: HASH_MOD
            props:
              sharding-count: 4

在实体类和Repository中使用:

@Entity
@Table(name = "t_order")
public class Order {
    @Id
    private Long orderId;  // 分片键
    priandroidvate Long userId;
    private BigDecimal amount;
    private String status;
    // getters and setters
}

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    // 查询方法
}

2.3 优缺点分析

优点:

  • 数据分布均匀:哈希函数确保数据在各分片上分布均衡
  • 读写负载均衡:避免热点分片问题
  • 简单易实现:配置简单,无需复杂的分片算法

缺点:

  • 不支持范围查询:相邻的键值会被分散到不同分片,导致范围查询需要访问所有分片
  • 分片扩缩容困难:增加或减少分片数量时,需要重新哈希和迁移大量数据
  • 不易直观理解:数据分布不直观,难以预测特定记录在哪个分片

2.4 适用场景

读写操作频繁且均衡的OLTP系统

点查询(如通过主键或唯一索引查询)为主的应用

对数据分布均匀性要求高的系统

分片数量相对稳定的环境

三、范围分片

3.1 原理

范围分片键通过将数据按照分片键的值范围划分到不同的分片中。每个分片负责存储特定范围内的数据,数据在逻辑上保持有序。

如:订单ID 1-1000000 存储在分片1
    订单ID 1000001-2000000 存储在分片2
    ...

3.2 SpringBoot实现

使用ShardingSphere-JDBC实现范围分片:

spring:
  shardingsphere:
    datasource:
      names: ds0,ds1,ds2,ds3
      # 数据源配置...
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds${0..3}.t_order
            database-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: order-id-range
        sharding-algorithms:
          order-id-range:
            type: RANGE
            props:
              strategy: standard
              range-lower: 0,1000000,2000000,3000000
              range-upper: 999999,1999999,2999999,3999999

自定义更复杂的范围分片算法:

@Component
public class CustomRangeShardingAlgorithm implements StandardShardingAlgorithm<Long> {
    @Override
    public String DOSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
        Long orderId = shardingValue.getValue();
        for (String targetName : availableTargetNames) {
            if (targetName.endsWith("0") && orderId <= 1000000) {
                return targetName;
            } else if (targetName.endsWith("1") && orderId > 1000000 && orderId <= 2000000) {
                return targetName;
            } else if (targetName.endsWith("2") && orderId > 2000000 && orderId <= 3000000) {
                return targetName;
            } else if (targetName.endsWith("3") && orderId > 3000000) {
                return targetName;
            }
        }
        throw new UnsupportedOperationException("No available target name for order id: " + orderId);
    }

    // 其他必要的方法实现...
}

3.3 优缺点分析

优点:

  • 支持范围查询:同一范围的数据存储在同一分片,范围查询效率高
  • 数据局部性好:相关数据聚集在一起,提高查询效率
  • 易于理解和维护:分片规则直观,数据分布清晰

缺点:

  • 数据倾斜风险:如果数据分布不均,可能导致某些分片负载过重
  • 热点分片问题:新数据往往落在最新范围的分片上,造成访问热点
  • 分片边界固定:预先定义的范围边界难以动态调整

3.4 适用场景

  • 时间序列数据或自增ID数据的存储
  • 范围查询频繁的应用
  • 历史数据访问频率低但需要保留的系统
  • 可以预测数据增长模式的业务

四、复合分片

4.1 原理

复合分片键使用多个字段的组合作为分片依据,提供更精细和灵活的分片控制。可以同时考虑多个业务维度,使分片更贴合业务特性。

常见的复合分片策略包括:

  • 多字段哈希组合
  • 一级字段范围+二级字段哈希
  • 字段组合后再应用分片算法

4.2 SpringBoot实现

使用ShardingSphere-JDBC实现复合分片:

spring:
  shardingsphere:
    datasource:
      names: ds0,ds1,ds2,ds3
      # 数据源配置...
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds${0..3}.t_order
            database-strategy:
              complex:
                sharding-columns: user_id,order_date
                sharding-algorithm-name: complex-algorithm
        shardinpythong-algorithms:
          complex-algorithm:
            type: CLASS_BASED
            props:
              strategy: COMPLEX
              algorithmClassName: com.example.CustomComplexShardingAlgorithm

自定义复合分片算法:

@Component
public class CustomComplexShardingAlgorithm implements ComplexKeysShardingAlgorithm<Comparable<?>> {
    
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, 
                                        ComplexKeysShardingValue<Comparable<?>> shardingValue) {
        Map<String, Collection<Comparable<?>>> columnNameAndShardingValuesMap = 
            shardingValue.getColumnNameAndShardingValuesMap();
        
        Collection<Comparable<?>> userIds = columnNameAndShardingValuesMap.get("user_id");
        Collection<Comparable<?>> orderDates = columnNameAndShardingValuesMap.get("order_date");
        
        List<String> resjavascriptult = new ArrayList<>();
        
        for (Comparable<?> userId : userIds) {
            for (Comparable<?> orderDate : orderDates) {
                // 使用用户ID和订单日期的组合确定分片
                LocalDate date = (LocalDate) orderDate;
                long hash = (long) userId * 31 + date.getYear() * 12 + date.getMonthValue();
                int shardingKey = (int) (hash % 4);  // 分4个片
                
                for (String targetName : availableTargetNames) {
                    if (targetName.endsWith(String.valueOf(shardingKey))) {
                        result.add(targetName);
                    }
                }
            }
        }
        
        return result;
    }
}

实体类定义:

@Entity
@Table(name = "t_order")
public class Order {
    @Id
    private Long orderId;
    private Long userId;  // 复合分片键之一
    private LocalDate orderDate;  // 复合分片键之一
    private BigDecimal amount;
    private String status;
    // getters and setters
}

4.3 优缺点分析

优点:

  • 分片更精细:可以结合多个业务维度进行数据分布
  • 数据分布更均衡:减少单维度分片可能带来的数据倾斜
  • 查询灵活性:支持多种条件的查询优化
  • 更贴合业务:可以根据实际业务特性定制分片策略

缺点:

  • 实现复杂:需要自定义复杂的分片算法
  • 维护困难:分片逻辑复杂,难以理解和维护
  • 查询成本高:如果查询条件不包含所有分片键,可能需要查询多个分片
  • 测试难度大:需要全面测试各种查询场景

4.4 适用场景

单一分片键无法满足均衡性要求的系统

多维度查询频繁的应用

数据有明显多维度特性的业务

需要精细化控制数据分布的场景

五、时间序列分片

5.1 原理

时间序列分片键使用时间相关字段(如创建时间、交易日期)作为分片依据,通常将特定时间段的数据存储在同一分片中。

这种方式特别适合具有明显时间属性的数据,如日志、订单、交易记录等。

常见的时间分片策略包括:

  • 按年/月/周/日分片
  • 时间窗口滚动分片
  • 时间+其他字段组合分片

5.2 SpringBoot实现

使用ShardingSphere-JDBC实现时间序列分片:

spring:
  shardingsphere:
    datasource:
      names: ds0,ds1,ds2,ds3
      # 数据源配置...
    rules:
      sharding:
        tables:
          t_order:
            actual-data-nodes: ds${0..3}.t_order
            database-strategy:
              standard:
                sharding-column: create_time
                sharding-algorithm-name: time-sharding
        sharding-algorithms:
          time-sharding:
            type: INTERVAL
            props:
              datetime-pattern: yyyy-MM-dd HH:mm:ss
              datetime-lower: 2023-01-01 00:00:00
              datetime-upper: 2023-12-31 23:59:59
              sharding-suffix-pattern: yyyyMM
              datetime-interval-amount: 3
              datetime-interval-unit: MONTHS

自定义更复杂的时间分片算法:

@Component
public class QuarterlyShardingAlgorithm implements StandardShardingAlgorithm<Date> {
    
    private static final Map<Integer, Integer> QUARTER_MAP = new HashMap<>();
    
    static {
        // 将月份映射到季度
        QUARTER_MAP.put(1, 1);
        QUARTER_MAP.put(2, 1);
        QUARTER_MAP.put(3, 1);
        QUARTER_MAP.put(4, 2);
        QUARTER_MAP.put(5, 2);
        QUARTER_MAP.put(6, 2);
        QUAvUcqpveRTER_MAP.put(7, 3);
        QUARTER_MAP.put(8, 3);
        QUARTER_MAP.put(9, 3);
        QUARTER_MAP.put(10, 4);
        QUARTER_MAP.put(11, 4);
        QUARTER_MAP.put(12, 4);
    }
    
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {
        Date date = shardingValue.getValue();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH) + 1;
        int quarter = QUARTER_MAP.get(month);
        
        // 将分片策略设计为按季度循环到不同数据源
        // 例如:2023Q1->ds0, 2023Q2->ds1, 2023Q3->ds2, 2023Q4->ds3, 2024Q1->ds0, ...
        int shardIndex = ((year - 2023) * 4 + quarter - 1) % 4;
        
        for (String targetName : availableTargetNames) {
            if (targetName.endsWith(String.valueOf(shardIndex))) {
                return targetName;
            }
        }
        
        throw new UnsupportedOperationException("No available target name for date: " + date);
    }
    
    // 其他必要的方法实现...
}

实体类定义:

@Entity
@Table(name = "t_order")
public class Order {
    @Id
    private Long orderId;
    private Long userId;
    @Temporal(TemporalType.TIMESTAMP)
    private Date createTime;  // 时间分片键
    private BigDecimal amount;
    private String status;
    // getters and setters
}

5.3 优缺点分析

优点:

  • 数据生命周期管理:便于实现数据的生命周期管理和归档
  • 高效的时间范围查询:同一时间段的数据位于同一分片,时间范围查询高效
  • 分片扩展自然:随着时间推移自然扩展到新分片
  • 冷热数据分离:可以针对不同时间段的数据采用不同的存储策略

缺点:

  • 数据分布不均:如果时间分布不均匀,可能导致分片数据量差异大
  • 写入热点问题:当前时间段的分片成为写入热点
  • 历史分片访问少:旧分片利用率低但仍占用资源
  • 跨时间段查询复杂:大范围的时间查询可能需要访问多个分片

5.4 适用场景

  • 具有明显时间属性和时间局部性的数据
  • 日志、订单、交易等时间序列数据
  • 需要定期归档历史数据的系统
  • 查询通常限定在特定时间范围内的应用

六、分片策略对比

6.2 适用场景分析

分片类型数据分布均衡性查询效率扩展性实现复杂度最适合场景
哈希分片点查询高,范围查询低点查询为主的OLTP系统
范围分片中低范围查询高,点查询中范围查询频繁的系统
复合分片多维查询高,单维查询中android多维查询和复杂业务场景
时间序列分片时间范围查询高时间相关数据和归档需求

6.3 扩容影响分析

分片类型增加分片的数据迁移量查询路由改变应用改造复杂度
哈希分片高(约50%数据需迁移)
范围分片低(仅需调整边界数据)
复合分片中高(取决于算法设计)
时间序列分片低(仅影响新数据)

七、总结

在实际应用中,应根据业务特点、查询模式、性能需求和扩展预期来选择最合适的分片策略。

无论选择哪种分片策略,都应保持分片逻辑的简洁性和可维护性,并在系统设计初期就考虑未来的扩展需求。

通过合理选择分片键,结合SpringBoot和ShardingSphere等工具,可以构建出既满足业务需求又具备良好扩展性的应用。

到此这篇关于SpringBoot中4种数据水平分片策略的文章就介绍到这了,更多相关SpringBoot数据水平分片内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于SpringBoot中4种数据水平分片策略的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155176

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、