分库分表核心理念

文章目录

    • 分库,分表,分库分表
      • 什么时候分库?
      • 什么时候分表?
      • 什么时候既分库又分表?
      • 横向拆分 & 纵向拆分
    • 分表算法
      • Range 范围
      • Hash 取模
      • 一致性 Hash
      • 斐波那契散列
    • 严格雪崩标准(SAC)
    • 订单分库分表实战
    • 全局 ID 的生成
      • UUID
      • 基于某个单表做自增主键
      • 雪花算法
        • 时间回拨问题
    • 分库分表迁移
      • 停机迁移方案
      • 双写迁移方案
    • 分库分表带来的问题
    • 参考 & 推荐文章

分库,分表,分库分表

首先,我们需要知道所谓的"分库分表",根本就不是一件事,而是三件事,它们要解决的问题也都不一样。

这三件事分别是"只分库不分表"、“只分表不分库”、以及"既分库又分表"。

什么时候分库?

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

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

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

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

什么时候分表?

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

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

一般我们认为,单表行数超过 500 万行或者单表容量超过 2GB 时,才需要考虑做分库分表。

那我们是不是等到数据量到达 500 万后,才开始分库分表呢?

这个也不绝对,应该提前规划分库分表,如果估算 3 年后,表的数据量都不会到达 500 万,则不需要分库分表。

**分库分表的时候需要考虑数据未来 2~3 年的一个增量,**即使现在数据量不多,但是每天的数据增量很可观,几个月之后就可以突破 500 万上限,那么不是等到数据量到达 500 万的时候才分库分表,而是现在就应该考虑了。

什么时候既分库又分表?

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

横向拆分 & 纵向拆分

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

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

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

横向拆分的结果是数据库表中的数据会分散到多张分表中,使得每一个单表中的数据的条数都有所下降。比如我们可以把不同的用户的订单,分表拆分放到不同的表中。

纵向拆分的结果是数据库表中的数据的字段数会变少,使得每一个单表中的数据的存储有所下降。比如可以把商品详情信息、价格信息、库存信息等等分别拆分到不同的表中。

纵向拆分比较适合做冷热分离,可以使得行数据变小,一个数据页就能存放更多的数据,在查询时就会减少I/O次数。

分表算法

选定了分表字段之后,如何基于这个分表字段来准确的把数据分表到某一张表中呢?

这就是分表算法要做的事情了,但是不管什么算法,我们都需要确保一个前提,那就是同一个分表字段,经过这个算法处理后,得到的结果一定是一致的,不可变的。

通常的分表算法有以下几种:

Range 范围

Range,即范围策略划分表。比如我们可以将表的主键 order_id,按照从 0~300万 的划分为一个表,300万 ~ 600万划分到另外一个表。

有时候我们也可以按时间范围来划分,如不同年月的订单放到不同的表。

  • 优点:范围分表,有利于扩容。
  • 缺点:最近一段时间的数据都是汇聚在一张表里面,可能会有热点问题。比如最近一个月的订单都在 0~300万之间,平时用户一般都查最近一个月的订单比较多,那么请求就都打到 order_01 了。

Hash 取模

Hash 取模策略:

指定的路由key(一般是 user_id、order_id 等作为key)对分表总数进行取模,把数据分散到各个表中。

比如原始订单表信息,我们把它分成4张分表:

比如 id=1,对 4 取模,就会得到1,就把它放到 t_order_1 ;

一般,我们会取哈希值,再做取余:

Math.abs(orderId.hashCode()) % table_number

  • 优点:Hash取模的方式,不会存在明显的热点问题。
  • 缺点:如果未来某个时候,表数据量又到瓶颈了,需要扩容,就比较麻烦。所以一般建议提前规划好,一次性分够(可以考虑一致性哈希)。

一致性 Hash

为了解决 Hash 扩容的问题,我们可以采用一致性哈希的方式来做分表。

一致性哈希可以按照常用的 Hash 算法来将对应的 key 哈希到一个具有 2^32 次方个节点的空间中,形成一个顺时针首尾相接的闭合环形,这个环称为哈希环

当添加一台新的数据库服务器时,只有增加服务器的位置和逆时针方向第一台服务器之间的键会受影响。

简单来说,一致性哈希算法能够使机器节点的变动对整个集群的影响达到最小。

一致性哈希也存在一些问题,如:节点漂移、数据倾斜。这些都有对应的解决方案,大家可以参考我发的文章链接,这里不再赘述。

参考:一致性哈希问题及其解决方案。

斐波那契散列

前面几种分表算法,大家会接触多一点,斐波那契散列实际在分表算法中几乎不被使用。

JDK 的 ThreadLocal 源码中有一段有意思的代码,如下所示:

定义了一个魔法值 HASH_INCREMENT = 0x61c88647,这个值被称之为 “魔数”。

0x61c88647 与一个神奇的数字产生了联系,它就是 (Math.sqrt(5) - 1)/2。也就是传说中的黄金比例 0.618(0.618 只是一个粗略值),即0x61c88647 = 2^32 * 黄金分割比,同时也对应了上文提到的斐波那契散列。

它常用于在散列中增加哈希值。上面的代码注释中也解释到是为了让哈希码能均匀的分布在 2 的 N 次方的数组里。

至于为什么使用斐波那契数列后散列更均匀,就涉及到相关数学问题了,此处不做更多解释。

详细解释可参考:从ThreadLocal的实现看散列算法

严格雪崩标准(SAC)

上面介绍了一些分表算法,那么一个好的分表算法有没有参考标准呢?

在密码学中,雪崩效应avalanche effect)指加密算法的一种理想属性。雪崩效应是指当输入发生最微小的改变(例如,反转一个二进制位)时,也会导致输出的不可区分性改变(输出中每个二进制位有50%的概率发生反转)。

严格雪崩标准(SAC),建立于密码学的完全性概念上,是雪崩效应的形式化。它指出,当任何一个输入位被反转时,输出中的每一位均有 50% 的概率发生变化。

简单来说,当我们对数据库从 8库32表 扩容到 16库32表 的时候,每一个表中的数据总量都应该以 50% 的数量进行减少。这样才是合理的。

引入严格雪崩标准(SAC) 之后,斐波那契散列是不满足这个标准的,也就是说使用斐波那契散列,在分库分表扩容情况下,可能导致数据分布不均匀,这也是为什么斐波那契散列几乎不用于分表算法的原因。

订单分库分表实战

背景:订单表的读写场景复杂,⼀般有买家维度、卖家维度、订单号维度 3 个主要维度。多读写维度情况下⽆论采取哪种维度做分库分表,对另外两种维度的查询性能来说,基本都是灾难。

解决方案:双拆分列哈希(RANGE_HASH)。

选取两个拆分键,两个拆分键的后 N 位需确保一致,根据任一拆分键后 N 位计算哈希值,然后再按分库数取模,完成路由计算。

先采用 RANGE_HASH 拆分算法按买家 id 后 N 位、订单号后 N 位维度做分库分表,作为买家表逻辑表。再用 HASH 拆分函数按商家 id 冗余一份数据,作为卖家表逻辑表。

订单号生成规则需要根据买家表分表特性订单号后 N 位等于买家 id 后 N 位做设计。

比如用户id为 12345678,则用户在下单时生成的单号为:xxxxxxxxx345678,单号前几位可以根据公司自己规则设定,但是要注意不能重复。

全局 ID 的生成

涉及到分库分表,就会引申出分布式系统中唯一主键 ID 的生成问题,有以下几种方式:

UUID

UUID 是可以做到全局唯一的,而且生成方式也简单,但是我们通常不推荐使用它做唯一ID,首先 UUID 太长了,其次字符串的查询效率也比较慢,而且没有业务含义,根本看不懂。

基于某个单表做自增主键

多张单表生成的自增主键会冲突,但是如果所有表的主键都从同一张表生成是不是就可以了。

所有的表在需要主键的时候,都到这张表中获取一个自增的 ID。

这样做是可以做到唯一,也能实现自增,但是问题是这个单表就变成整个系统的瓶颈,而且也存在单点问题,一旦他挂了,那整个数据库就都无法写入了。

雪花算法

雪花算法也是比较常用的一种分布式 ID 的生成方式,它具有全局唯一、递增、高可用的特点。

雪花算法生成的主键主要由 4 部分组成,1bit 符号位、41bit 时间戳位、10bit 工作进程位以及 12bit 序列号位。

时间戳占用 41bit,精确到毫秒,总共可以容纳约 69 年的时间。

工作进程位占用 10bit,其中高位 5bit 是数据中心 ID,低位 5bit 是工作节点 ID,最多可以容纳 1024 个节点。

序列号占用 12bit,每个节点每毫秒从0开始不断累加,最多可以累加到 4095,一共可以产生 4096 个 ID。

所以,雪花算法在同一毫秒内最多可以生成 1024 X 4096 = 4194304 个唯一的 ID。

时间回拨问题

熟悉雪花算法的可能了解到雪花算法存在名为“时间回拨” 的问题。

时间回拨:由于机器的时间是动态调整的,有可能会出现时间跑到之前几毫秒,如果这个时候获取到了这种时间,则会出现数据重复。

时间回拨问题解决思路可以参考美团开源的 Leaf。

