MySQL分库分表 看完吊打面试官

2023-11-16

提示:下方有源代码地址,请自行拿取


前言

在互联网项目中比较常用到的关系型数据库是MySQL,随着用户和业务的增长,传统的单库单表模式难以满足大量的业务数据存储以及查询,单库单表中大量的数据会使写入、查询效率非常之慢,此时应该采取分库分表策略来解决。


提示:以下是本篇文章正文内容,案例仅供参考

一、业务场景介绍

假设目前有一个电商系统使用的是MySQL,要设计大数据量存储、高并发、高性能可扩展的方案,数据库中有用户表。用户会非常多,并且要实现高扩展性,你会怎么去设计?
OK咱们先看传统的分库分表方式
在这里插入图片描述

当然还有些小伙伴知道按照省份/地区或一定的业务关系进行数据库拆分
在这里插入图片描述
OK,问题来了,如何保证合理的让数据存储在不同的库不同的表里呢?让库减少并发压力?应该怎么去制定分库分表的规则?不用急,这不就来了

二、水平分库分表方法

1.RANGE

第一种方法们可以指定一个数据范围来进行分表,例如从1~1000000,1000001-2000000,使用一百万一张表的方式,如下图所示

在这里插入图片描述
当然这种方法需要维护表的ID,特别是分布式环境下,这种分布式ID,在不使用第三方分表工具的情况下,建议使用Redis,Redis的incr操作可以轻松的维护分布式的表ID。

RANGE方法优点: 扩容简单,提前建好库、表就好

RANGE方法缺点: 大部分读和写都访会问新的数据,有IO瓶颈,这样子造成新库压力过大,不建议采用。

2.HASH取模

针对上述RANGE方式分表有IO瓶颈的问题,咱们可以采用根据用户ID HASG取模的方式进行分库分表,如图所示:
在这里插入图片描述

这样就可以将数据分散在不同的库、表中,避免了IO瓶颈的问题。

HASH取模方法优点: 能保证数据较均匀的分散落在不同的库、表中,减轻了数据库压力

HASH取模方法缺点: 扩容麻烦、迁移数据时每次都需要重新计算hash值分配到不同的库和表

3.一致性HASH

通过HASH取模也不是最完美的办法,那什么才是呢?

使用一致性HASH算法能完美的解决问题

普通HASH算法:

普通哈希算法将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表示形式。

普通的hash算法在分布式应用中的不足:在分布式的存储系统中,要将数据存储到具体的节点上,如果我们采用普通的hash算法进行路由,将数据映射到具体的节点上,如key%n,key是数据的key,n是机器节点数,如果有一个机器加入或退出集群,则所有的数据映射都无效了,如果是持久化存储则要做数据迁移,如果是分布式缓存,则其他缓存就失效了。

一致性HASH算法:
按照常用的hash算法来将对应的key哈希到一个具有2^32次方个节点的空间中,即0~ (2^32)-1的数字空间中。现在我们可以将这些数字头尾相连,想象成一个闭合的环形,如下图所示。

在这里插入图片描述
这个圆环首尾相连,那么假设现在有三个数据库服务器节点node1、node2、node3三个节点,每个节点负责自己这部分的用户数据存储,假设有用户user1、user2、user3,我们可以对服务器节点进行HASH运算,假设HASH计算后,user1落在node1上,user2落在node2上,user3落在user3上

在这里插入图片描述
OK,现在咱们假设node3节点失效了
在这里插入图片描述
user3将会落到node1上,而之前的node1和node2数据不会改变,再假设新增了节点node4

在这里插入图片描述

你会发现user3会落到node4上,你会发现,通过对节点的添加和删除的分析,一致性哈希算法在保持了单调性的同时,还是数据的迁移达到了最小,这样的算法对分布式集群来说是非常合适的,避免了大量数据迁移,减小了服务器的的压力。

