【进阶篇】MySQL分库分表详解

news2025/1/24 1:37:05

文章目录

  • 0. 前言
    • 1. 垂直分库分表
    • 2. 水平分库分表
  • 1. 理解过程及实现方案
      • 问题讨论
      • 衍生出分库分表策略
      • 借助成熟组件使用
      • 分库分表阶段完成后面临的问题
        • 1. 异地多活问题
        • 2. 数据迁移问题
        • 3. 分布式事务问题
        • 4. join查询的问题
    • 分库分表的策略实现示例
  • 2. 参考文档

在这里插入图片描述

0. 前言

假设有一个电商网站,随着用户量和订单量的增加,单一数据库难以承载如此庞大的数据量,查询速度也逐渐降低,这时就需要进行分库分表。

1. 垂直分库分表

电商网站有用户模块和订单模块,可以将用户表和订单表拆分到不同的数据库中。例如,原本在同一个数据库中的用户表(包含用户基础信息和用户扩展信息)和订单表,可以拆分为两个数据库,一个数据库存储用户的基础信息,另一个数据库存储用户的扩展信息和订单信息。

最常见的博客文章都是以电商系统作为案例,因为他是最具备代表性,电商网站的用户模块和订单模块是可以拆分到不同的数据库中的。这种拆分方式可以提高系统的可扩展性和性能。

假设有一个电商网站,它有一个数据库,其中包含两个表:用户表(User)和订单表(Order)。用户表包含用户的基础信息(如用户名、密码等)和用户的扩展信息(如用户的购物偏好、历史订单等)。订单表包含订单的详细信息(如订单号、购买的商品、数量、价格等)。

原始结构可能如下:

Database: EcommerceDB
    Table: User
        - UserID
        - Username
        - Password
        - Preferences
        - HistoryOrders

    Table: Order
        - OrderID
        - UserID
        - Product
        - Quantity
        - Price

可以将这个数据库拆分为两个数据库:一个数据库(UserDB)存储用户的基础信息,另一个数据库(OrderDB)存储用户的扩展信息和订单信息。

拆分后的结构可能如下:

Database: UserDB
    Table: UserBasic
        - UserID
        - Username
        - Password

Database: OrderDB
    Table: UserExtension
        - UserID
        - Preferences
        - HistoryOrders

    Table: Order
        - OrderID
        - UserID
        - Product
        - Quantity
        - Price

这样,当用户的基础信息和订单信息需要进行大量的读写操作时,这两个操作可以在不同的数据库上并行进行,从而提高系统的性能。同时,由于用户的基础信息和订单信息被存储在不同的数据库中,因此,如果其中一个数据库出现问题,也不会影响到另一个数据库的正常运行,从而提高了系统的可用性。

2. 水平分库分表

假设订单表中有上百万条数据,查询和写入速度逐渐下降。此时,可以根据订单ID进行水平分表,比如订单ID为奇数的存入订单表1,订单ID为偶数的存入订单表2。这样,原本一个表需要处理的数据量就减半了,可以提高查询和写入速度。

在进行分库分表后,对于程序查询也需要做相应的调整。例如在进行水平分表后,查询某个订单信息时,需要先判断订单ID是奇数还是偶数,然后再决定查询哪个表。

Database: OrderDB
    Table: Order
        - OrderID
        - UserID
        - Product
        - Quantity
        - Price
Database: OrderDB
    Table: Order0
        - OrderID
        - UserID
        - Product
        - Quantity
        - Price

    Table: Order1
        - OrderID
        - UserID
        - Product
        - Quantity
        - Price

在这种情况下,Order0用于存储ID为偶数的订单,Order1用于存储ID为奇数的订单。

查询和写入的伪代码可能如下所示:

def get_order(order_id):
    if order_id % 2 == 0:
        # Query Order0 table
        return query_order_0(order_id)
    else:
        # Query Order1 table
        return query_order_1(order_id)

这样,我们可以根据订单ID的奇偶性来决定查询或写入哪个表,从而将原本一个表需要处理的数据量减半,提高查询和写入速度。

实际的分库分表会更复杂。同时,分库分表也可能会带来一些问题,如数据一致性问题、跨库事务问题等。因此,在设计分库分表方案时,需要进行充分的考虑,并可能需要引入其他的技术(如分布式事务)来解决这些问题。

那么,让一起深入了解一下数据库知识应用实践之分库分表。

1. 理解过程及实现方案

记住一句话,不是所有系统一上来就搞分库分表,包括淘宝,京东。尤其这种超前设计,对小公司来说就是累赘,甚至隐患。不仅人才成本,资源成本,运维成本。甚至学习成本都不容小觑。所以基本上都是不断衍生到分库分表,才算是中小公司正确的技术路线和最优实践。

