NOTE

数据库分库分表

1. 为什么需要分库分表 分布式系统分区.md 2. 分库分表是什么 分库是把原来的一个库分成多个库,分表是把原来的一张表分成多张表 分库和分表都有水平拆分和垂直拆分的方式 2.1. 垂直拆分 2.1.1. 垂直分表 每个表的结构都不一样,每个表的数据也不一样 把一张表的字段按照使用频率、是否大字段分成多张表(一对一关系)。比如把商品信息表分成商品基本信息表和商品描述表 这个操作一般在设计之初完成 2.1.2. 垂直分库 每个库的结构都不一样,每个库的数据也不一样 按照业务逻辑耦合程度把一个库分成多个库,部署在不同机器上。本质上和微服务按照业务拆分一个逻辑,每个服务对应一个数据库,比如商品信息

Database创建于 更新于 约 4 分钟读完historical

这是历史学习笔记,可能存在过时或不完整的理解。

1. 为什么需要分库分表

分布式系统分区.md

2. 分库分表是什么

分库是把原来的一个库分成多个库,分表是把原来的一张表分成多张表 分库和分表都有水平拆分和垂直拆分的方式

2.1. 垂直拆分

2.1.1. 垂直分表

每个表的结构都不一样,每个表的数据也不一样 把一张表的字段按照使用频率、是否大字段分成多张表(一对一关系)。比如把商品信息表分成商品基本信息表和商品描述表 这个操作一般在设计之初完成

2.1.2. 垂直分库

每个库的结构都不一样,每个库的数据也不一样 按照业务逻辑耦合程度把一个库分成多个库,部署在不同机器上。本质上和微服务按照业务拆分一个逻辑,每个服务对应一个数据库,比如商品信息库可以分为商品库和店铺库 这个操作一般在设计之初完成

2.2. 水平拆分

2.2.1. 水平分表

每个表的结构都一样,每个表的数据都不一样

单表的行数很大,需要数据行数分开到不同表中.比如商品表有50W数据,那么分成5份表。每份有10W数据 数据量中等规模时能小幅度提升性能,数据量一大还是得用水平分库

2.2.2. 水平分库

每个库的结构都一样,每个库的数据都不一样 单表的行数很大,需要按照数据行数分开到不同数据库中.比如商品库有500W数据,那么分成50份库。每份有10W数据 这个操作是在数据库索引优化、加缓存、读写分离之后,还有性能问题时才使用

3. 分库分表带来的问题

3.1. 主键ID唯一的问题

3.2. 事务一致性问题

3.3. 跨节点排序、聚合、分页问题

  • 并发查询在代码层对结果封装。比如排序,可以发送请求给所有节点,取出前N条数据,聚合后排序取出前N条。参考Elasticsearch CRUD流程.md

3.4. 跨节点Join的问题

  1. 使用单表查询然后在代码层对结果封装
  2. 做宽表,重写轻读
  3. 使用搜索引擎

4. 分库分表中间件

4.1. 分类

  • 主要分为client和proxy两类
    • sharding-jdbc(client)
    • mycat(proxy)

4.2. Sharding JDBC

4.2.1. 是什么

一个客户端的分库分表中间件,可以理解为增强版的JDBC驱动,核心功能有数据分片、读写分离。

4.2.2. 不支持项

子查询必须有sharding-column 不支持批量插入 聚合函数必须取别名 sql不能有括号,比如limit语句

5. 水平拆分方案

5.1. 拆分数据

5.1.1. 拆分Key的选择

主键ID或者业务ID或者时间,兼顾查询效率和拆分均匀

5.1.2. 拆分策略

5.1.2.1. 单独的映射表
  • 单独的映射表
  • 优点:ID和库的Mapping算法可以随意更改。
  • 缺点:
    • 映射库数据量大了也得分库分表
    • 写入需要同时写业务库和映射库,属于分布式事务问题。可以通过定时任务对比保证最终一致性
5.1.2.2. 范围切分/顺序切分
  • 优点:单表大小可控,天然水平扩展。
  • 缺点:会有热点数据的问题。新增加的数据集中在一个节点,会有写入瓶颈
  • 适合时间拆分用于冷热分离场景
5.1.2.3. Hash切分
  • 优点:基本不会有热点问题
  • 缺点:扩容时需要迁移数据
  • 必须用一致性Hash,要么是跟Redis一样的hash slot,即提前规划好库的数量;要么是hash ring

5.2. 请求处理

5.2.1. 路由组件

要么是客户端,要么是代理

5.2.2. 一级索引

分布式系统分区之请求处理.md

5.2.3. 二级索引

  • 根据非sharding key查,那么需要发送请求给所有节点,最后聚合。但是效率低
5.2.3.1. 冗余两份数据

冗余两份数据,一份按ID1分库,另一份按ID2分库。写入的时候可以业务双写,也可以业务单写+bin log监听

5.2.3.2. 统一维度

比如ID1作为ID2的前几位,这样ID2就包含了ID1的信息。按ID1查询的时候没问题,按ID2查询的时候先取出ID1,再进行查询

5.3. 分区分配

分布式系统分区之分区分配.md 就是扩缩容时的数据迁移,需要手动处理

5.3.1. 停机

在凌晨没有用户的时候停机,然后把旧的单库的数据拉出来,重新刷到新的分库中,刷完之后修改代码连分库对外服务

5.3.2. 不停机双写

修改代码,写数据的时候同时写旧的单库和新的分库,对于旧库的数据,用脚本取出来重新刷到分库

6. 单表数据量多大得考虑分库分表

2KW,因为对应的树高为3,IO次数大概为3

MySQL InnoDB存储引擎的主键索引对应的B+树,假设page size为16KB,主键是bigint类型的,每行记录大小为1KB,那么三层可以存放多少条记录? page size为16KB,key为8B,指针为6B,那么一个page可以存放16*1024/(8+6)=1170个key page size为16KB,value为1KB,那么一个page可以存放16/1=16个value 假设树高为3,那么 第0层:root层。常驻内存 第一层:key层。可以存放1170个key,对应1170个分叉 第二层:key层。可以存放1170*1170个key,对应1368900个分叉 第三层:value层。可以存放1170*1170*16个value,对应21902400(大概2KW)行 。 前两个key层大概占用(1170+1368900)*16/1024/1024=21MB,这个层次内存可以缓存索引,再高一层那么(1170+1368900+1601613000)*16/1024/1024=24GB,内存缓存不下。最后的value层大概占用21902400/1024/1024=21GB磁盘空间

7. 参考

讨论

使用 GitHub 账号参与讨论,评论会保存在 GitHub Issues 中。在 GitHub 查看