文章链接:Leaf——美团点评分布式ID生成系统

美团 Leaf 引入了 Zookeeper 来解决时钟回拨问题,其大致思路为:每个 Leaf 运行时定时向 zk 上报时间戳。每次 Leaf 服务启动时,先校验本机时间与上次发 ID 的时间,再校验与 zk 上所有节点的平均时间戳。如果任何一个阶段有异常,那么就启动失败报警。

这个解决方案还是比较好理解的,就是对比上次发 ID 的时间,还有其他机器的平均时间,通过本地存储时间戳 + 定时上报时间戳的方式,解决了时间回拨的问题。

分库分表迁移

有一个未分库分表的系统,现在要分库分表,如何才可以让系统从未分库分表切换到分库分表上?

停机迁移方案

先说一个最 low 的方案,就是很简单,大伙凌晨 12点 开始运维,网站或者 app 挂个公告,说 0 点到早上 6 点进行服务器维护,无法访问…

接着到 0 点,停机,系统停掉,没有流量写入了,此时老的单库单表数据库静止了。然后提前写好一个导数的一次性工具,此时直接跑起来,然后将单库单表的数据读出来,写到分库分表里面去。

导数完了之后,就 ok 了,修改系统的数据库连接配置啥的,包括可能代码和 SQL 也许有修改,那你就用最新的代码,然后直接启动连到新的分库分表上去。

但是这个方案比较 low,有个致命的问题就是业务要中断,来看看高大上一点的方案。

双写迁移方案

这个是常用的一种迁移方案,比较靠谱一些,不用停机。

大致步骤如下:

  1. 先改造我们的数据写入端, 使数据同时写入旧数据库和新数据库。
  2. 对存量数据进行不停机的迁移。
  3. 等到双写服务运行一段时间,再次进行旧数据和新数据的校验同步。
  4. 完全切换读取的数据源为新数据库,关闭旧数据库的写入和读取,下线旧数据库。

这种方式的好处是:迁移的过程可以随时回滚,将迁移的风险降到了最低。劣势是:时间周期比较长,应用有改造的成本。

分库分表带来的问题

分库分表之后,会带来很多问题。

首先,做了分库分表之后,所有的读和写操作,都需要带着分表字段,这样才能知道具体去哪个库、哪张表中去查询数据。如果不带的话,就得支持全表扫描。

还有,一旦我们要从多个数据库中查询或者写入数据,就有很多事情都不能做了,比如跨库事务就是不支持的。

所以,分库分表之后就会带来因为不支持事务而导致的数据一致性的问题。

其次,做了分库分表之后,以前单表中很方便的分页查询、排序等等操作就都失效了。因为我们不能跨多表进行分页、排序。

总之,分库分表虽然能解决一些大数据量、高并发的问题,但是同时也会带来一些新的问题。所以,在做数据库优化的时候,还是建议大家优先选择其他的优化方式,最后再考虑分库分表。

参考 & 推荐文章

分库分表经典15连问

再有人问你什么是分库分表,直接把这篇文章发给他

Leaf——美团点评分布式ID生成系统

大众点评订单系统分库分表实践

从ThreadLocal的实现看散列算法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/873656.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

导入word模板的数据到DB,偏自学,可自改套用

GetMapping("/importTestPeople")public void importTestPeople(RequestParam("file") MultipartFile multipartFile) throws IOException {InputStream inputStream null;File file null;try {// 创建临时文件file File.createTempFile("temp&quo…

从0开始深入理解并发、线程与等待通知机制

1、 从0开始深入理解并发、线程与等待通知机制 从上面两大互联网公司的招聘需求可以看到,大厂的Java岗的并发编程能力属于标配。 而在非大厂的公司,并发编程能力也是面试的极大加分项,而工作时善用并发编程则可以极大提升程序员在公司的技术…

前向渲染路径

1、前向渲染路径处理光照的方式 前向渲染路径中会将光源分为以下3种处理方式: 逐像素处理(需要高等质量处理的光)逐顶点处理(需要中等质量处理的光)球谐函数(SH)处理(需要低等质量…

phpmyadmin报错mysqli::real_connect(): (HY000/1045): Access denied for user ‘

问题分析 这是因为本身还安装了MySQL,导致发生冲突,只需要找到自己安装的进行关闭即可 方法 在任务管理器(快捷键:ctrlaltdelete)-服务中,找到对应的MySQL进行关闭

爬虫 可视化 管理:scrapyd、Gerapy、Scrapydweb、spider-admin-pro、crawllab、feaplat、XXL-JOB

1、scrapyd 大多数现有的平台都依赖于 Scrapyd,这将选择限制在 python 和 scrapy 之间。当然 scrapy 是一个很棒的网络抓取框架,但是它不能做所有的事情。 对于重度 scrapy 爬虫依赖的、又不想折腾的开发者,可以考虑 Scrapydweb;…

基于springboot+vue实现的在线商城系统

系统主要功能: (1)商品管理模块:实现了商品的基本信息录入、图片上传、状态管理等相关功能。 (2)商品分类模块:实现了分类的增删改查、分类层级管理、商品分类的关联等功能。 (3&…

基于 SpringBoot 的私人健身与教练预约管理系统

专业团队,咨询送免费开题报告,大家可以来留言。 摘 要 随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,…

【机器学习】高斯网络的基本概念和应用领域以及在python中的实例

引言 高斯网络(Gaussian Network)通常指的是一个概率图模型,其中所有的随机变量(或节点)都遵循高斯分布 文章目录 引言一、高斯网络(Gaussian Network)1.1 高斯过程(Gaussian Proces…

idea如何配置模板

配置生成代码指令模板 注:我们常用的有sout,main等指令 第一步打开设置面板 1)按如下操作 2)或者CtrlAltS快捷键直接弹出 第二步找 Editor>LiveTemplates 第三步创建模板 步骤如下 1)创建分组名字 2)分组名字 3)创建自己的模板…

