栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

分库分表总结笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

分库分表总结笔记

分库分表

程序员囧辉 大厂面试题之分库分表总结笔记

文章目录
  • 分库分表
    • 什么是分库分表
      • 拆分方式
      • 为什么要分库分表
      • 如何进行分库分表
        • 何时分库分表
        • 如何选择分库分表
    • 十亿级数据库分库核心流程详解(*)
      • 完整流程
      • 拆分SOP
        • 1.目标评估
        • 2.切分策略
        • 3.分表字段
        • 4.资源准备、代码改造
        • 5.增量数据同步(双写)
        • 6.全(存)量数据迁移
        • 7.数据一致性校验、优化、补偿(最重要,花时间最多)
        • 8.灰度切读
        • 9.databus切新库
        • 10.下游切数据源
        • 11.停写老库
      • 相关工具
    • 如何解决分库分表带来的问题
      • 1.分布式唯一ID
      • 2.分布式事务
      • 3.保证数据的最终一致性
      • 4.跨库JOIN/分页查询

什么是分库分表

分库:在表数量不变的情况下,对数据库进行拆分

分表:数据库数量不变的情况下,对数据库表进行拆分

分库分表:数据库的数量和表的数量都发生变更

拆分方式
  • 水平拆分:整个表数据结构不发生变化的情况下,将一张表的数据拆分成多张表,单张表数据记录越来越大时,这张表的查询跟写入性能相应越来越慢,通过拆表让每张表的数据变小,提供更优的读写性能
  • 垂直拆分:将本来放在一张表的字段拆分到多张表中,随着业务的发展,某些字段可能越来越大影响基本信息的查询
为什么要分库分表

单台mysql服务器的硬件资源时有限的,随着业务的不断发展,请求量和数据量会不断增加,数据库的压力越来越大,某时数据库的读写性能开始下降,数据库将变成请求链路中的一个瓶颈,需要对数据库进行优化,业务初期,使用增加索引、读写分离、增加从库的手段进行优化,随着数据量的不断增加,这些优化手段作用越来越小,此时使用分库分表进行优化,对数据进行切分,将单表和单库的数据量控制在一个合理的范围,保证高效的读写能力

如何进行分库分表 何时分库分表

性能出现瓶颈,其他优化手段无法很好解决,分库分表作为最后的手段进行优化

  • 单表出现瓶颈

    • 单表数据量大,导致读写速度慢
  • 单库出现瓶颈

    • CPU压力过大(busy、load高),导致读写性能较慢
    • 内存不足(缓存池命中低、磁盘读写IOPS较高)导致读写性能较慢
    • 磁盘空间不足,导致无法正常写入数据
    • 网络带宽不足,导致读写性能较慢
如何选择分库分表

经过评估单库的容量和性能可以支持未来几年的增长

  • 只分表
    • 单表数据量较大,单表读写性能出现瓶颈
  • 只分库
    • 数据库(读)写压力大,数据库出现存储性能瓶颈
  • 分库分表
    • 单表数据量较大,单表读写性能出现瓶颈
    • 数据库(读)写压力大,数据库出现存储性能瓶颈
十亿级数据库分库核心流程详解(*)

分库分表核心内容——拆分sop

完整流程
  • 评估是否需要拆分
  • 拆分详细技术方案
    • 流程梳理及影响评估
    • 方案选型
    • 拆分SOP
    • 稳定性保障
  • 技术方案内部评审及优化
  • 同步相关影响方
  • 进行拆分
拆分SOP

SOP:标准作业程序

1.目标评估

评估:拆分几个库,几个表

目标:读写能力提升、负载降低、容量支持未来几年的发展

大多数情况下,将单表的行数作为一个重要的参考指标,控制在千万级以下