我们以一个场景:例如有一个电子商务网站,随着业务的发展,用户数量、商品数量和交易数量都在快速增长。原来单一的数据库已经无法满足需求,查询速度慢,系统负载高,甚至出现宕机情况。这样的情况下,为了提高系统的性能和稳定性,就需要进行数据库的分库分表。

问题讨论

分库分表的目的是为了解决单个数据库无法承受大量数据和高并发的问题。但是分库分表也会带来一些问题,例如数据一致性问题、分布式事务问题、跨库跨表查询问题、数据迁移问题等。

衍生出分库分表策略

  1. 垂直分库分表:按业务模块进行分库,将大表按照字段进行分表,例如用户库、商品库等。

  2. 水平分库分表:将数据按一定的规则分散到不同的数据库或表中,例如按用户ID的hash值分库,按订单时间分表等。

借助成熟组件使用

常见框架:

  1. MyCat:一个开源的分库分表中间件,支持自定义分片规则,可以实现读写分离、事务和SQL的完全透明。
  2. Sharding-JDBC:一款轻量级的Java框架,提供了强大的分库分表、读写分离、分布式事务和分布式序列等功能。
  3. Shardingsphere:包含了Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar这三款独立的产品,可以满足不同场景下的数据分片需求。
    分库分表的复杂问题:

分库分表阶段完成后面临的问题

当技术演进到我们已经解决了分库分表基本问题,解决了并发问题,解决性能问题,接下来我们基本上面临如下几个问题。关于这些问题又需要我们继续去学习和研究尝试更多方案。此处我们不做展开。

1. 异地多活问题

在数据库地理分布、灾备等问题上,如果是多库多表的情况,数据同步和备份的复杂度将会增加。

2. 数据迁移问题

.在分库分表后,如何对旧数据进行迁移以及如何在迁移过程中保证业务的正常运行是个大问题。

3. 分布式事务问题

在传统的单体数据库中,事务是保证数据一致性的重要手段。但在分库分表的环境下,原有的事务机制不能再使用,需要引入新的分布式事务解决方案。

4. join查询的问题

在分库分表后,原本在一个库或一个表中可以做的join查询就变得困难,需要通过应用层去做关联和组合。

分库分表的策略实现示例

  1. 函数法(哈希、取模等):根据某个字段(比如用户ID)的函数结果进行分库分表。
  2. 范围法(枚举、区间):比如根据日期范围,订单ID范围等进行分库分表。
  3. 一致性哈希:在新增或减少数据库节点的时候,能够最小化数据的迁移。

我们用Java写一些伪代码方便大家理解。
假设我们有多种根据某个值(如用户ID或订单ID)来确定应将数据存储在哪个数据库的方式。我们分别用函数法,范围法和一致性哈希法三种方法模拟一下。

  1. 函数法:我们可以将用户ID除以数据库数量的余数作为数据库的索引。
public String getDatabase(int userId) {
    int dbCount = 2; // 假设我们有2个数据库
    int dbIndex = userId % dbCount;
    return "db" + dbIndex;
}
  1. 范围法:我们可以使用订单ID作为范围进行划分,比如订单ID小于10000的存储在一个数据库,大于等于10000的存储在另一个数据库。
public String getDatabase(int orderId) {
    if (orderId < 10000) {
        return "db0";
    } else {
        return "db1";
    }
}
  1. 一致性哈希:这种方法需要使用到特殊的数据结构,例如TreeMap。首先,我们将每个数据库(称为节点)添加到一个TreeMap中,然后计算键(如用户ID)的哈希值,并在TreeMap中找到该哈希值对应的数据库。如果没有找到,则返回哈希值最接近的数据库。
public class ConsistentHashing {
    private TreeMap<Integer, String> nodes = new TreeMap<>();

    public void addNode(String nodeName) {
        int hash = nodeName.hashCode();
        nodes.put(hash, nodeName);
    }

    public void removeNode(String nodeName) {
        int hash = nodeName.hashCode();
        nodes.remove(hash);
    }

    public String getDatabase(String key) {
        if (nodes.isEmpty()) {
            return null;
        }

        int hash = key.hashCode();
        if (!nodes.containsKey(hash)) {
            SortedMap<Integer, String> tailMap = nodes.tailMap(hash);
            hash = tailMap.isEmpty() ? nodes.firstKey() : tailMap.firstKey();
        }
        return nodes.get(hash);
    }
}