计算机网络与Internet应用

一、计算机网络 1.计算机网络的定义 网络定义:计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享…

国产芯片LT8619C:HDMI转RGB/LVDS转换器,4k x 2k 30Hz高分辨率

以下为LT8619C转换芯片的简介,如有不足或错误,请指正: LT8619C是一款高性能HDMI/双模DP接收器芯片,符合HDMI 1.4规范。支持TTL或LVDS信号输出,TTL输出时,可支持输出RGB、BT656、BT1120信号,输出…

深度置信网络(深度信念网络)DBN分类模型(二分类多分类)-MATLAB代码实现

一、深度置信网络DBN(代码获取:底部公众号) 深度置信网络(Deep Belief Network,DBN)是一种基于无监督学习的深度神经网络模型,它由多个受限玻尔兹曼机(Restricted Boltzmann Machin…

SAP与湃睿PLM系统集成案例

一、项目背景 浙江某家用电机有限公司, 该公司的产品涵盖洗衣机、‌空调、‌冰箱及厨房用具等家电电机的制造,‌具备年产4600万台电机的生产能力,‌是中国最大的家电电机生产基地之一。 为确保工艺路线信息在设计与生产执行层面的无缝传递&#xff0…

misc音频隐写

一、MP3隐写 (1)题解:下载附件之后是一个mp3的音频文件;并且题目提示keysyclovergeek;所以直接使用MP3stego对音频文件进行解密;mp3stego工具是音频数据分析与隐写工具 (2)mp3stego工具的使用:…

攻防世界--->迷宫

做题笔记。 下载 查壳 64ida打开。 对于迷宫_Maze 一般都可以分为: ① 找地图 ② 找方向键 ③ 分析路径 ④ 得到路径 其中,可以手动,也可以写脚本(利用DFS以及BFS) 正题: 前置&…

树 --- 二叉树

树的物理结构和逻辑结构上都是树形结构。 树形结构:由一个根和若干个子节点组成的集合。 叶子节点:最外围的节点,只有前驱而没有后继。 (一)树的性质 • ⼦树是不相交的 • 除了根结点外,每个结点有且仅…

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我…

解锁高效驱动密码:SiLM8260A系列SiLM8260ABCS-DG 集成米勒钳位的双通道隔离驱动芯片

附上SiLM8260A同系列型号参考: SiLM8260ADCS-DG 12.5V/11.5V SiLM8260ABCS-DG 8.5V/7.5V SiLM8260AACS-DG 5.5V/5V SiLM8260AGCS-DG 3.5V/3V SiLM8260ABCS-DG是一款集成了米勒钳位功能的双通道隔离驱动芯片,它精准地满足了上述严苛条件。具备…

研发效能DevOps: VSCode进行前端项目初始配置

目录 一、实验 1.环境 2.安装Node.js 3.初始化前端项目 二、问题 1.cnpm安装报错 2.如何删除cnpm与指定cnpm版本 3.前端项目运行报错 4.node版本与npm版本对应关系如何查询 一、实验 1.环境 (1)主机 表1 主机 系统 软件版本备注Windows11VS …

【vscode】vscode paste image插件设置

本文首发于 ❄️慕雪的寒舍 vscode编辑md文件的时候,如果想插入图片,自带的粘贴只会粘贴到当前目录下,也没有文件重命名,很不友好。 在扩展商店里面有mushan的Paste Image插件,相比自带的,更加友好一点。但…