当然还有一个问题还需要解决,那就是平衡性。从图我们可以看出,当服务器节点比较少的时候,会出现一个问题,就是此时必然造成大量数据集中到一个节点上面,极少数数据集中到另外的节点上面。

为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个节点,称为虚拟节点。具体做法可以先确定每个物理节点关联的虚拟节点数量,然后在ip或者主机名后面增加编号。例如上面的情况,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “node 1-1”、“node 1-2”、“node 1-3”、“node 2-1”、“node 2-2”、“node 2-3”、“node 3-1”、“node 3-2”、“node 3-3”的哈希值,这样形成九个虚拟节点

例如user1定位到node 1-1、node 1-2、node 1-3上其实都是定位到node1这个节点上,这样能够解决服务节点少时数据倾斜的问题,当然这个虚拟节点的个数不是说固定三个或者至多、至少三个,这里只是一个例子,具体虚拟节点的多少,需要根据实际的业务情况而定。

一致性HASH方法优点: 通过虚拟节点方式能保证数据较均匀的分散落在不同的库、表中,并且新增、删除节点不影响其他节点的数据,高可用、容灾性强。

一致性取模方法缺点: 嗯,比起以上两种,可以认为没有。

三、单元测试

OK,不废话,接下来上单元测试,假设有三个节点,每个节点有三个虚拟节点的情况

package com.hyh.core.test;

import com.hyh.utils.common.StringUtils;
import org.junit.Test;

import java.util.LinkedList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 一致性HASH TEST
 *
 * @Author heyuhua
 * @create 2021/1/31 19:50
 */
public class ConsistentHashTest {

    //待添加入Hash环的服务器列表
    private static String[] servers = {"192.168.5.1", "192.168.5.2", "192.168.5.3"};

    //真实结点列表,考虑到服务器上线、下线的场景,即添加、删除的场景会比较频繁,这里使用LinkedList会更好
    private static List<String> realNodes = new LinkedList<>();

    //虚拟节点,key表示虚拟节点的hash值,value表示虚拟节点的名称
    private static SortedMap<Integer, String> virtualNodes = new TreeMap<>();

    //一个真实结点对应3个虚拟节点
    private static final int VIRTUAL_NODES = 3;

    /**
     * 测试有虚拟节点的一致性HASH
     */
    @Test
    public void testConsistentHash() {
        initNodes();
        String[] users = {"user1", "user2", "user3", "user4", "user5", "user6", "user7", "user8", "user9"};
        for (int i = 0; i < users.length; i++)
            System.out.println("[" + users[i] + "]的hash值为" +
                    getHash(users[i]) + ", 被路由到结点[" + getServer(users[i]) + "]");
    }

    /**
     * 先把原始的服务器添加到真实结点列表中
     */
    public void initNodes() {
        for (int i = 0; i < servers.length; i++)
            realNodes.add(servers[i]);
        for (String str : realNodes) {
            for (int i = 0; i < VIRTUAL_NODES; i++) {
                String virtualNodeName = str + "-虚拟节点" + String.valueOf(i);
                int hash = getHash(virtualNodeName);
                System.out.println("虚拟节点[" + virtualNodeName + "]被添加, hash值为" + hash);
                virtualNodes.put(hash, virtualNodeName);
            }
        }
        System.out.println();
    }

    //使用FNV1_32_HASH算法计算服务器的Hash值,这里不使用重写hashCode的方法,最终效果没区别
    private static int getHash(String str) {
        final int p = 16777619;
        int hash = (int) 2166136261L;
        for (int i = 0; i < str.length(); i++)
            hash = (hash ^ str.charAt(i)) * p;
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;

        // 如果算出来的值为负数则取其绝对值
        if (hash < 0)
            hash = Math.abs(hash);
        return hash;
    }

