数据库分库分表

news/2024/7/8 2:09:32

文章目录

      • 为什么要分库分表?
      • 数据切分
        • 垂直切分
        • 水平切分(每个表的结构相同)
          • 范围拆分
          • 取模拆分(一般为业务主键)
      • 分库分表带来的问题
        • 数据倾斜问题
        • 热点问题
        • 事务问题
        • 聚合查询问题
        • 分页问题
        • 非分区业务查询
      • 分库分表实现或工具
      • hash分库分表在线处理方案
        • 水平扩库

为什么要分库分表?

随着业务的发展,单库单表难以满足我们对性能的要求,在分库之前,可能我们经历了sql调优、索引优化、数据库参数优化、读写分离、使用分布式缓存来降低我们数据库的压力。但是随着业务再一步扩大,以上的方法可能就不适用了;
基本上就以下几种情况

  • IO瓶颈
    • 并发量过大,查询磁盘次数过大,特别是缓存命中率很低的时候更严重(分库)
    • 表太大,一次查询磁盘操作过多(分库、或分表,单表容量控制在2000万以内,根据业务增长预估提前规划)
    • 进出数据比较多,网络IO较大 (分库)
    • 数据库参数未优化,比如可以开启组提交来降低IO操作
    • 缓存设置过小
  • CPU瓶颈
    • 大SQL查询导致cpu彪高,单独从库,或者走大数据
    • 函数操作,特别是聚合、排序、分组、关联查询,能在程序中处理最好在程序中处理
    • 未走索引,优化sql,或者添加索引
  • 存储瓶颈
    • 比如阿里单实例最大3TB,业务数据过多无法存储(分库)
    • 一般不建议单实例存储太多的数据,存储数据多,代表着表多,表多了磁盘寻址也是个性能损耗
  • 连接数瓶颈
    • 并发量过大,链接数不够用(分库)
    • 死锁导致堆积,也会带来cpu瓶颈(实时监控与治理)
    • 慢查询,导致连接消耗也会带来cpu瓶颈

我还遇到一个因微服务拆分带来的分库需求,由单体拆出去以后多个项目共用一个数据库,多源写导致数据操作难以控制。

数据切分

既然要分库分表,那一定会涉及到数据的切分,一般有两种模式,垂直切分,和水平切分;

垂直切分

一种是分库

  • 将不同的系统模块的表拆解到不同的库中;
  • 将同一个系统模块中根据数据量或者业务规则拆到不同的库中(程序关联性大的放在一起);
  • 将不同的租户数据拆到不同的库里(表结构一样)

举个例子:比如我们授信系统,拆成了两个库auth 和auth2

image-20221214111216460

  • auth库里是用户授信记录
  • auth2库是风控相关的操作记录

一种是分表

  • 将字段特别多的表拆分为多个表;
  • 将大字段拆出(单行超过700多个字节的时候,会拆到额外的存储区域)

举个例子:

image-20221214112842853

还有我们在做业务的时候,也会不自觉的采用分表,比如我们的工单表和工单扩展表

水平切分(每个表的结构相同)

这个主要是解决单表数据量过大的问题,比如说,10亿的用户,单表存储不现实的吧,一般会拆成10张表;

水平拆分,一般由程序进行控制,一般有以下几种方式:

范围拆分
  • 按照id范围,比如id[11000万)一张表,id[1000万2000万)一张表,以此类推

    • 增删数据库实例方便,全量顺序查找方便
  • 按照创建时间,比如一个月或者一年一张表

    • 业务操作都得带着时间,比如查询最近一个月的账单,可以拆分出来查哪些库表
    • 交易订单、银行流水等按照时间排序的
  • 按照某个分类比如租户分表

    • 业务操作都得带租户,按租户查找方便,按租户新增库表方便

image-20221214144512589

优点:

  • 扩/缩容方便(增加或删除库表方便)
  • 归档方便

缺点:

  • 流量倾斜,热点数据不均衡(热点一般都是新进来的数据)
  • 某个库表压力过大
取模拆分(一般为业务主键)

note: 分表尽可能使用比较均匀的字段,同时要考虑到用户的行为习惯,比如手机号码,大部分人不会选择尾号为4的手机号,如果按尾号分,很容易出现尾号4的库表访问量少的问题,同时还有一个身份证,也存在同样的问题(x相对较少),如果必须要用这两个,建议hash以后再分

  • 如果是整数,直接进行取模,比如 id/10 路由到不同的库表中;
  • 如果是字符串,可以先hash,然后再取模(避免流量倾斜)