2.切分策略
  • 范围切分

    • 优点:天然水平扩展。单表大小可控,后续扩容方便,无需进行数据迁移,可自动化处理
    • 缺点:存在明显的写偏移,写流量基本都集中在最新的表上,没有起到将写流量均匀分摊到各库表的效果,读流量也可能存在偏移,最近增加的数据被查询的概率更大
  • 中间表映射

    • 将分表键和数据库的映射关系存放在单独的表上,每次路由前先查询这张表,得到具体的路由数据库和数据表,进行具体操作
    • 优点:灵活
    • 缺点:引入额外的单点,增加了流程复杂度,这个表会很大,查询的QPS会非常高,保证其高性能和高可用也是一个问题
  • 哈希切分(主流方案)

    • 通过对分键表进行一定的运算,通常取模,决定路由到哪个库和表
    • 优点:数据分布比较均匀,不容易出现热点和并发访问的瓶颈
    • 缺点:后续扩容需要迁移数据,存在跨节点查询的问题
3.分表字段

核心思路:合理选择,尽量减少出现跨库、跨表查询

4.资源准备、代码改造

资源准备:新集群的所需数据库资源可以尽早跟dba申请,特别拆分集群比较多的情况,一方面因为dba搭建新集群,花费一定时间,另一方面避免出现资源不足导致延期的情况。

代码改造:将新集群的数据源引入到我们的服务中,支持灵活的灰度读写切换,数据全量迁移和一致性校验等任务;因为整个分库分表的过程是不停机并且无损拆分,拆分过程中新老数据会同时存在一段时间,称为灰度,这段灰度期间,通过配置中心和相关规则灵活控制

核心流程:

  • 数据库资源准备
  • 分库分表规则配置
  • 代码改造
    • 写入:单写老库、双写、单写新库
    • 读取:读老、读新、部分读老部分读新
    • 灰度:指定门店灰度、比例灰度
5.增量数据同步(双写)

写新库是为了后续切换到新库,因此新库必须有全部的数据,写老库是因为不确定拆分过程中是否存在问题,写老库保证有全部数据,万一新库流程有问题,及时切换到老库的流程,保证服务的高可用跟稳定性

作用:保证增量数据在新库和老库都有

方案:

  • 同步双写:同步写新库和老库,所有写数据的地方进行修改成写两份数据,一般不会写全部的写逻辑,而是在底层通过aop的方式实现
  • 异步双写:写老库,监听binlog异步同步到新库,自己动手
  • 中间件同步工具:通过一定的规则将数据同步到目标库表,中间件团队帮你做

注意点:

  • 写新库异常不能影响流程
6.全(存)量数据迁移

作用:迁移老库的历史数据,保证新库有全量数据

方案:

  • RD开发Job:查询老库数据,写入新库
  • 中间件同步工具:通过一定的规则将数据同步到目标库表

注意点:

  • 控制好同步速率
  • 增量数据的并发问题
7.数据一致性校验、优化、补偿(最重要,花时间最多)

在全量数据迁移完毕,增量同步正常运行之后,还不能直接将流量切换到新库,还要验证数据的一致性,例如改造存在遗漏的地方,并发修改等导致数据不一致,并发下会出现很多不一致场景,是切读之前的最后一个保障,必须再三确认数据的正确,否则切读后导致线上问题

作用:确保新库数据正确,达到切读标准、检查是否存在改造遗漏点

方案:增量数据校验、全量数据校验、人工抽检

核心流程:

  • 读取老库数据
  • 读取新库数据
  • 比较新老库数据,一致则继续比较下一条数据
  • 不一致则进行补偿
    • 新库存在,老库不存在:新库删除数据
    • 新库不存在,老库存在:新库插入数据
    • 新库存在,老库存在:比较所有字段,不一致则将新库更新为老库的数据
8.灰度切读

数据校验一致性通过后将部分流量切换到新数据库,灰度早期会先拿少量的门店进行灰度,观察一段时间后,没有问题再继续增加灰度的门店,后期逐步使用比例来进行灰度,直到最终将全部的流量都切换到新的数据库上

作用:开始将流量切到新库

原则:

  • 有问题及时切回老库
  • 灰度放量先慢后快,每次放量观察一段时间
  • 支持灵活的规则:门店维护灰度、百(万)分比灰度
9.databus切新库

databus:监听binlog的组件,将读流量全部切换到新库后,此时新流程已经基本全部验证通过,开始为停写老库做准备,将监听的binlog从老库切换到新库

作用:使用新库的databus

