【经验总结】ShardingSphere+Springboot-02 数据分片、标准分片算法、时间间隔分片算法

三、分片算法配置

3.1 数据分片

理解数据分片有助于后续分片算法的配置和设计出适配分库分表的SQL语句和业务逻辑。推荐阅读原文

  • https://shardingsphere.apache.org/document/5.2.1/cn/features/sharding/

这里摘要一些精华片段

传统的将数据集中存储至单一节点的解决方案,在性能、可用性和运维成本这三方面已经难于满足海量数据的场景。

从运维成本方面考虑,当一个数据库实例中的数据达到阈值以上,对于 DBA 的运维压力就会增大。 数据备份和恢复的时间成本都将随着数据量的大小而愈发不可控。一般来讲,单一数据库实例的数据的阈值在 1TB 之内,是比较合理的范围。

通过分库和分表进行数据的拆分来使得各个表的数据量保持在阈值以下,以及对流量进行疏导应对高访问量,是应对高并发和海量数据系统的有效手段。 数据分片的拆分方式又分为垂直分片和水平分片。

3.1.2 垂直分片

按照业务拆分的方式称为垂直分片,又称为纵向拆分,它的核心理念是专库专用。 ……下图展示了根据业务需要,将用户表和订单表垂直分片到不同的数据库的方案。

image-20240807150922245

优点:垂直分片在不借助专业的分片设计组件实现根据业务拆分数据库

缺点:垂直分片往往需要对架构和设计进行调整;通常来讲,是来不及应对互联网业务需求快速变化的;而且,它也并无法真正的解决单点瓶颈

技术选型:借助多数据源组件和业务逻辑的修改即可实现,比较常见的微服务架构在考虑模块区分时一般也会将数据库按模块进行划分,这就是一种常见的垂直分片设计。

3.1.2 水平分片

水平分片又称为横向拆分。 相对于垂直分片,它不再将数据根据业务逻辑分类,而是通过某个字段(或某几个字段),根据某种规则将数据分散至多个库或表中,每个分片仅包含数据的一部分。

例如:根据主键分片,偶数主键的记录放入 0 库(或表),奇数主键的记录放入 1 库(或表),如下图所示。

image-20240807151010924

优点:水平分片从理论上突破了单机数据量处理的瓶颈,并且扩展相对自由,是数据分片的标准解决方案。

缺点&挑战

  • 面对如此散乱的分片之后的数据,应用开发工程师和数据库管理员对数据库的操作变得异常繁重就是其中的重要挑战之一。 他们需要知道数据需要从哪个具体的数据库的子表中获取

  • 另一个挑战则是,能够正确的运行在单节点数据库中的 SQL,在分片之后的数据库中并不一定能够正确运行。 例如,分表导致表名称的修改,或者分页、排序、聚合分组等操作的不正确处理。

  • 跨库事务也是分布式的数据库集群要面对的棘手事情。…… 合理采用分表,可以在降低单表数据量的情况下,尽量使用本地事务……不能避免跨库事务的场景,有些业务仍然需要保持事务的一致……互联网巨头大多采用最终一致性的柔性事务代替强一致事务。

ShardingSphere的设计目的:

尽量透明化分库分表所带来的影响,让使用方尽量像使用一个数据库一样使用水平分片之后的数据库集群,是 Apache ShardingSphere 数据分片模块的主要设计目标。

3.2 (标准)分片算法

官方提供了很多算法,具体可以看官方文档,在实际开发场景中可以根据业务实际情况来选择分片算法;这里主要介绍一下常用的几类标准分片算法。

3.2.1 INLINE 基于行表达式的分片算法 (必须掌握)

使用 Groovy 的表达式,提供对 SQL 语句中的 =IN 的分片操作支持,只支持单分片键。 对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的 Java 代码开发,如: t_user_$->{u_id % 8} 表示 t_user 表根据 u_id 模 8,而分成 8 张表,表名称为 t_user_0t_user_7

可用的配置参数如下:

属性名称数据类型说明默认值
algorithm-expressionString分片算法的行表达式
allow-range-query-with-inline-sharding (?)boolean是否允许范围查询。注意:范围查询会无视分片策略,进行全路由false

举例:想上面快速开始中的配置

#...
        # 配置分片算法
        sharding-algorithms:
          # 分库策略:根据age取余2
          student_age_inline:
            type: INLINE
            props:
              algorithm-expression: ds$->{age % 2}
          # 分表策略:根据classid取余2
          student_class_id_inline:
            type: INLINE
            props:
              algorithm-expression: student$->{class_id % 2}
关于是否开启范围查找
  • 允许范围查询:当设置为 true 时,这个配置项允许使用行表达式分片算法的表进行范围查询。范围查询是指查询条件中包含大于(>)、小于(<)、大于等于(>=)、小于等于(<=)等操作符的查询。

  • 限制:默认情况下,行表达式分片算法不支持范围查询,因为范围查询可能涉及多个分片,而简单的行表达式分片算法通常只能将查询路由到单个分片。如果设置为 false(默认值),则范围查询会被限制,只能针对单个分片进行,这可能会导致查询结果不完整或者查询失败。

