当前位置: 首页 > article >正文

【MySQL高可用】什么时候分库、分表?怎样进行分库分表?

你将学到

1. 什么时候分库?

2. 什么时候分表?

3. 什么时候既分库又分表?

4. 横向拆分和纵向拆分

4.1 纵向拆分

4.2 横向拆分

4.2.1 哈希分片

4.2.2 范围分片

4.2.3 查表法(分片元数据)


当读多写少场景下,我们可以采取主从复制技术实现读写分离,将读请求分散在多个从服务器上进而提升MySQL的可用性,对外提供更高的QPS。过主从复制的技术把数据复制多份,读操作只读取从数据库中的数据,这样就增强了抵抗大量并发读请求的能力,提升了数据库的查询性能。

假设在这样的背景下,面试官继续追问:

公司现有业务不断发展,流量剧增,交易数量突破了千万订单,但是订单数据还是单表存储,主从分离后,虽然减少了缓解读请求的压力,但随着写入压力增加,数据库的查询和写入性能都在下降,这时你要怎么设计架构?

这个问题可以归纳为:数据库写入请求量过大,导致系统出现性能与可用性问题。

这个时候咱们的脑海里肯定要想到 分库分表

首先,我们需要知道所谓"分库分表",根本就不是一件事儿,而是三件事儿,他们要解决的问题也都不一样。这三个事儿分别是"只分库不分表"、"只分表不分库"、以及"既分库又分表"。

1. 什么时候分库?

其实,分库主要解决的是并发量大的问题。因为并发量一旦上来了,那么数据库就可能会成为瓶颈,因为数据库的连接数是有限的,虽然可以调整,但是也不是无限调整的。

所以,当你的数据库的读或者写的QPS过高,导致你的数据库连接数不足了的时候,就需要考虑分库了,通过增加数据库实例的方式来提供更多的可用数据库链接,从而提升系统的并发度。

比较典型的分库的场景就是我们在做微服务拆分的时候,就会按照业务边界,把各个业务的数据从一个单一的数据库中拆分开,分表把订单、物流、商品、会员等单独放到单独的数据库中。

还有就是有的时候可能会需要把历史订单挪到历史库里面去。这也是分库的一种具体做法。

2. 什么时候分表?

分库主要解决的是并发量大的问题,那分表其实主要解决的是数据量大的问题。

假如你的单表数据量非常大,因为并发不高,数据量连接可能还够,但是存储和查询的性能遇到了瓶颈了,你做了很多优化之后还是无法提升效率的时候,就需要考虑做分表了。

通过将数据拆分到多张表中,来减少单表的数据量,从而提升查询速度。

一般我们认为,单表行数超过 500 万行或者单表容量超过 2GB之后,才需要考虑做分库分表了,小于这个数据量,遇到性能问题先建议大家通过其他优化来解决。

3. 什么时候既分库又分表?

那么什么时候分库又分表呢,那就是既需要解决并发量大的问题,又需要解决数据量大的问题时候。通常情况下,高并发和数据量大的问题都是同时发生的,所以,我们会经常遇到分库分表需要同时进行的情况。

所以,当你的数据库链接也不够了,并且单表数据量也很大导致查询比较慢的时候,就需要做既分库又分表了。

4. 横向拆分和纵向拆分

谈及到分库分表,那就要涉及到该如何做拆分的问题。

通常在做拆分的时候有两种分法,分别是横向拆分(水平拆分)和纵向拆分(垂直拆分)。

假如我们有一张表,如果把这张表中某一条记录的多个字段,拆分到多张表中,这种就是纵向拆分。那如果把一张表中的不同的记录分别放到不同的表中,这种就是横向拆分。

4.1 纵向拆分

垂直拆分(纵向拆分)是根据数据的业务相关性进行拆分。比如一个数据库里面既存在商品数据,又存在订单数据,那么垂直拆分可以把商品数据放到商品库,把订单数据放到订单库。一般情况,垂直拆库常伴随着系统架构上的调整。

比如在对做系统“微服务”改造时,将原本一个单体系统拆分成多个子系统,每个系统提供单独的服务,那么随着应用层面的拆分带来的也有数据层面的拆分,将一个主库的数据表,拆分到多个独立的子库中去。