核心流程:

  • 启动新库databus,此时下游会同时收到新老库的binlog
  • 观察一段时间是否正常
  • 有问题及时关闭
  • 没问题关闭老库的databus

注意点:

  • 监听binlog的流程一般会收敛在团队内部,外部团队想监听binlog,一般使用封装过后的消息,这样在进行分库分表改造时对外部团队基本没有影响,改造更加方便
10.下游切数据源

除了binglog外,主要的下游是数仓,数仓将商品数据定期同步到hive上,用于进行数据的相关操作,需要让数仓将数据源切换到新的数据源上

作用:确保下游迁移到新数据源,主要是数仓

数仓一般每天同步一次数据,因此在指定的时间内切换即可。对实时性的要求不高

11.停写老库

原则:确认老库数据源全部迁移后,停写老库

至此,核心拆分流程结束,后续操作逐步将老数据库资源逐步下线

相关工具
  • binlog监听工具

    binlog是一个二进制文件,用于记录数据库表结构和表记录的变更,通过binlog文件,可知道数据库中哪些数据发生变更,从什么变成什么。binlog监听工具主要监听mysql产生的binlog,进行解析成我们易懂的格式,最后通过一定手段发送到下游,如消息队列

    • Databus
    • Canal
  • 分库分表工具

    • 增强版JDBC驱动(常见用法)

      以客户端jar包形式提供对JDBC的封装,客户端直接连接数据库

      • Sharding-JDBC、TDDL、Zebra
    • 数据库代理

      需要单独部署,客户端连接代理服务,代理服务负责和数据库打交道

      • Sharding-Proxy、MyCat
如何解决分库分表带来的问题 1.分布式唯一ID

单库单表时使用自增ID就可保证ID的唯一性,分库分表之后,一张表被拆分成多张表,自增ID无法保证唯一性,需要引入以下方案保证ID唯一性

  • UUID

    • 优点:本地生成,性能高
    • 缺点:
      • 更占存储空间,一般为长度36的字符串
      • 不适合作为MySQL主键,MySQL主键推荐使用单调递增的数字,主键使用的是聚簇索引,把相邻主键的数据放在相邻物理存储位置上,当主键单调递增时,只需简单的将数据追加到索引最后面即可
        • 无序性会导致磁盘随机IO(将数据插入之前已有的数据中间,插入位置所在的数据页不在内存中,可能需要先从磁盘读取到内存中,导致磁盘随机IO)、叶分裂(数据页的空间不足,移动大量数据)等问题
        • 普通索引需要存储主键值,如果主键索引的值更占用内存空间,导致普通索引B+树“变高”,IO次数变多
      • 基于MAC地址生成的算法可能导致MAC地址泄露
  • 雪花算法

    核心思想:通过一定的规则生成一个64位的long类型数字,除了最高位的1位不用之外,其他63位由三部分组成

    • 1bit:不用
    • 41bit:时间戳,可用69年
    • 10bit:工作机器id,可部署1024台服务器
    • 12bit:序列号,每毫秒可生成4096个ID,每秒即为409万
  • 号段模式

    数据库生成的方式:使用一个额外表的自增ID来作为分布式ID,因为ID都是由同一张表生成,所以可以保证全局的唯一性,缺点是每次使用分布式唯一ID都需要来读写这张表,当并发量大时,数据库存在严重的性能问题

    号段模式即在此基础上优化,将之前的每次获取分布式ID读写,优化成批量的方式,获取一批ID,放在本地缓存中,用完之后再申请下一批,大大降低数据库的读写压力

2.分布式事务