image-20221214144908550

优点:

  • 数据分散相对均匀
  • 数据库表的负载压力均衡

缺点:

  • 扩展能力差(增加库表,数据需要进行迁移)
  • 平衡难以把控,一次规划好,浪费资源,业务增量大,迁移麻烦

扩展能力差的问题,可以用一次性hash的方式来降低迁移的难度;

分库分表带来的问题

数据倾斜问题

  • 业务与倾斜要选一个平衡,比如电商类,在双十一和双十二的订单里会很大,避免不了的倾斜;
  • 数据倾斜不一定会出现热点问题;

热点问题

  • 热点问题,通过取模或一致性hash解决

事务问题

  • 一般互联网应用中不建议使用事务,建议业务补偿,保证最终一致性;
  • 分库时,尽可能将一个事务内的操作放入到一个库,避免跨库事务;
  • 实在避免不了,还必须用事务,用分布式事务解决
    • 本地消息表
    • TCC
    • RocketMQ的事务消息
    • 事件中心
    • saga

聚合查询问题

  • 一般是统计报表或数据分析,需要用到聚合函数或join,这种情况下不建议在业务系统里;
  • 如果是业务系统使用,一般会冗余一些字段,减少聚合查;
  • 一般互联网应用不建议聚合查,代码层进行组装,降低数据库的压力;

image-20221214151318987

  • 通过中间件将各分库的数据同步到聚合库,这个聚合库可以是hbase或者Cassandra里,也可以是oracle,当然也可以是mysql;
  • 分库以后各表的主键id建议使用分布式id,使用雪花算法yyyyMMddHHmmssSSS+机器位+内存序列,能保证数据相对有序,毫秒级的数据能落入到一个数据页中,性能损耗不大,根据实际需求,可以把自己定义机器位的大小和内存序列的大小;

分页问题

  • 如果是以分区主键来查询业务,分页问题就不是问题;
  • 如果是全局查,且分页,建议使用聚合库做查询分页,操作接口再到具体的分库分表上;
    • 根据业务属性增加一层处理机制,比如审核类的,我们分页查找一般都是进行中的,审核完的,只需要精确查找就行;
    • 我们抽出一个处理中的表,在这里进行业务操作处理;
    • 处理完的一般都是精确查找,如果需要业务分析,大数据处理
  • 如果不使用聚合库,只能使用全局查找,先把符合条件的数据查到,然后在内存中聚合,然后再分页,如果数据量大,oom了,同时对于数据库的压力也不小;

非分区业务查询

比如我们使用用户的uuid分表,然后我们要通过手机号mobile查用户的信息,怎么办?总不能来个全库扫描吧。那怎么办?

  • 关系映射

    • 将映射关系存储到一张索引表,通过旁路缓存查询
    • 直接永久缓存起来(需要考虑缓存失效的问题,缓存中没有怎么处理?)

    当通过mobile查询的时候,先查到uuid再去具体的表里查

  • 通过手机号生成uuid

    • 在往用户表插入数据的时候,通过一个固定方法把mobile转成uuid(需要考虑去重,极小的概率会重复)
  • 基因法(高低位存储)

    • 自己设计一个id生成器生成uuid,高位存储时间戳(20221201221126945)+3位机器位+3位内存毫秒级自增序列,
    • 低位(2~3位)存储分区规则,分区规则由mobile生成

分库分表实现或工具

  • 直接写mybatis插件自己实现
    • 通过库名和表名替换实现路由
  • TDDL(淘宝)
    • https://github.com/alibaba/tb_tddl 2012年后未更新
    • jar包的方式供应用调用,可以理解为自己实现的增强版
    • 通过规则匹配后做表名替换,然后将sql转发过去
  • Cobar中间件(阿里巴巴)
    • https://github.com/alibaba/cobar 2018年以后未更新
    • cobar是在amoeba基础上进化的版本
    • 代理方式
  • mycat 中间件
    • 基于ocbar开发的
    • http://mycatone.top/ 现在社区在推2.x的版本
    • 也是基于代理的方式,所有的规则都配置在mycat里;
  • ShardingSphere-JDBC 和ShardingSphere-Proxy
    • https://shardingsphere.apache.org
    • ShardingSphere-JDBC 仅支持java,低损耗、无中心化的工具
    • ShardingSphere-Proxy(代理)