对数据库进行垂直拆分最常规,优缺点也很明显。

垂直拆分可以把不同的业务数据进行隔离,让系统和数据更为“纯粹”,更有助于架构上的扩展。但它依然不能解决某一个业务的数据大量膨胀的问题,一旦系统中的某一个业务库的数据量剧增,比如商品系统接入了一个大客户的供应链,对于商品数据的存储需求量暴增,在这个时候,就要把数据拆分到多个数据库和数据表中,也就是对数据做水平拆分。

4.2 横向拆分

横向拆分(水平拆分)的结果是数据库表中的数据会分散到多张分表中,使得每一个单表中的数据的条数都有所下降。比如我们可以把不同的用户的订单分表拆分放到不同的表中。横向拆分时表的整体结构是没有发生变化的。

比如把单表 1 亿的数据按 Hash 取模拆分到 10 个相同结构的表中,每个表 1 千万的数据。并且拆分出来的表,可以分别存放到不同的物理数据库中,关注点在于数据扩展。

既然要把数据分布到多个节点,就会存在数据分片的问题。数据分片即按照一定的规则将数据路由到相应的存储节点中,从而降低单存储节点带来的读写压力。常见的实现方案有 Hash(哈希分片)与 Range(范围分片)。

4.2.1 哈希分片

比如,商品表包括主键、商品 ID、商品名称、所属品类和上架时间等字段。如果以商品 ID 作为关键字进行分片,系统会通过一个 Hash 函数计算商品 ID 的 Hash 值,然后取模,就能得到对应的分片。模为 4 就表示系统一共有四个节点,每个节点作为一个分片。

假设Hash 函数为 “商品 ID % 节点个数 4”,通过计算可以得到每个数据应该存入的节点:计算结果为 0 的数据存入节点 A;结果为 1 的数据存入节点 B;结果为 2 的数据存入节点 C;计算为 3 的数据存储节点 D。

12.png

可以看出,Hash 分片的优点在于可以保证数据非常均匀地分布到多个分片上,并且实现起来简单,但扩展性很差,因为分片的计算方式就是直接用节点取模,节点数量变动,就需要重新计算 Hash,就会导致大规模数据迁移的工作。

这时,就会延伸出第二个问题,如何解决 Hash 分片的缺点,既保证数据均匀分布,又保证扩展性?

答案就是一致性 Hash :它是指将存储节点和数据都映射到一个首尾相连的哈希环上。存储节点一般可以根据 IP 地址进行 Hash 计算,数据的存储位置是从数据映射在环上的位置开始,依照顺时针方向所找到的第一个存储节点。

在具体操作过程中,通常会选择带有虚拟节点的一致性 Hash。假设在这个案例中将虚拟节点的数量设定为 10 个,就形成 10 个分片,而这 10 个分片构成了整个 Hash 空间。现在让 A 节点对应虚拟节点 0 ~ 3,B 节点对应虚拟节点 4 ~ 6,C 节点对应虚拟节点 7 ~ 8,D 节点对应虚拟节点 9。

同样根据哈希函数为 “商品 ID % 节点个数 10”得到每一个商品在 Hash 环上的位置,然后根据顺时针查找最近的存储节点,即数据实际映射的位置。计算结果为:0 ~ 3 的数据存入节点 A;结果为 4 ~ 6 的数据存入节点 B;结果为 7 ~ 8 的数据存入节点 C;计算为 9 的数据存储节点 D。

当我们新增一台服务器,即节点 E 时,受影响的数据仅仅是新服务器到所处环空间中前一台服务器(即沿着逆时针方向的第一台服务器)之间的数据。结合我们的示例,只有商品 100 和商品 101 从节点 A 被移动到节点 E,其他节点的数据保持不变。此后,节点 A 只存储 Hash 值为 2 和 3 的商品,节点 E 存储 Hash 值为 0 和 1 的商品。

一致性 Hash 分片的优点是数据可以较为均匀地分配到各节点,其并发写入性能表现也不错。如果你应聘的是初级研发工程师,面试官通常不会追问下去,但是应聘中高级别研发的话,这样的回答还不够,你还要进一步阐述对分布式数据存储的理解。

