数据分片
- 一般单表的数据阈值在1TB之内,当超过1TB的时候,就需要进行数据分片了
垂直分片
按照业务进行拆分,不同的业务在不同的库中,将数据访问压力由单库分散到不同的库中,但是,当表数据量超过单节点阈值的时候,还是需要进行水平分片
水平分片
水平分片一般是指将单表根据某个或者某几个字段 分成多个库或者多个表内,每个分片只包含数据的一部分。
逻辑表
水平拆分的数据库(表)的相同逻辑和数据结构表的总称。例:订单数据根据主键尾数拆分为 10 张表,分别是 t_order_0 到 t_order_9,他们的逻辑表名为 t_order。不是一个真实存在的表
真实表
在分片的数据库中真实存在的物理表。即上个示例中的 t_order_0 到 t_order_9。
数据节点
数据分片的最小单元。由数据源名称和数据表组成,例:ds_0.t_order_0。
绑定表
- 指分片规则一致的主表和子表。例如:t_order 表和 t_order_item 表,均按照 order_id 分片,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升
- 想我们一般的电商系统中,一般都会使用用户id进行分片,这样,用户的订单信息,会员信息,优惠价信息等等都在一个分片中
- 如不配置,那么在进行联表查询的时候,如果两个表,会双双组合,成
01,10,11,00,四个语句,如果配置了,只会在产生11,00两条查询语句。
广播表
指所有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中均完全一致。适用于数据量不大且需要与海量数据的表进行关联查询的场景
分片策略
- 标准分片策略 -> 单字段分片
- 符合分片策略 -> 多字段分片
- Hint分片策略 -> 外部条件分片
分片维度
- 表维度 - 分表
- 数据源维度 - 分库
核心原理
解析引擎
将sql语句解析为语法树,分为词法分析和语法分析。
路由引擎
用于根据分片键进行路由的场景,又细分为直接路由、标准路由和笛卡尔积路由这 3 种类型。
改写引擎
- 标识符改写
将原始逻辑表sql语句更改为可以执行的真实表sql语句,一般为更改表名,还有其他的索引,视图。
SELECT order_id FROM t_order WHERE order_id=1 AND remarks=' t_order xxx';
改为
SELECT order_id FROM t_order_1 WHERE order_id=1 AND remarks=' t_order xxx';
- 补列
- 对于分组和排序的sql,如果在返回结果中没有排序或者分组的列,会自动将这列进行补充
- 使用avg函数的时候,需要把sum和cout,补充,否则计算也是不准确的
执行引擎
sql的执行需要考虑是否为每一个sql语句创建一个连接,同一个库里面是否只创建一个连接。
- 内存限制模式
不限制连接数量,每张表创建一个连接,并使用多线程的方式处理,速度最快。 - 连接限制模式
同一个库只会创建一个连接,不同库中的表,每个库也只会创建一个连接
自动化执行引擎
-
- 不必配置执行模式,Sharding根据当前情况自动选择,根据用户设置的 maxConnectionSizePerQuery ,每个连接最多执行的sql数
1. 准备阶段
- 用于准备执行的数据。它分为结果集分组和执行单元创建两个步骤。
-
首先根据库进行分组,然后使用下图的方式进行判断
- 为了防止获取连接的时候死锁的出现,每次获取连接都是原子性获取本次需要的全部连接
2. 执行阶段
用于真正的执行 SQL,它分为分组执行和归并结果集生成两个步骤。
归并引擎
流式归并是指每一次从结果集中获取到的数据,都能够通过逐条获取的方式返回正确的单条数据,它与数据库原生的返回结果集的方式最为契合。遍历、排序以及流式分组都属于流式归并的一种。
内存归并则是需要将结果集的所有数据都遍历并存储在内存中,再通过统一的分组、排序以及聚合等计算之后,再将其封装成为逐条访问的数据结果集返回。
装饰者归并是对所有的结果集归并进行统一的功能增强,目前装饰者归并有分页归并和聚合归并这 2 种类型。
遍历归并
将多个数据集合并为一个数据集
排序归并
将每个数据集,采用游标的方式,一步一步的去处数据,实现排序
分组归并
- 流式归并
需要排序字段和分组字段一致,这样使用next指针不用回溯就可以实现归并 - 内存归并
全部数据取出来,在内存中进行排序
聚合归并
- 其他聚合能力的装饰
比较类型的聚合函数是指 MAX 和 MIN。它们需要对每一个同组的结果集数据进行比较,并且直接返回其最大或最小值即可。
累加类型的聚合函数是指 SUM 和 COUNT。它们需要将每一个同组的结果集数据进行累加。
求平均值的聚合函数只有 AVG。它必须通过 SQL 改写的 SUM 和 COUNT 进行计算。
分页归并
- 其他聚合能力的装饰
- 如果直接使用limit进行分页,可能会造成,将查询页前所有数据查询出来,造成大量的内存浪费,为了解决这个问题,除了内存分组,其他情况Sharding采用流式归并的方法获取结果集
- 另外采用官方建议采用id分页的方式
分片算法
求余算法:
只要根据分片参数进行求余即可得出分片。性能也比较好。缺点是伸缩性不太好,一旦要增加一个新的分片,就需要对全部旧的数据进行数据迁移。
哈希一致性算法
- 假设一个圆由2的32次方组成,这个圆称为为哈希环。每个节点负责一个区域,获取到哈希值的时候,其落点的下一个节点就是分片位置。这样当需要节点增减的时候,只需要移动最近的一个节点数据即可,而不用对全部分片的数据都进行迁移。
- 哈希值划分不均匀,导致每个区域不太一致
-
划分的节点太少的时候,容易造成一个很大的区间,导致所有的数据都流入某一个节点,解决方式,使用虚拟节点,将区域划分为多个虚拟节点,每个节点负载几个虚拟节点。这样即使只有很少的节点,也可以实现均衡。
- 在java中实现中,如果觉得划分过大,可以减少哈希环的大小,比如1000个,然后我们使用 SortedMap 来实现,这样,我们使用tailMap方法 ,就可以获取比当前值大的一个节点,很容易实现哈希算法
热点分配
- 大部分数据都不是热点数据,但是若是某一个库的数据热点数据较多的时候,这个库压力就很大,等于我们的分片失去了应有的作用,这个时候可以使用热点分配法
- 建立用户级别热点映射表,根据用户的行为将用户标示为热点用户,以及其对应的库表,热点数据可以定期迁移,保持各个节点之间的均衡
分布式ID
雪花算法 snowflake
生成一个long类型的数字id,其实就是对这64位的二进制形式里面填值,把这64位分成几个部分,彼此间互不影响,每部分都有自己的生成规则,这样在一定的简单的大前提下,能保证全局唯一。
读写分离
对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善
ShardingSphere实现了 透明化读写分离所带来的影响,让使用方尽量像使用一个数据库一样使用主从数据库集群
https://shardingsphere.apache.org/document/current/cn/features/sharding/principle/parse/