news 2026/1/3 10:13:35

分库分表详细讲解及技术选型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
分库分表详细讲解及技术选型

为什么需要分库分表

为什么要分库

分库主要解决的是并发量过⼤的问题,因为并发量⼀旦上升了,那么数据库就可能成为系统的瓶颈,因为数据库的连接数量是有上限的,虽然你可以进⾏调整,但并不是⽆限调整的。所以,当你的数据库的读或者写的 QPS 太⾼,从⽽导致你的数据库连接数量不⾜的时候,就需要考虑到分库了,通过在增加数 据库实例的⽅式来提供更多的数据库连接,从⽽提升系统的并发度。
微服务架构出现,就是为了应对高并发。它把订单、用户、商品等不同模块,拆分成多个应用,并且把单个数据库也拆分成多个不同功能模块的数据库(订单库、用户库、商品库),以分担读写压力。
MySQL单机磁盘容量几乎称满。

为什么分表

分表主要解决的是数据量⼤的问题,即通过将数据拆分到多个表,减少单表的数据量, 从⽽提升查询速度。
一般B+树层级是2-3层,3层大概可以存放两千万左右的记录,因此单表数据量超过千万,就需要考虑分表啦。

数据量过大解决方案

阿里开发手册: 单表行数超过500W或者单表数据容量超过2G
● 分库分表
● 冷热数据分离
● 历史数据归档

如何分库分表

垂直拆分

垂直分库

可以按照系统中的不同业务进行拆分,比如拆分成用户库、订单库、积分库、商品库,把它们部署在不同的数据库服务器,这就是垂直分库。

垂直分表

如果一个单表包含了几十列甚至上百列,管理起来很混乱,每次都select *的话,还占用IO资源。这时候,我们可以将一些不常用的、数据较大或者长度较长的列拆分到另外一张表。
比如一张用户表,它包含user_id、user_name、mobile_no、age、email、nickname、address、user_desc,如果email、address、user_desc等字段不常用,我们可以把它拆分到另外一张表,命名为用户详细信息表。这就是垂直分表。

水平拆分

水平分库

水平分库是指,将表的数据量切分到不同的数据库服务器上,每个服务器具有相同的库和表,只是表中的数据集合不一样。它可以有效的缓解单机单库的性能瓶颈和压力。

水平分表

如果一个表的数据量太大,可以按照某种规则(如hash取模、range),把数据切分到多张表去。

一张订单表,按时间range拆分如下:

水平分库分表策略

分库分表策略一般有几种,使用与不同的场景:
● range范围
● hash取模
● 一致性hash算法

range范围

range,即范围策略划分表。比如我们可以将表的主键,按照从0~1000万的划分为一个表,1000 ~ 2000万划分到另外一个表。如下图:

当然,有时候我们也可以按时间范围来划分,如不同年月的订单放到不同的表,它也是一种range的划分策略。
这种方案的优点:
● 这种方案有利于扩容,不需要数据迁移。假设数据量增加到5千万,我们只需要水平增加一张表就好啦,之前0~4000万的数据,不需要迁移。
缺点:
● 这种方案会有热点问题,因为订单id是一直在增大的,也就是说最近一段时间都是汇聚在一张表里面的。比如最近一个月的订单都在1000万~2000万之间,平时用户一般都查最近一个月的订单比较多,请求都打到order_1表啦,这就导致表的数据热点问题。

hash取模

hash取模策略:指定的路由key(一般是user_id、订单id作为key)对分表总数进行取模,把数据分散到各个表中。
比如原始订单表信息,我们把它分成4张分表:

● 比如id=1,对4取模,就会得到1,就把它放到第1张表,即t_order_0;
● id=3,对4取模,就会得到3,就把它放到第3张表,即t_order_2;
这种方案的优点:
● hash取模的方式,不会存在明显的热点问题。
缺点:
● 如果一开始按照hash取模分成4个表了,未来某个时候,表数据量又到瓶颈了,需要扩容,这就比较棘手了。比如你从4张表,又扩容成8张表,那之前id=5的数据是在(5%4=1,即第一张表),现在应该放到(5%8=5,即第5张表),也就是说历史数据要做迁移了。