要知道,虽然一致性 Hash 提升了稳定性,使节点的加入和退出不会造成大规模的数据迁移,但本质上 Hash 分片是一种静态的分片方式,必须要提前设定分片的最大规模,而且无法避免单一热点问题, 某一数据被海量并发请求后,不论如何进行 Hash,数据也只能存在一个节点上,这势必会带来热点请求问题。比如案例中的电商商品,如果某些商品卖得非常火爆,通过 Hash 分片的方式很难针对热点商品做单独的架构设计。

答案是做 Range(范围)分片。

4.2.2 范围分片

与 Hash 分片不同的是,Range 分片能结合业务逻辑规则,例如,我们用 “Category(商品类目)” 作为关键字进行分片时,不是以统一的商品一级类目为标准,而是可以按照一、二、三级类目进行灵活分片。例如,对于京东强势的 3C 品类,可以按照 3C 的三级品类设置分片;对于弱势品类,可以先按照一级品类进行分片,这样会让分片间的数据更加平衡。(让热点数据分的更细,一般数据按照大类来分)

它的核心思想是按照某个字段的范围(如 ID、时间等)将数据划分到不同的分片(库或表)中。范围分片适用于需要按范围查询的场景,例如按时间范围查询日志、按 ID 范围查询用户数据、商品分类等。

4.2.3 查表法(分片元数据)

查表法其实就是没有分片算法,决定某个 Sharding Key 落在哪个分片上,全靠人为来分配,分配的结果记录在一张表里面。每次执行查询的时候,先去表里查一下要找的数据在哪个分片中。

查表法的好处就是灵活,怎么分都可以,你用上面两种分片算法都没法分均匀的情况下,就可以用查表法,人为地来把数据分均匀了。查表法还有一个特好的地方是,它的分片是可以随时改变的。比如我发现某个分片已经是热点了,那我可以把这个分片再拆成几个分片,或者把这个分片的数据移到其他分片中去,然后修改一下分片映射表,就可以在线完成数据拆分了。

但是高灵活性就会带来高复杂性,从存储的角度看,元数据(分片映射表)也是数据,特殊之处在于它类似一个路由表,每一次请求都要访问它,所以分片元数据本身就要做到高可用。如果系统支持动态分片,那么分片信息的变更数据还要在节点之间进行同步。


参考

1. 亿级商品存储下,如何深度回答分布式系统的原理性问题?

2. https://www.51cto.com/article/709614.html

3. 写多读少:MySQL 如何优化数据存储方案?


http://www.kler.cn/a/473022.html

相关文章:

  • H5通过URL Scheme唤醒手机地图APP
  • Flink源码解析之:Flink on k8s 客户端提交任务源码分析
  • 【传统枪机现代枪机的功能需求】
  • 高山旅游景区有效降低成本,无人机山下到山上物资吊运技术详解
  • Agentic RAG 解释
  • Mac-docker配置
  • 网络安全 | 网络安全的未来趋势
  • 数智大咖谈 | 志邦家居:智慧家居与智慧管理并驾齐驱
  • select下拉框,首次进入页面没有显示value的情况
  • 如何构建多层决策树
  • c#编写基于ffmpeg的视频裁剪
  • 【VBA】【EXCEL】将某列内容横向粘贴到指定行
  • 点击底部的 tabBar 属于 wx.switchTab 跳转方式,目标页面的 onLoad 不会触发(除非是第一次加载)
  • P1909 [NOIP2016 普及组] 买铅笔 题解
  • python初体验: 处理excel数据
  • redis的学习(四)
  • UART串口数据分析
  • 一个海外产品经理的 AI 日常
  • Linux下常用命令
  • Lua协同程序(线程)
  • 【Linux】进程铺垫——冯诺依曼体系与操作系统概念
  • 代码随想录-训练营-day1
  • SQL 数据类型
  • 个人博客搭建(二)—Typora+PicGo+OSS
  • 哈密顿原理
  • 基于华为ENSP的OSPF数据报文保姆级别详解(3)