分库分表思考与平滑迁移

为什么要分库分表:
  • 分表:当单表数据量太大时,会极大影响sql执行的性能,会导致sql查询很慢。分表是把一个表的数据放到多个表中,比如用户信息表,用用户id来分表,每张表控制在200w数据量。那么操作时数据量就少,执行sql速度也会快。
  • 分库:单库的并发量有限(2000) ,当并发请求超过数据库的瓶颈,那么就需要进行分库,来保证数据库服务有健康的并发支持。
  • 分库分表后,数据库服务能承受的并发量增加,sql执行效率提升,同时分库分表也可用作灾备提升数据库服务可用性
设计高并发系统时,数据库层面怎么设计:
  • 当用户量剧增,数据库单表日增长量和单表数据量非常大,请求高峰期并发也很大时根据实际需要考虑分库分表
  • 数据库读/写QPS过高,数据库连接数不足:只分库不分表
  • 单表数据量过大,存储性能遇到瓶颈:只分表不分库
  • 连接数不足+数据量过大引起存储的性能瓶颈:分库分表
怎么算分表和分库的数量:
  1. 步骤:根据当前容量和增长量评估分库或分表个数->选择key(均匀)->分表规则(hash或range等)->执行(双写)->扩容(尽量减少数据移动)
  2. 分表:按数据增量+存储要求。以支线任务为例,日数据5w,随着业务扩展预计到8w/天。支线任务5年数据量:5 * 365 * 8=1.46亿,单表500w的话,需要32张表切分
  3. 分库:分库要考虑平时业务峰值QPS,还要考虑大促QPS,做好提前预估。支线任务查询的qps大概在3500数据库连接数,如果单库承载最高1000,那么可以拆分四个库。
垂直拆分和水平拆分:
  • 垂直拆分:将属性较多,一行数据较大的表,讲不同属性拆分到不同表中,降低表大小,达到性能提升的目的。
    • 垂直拆分后库(表)的特点是:
      • 每个库(表)结构不同
      • 每个库(表)的属性至少有一列交集
      • 所有库(表)的并集是全量数据
    • 垂直拆分的依据:
      • 长度较短,访问频率较高的属性放在一个表,为主表
      • 字段较长,访问频率较低的属性放在一个表,为扩展表
      • 经常需要一起访问的属性,放在一个表
      • 一般优先考虑主表、扩展表。如果属性过多,两种表可以有多个
    • 访问限制:
      • 应用需要同时访问主表和扩展表时,不要使用join,而是分两次查询
      • 在大数据高并发下,吞吐量和扩展性是主要矛盾。join更消耗数据库性能
      • join会让主表和扩展表耦合在一起(两个表必须在一个DB实例),不利于数据量大时拆分到不同的DB实例上。减少数据量,提升性能才是垂直拆分的初衷
    • 拆分原因:
      • 为何要将字段短、访问频率高的属性放到一张表。垂直拆分这样怎样提升性能
      • DB会把磁盘数据加载到内存buffer,缓存数据是以row为单位。在内存有限情况下,缓存短row,就能缓存更多数据。在DB内存buffer里缓存访问频率高的row,可以提升缓存命中率,减少磁盘访问
  • 水平拆分:一个表的数据弄到多个库多个表中,每个库的表结构一样。只不过每个库表里放的数据不同,所有库表的数据加起来就是全部数据。水平拆分的意义,就是将数据均匀放更多的库,然后用多个库来抗更高的并发,还有就是多个库的存储容量来进行扩容。
    • 拆分方法:
      • 范围法:根据数据量将数据水平切分到多个实例或多个表
        • 优点:切分策略简单,根据key按照范围,可以快速定位数据位置。扩容简单,容量不够加新表就行
        • 缺点:key要满足递增特性。数据量会不均匀,新增表在初期数据会比较少。请求量不均匀,一般来说新数据访问热度比旧数据高,会造成服务负载差异。
      • 哈希法:以key对实例数或分表数进行取模
        • 优点:切分策略简单,根据key按照范围,可以快速定位数据位置。数据量均匀,请求量均匀,只要key均匀,数据分布负载就会是均匀的
        • 缺点:扩容麻烦。容量不够,需要重新hash,数据迁移。如何平滑进行数据迁移需要解决
