分库分表的故事

数据分片

  • 一般单表的数据阈值在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语句解析为语法树,分为词法分析和语法分析。


抽象语法树中的关键字的 Token 用绿色表示,变量的 Token 用红色表示,灰色表示需要进一步拆分

路由引擎

用于根据分片键进行路由的场景,又细分为直接路由、标准路由和笛卡尔积路由这 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';
  • 补列
  1. 对于分组和排序的sql,如果在返回结果中没有排序或者分组的列,会自动将这列进行补充
  2. 使用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位分成几个部分,彼此间互不影响,每部分都有自己的生成规则,这样在一定的简单的大前提下,能保证全局唯一。


image.png

读写分离

  • 对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善

  • ShardingSphere实现了 透明化读写分离所带来的影响,让使用方尽量像使用一个数据库一样使用主从数据库集群

https://shardingsphere.apache.org/document/current/cn/features/sharding/principle/parse/

你可能感兴趣的:(分库分表的故事)