万隆的笔记 万隆的笔记
博文索引
笔试面试
  • 在线学站

    • 菜鸟教程 (opens new window)
    • 入门教程 (opens new window)
    • Coursera (opens new window)
  • 在线文档

    • w3school (opens new window)
    • Bootstrap (opens new window)
    • Vue (opens new window)
    • 阿里开发者藏经阁 (opens new window)
  • 在线工具

    • tool 工具集 (opens new window)
    • bejson 工具集 (opens new window)
    • 文档转换 (opens new window)
  • 更多在线资源
  • Changlog
  • Aboutme
GitHub (opens new window)
博文索引
笔试面试
  • 在线学站

    • 菜鸟教程 (opens new window)
    • 入门教程 (opens new window)
    • Coursera (opens new window)
  • 在线文档

    • w3school (opens new window)
    • Bootstrap (opens new window)
    • Vue (opens new window)
    • 阿里开发者藏经阁 (opens new window)
  • 在线工具

    • tool 工具集 (opens new window)
    • bejson 工具集 (opens new window)
    • 文档转换 (opens new window)
  • 更多在线资源
  • Changlog
  • Aboutme
GitHub (opens new window)
  • MySQL

  • 架构原理

  • 索引与优化

  • 事务和锁

  • 集群架构

    • 集群架构设计
    • 主从模式
    • 双主模式
    • 分库分表
    • 分库分表案例
      • 背景描述
      • 遇到的问题
      • 如何解决
      • 分库分表后面临的一些问题
    • Docker搭建MySQL主从架构
    • MySQL Proxy读写分离
    • Docker搭建MySQL双主架构
  • MySQL
  • 集群架构
2022-03-23
目录

分库分表案例

# 分库分表案例

以下是拉勾网分库分表上的案例,包含了数据库架构的演进以及他们对分库分表分片策略的选择,我们可以参考一下。

# 背景描述

  • 刚开始我们的系统只用了单机数据库
  • 随着用户的不断增多,考虑到系统的高可用和越来越多的用户请求,我们开始使用数据库主从架构
  • 当用户量级和业务进一步提升后,写请求越来越多,这时我们开始使用了分库分表

# 遇到的问题

  • 用户请求量太大:单服务器TPS、内存、IO都是有上限的,需要将请求打散分布到多个服务器
  • 单库数据量太大:单个数据库处理能力有限;单库所在服务器的磁盘空间有限;单库上的操作IO有瓶颈
  • 单表数据量太大:查询、插入、更新操作都会变慢,在加字段、加索引、机器迁移都会产生高负载,影响服务

# 如何解决

# 垂直拆分

# 垂直分库

微服务架构时,业务切割得足够独立,数据也会按照业务切分,保证业务数据隔离,大大提升了数据库的吞吐能力。

lago_demo_1.png

# 垂直分表

表中字段太多且包含大字段的时候,在查询时对数据库的IO、内存会受到影响,同时更新数据时,产生的binlog文件会很大,MySQL在主从同步时也会有延迟的风险。

lago_demo_2.png

# 水平拆分

# 水平分表

针对数据量巨大的单张表(比如订单表),按照规则把一张表的数据切分到多张表里面去。但是这些表还是在同一个库中,所以库级别的数据库操作还是有IO瓶颈。

lago_demo_3.png

# 水平分库

将单张表的数据切分到多个服务器上去,每个服务器具有相应的库与表,只是表中数据集合不同。 水平分库分表(这种相对复杂,但是优化效果相对较好 )能够有效的缓解单机和单库的性能瓶颈和压力,突破IO、连接数、硬件资源等的瓶颈。

lago_demo_4.png

水平分库规则:

  • 不跨库、不跨表,保证同一类的数据都在同一个服务器上面。
  • 数据在切分之前,需要考虑如何高效的进行数据获取,如果每次查询都要跨越多个节点,就需要谨慎使用。

水平分表规则:

  • RANGE(范围划分)
    • 时间:按照年、月、日去切分。例如order_2020、order_202005、order_20200501
    • 地域:按照省或市去切分。例如order_beijing、order_shanghai、order_chengdu
    • 大小:从0到1000000一个表。例如1000001-2000000放一个表,每100万放一个表
  • HASH
    • 用户ID取模
    • 不同的业务使用的切分规则是不一样,就上面提到的切分规则,详见水平分表的案例。

水平分表案例

  • 站内信:用户维度,用户只能看到发送给自己的消息,其他用户是不可见的,这种情况下是按照 用户ID hash分库,在用户查看历史记录翻页查询时,所有的查询请求都在同一个库内

  • 用户表:

    • 范围法:以用户ID为划分依据,将数据水平切分到两个数据库实例,如:1到1000W在一张表,1000W到2000W在一张表,这种情况会出现单表的负载较高

    • 按照用户ID HASH尽量保证用户数据均衡分到数据库中。

      如果在登录场景下,用户输入手机号和验证码进行登录,这种情况下,登录时是不是需要扫描所有分库的信息?

      最终方案:用户信息采用ID做切分处理,同时存储用户ID和手机号的映射的关系表(新增一个关系表),关系表采用手机号进行切分。可以通过关系表根据手机号查询到对应的ID,再定位用户信息。

  • 流水表:时间维度,可以根据每天新增的流水来判断,选择按照年份分库,还是按照月份分库,甚至也可以按照日期分库

  • 订单表:在拉勾网,求职者(下面统称C端用户)投递企业(下面统称B端用户)的职位产生的记录称之为订单表。

    在线上的业务场景中,C端用户看自己的投递记录,每次的投递到了哪个状态,B端用户查看自己收到的简历,对于合适的简历会进行下一步沟通,同一个公司内的员工可以协作处理简历。

    如何能同时满足C端和B端对数据查询,不进行跨库处理?

    最终方案:为了同时满足两端用户的业务场景,采用空间换时间,将一次的投递记录存为两份,C端的投递记录以用户ID为分片键,B端收到的简历按照公司ID为分片键

    lago_demo_5.png

# 分库分表后面临的一些问题

# 主键选择

  • UUID:本地生成,不依赖数据库,缺点就是作为主键性能太差
  • SNOWFLAKE:百度UidGenerator、美团Leaf、基于SNOWFLAKE算法实现

数据一致性

  • 事务问题:一次投递需要插入两条记录,且分布在不同的服务器上,数据需要保障一致性。
    • 强一致性:XA协议
    • 最终一致性:TCC、saga、Seata

# 数据库扩容与清理

  • 成倍增加数据节点,实现平滑扩容
  • 成倍扩容以后,表中的部分数据请求已被路由到其他节点上面,可以清理掉

# 业务层改造

  • 基于代理层方式:Mycat、Sharding-Proxy、MySQL Proxy
  • 基于应用层方式:Sharding-jdbc

# 跨库跨表的join问题

  • 全局表(字典表):基础数据/配置数据,所有库都拷贝一份
  • 字段冗余:可以使用字段冗余就不用join查询了
  • 系统层组装:可以在业务层分别查询出来,然后组装起来,逻辑较复杂

# 额外的成本

  • 额外的数据管理负担和数据运算压力
  • 数据库扩容、维护成本变高
上次更新: 5/30/2023, 11:09:19 PM
Docker搭建MySQL主从架构

Docker搭建MySQL主从架构→

最近更新
01
2025
01-15
02
Elasticsearch面试题
07-17
03
Elasticsearch进阶
07-16
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式