其实真实场景中我们大多都使用分库分表组件或者中间件,最著名的和大家最常用的sharding-jdbc就是解决这类场景的一个优秀的,轻量级的分库分表组件,虽然有很多bug或者不足,但是足以解决我们90%的问题了。后面有一个章节我会写一个详细教程关于sharding-jdbc使用详解。除了它还有一些国产的中间件也不错。如下

  1. TDDL:阿里巴巴开源的分库分表中间件,支持复杂的分库分表策略。
  2. Oceanus:网易云数据库的分库分表解决方案,支持自动分库分表、读写分离、分布式事务等功能。

2. 参考文档

  1. “MySQL分库分表方案” - InfoQ。介绍了 MySQL 的分库分表方案,并对实施这种方案的步骤进行了详细的讨论。链接:https://www.infoq.cn/article/solution-of-mysql-sub-database-and-sub-table

  2. “MySQL 分库分表实践” - 阿里云社区的一篇文章也可以参考学习。介绍了 MySQL 分库分表的实践和经验。链接:https://developer.aliyun.com/article/776687

  3. “分库分表架构设计” 详细介绍了分库分表架构的设计和实施。链接:https://www.jianshu.com/p/d7f3d3808f25

  4. ShardingSphere 是 Apache 的一个开源项目包包含了我上面说的sharding-jdbc,提供了分库分表的解决方案。链接:https://shardingsphere.apache.org/document/current/cn/overview/

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

XSS的分析

目录 1、XSS的原理 2、XSS的攻击类型 2.1 反射型XSS 2.2 存储型XSS 2.3 DOM-based 型 2.4 基于字符集的 XSS 2.5 基于 Flash 的跨站 XSS 2.6 未经验证的跳转 XSS 3、复现 3.1 反射性 3.2 DOM-based型 1、XSS的原理 XSS的原理是恶意攻击者往 Web 页面里插入恶意可执行…

golong基础相关操作--一

package main//go语言以包作为管理单位&#xff0c;每个文件必须先声明包 //程序必须有一个main包 // 导入包&#xff0c;必须要要使用 // 变量声明了&#xff0c;必须要使用 import ("fmt" )/* * 包内部的变量 */ var aa 3var ss "kkk"var bb truevar …

在CentOS7上使用Docker安装和部署RabbitMQ

&#x1f680; 1 拉取RabbitMQ Docker镜像 首先&#xff0c;使用Docker命令从Docker Hub拉取RabbitMQ官方镜像。打开终端并运行以下命令&#xff1a; docker pull rabbitmq&#x1f680; 2 创建RabbitMQ容器 一旦镜像下载完成&#xff0c;使用以下命令创建RabbitMQ容器&…

SpringBoot整合Websocket(Java websocket怎么使用)

目录 1 Websocket是什么2 Websocket可以做什么3 Springboot整合Websocket3.1 服务端3.2 客户端 1 Websocket是什么 WebSocket 是一种基于 TCP 协议的全双工通信协议&#xff0c;可以在浏览器和服务器之间建立实时、双向的数据通信。可以用于在线聊天、在线游戏、实时数据展示等…

【Stable Diffusion安装】支持python3.11 window版

前言 主要的安装步骤是参考B站播放量第一的视频&#xff0c;但是那位阿婆主应该是没有编程经验&#xff0c;只强调使用3.10&#xff0c;而python最新版本是3.11。 理论上来说&#xff0c;只是一个小版本的不同&#xff0c;应该是可以安装成功了。自己摸索了下&#xff0c;挺费…

springboot使用logback配置彩色日志

springboot使用logback配置彩色日志 前言一、logback文件二、效果 前言 应该有很多同学发现&#xff0c;使用了logback以后&#xff0c;我们的控制台日志都变成灰色了&#xff0c;网络上搜到的logback配置大多数没有进行配色&#xff0c;所以会把springboot的默认配色方案给覆盖…

Unity中Shader的帧缓存区Clear(color+Z+stencil)

文章目录 前言一、什么是帧缓冲区二、片段运算三、随机扫描显示器&#xff08;可以按照自定义路径绘制帧&#xff09;四、光栅扫描显示器&#xff08;从左到右&#xff0c;从上到下&#xff0c;依次绘制&#xff09;五、缓冲的方式&#xff1a;单缓冲 和 双缓冲1、单缓冲2、双缓…

认识JVM的内存模型

从上一节了解到整个JVM大的内存区域&#xff0c;分为线程共享的heap&#xff08;堆&#xff09;&#xff0c;MethodArea&#xff08;方法区&#xff09;&#xff0c;和线程独享的 The pc Register&#xff08;程序计数器&#xff09;、Java Virtual Machine Stacks&#xff08;…

2. postgresql并行扫描(1)——pg强制走并行扫描建表及参数配置