水平切分后非路由key属性查询方案:
对于非路由key的属性查询,由于不知道数据具体在哪个库(表),往往需要遍历所有库(表),性能下降。
  • 单行查询:除路由键外,还有少量其他属性查询
    • 映射法:建立一个索引表记录路由键和属性的映射关系,通过索引检索到对应路由键,定位相应的库(表),索引表属性较少可以容纳很多数据,一般不需要分库。如果数据量大,可以通过属性再进行分库分表。不足:会多一次数据库查询,性能下降一倍。
    • 缓存映射法:访问索引表性能过低,可以把映射关系放在缓存。通过属性查缓存拿到路由键,再定位相应的库(表)。如果缓存失效就扫全库获取对应映射,存入缓存。如果数据量大,可以根据属性进行缓存水平切分。不足:多一次缓存查询
    • 基因法:从属性中抽取基因,融入路由键。
  • 多key查询业务,多个属性存在前台在线查询需求:
    • 问题:数据量增大后进行分库分表,由于存在多key查询需求,用哪个字段切分成为了问题。比如A切分,那么B和C字段都需要遍历多库
    • 分析:首先要看哪些属性上有查询需求,分析需求的访问流量占比。一般业务需求分为两类:前台访问:用户侧,吞吐量大,服务要求高可用,用户对数据的访问一致性要求高,后台对订单的访问一致性要求相对较低,可以接受一定时间的延时。后台访问:运营测业务需求各异,基本上是批量分页的查询,由于是内部系统,访问量很低,对可用性的要求不高,对一致性的要求也没这么严格,允许秒级甚至十秒级别的查询延时。
    • 前后台拆分:
      • 运营测业务需求各异,基本是批量分页的访问,计算量大,返回数据量大,消耗数据库性能。
      • 并且后台访问往往会在数据库建各种索引提升性能,会占用大量内存。影响用户侧业务的查询和写入性能。如果前后台公用一批服务和一个数据库,可能导致后台的几个少数请求批量查询,导致数据库cpu偶尔高负载,影响前台正常用户访问
      • 前后台分离,前台架构不变,后台独立抽取db服务来支持。实时性要求不高的数据,可以用过mq或异步同步数据。数据非常大的数据,可以使用es搜索引擎,或大数据处理。
数据平滑迁移:需要做数据迁移的场景:大表底层表结构变更、分库个数变换、底层存储介质变化(切换数据库)
停机迁移:
  • 停机公告,在通知时间段进行服务停机
  • 通过离线数据迁移工具,进行数据迁移
  • 恢复服务,修改对应配置,不同需求升级不同服务
不停机迁移:
追日志法:数据迁移前,上游业务应用通过旧的服务访问旧数据
  • 一、服务进行升级,记录对旧库上的数据修改日志(数据的增、删、改)。不需要详细记录数据,主要记录被修改的库,被修改的表,被修改的唯一主键。具体新增了什么行,修改后的数据格式不记录,
    • 优点:这样达到不论业务细节如何变化,日志都是固定的,保证方案通用性。写接口是少数接口,代码改动小。升级只是增加日志写入,对业务没有影响。
  • 二、研发一个数据迁移工具,进行数据迁移,把旧库数据转移到新库。
    • 整个过程中依然是旧库提供服务,任何时间发现数据问题,都可以把新库数据干掉重来。可以限速迁移,技术没有时间压力
    • 数据迁移完后,不能立即切换到新库。因为迁移过程,旧库仍然提供服务,数据随便在变化。新旧库数据不一致。
  • 三、研发一个读取日志并迁移的工具,把步骤二迁移数据过程产生的差异数据追平
    • 该工具功能:读取日志,得到哪个库、表、主键发生变化。把旧库中对应主键记录读出来,把新库中对应主键记录替换掉。达到日志重放
    • 日志重放后,不能立即切新库,还是因为旧库有数据变化。这个日志读取和重放的过程是无限逼近的过程
  • 四、在持续重放日志,追平数据过程中,研发数据校验工具,将新旧库数据进行对比,直到数据完全一致
  • 五、在数据比对完全一致后,将流量迁移到新库,新库提供服务,完成迁移。
  • 步骤四的数据一直是99.9%的一致,也很正常。可以做一个秒级的旧库只读,等日志重放程序追上数据后,再进行切库
posted @ 2025-04-16 18:20  难得  阅读(51)  评论(0)    收藏  举报