    //得到应当路由到的结点
    private static String getServer(String key) {
        //得到该key的hash值
        int hash = getHash(key);
        // 得到大于该Hash值的所有Map
        SortedMap<Integer, String> subMap = virtualNodes.tailMap(hash);
        String virtualNode;
        if (subMap.isEmpty()) {
            //如果没有比该key的hash值大的,则从第一个node开始
            Integer i = virtualNodes.firstKey();
            //返回对应的服务器
            virtualNode = virtualNodes.get(i);
        } else {
            //第一个Key就是顺时针过去离node最近的那个结点
            Integer i = subMap.firstKey();
            //返回对应的服务器
            virtualNode = subMap.get(i);
        }
        //virtualNode虚拟节点名称要截取一下
        if (StringUtils.isNotBlank(virtualNode)) {
            return virtualNode.substring(0, virtualNode.indexOf("-"));
        }
        return null;
    }
}

这里模拟9个用户对象hash后被路由的情况,看下结果
在这里插入图片描述

总结

分库分表在分布式微服务架构环境下建议强烈使用一致性HASH算法来做,当然分布式环境下也会产生业务数据数据一致性、分布式事务问题,下期咱们再来探讨数据一致性、分布式事务的解决方案

作者寄语

路漫漫其修远兮,吾愿与君上下而求索,感谢各位帅哥、靓妹的点赞、收藏和评论,我们下期见。

关注我带你走进架构师的成长之路。

源码地址:点此查看源码.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL分库分表 看完吊打面试官 的相关文章