转载自&#xff1a;https://developer.aliyun.com/article/700370 1. 参数设置 1.1 postgresql.conf中修改 # 1、总的可开启的WORKER足够大 max_worker_processes 128# 2、所有会话同时执行并行计算的并行度足够大 max_parallel_workers64# 3、单个QUERY中并行计算NODE开…

测试验证平台

测试验证平台 1.功能说明&#xff1a; 模拟智能终端车端数据采集及上报的功能&#xff0c;提供数据管理平台的模拟和验证功能。 2.系统组成&#xff1a; 系统示意图 功能要求&#xff1a; 本地电脑实现Imx6配置功能&#xff0c;能够通过运行不同的脚本&#xff0c;模拟不…

C语言记录程序日志

我们写程序&#xff0c;不可能一次就写的一个bug都没有&#xff0c;必须要不停地修改&#xff0c;有可能自己调试已经没有问题了&#xff0c;发给客户后还是问题很多&#xff0c;这个时候跑到客户处解决问题就不现实了&#xff0c;自己不在还要找到问题的所在&#xff0c;最好的…

质谱技术对蛋白质进行鉴定

参考B站教学视频: 质谱如何鉴定蛋白质_哔哩哔哩_bilibili 针对该视频&#xff0c;别人的 笔记 质谱是一台体重秤&#xff0c;称的不是人&#xff0c;而是分子、原子的体重 不同分子有不同分子量是质谱仪工作的底层逻辑 图片来自&#xff1a;【蛋白组】蛋白质组定量技术的原理和…

如何设计一个好的游戏剧情(Part 1:主题的设定)

提醒&#xff1a;此教程仅仅为作者的一些经验和感悟&#xff0c;非专业教程&#xff0c;若介意请前往网上搜集或者书本查阅相关资料&#xff01; 前言&#xff1a;游戏为什么要有剧情——游戏剧情的重要性 游戏剧情的重要性难以低估。一个精彩的剧情可以让玩家感受到强烈的情感…

ZigBee案例笔记 -- RFID卡片读写(模拟饭卡)

RFID模拟饭卡应用 RFID&#xff08;射频识别技术&#xff09;RFID通讯协议RFID发展历史RFID操作流程说明RFID卡片读写流程RFID寻卡RFID防碰撞RFID选卡RFID卡密验证RFID读卡RFID写卡读写数据流程 RFID饭卡模拟案例驱动代码串口协议饭卡操作案例结果优化建议 RFID&#xff08;射频…

QUdpSocket Class

继承自 QAbstractSocket 类 QUdpSocket类提供UDP套接字。 UDP(用户数据报协议)是一种轻量级、不可靠、面向数据报、无连接的协议。它可以在可靠性不重要的情况下使用。QUdpSocket是QAbstractSocket的一个子类&#xff0c;它允许您发送和接收UDP数据报。 使用这个类最常见的方法…

Laravel 模型1对1关联 1对多关联 多对多关联 ⑩①

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; THINK PHP &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f44…

说说CDN和负载均衡具体是怎么实现的

分析&回答 什么是 CDN CDN (全称 Content Delivery Network)&#xff0c;即内容分发网络。 构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的负载均衡、内容分发、调度等功能模块&#xff0c;使用户就近获取所需…

如何让你的jupyter notebook 排版得像Word(Markdown和网页文件写法)

案例背景 很多时候我们在jupyter notebook里面的写代码&#xff0c;画图&#xff0c;但是文字分析什么的写在里面纯文本不好看&#xff0c;需要进行排版&#xff0c;那么就得用markdown的写法&#xff0c;如何还想居中或者更花里胡哨的字体&#xff0c;那就得要网页文件的一些…

【MATLAB第70期】基于MATLAB的LightGbm(LGBM)梯度增强决策树多输入单输出回归预测及多分类预测模型(全网首发)

【MATLAB第70期】基于MATLAB的LightGbm(LGBM)梯度增强决策树多输入单输出回归预测及多分类预测模型&#xff08;全网首发&#xff09; 一、学习资料 (LGBM)是一种基于梯度增强决策树(GBDT)算法。 本次研究三个内容&#xff0c;分别是回归预测&#xff0c;二分类预测和多分类预…

终端安全与端点保护:讨论保护终端设备免受恶意软件、恶意链接和其他威胁的方法,包括终端保护工具和实践

第一章&#xff1a;引言 在当今数字化世界中&#xff0c;终端设备如电脑、手机和平板成为我们生活与工作的不可或缺的一部分。然而&#xff0c;随着技术的进步&#xff0c;恶意软件、网络攻击和数据泄露等威胁也不断增加&#xff0c;对终端设备的安全提出了更高的要求。本文将…