点一下关注吧!!!非常感谢!!持续更新!!!
🚀 AI篇持续更新中!(长期更新)
AI炼丹日志-31- 千呼万唤始出来 GPT-5 发布!“快的模型 + 深度思考模型 + 实时路由”,持续打造实用AI工具指南!📐🤖
💻 Java篇正式开启!(300篇)
目前2025年09月01日更新到:
Java-113 深入浅出 MySQL 扩容全攻略:触发条件、迁移方案与性能优化
MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!
📊 大数据板块已完成多项干货更新(300篇):
包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解
分片剖析
核心概念
表概念详解
1. 真实表(Physical Table)
真实表是指实际存在于数据库中的物理表,是数据最终存储的实体表。在分库分表场景中,这些表通常带有数字后缀来表示分片。例如:
b_order0
:表示订单表的第一个分片b_order1
:表示订单表的第二个分片b_order_item0
:表示订单明细表的第一个分片
这些真实表分散在不同的数据库实例或数据源中,每个表存储部分数据。
2. 逻辑表(Logical Table)
逻辑表是对同一类表结构的抽象表示,是应用程序中使用的表名。它代表了一组分片表的集合。例如:
b_order
:表示所有订单分片表的逻辑名称b_order_item
:表示所有订单明细分片表的逻辑名称
在SQL查询中,开发者只需要使用逻辑表名,分片中间件会自动将查询路由到对应的真实表。
3. 数据节点(Data Node)
数据节点是分片后的基本数据单元,由数据源和真实表组成,格式通常为数据源名.真实表名
。例如:
ds0.b_order1
:表示数据源ds0中的b_order1表ds1.b_order_item2
:表示数据源ds1中的b_order_item2表
数据节点明确了数据存储在哪个数据库实例的哪个表中。
4. 绑定表(Binding Table)
绑定表是指分片规则完全一致的主表和子表关系。它们具有相同的分片键和分片算法,确保关联数据存储在同一分片上。典型特征:
- 相同的分片键:如都使用order_id分片
- 相同的分片算法:如都采用取模算法
- 相同的分片数量:如都分为4个分片
常见绑定表示例:
- 订单表
b_order
和订单明细表b_order_item
,都按order_id % 4
分片 - 用户表
t_user
和用户地址表t_user_address
,都按user_id
范围分片
绑定表优势:
- 关联查询时可直接在单个分片上完成,避免跨分片查询
- 消除笛卡尔积问题,提高查询效率
- 保证事务一致性,相关数据在同一分片
绑定表配置示例(YAML):
spring:
shardingsphere:
sharding:
binding-tables:
- b_order,b_order_item
- t_user,t_user_address
注意事项:
- 绑定表必须使用完全相同的分片规则
- 新增绑定表时需要确保历史数据的分片一致性
- 不建议频繁修改绑定表关系,可能导致数据迁移
在绑定表中,我们会遇到如下的情况:
b_order: b_order0, b_order1
b_order_item: b_order_item0, b_order_item1
SELECT * FROM b_order o
JOIN b_order_item i
ON(o.order_id=i.order_id)
WHERE o.order_id IN (10, 11);
如果我们不配置绑定表关系,采用笛卡尔积关系关联,会生成4个SQL:
SELECT * FROM b_order0 o
JOIN b_order_item0 i
ON(o.order_id=i.order_id)
WHERE o.order_id IN(10,11);
SELECT * FROM b_order0 o
JOIN b_order_item1 i
ON(o.order_id=i.order_id)
WHERE o.order_id IN(10,11);
SELECT * FROM b_order1 o
JOIN b_order_item0 i
ON(o.order_id=i.order_id)
WHERE o.order_id IN(10,11);
SELECT * FROM b_order1 o
JOIN b_order_item1 i
ON(o.order_id=i.order_id)
WHERE o.order_id IN(10,11);
如果我们配置了绑定表关系,生成2个SQL:
SELECT * FROM b_order0 o
JOIN b_order_item0
ON(o.order_id=i.order_id)
WHERE o.order_id IN(10,11);
SELECT * FROM b_order1 o
JOIN b_order_item1
ON(o.order_id=i.order_id)
WHERE o.order_id IN(10,11);
广播表是一种特殊的数据分片策略,主要用于处理那些不需要分片但又需要频繁关联查询的小型数据表。这类表通常具有以下特点:
数据规模较小
- 数据量通常在几百条到几万条之间
- 例如:系统字典表、省份城市表、行政区划表、货币汇率表等
高频率的关联查询需求
- 需要与海量数据进行JOIN操作
- 例如:订单表需要关联省份表获取地区信息
数据一致性要求高
- 内容相对静态,更新频率低
- 需要保证所有节点数据完全一致
实现原理:
数据同步机制
- 当广播表数据更新时,会自动同步到所有数据节点
- 一般采用"发布-订阅"模式进行数据同步
存储方式
- 在每个数据节点上都保存完整的表副本
- 表结构和数据内容完全相同
查询优化
- 查询时可以直接访问本地副本
- 避免了跨节点的网络开销
典型应用场景:
基础数据表
- 国家/地区代码表
- 产品分类表
系统配置表
- 系统参数表
- 用户角色表
维度表
- 时间维度表
- 组织机构表
注意事项:
- 不适合数据量大的表
- 会占用多个节点的存储空间
- 更新操作会有性能开销
- 需要同步到所有节点
- 需要合理控制广播表数量
- 过多的广播表会影响系统性能
分片算法详解
分片算法作为分布式数据库中的核心组件,其设计需要与具体业务场景紧密结合。由于不同业务对数据分片的需求差异较大,系统采用抽象分片策略的方式,将常见场景进行模式化提炼,同时提供可扩展接口让开发者根据实际需求实现定制化分片逻辑。
目前系统支持四种主要分片算法类型,每种算法针对不同的查询场景和业务需求:
1. 精确分片算法(PreciseShardingAlgorithm)
- 适用场景:处理使用单一分片键的等值查询(IN操作符)场景
- 典型查询示例:
WHERE user_id IN (1001, 1002, 1003)
- 实现要求:需要实现
doSharding()
方法,根据精确值路由到目标分片 - 应用案例:电商系统中按用户ID查询订单信息,将特定用户的订单路由到对应分片
2. 范围分片算法(RangeShardingAlgorithm)
- 适用场景:处理使用单一分片键的范围查询场景
- 支持操作符:BETWEEN AND、>、<、>=、<=
- 典型查询示例:
WHERE order_date BETWEEN '2023-01-01' AND '2023-01-31'
- 实现特点:需要处理区间范围到分片的映射关系
- 应用案例:金融系统按交易日期范围查询历史记录
3. 复合分片算法(ComplexKeysShardingAlgorithm)
- 适用场景:处理多分片键组合的复杂分片逻辑
- 特点:
- 支持多个分片键的组合条件
- 分片逻辑复杂度由开发者自行控制
- 可实现AND/OR等复杂条件组合
- 典型查询示例:
WHERE region='north' AND department='sales'
- 应用案例:多租户SAAS系统按租户ID+业务部门进行数据隔离
4. Hint分片算法(HintShardingAlgorithm)
- 适用场景:分片字段不由SQL决定,而由外部条件指定的特殊场景
- 实现方式:
- Java API:通过编程方式指定分片信息
- SQL注释:通过特殊注释语法注入分片信息
- 典型应用:
- 内部管理系统按员工登录ID分片,但数据库无此字段
- 需要绕过SQL直接指定路由的场景
- 使用示例:
// Java API方式
HintManager.getInstance().addDatabaseShardingValue("logicTable", "shardingValue");
/* Sharding:table=employee_01 */ SELECT * FROM employee
每种分片算法都通过标准接口定义,开发者只需实现对应接口即可集成自定义分片逻辑。这种设计既保证了核心功能的统一性,又为特殊业务场景提供了足够的灵活性。
分片策略详解
ShardingStrategy(分片策略)是分布式数据库分片的核心组件,它由分片键和分片算法组成。分片键用于确定数据分布的依据字段,分片算法则定义了具体的分片规则。目前ShardingSphere提供了5种主要的分片策略,每种策略适用于不同的业务场景:
1. 标准分片策略(StandardShardingStrategy)
- 特点:仅支持单个分片键,适用于大多数常规分片场景
- 支持的操作:=、>=、IN、BETWEEN AND等SQL操作符
- 算法组件:
- PreciseShardingAlgorithm(精确分片算法):必须实现,用于处理=和IN操作
- RangeShardingAlgorithm(范围分片算法):可选实现,用于处理BETWEEN、>、<等范围查询
- 注意事项:
- 当执行范围查询时,如果未配置RangeShardingAlgorithm,系统会退化为全库路由扫描,严重影响性能
- 示例:对于订单表按order_id分片,可配置精确算法实现按ID哈希,范围算法实现按时间范围查询
2. 复合分片策略(ComplexShardingStrategy)
- 特点:支持多个分片键组合,适合复杂业务场景
- 支持的操作:=、>、<、IN等所有基本操作符
- 实现方式:
- 将多个分片键的组合值和操作符直接透传给分片算法
- 完全由开发者自定义实现逻辑,灵活性最高
- 典型场景:
- 需要同时按用户ID和地区ID分片的场景
- 多维度联合分片的业务需求
3. 行表达式分片策略(InlineShardingStrategy)
- 特点:基于Groovy表达式的轻量级分片方案
- 支持的操作:=和IN操作
- 优势:
- 无需编写Java代码,通过简单配置即可实现
- 适合简单的分片规则
- 配置示例:
# 按用户ID取模分8张表 t_user_$->{u_id % 8}
- 实际效果:生成t_user_0到t_user_7共8张表
- 应用场景:中小型系统的快速分片方案
4. Hint分片策略(HintShardingStrategy)
- 特点:通过编程方式指定分片值
- 实现方式:
- 使用HintManager强制指定路由分片
- 完全绕过SQL解析过程
- 使用场景:
- 特殊业务需要强制路由的场景
- 分片键不在SQL中的查询
- 运维操作需要指定特定分片
- 示例:
HintManager.getInstance().addDatabaseShardingValue("t_order", 1);
5. 不分片策略(NoneShardingStrategy)
- 特点:显式声明不分片
- 使用场景:
- 小表不需要分片的场景
- 全局表配置
- 测试环境调试
- 注意事项:
- 配置该策略的表会在所有数据节点上完整存储
- 适合数据量小且需要频繁关联查询的表
每种分片策略都有其特定的适用场景,实际选择时需要综合考虑业务特点、查询模式和数据规模等因素。对于复杂系统,通常会混合使用多种分片策略以满足不同业务组件的需求。
策略配置
在分布式数据库系统中,分片策略的配置主要分为两种类型:数据源分片策略和表分片策略。这两种策略虽然作用范围不同,但提供了完全一致的API接口,便于统一管理和维护。
1. 数据源分片策略
数据源分片策略主要用于定义数据记录如何被分配到不同的物理数据源节点上。具体功能包括:
- 数据源映射配置:明确指定数据记录应该被路由到哪个具体的数据源实例
- 分片算法定义:支持多种分片算法,如:
- 哈希分片(基于主键哈希值)
- 范围分片(如按ID范围划分)
- 时间分片(如按月/季度划分)
- 权重配置:可以为不同数据源节点设置不同的存储权重
典型应用场景:
- 电商系统的用户数据按地域分片
- 日志系统按时间分片到不同数据源
- 多租户SaaS应用按租户ID分片
2. 表分片策略
表分片策略负责定义数据记录在目标数据源内部的表级分布规则,其特点是:
- 依赖关系:必须基于数据源分片策略的结果,因为表必须存在于某个数据源中
- 多级分片:支持表内进一步分片,如:
- 水平分表(将表数据分散到多个相同结构的表中)
- 垂直分表(按列拆分到不同表)
- 动态扩展:支持运行时动态添加新的分表
配置示例:
# 数据源分片策略
datasource-sharding:
strategy: hash
sharding-key: user_id
nodes: ds0,ds1,ds2
# 表分片策略
table-sharding:
strategy: range
sharding-key: order_date
tables: order_2023q1, order_2023q2
actual-data-nodes: ds$->{0..2}.order_$->{2023q1,2023q2}
两种策略的协同工作流程:
- 系统首先根据数据源分片策略确定目标数据源
- 然后在选定的数据源中应用表分片策略确定具体的目标表
- 最终完成数据的路由和存储
维护注意事项:
- 修改分片策略时需要确保数据迁移方案
- 建议先维护数据源策略再维护表策略
- 策略变更后需要验证历史数据的可访问性