举个例子,比如分片键为字段age,要执行SQL语句

SELECT id,name,age,class_id FROM student WHERE (age BETWEEN ? AND ?)

如果不开启该项,运行相关的sql则会报错

image-20240807153832048

修改配置后则会看到正确的运行了范围查询语句

Actual SQL: ds0 ::: SELECT  id,name,age,class_id  FROM student0 WHERE (age BETWEEN ? AND ?) UNION ALL SELECT  id,name,age,class_id  FROM student1 WHERE (age BETWEEN ? AND ?) ::: [20, 30, 20, 30]

Actual SQL: ds1 ::: SELECT  id,name,age,class_id  FROM student0  WHERE (age BETWEEN ? AND ?) UNION ALL SELECT  id,name,age,class_id  FROM student1  WHERE (age BETWEEN ? AND ?) ::: [20, 30, 20, 30]
3.2.2 INTERVAL 时间范围分片算法

源码位置:org.apache.shardingsphere.sharding.algorithm.sharding.datetime.IntervalShardingAlgorithm

属性名称数据类型说明默认值
datetime-patternString分片键的时间戳格式,必须遵循 Java DateTimeFormatter 的格式。例如:yyyy-MM-dd HH:mm:ss,yyyy-MM-dd 或 HH:mm:ss 等。但不支持与 java.time.chrono.JapaneseDate 相关的 Gy-MM 等
datetime-lowerString时间分片下界值,格式与 datetime-pattern 定义的时间戳格式一致
datetime-upper (?)String时间分片上界值,格式与 datetime-pattern 定义的时间戳格式一致当前时间
sharding-suffix-patternString分片数据源或真实表的后缀格式,必须遵循 Java DateTimeFormatter 的格式,必须和 datetime-interval-unit 保持一致。例如:yyyyMM
datetime-interval-amount (?)int分片键时间间隔,超过该时间间隔将进入下一分片1
datetime-interval-unit (?)String分片键时间间隔单位,必须遵循 Java ChronoUnit 的枚举值。例如:MONTHSDAYS

官方对于该算法的描述是在让人看不懂,我尝试通过一个测试可行的例子 帮助理解;

创建表结构如下,在同一个数据源中创建若干个表结构一样的打卡表

image-20240809090759514

实现目标:使用时间间隔分片算法,根据打卡时间进行分库(分表)

yaml配置文件

    rules:
      sharding:
        tables:
         # ...
          stu_clock:
            actual-data-nodes: dsmain.stu_clock${202301..202501}
            key-generate-strategy:
              column: id
              key-generator-name: snowflake
            table-strategy:
              standard:
                sharding-column: clockin_time
                sharding-algorithm-name: student_clock_interval
        # 配置分片算法
        sharding-algorithms:
        	# 声明一个时间间隔算法
          student_clock_interval:
            type: INTERVAL
            props:
              # 	时间分片下界值,格式与 datetime-pattern 定义的时间戳格式一致
              datetime-pattern: 'yyyy-MM-dd HH:mm:ss'
              datetime-lower: '2023-01-01 00:00:00'
              # 时间分片上界值,格式与 datetime-pattern 定义的时间戳格式一致 默认当前时间
              datetime-upper: '2025-01-01 00:00:00'
              # 分片数据源或真实表的后缀格式,必须遵循 Java DateTimeFormatter 的格式,必须和 datetime-interval-unit 保持一致。例如:yyyyMM
              sharding-suffix-pattern: 'yyyyMM'
              # 分片键时间间隔,超过该时间间隔将进入下一分片
              datetime-interval-amount: 1
              # 分片键时间间隔单位,必须遵循 Java ChronoUnit 的枚举值。例如:MONTHS
              datetime-interval-unit: 'MONTHS'

配置解释

INTERVAL算法的核心是让数据根据分片键按一定间隔规律进行分库分表,比如在上述配置中,

  • 我们声明了一个stu_clock表根据分片键【打卡时间】clockin_time进行【分表】配置;

  • 分片规则是按月份作为分片键间隔单位 datetime-interval-unit: 'MONTHS'

  • 并且声明了间隔单位为1,即每隔一个月就进入下一个分片 datetime-interval-amount: 1

    声明了间隔和单位后,我们已经可以推测出一定的分片规则,但是仍然有几个问题需要解决->

    • 如何体现当前分片
    • 解决“由于起终时间不同,间隔相同时,分片不确定的”问题
  • 所以进一步需要 :声明时间分片上下界值和分片数据源或真实表的后缀格式

进行测试

插入打卡时间为2024-08-08 17:42:54数据,SS根据分片算法识别到stu_clock202408 并成功插入数据

image-20240808174312851

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xcong_Zhu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值