在分库分表之前,全部的表都在同一个库中,可以使用本地事务保证数据的正确性,引入分库分表之后,数据库表在不同的数据库中,无法使用本地事务,需要引入分布式事务保证数据的正确性

  • 2PC(Two Phase Commitment)两阶段提交 应用层面的处理

    • 核心思想:将事务分为两个阶段

      • 第一阶段:协调者首先询问所有的事务参与者是否可以执行事务的提交操作

      • 第二阶段:协调者根据所有参与者的返回结果决定是否提交操作

        • 全部的参与者都返回成功,则协调者向所有参与者发送事务提交请求
        • 否则协调者向所有参与者发送事务回滚请求
    • 优点:整理流程比较简单

    • 缺点:存在同步阻塞、协调者单点等问题

  • TCC(try confirm cancel) 数据库层面的处理

  • 核心思想:针对每个操作都有一个对应的确定和取消操作

  • 核心流程:

    • 主服务调用所有从服务的try接口进行业务检查和资源预留
    • 主服务根据所有从服务的返回结果决定是否提交事务
      • 所有从服务都返回成功,则主服务调用所有从服务的confirm接口进行事务确认提交操作
      • 否则主服务调用所有从服务的cancel接口进行事务取消并释放预留资源
  • 三阶段提交、本地消息表、事务消息…

3.保证数据的最终一致性

在实际的高并发业务中,一般不会使用强一致性的分布式事务,金融场景除外,更多通过一些手段保证最终一致性。常见手段有 回滚重试监控告警幂等对账等等,终极手段人工补偿

以外卖下单为例子:

整个用户下单的流程涉及到很多步骤,最核心的包括创建订单、扣减商品库存、核销优惠券、核销会员红包等等,如果其中有一环节失败,则导致整个下单流程失败,需要将其他的流程都进行回滚,以保证不会产生资损,否则可能用户下单失败,但是出现用户红包被扣掉的情况

为了避免网络抖动等情况导致回滚失败,一般会有回滚、重试的流程,但是重试一般有次数的上限(监控),如果重试多次还是失败,有可能是其他问题,比如代码bug。超过这个上限,还是回滚失败,则需要发送告警,人为介入排查,人工修复补偿这些数据。

对于订单的下游服务,例如库存和优惠券则需要做好接口的幂等,没有做好幂等则可能出现数据重复回滚的情况,造成数据的错误和资损。

广义上讲,保证最终一致性也属于分布式事务的一种,不使用强一致性的分布式事务来保证可能有以下原因:

  • 带来严重的性能损耗,导致下单的流程耗时增加,最终导致服务吞吐量下降,用户下单体验变差
  • 会引入额外的复杂度,开发和维护的成本比较高
  • 实际业务中由于部分成功导致数据不一致的场景发生的概率比较低

总结就是一个取舍的问题,目前大部分业务场景使用强一致性分布式事务的ROI并不高,因此一般不会选择强一致性事务,而是选择柔性事务,保证数据的最终一致性

4.跨库JOIN/分页查询

在单库单表时,全部数据都放一张表中,因此可以随意进行join和分页操作,但是如果进行分库分表,数据会分到不同的数据库和数据表上,导致原本进行分页的数据分到了不同的数据库中,导致跨库查询问题

目前业界主流的解决方案有以下几种:

  • 合适的分表字段(sharding key)

    分表字段的合理选择,要能保证绝大部分高频查询场景不会出现跨库查询问题

    实际业务中,分表字段选择合理,可避免95%~99%的跨库查询问题

  • 搜索引擎支持:ES

    将全量的数据冗余一份到ES中。使用ES支持复杂查询

    • 核心流程:

      • 使用ES查询出关键字段

      • 使用关键字段去数据库查询完整的数据

    • 注意点:

      • ES只存储需要搜索的字段,控制ES的大小,避免ES过大,导致性能和存储问题

      • ES只用于支持数据库难以查询的查询场景,如跨库查询、复杂搜索查询,这种查询不会太多,保证ES的整体压力不会太大

  • 分开查询,内存中聚合

    和 join 大同小异,区别是 join是数据库来做这个聚合操作,分开查询是应用层面做聚合操作

    不建议在数据库中使用 join操作,而是建议分开查询再去内存中聚合,因为数据库资源相对应用服务器更宝贵一点,且更容易成为链路中的瓶颈,因此尽量不要让数据库做太复杂的查询,避免占用更多的数据库资源

    • 核心流程:
      • 先查询A表的数据
      • 根据A表的结果查询B表
    • 注意点:
      • 查询出来的数据量
      • 占用内存情况
  • 冗余字段

    • 核心流程:A表查询需要B表的field1字段,则将B表的field1字段存储一份到A表上
    • 注意点:适用于只需要少量字段,则可以直接冗余
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/979482.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号