# 分库分表背景介绍


<!--more-->


## 背景描述

- 刚开始我们的系统只用了单机数据库

- 随着用户的不断增多，考虑到系统的高可用和越来越多的用户请求，我们开始使用数据库主从架构

- 当用户量级和业务进一步提升后，写请求越来越多，这时我们开始使用了分库分表

## 遇到问题

- 用户请求量太大

  单服务器TPS、内存、IO都是有上限的，需要将请求打散分布到多个服务器

- 单库数据量太大

  单个数据库处理能力有限；单库所在服务器的磁盘空间有限；单库上的操作IO有瓶颈

- 单表数据量太大

  查询、插入、更新操作都会变慢，在加字段、加索引、机器迁移都会产生高负载，影响服务

## 如何解决

### 垂直拆分

- 垂直分库

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

- 垂直分表

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

### 水平拆分

- 水平分表

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

- 水平分库

  将单张表的数据切分到多个服务器上去，每个服务器具有相应的库与表，只是表中数据集合不同。水平分库分表能够有效的缓解单机和单库的性能瓶颈和压力，突破IO、连接数、硬件资源等的瓶颈

- 水平分库规则

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

- 水平分表规则

  - 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在一张表，这种情况会出现单表的负载较高

    按照用户IDHASH尽量保证用户数据均衡分到数据库中

    > 如果在登录场景下，用户输入手机号和验证码进行登录，这种情况下，登录时是不是需要扫描所有分库的信息？
    >
    > 最终方案：用户信息采用ID做切分处理，同时存储用户ID和手机号的映射的关系表（新增一个关系表），关系表采用手机号进行切分。可以通过关系表根据手机号查询到对应的ID，再定位用户信息。

  - 流水表

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

  - 订单表

    > 在拉勾网，求职者（下面统称C端用户）投递企业（下面统称B端用户）的职位产生的记录称之为订单表。在线上的业务场景中，C端用户看自己的投递记录，每次的投递到了哪个状态，B端用户查看自己收到的简历，对于合适的简历会进行下一步沟通，同一个公司内的员工可以协作处理简历。
    >
    > 如何能同时满足C端和B端对数据查询，不进行跨库处理？
    >
    > 最终方案：为了同时满足两端用户的业务场景，采用空间换时间，将一次的投递记录存为两份，C端的投递记录以用户ID为分片键，B端收到的简历按照公司ID为分片键

- 主键选择

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

  强一致性：XA协议
  
  最终一致性：TCC、saga、Seata
  
- 数据扩容

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

  基于代理层方式：Mycat、Sharding-Proxy、MySQLProxy 
  
  基于应用层方式：Sharding-jdbc
  
- 分库后面临问题

  事务问题：一次投递需要插入两条记录，且分布在不同的服务器上，数据需要保障一致性。
  
  跨库跨表的join问题:
  
  - 全局表（字典表）：基础数据/配置数据，所有库都拷贝一份
  - 字段冗余：可以使用字段冗余就不用join查询了
  - 系统层组装：可以在业务层分别查询出来，然后组装起来，逻辑较复杂
  - 额外的数据管理负担和数据运算压力：数据库扩容、维护成本变高