一致性哈希算法
在节点的动态增加或者删除的时候,尽可能减少数据迁移和重新分布的成本。
实现一致性哈希算法首先需要构造一个哈希环,然后把他划分为固定数量的虚拟节点,一般都是。那么他的节点编号就是

假设现在有4个表,在一致性hash算法中,取模不是直接对这4个表来完成,而是对
来实现。
将4个表作为节点映射到这个环上。
hash(表编号)%

然后就把目标表和hash环关联起来了。
当添加一个数据的时候,同样通过hash和hash环取模得到一个值,然后根据目标值所在的hash环的位置顺时针找到最近的一个目标表,把数据存储到这个目标表中。

当需要删除或者添加某个表时,影响到的数据范围是局部的,而不是全局。是这个表的节点到逆时针上一个表的节点中间的见会受影响。
hash倾斜
可能会存在目标表没有均匀分配到hash环中,导致大量数据集中在一个表

为了解决这个问题,必须要保证目标节点要均匀的分布在整个hash环中,但是真实的节点就只有4个,可以 把这四个节点分别复制一份出来分散到这个hash环中,这个复制出来的节点叫虚拟节点,根据实际需要可以虚拟出多个节点出来 ,尽可能分散节点。

分库分表的数量为什么一般选择2的幂?

● 可以位运算代替取模运算,效率更高 hash%n = hash&(n-1)
● 可以分的更加分散

分库分表会导致哪些问题

分库分表之后,也会存在一些问题:
● 事务问题
● 跨库关联
● 排序问题
● 分页问题
● 分布式ID

标题事务问题

分库分表后,假设两个表在不同的数据库,那么本地事务已经无效啦,需要使用分布式事务了。

标题跨库关联

跨节点Join的问题:解决这一问题可以分两次查询实现

标题排序问题

跨节点的count,order by,group by以及聚合函数等问题:可以分别在各个节点上得到结果后在应用程序端进行合并。

分页问题

当分的库不多的时候,可以通过扫表的方式把多个库中的数据读出来,在内存中进行排序和分页。
当数据量大的时候,可以通过下面的方法来实现:

shardingkey查询

以订单为例,买家的订单是查询最高频的,以买家ID作为分片键,就可以路由到单个库进行分页查询了

非shardingkey查询

如果不仅有买家查询还有卖家查询,一般采用空间换时间的方式:
● 同步出一张按照卖家维度做分表的表,一般使用canal基于bin log做自动同步,虽然有秒级别的延迟,一般业务上来说是可以接受的。
● 还可以采用elasticSearch搜索引擎来满足复杂查询。

标题分布式ID

UUID

可以做到全局唯一,生成方式也简单
但是通常不用它来做唯一ID,因为
UUID太长了,字符串查询效率也比较慢
UUID无序, 每一次UUID数据的插入都 会对主键的b+树进行很大的修改,严重影响性能

数据库自增方案

在数据库中专门创建一张序列表,利用数据库表中的自增ID来为其他业务的数据生成一个全局ID,那么 每次要用ID的时候,直接从这个表中获取即可。
优点:
实现简单,成本小,ID单号自增,可以实现有特殊要求的业务。
缺点:
● 强依赖DB,当DB异常时整个系统不可用,属于致命问题。配置主从复制可以尽可能的增加可用 性,但是数据一致性在特殊情况下难以保证。主从切换时的不一致可能会导致重复发号。
● ID发号性能瓶颈限制在单台MySQL的读写性能。

标题雪花算法