大部分都是使用ShardingSphere-JDBC

hash分库分表在线处理方案

水平扩库

note: 如果生产上能对这个库的写拦截10秒左右最好,数据无损

中间件+程序改造

比如dts同步到kafka,然后消费kafka处理

image-20221214154051298

  1. 将最近一次备份库恢复;
  2. 将数据按分片规则清洗到不同的库里;
  3. 清洗完成后,回放备份时间点以后的binlog;
  4. 将binlog根据分片规则回放到不同的实例里;
  5. 确保同步是准实时;
  6. 将写入的数据缓存到分布式缓存里5分钟(确保时差内可以同步过去);
  7. 应用依次发布(新应用发布以后,老应用还在写数据,新应用优先从缓存里读);

从库机制

image-20221214154139996

  • 先给要分库的库挂对应数量的从库,比如要分10个库,挂10个从库

  • 同时申请10个VIP挂先挂指向主库db_user上

  • 增加路由规则,uuid%10=0的走u0.vip,其他的类似

  • 虽然路由到了不同的vip上,但是这个vip都指向了主库;

image-20221207180745831


http://lihuaxi.xjx100.cn/news/413450.html

相关文章

通过地址偏移访问和修改类的成员变量

假设有如下类: class Test { public:int age { 100 }; }有下列两种方式访问和修改age字段。 方法一: 通过原始的地址偏移方式 Test test; // 还可以这样计算offset: // int Test::* age_p = &Test::age; // int offset = *(int*)&age_p; int offset = (size_t)&…

携程季报图解:营收69亿同比增29% 净利为2.45亿

雷递网 雷建平 12月15日携程集团有限公司(纳斯达克:TCOM;香港联交所:9961)今日发布财报。财报显示,携程2022年第三季度营收为69亿元,同比增长29%;净利润为2.45亿元;经调整…

设置视频的亮度

//设置MPEG压缩的P帧间隔,取3 HVFSet(m_hVFDrv, m_bStream, VF_INDEX_VID_BINTERVAL, PINTERVAL_DEFAULT); //设置视频的制式,采用PAL制式 HVFSet(m_hVFDrv, m_bStream, VF_INDEX_VID_MODE, VF_FLAG_VID_PAL); //设置视频的输入源的类型…

数据结构-哈希表的查找

目录 哈希函数的构造方法1、直接定址法⭐2、除留余数法3、数字分析法哈希冲突解决方法1、开放定址法线性探测法例成功查找ASL计算不成功查找的情况平方探查法2、拉链法例成功查找ASL计算不成功查找ASL计算开放定址法和拉链法总结不同关键字而具有相同哈希地址的这种冲突称为 同…

JAV spi 和扩展方式

SPI 机制(Service Provider Interface)其实源自服务提供者框架(Service Provider Framework,参考【EffectiveJava】page6),是一种将服务接口与服务实现分离以达到解耦、大大提升了程序可扩展性的机制。引入…

eslint Parsing error: The keyword ‘export‘ is reserved

报错 原因 ECMAScript modules(import/export) 是 es6 的语法。 根据 eslint 官方文档 Configure language options ,eslint 默认使用 es5 语法: 解决 要让 eslint 知道我在使用 es6 的 modules 语法。有下面几种方法: 设置 env 为 es6&am…

Kubelet v1.25.x源码——SecretManager

1. 环境说明 Kubernetes源码版本:remotes/origin/release-1.25 Kubernetes编译出来的Kubelet版本:Kubernetes v1.24.0-beta.0.2463ee7799bab469d7 Kubernetes集群实验环境:使用Kubernetes v1.25.4二进制的方式搭建了一个单节点集群 K8S 单节…

什么是零拷贝, 从 Java 到 Netty

前言 零拷贝是老生常谈的话题了, 不管是Kafka还是Netty都用到了零拷贝的知识, 本篇着重讲解了什么是零拷贝, 同时在Java和Netty中分别是怎么实现零拷贝的 什么是零拷贝 零拷贝是指计算机在执行IO操作的时候, CPU不需要将数据从一个存储区复制到另一个存储区, 进而减少上下文切…