随机推荐

  • 电子工程师的自我修养 - OD输出

    开漏输出 Open Drain Output OD门 漏极开路 Open Drain 电路特点 利用外部电路的驱动能力 减少IC内部的驱动 可以将多个开漏输出的pin连接到一条线上 通过一个上拉电阻 在不增加任何器件的情况下 形成 线与 关
  • 使用myisamchK 命令修复数据

    使用myisamchk 命令修复表 myisam使用程序可以用来获得有关你的数据库表的统计信息 检查 修复 优化他们 命令格式 myisamchk option tables frm 常用的检查选项 information i 打印所检察标
  • 韦东山 IMX6ULL和正点原子_「正点原子NANO STM32开发板资料连载」第三章 MDK5 软件入门1...

    1 实验平台 ALIENTEK NANO STM32F411 V1开发板 2 摘自 正点原子STM32F4 开发指南 HAL 库版 关注官方微信号公众号 获取更多资料 正点原子 第三章 MDK5 软件入门 本章将向大家介绍 MDK5 软件和
  • Blazor组件自做四 : 使用JS隔离封装signature_pad签名组件

    运行截图 演示地址 响应式 感谢szimek写的棒棒的signature pad js项目 来源 https github com szimek signature pad 正式开始 1 在文件夹wwwroot lib 添加signatur
  • python3+requests+unittest实战系列【一】

    1 环境准备 python3 pycharm编辑器 2 框架目录展示 该套代码只是简单入门 有兴趣的可以不断后期完善 1 run py主运行文件 运行之后可以生成相应的测试报告 并以邮件形式发送 2 report文件夹存放测试结果报告 3
  • Python解偏微分方程

    2 u x
  • 能力强的项目经理是怎么催活儿的?(内附项目管理软件推荐)

    从确定做某个项目开始 项目经理就仿佛进入到了一个不断 催 的阶段 项目经理 催字符 恨不得贴到每个任务的负责人头上 仿佛项目经理除了开会就是催活儿 浪费大量精力和时间 今天分享的项目经理 催活儿指南 从计划 反馈以及隐形激励三个方面来梳理项
  • Linux 下的两个特殊的文件 -- /dev/null 和 /dev/zero 简介及对比

    1 概论 来自维基的解释 dev null 在类Unix系统中 dev null 或称空设备 是一个特殊的设备文件 它丢弃一切写入其中的数据 但报告写入操作成功 读取它则会立即得到一个EOF 在程序员行话 尤其是Unix行话中 dev nu
  • 链表类模板list

    描述 设计如下样式的链表类模板list 并对其进行简单使用 template
  • DataGridView数据显示和编辑控件的基本属性

    DataGridView是C Windows Forms中的一个数据显示和编辑控件 它具有许多有用的属性来控制其外观和行为 以下是DataGridView的一些基本属性 DataSource 设置或获取DataGridView绑定的数据源
  • clickhouse通过jdbc实现批量数据导入代码示例

    通过mybatis也可以实现批量写入clickhouse 但是效率太低 每秒大概只能写入300条数据 对于动辄上千万的数据或者更多数据 效率就太低了 而采用jdbc驱动的方式进行批量写入 每秒大约写入数据量达到10000条 效率得到大幅度的
  • 目标跟踪算法分类

    本文转载自 https www cnblogs com necp zwl p 6486326 html 上一篇文章写了 跟踪颜色块 自我感觉优化的空间很大 转载他人文章学习一下 运动目标跟踪主流算法大致分类 主要基于两种思路 a 不依赖于先
  • File类和Directory类

    File类和Directory类分别用来对文件和各种目录进行操作 这两类可以被实例化 但不能被其他类集成 1 File类 静态类 File类支持对文件的基本操作 它包括用于创建 复制 删除 移动和打开文件的静态方法 并协助创建FileStr
  • 渗透测试-完整渗透流程(一.信息收集)

    信息收集 1 信息收集分为主动收集和被动收集 主动收集会被防守方发现 被动收集不会被发现 主动信息收集 通过直接访问 扫描网站 这种流量将流经网站 被动信息收集 利用第三方的服务对目标进行访问了解 比例 Google搜索 Shodan搜索等
  • 设计模式(狂神说)

    1p 12p设计模式概述 代码 创建型模式 结构型模式 将类或者对象按照某种布局组成更大的结构 行为型模式 描述类和对象之间相互协作 2p 12p OOP七大原则 开闭原则 对扩展开放 对修改关闭 里氏替换原则 继承必须确保超类所拥有的性质
  • JVM常见知识点

    目录 JVM主要组成部分及其作用 JVM主要包含两个子系统和两个组件 Java程序运行机制 双亲委派机制 类加载器 双亲委派模型 双亲委派机制存在的意义 举例 栈 堆 队列 堆栈区别 队列跟栈区别 Java垃圾回收 方法区是在堆里面吗 JV
  • 国务院建议探索区块链等技术缩短承兑期限

    摘要 产业动态 全球大学区块链能力排名 新加坡国立大学第一 清华第八 韩国记者协会与与PUBLISH合作 采用区块链技术改善新闻媒体生态系统 Ripple与卡塔尔国家银行达成合作 将为其推出新汇款服务 政策相关 国务院 建议探索以互联网和区
  • B站S11破亿直播在线稳定性保障秘籍——演讲实录

    英雄联盟 S11全球总决赛 决赛中国战队夺冠 赛事直播盛况空前 观赛人数破亿 B站作为英雄联盟2021全球总决赛直播独家版权方不仅在整个比赛过程中保证了直播整体总体运行的平稳 还抗住了超预期的流量 如此大型的直播活动 B站究竟是如何保障系统
  • HarmonyOS UI组件在线预览,程序员直呼“不要太方便~”

    原文 UI组件在线预览 程序员直呼 不要太方便 点击链接查看更多技术内容 一 介绍 以往大家如果想查看组件的使用效果 需要打开DevEco Studio构建工程 现在为了便于大家高效开发 文档上线了JS UI组件在线预览功能 无需本地构建工
  • MySQL分库分表 看完吊打面试官

    MySQL分库分表 看完吊打面试官 前言 一 业务场景介绍 二 水平分库分表方法 1 RANGE 2 HASH取模 3 一致性HASH 三 单元测试 总结 作者寄语 提示 下方有源代码地址 请自行拿取 前言 在互联网项目中比较常用到的关系型