雪花算法具有全局唯一、递增、高可用特点。
它的核⼼思想 就是将⼀个 64位的ID划分成多个部分,每个部分都有不同的含义,包括时间戳、数据中心标识、机器标识和序列号等
⼀般来说,雪花算法生成的 ID 有以下几个部分:

  1. 符号位(1bit):预留的符号位,始终为0,占⽤1位。
  2. 时间戳(41bit):精确到毫秒级别,41位的时间戳可以容纳的毫秒数是2的41次幂,⼀年所使⽤的毫 秒数是:365 * 24 * 60 * 60 * 1000,算下来可以使⽤69年。
  3. 数据中⼼标识(5bit):可以⽤来区分不同的数据中⼼。
  4. 机器标识(5bit):可以⽤来区分不同的机器。
  5. 序列号(12bit):每个节点每毫秒 0 开始不断叠加,最多可以叠加到 4095 ,因此其⼀共可以⽣成 4096个不同的序列号

分库分表技术选型

Sharding-JDBC

定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使⽤客户端直连数据库,以jar包形式提供服务,⽆需额外部署和依赖,可理解为增强版的JDBC驱动 。
支持数据分片、分布式事务、读写分离、数据加密等。
分布式事务:基于XA和BASE的混合事务引擎。

分库分表后的数据迁移

分为三个阶段

第一阶段,新老库双写

● 数据库表的双写,老的数据库表和新的数据库表同步写入数据,事务的成功以老的模型为准,查询也走老的。
● 通过定时任务对数据进行核对,补平差异
● 通过定时任务把历史数据迁移到新的模型中

第二阶段,以新的模型为准

到了第二个阶段,历史数据已经迁移完了,数据校验也没有问题
● 仍然保持双写,但是事务的成功和查询都以新模型为准。
● 定时任务进行数据核对,补平差异数据

第三阶段,结束双写

到了第三阶段,说明数据已经全部迁移好了
● 取消双写,所有数据只保存到新的模型中,老模型不需要写入新的数据
● 如果仍有部分老业务依赖老模型,要等到所有业务改造完成后,再废弃老模型

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/24 6:55:29

Applite革命性体验:告别命令行,拥抱macOS软件管理的智能时代

Applite革命性体验:告别命令行,拥抱macOS软件管理的智能时代 【免费下载链接】Applite User-friendly GUI macOS application for Homebrew Casks 项目地址: https://gitcode.com/gh_mirrors/ap/Applite 你是否曾因繁琐的Homebrew命令而头疼&…

作者头像 李华
网站建设 2025/12/19 7:08:21

Performance-Fish如何解决《环世界》后期性能瓶颈?

Performance-Fish如何解决《环世界》后期性能瓶颈? 【免费下载链接】Performance-Fish Performance Mod for RimWorld 项目地址: https://gitcode.com/gh_mirrors/pe/Performance-Fish 当你的殖民地人口突破百人,建筑遍布地图,游戏帧率…

作者头像 李华
网站建设 2025/12/30 8:32:08

linux gpio获取

最近在工作中遇到了gpio解析失败的问题,跟踪发现设备树配置的字符串不匹配,在这里再次学习并记录下。 of_get_named_gpio 以前在工作中更多使用的是of_get_named_gpio这个标准函数,用以直接获取gpio。只要指定其具体的属性名,一般…

作者头像 李华
网站建设 2025/12/14 23:02:12

数据结构学习篇(4)---算法的时间复杂度

由于现在计算机的储存在硬件上能得到很好的解决,所以时间复杂度较空间复杂度更受关注。1.1 时间复杂度的概念时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一 个算法执行所耗费的时…

作者头像 李华
网站建设 2026/1/1 12:27:45

桌面开发,在线%RIP,路由表管理%系统,基于vs2022,c#,winform,txt,无数据库

经验心得帮客户完善一下RIP路由表拓扑结构图展示。代码很多地方不严谨帮客户修改一下就行。剩下就是搞懂路由表展示原理就行。 路由展示功能介绍 做这个路由展示功能时,最直观的感受就是重复的活干太多了。比如A到H这8个路由按钮,点每个按钮的逻辑几乎一…

作者头像 李华