数据库分库分表大全

数据库分库分表

关系型数据库本身容易成为系统瓶颈,单机存储量、连接数、处理能力有限,当单标数据量达到1000万或大于100G时,即使读写分离、优化索引,性能还是明显下降,此时需要考虑数据切分。
数据库分布式的核心就是数据切分以及切分后数据的定位、整合。将数据切分到多个数据库中,通过扩展主机的数量缓解单一数据库瓶颈。

1.数据切分

1.1 垂直切分

垂直分库

根据业务系统拆分,将耦合性低(关联度低)的表存储在不同的数据库中,类似于按照业务将大系统拆分多个子系统,做法与微服务治理相似,每个微服务依赖独立的数据库。例如拆分用户、订单、优惠、支付等

垂直分表

根据表字段拆分,例如将访问频率低、修改频率高的字段单独拆分出来,提高读写操作。大表拆小表可以避免分页、且方便开发维护,Mysql是将叶子节点设置为页的大小一次性加载的,如果大表的字段过多可能会导致分页。

优点

按照业务拆分,系统层级解耦,业务清晰。
方便开发维护,根据业务进行维护、监控、扩展等。
高并发场景下,垂直切分一定程度提升IO性能/连接数,缓解单机硬件瓶颈。

缺点

分布式事务处理复杂
部分表无法join,增加开发难度
依然存在单表数据量大的问题(需要水平切分)。

1.2 水平切分

当一个应用垂直拆分后,单库单表的数据量依然巨大,依然存在读写性能瓶颈,此时可以考虑水平切分。

库内分表

库内分表解决了单一表数据量过大的问题,但没有将表分不到不同机器的库上,无法解决单机硬件瓶颈,如CPU/IO/内存连接数等。

分库分表

既解决了单一表数据量过大的问题,且将表分不到不同机器的库上,有效缓解单机硬件资源瓶颈。

优点

不存在单库单表数据量过大、高并发瓶颈问题
应用端改造小,不需要拆分业务

缺点

分布式事务处理复杂
跨库join关联查询性能较差(开启FEDERATED引擎,且以此引擎建立中间表,CONNECTION=‘mysql://root:root@localhost:3306/b/school’)
数据扩展、维护难度大

1.2.1 切分方式

数值范围

优点

单表大小可控
水平扩展方便,扩容时只需添加节点,无需对原有分片迁移
使用分片字段范围查询时,快速定位分片,有效避免跨分片查寻

缺点

热点数据成为瓶颈。例如按照时间分片,存储最近时间的分片可能会被频繁的读写,成为热点数据,而存储历史数据的分片很少被读写。
不使用分片字段查询,需要跨分片查询

Hash取模

优点

数据分散相对均匀,不易出现热点数据

缺点

扩容复杂,需要迁移原有分片数据(hash取模,模数变了)
范围查询时,需要跨分片查询
不使用分片字段查询,需要跨分片查询

2.分库分表带来的问题

2.1 事务一致性

2.1.1 分布式事务

指事务操作位于不同的节点上,需要保证事务的ACID特性。
例如下单场景,订单和库存位于不同的节点上,涉及分布式事务问题。

本地消息表

消息表与业务表位于同一数据库,能够利用本地事务保证对两个表的操作满足事务特性,并利用消息队里保证最终一致性。

  1. 分布式事务一方将数据写入业务表后写入本地消息表,本地事务保证一定可以写入本地消息表。
  2. 写入本地消息表后将小发送到MQ消息队列,发送成功,将本地消息表删除,否则重发。
  3. 分布式事务另一方监听消息队列,读取消息执行操作。
2PC

两阶段提交(Two Phase Commit,2PC) ,通过引入协调者(Coordinator)来协调参与者,并最终决定这些参与者是否真正提交事务。

执行过程

  1. 协调者询问参与者,参与者返回事务执行结果
  2. 如果所有参与者返回执行结果成功,协调者通知所有参与者提交事务,否则回滚

存在问题

同步阻塞:所有参与者需要等待最后参与者的回馈结果
单点问题:协调者出现问题,会有 严重影响,尤其是第二阶段,所有参与者都要等待协调者通知
数据不一致:第二阶段通知提交出现网络问题,部分参与者提交事务
太过保守:一个参与者出现问题,所有参与者都要回滚,没有容错机制

2.1.2 最终一致性

对于性能要求很高,但对实时一致性要求不高的系统,可采用事务补偿的方式。一些常见的做法有:基于日志对比、基于标准数据源同步对比。

2.2 跨节点关联查询join问题

表冗余(全局表)

将系统中所有模块都依赖的一些表,避免跨库join查询,在每个库中都保留一份,一般这些表修改频率较低,不用担心一致性的问题。

字段冗余

一种典型的反范式设计,以空间换时间。例如订单表中存储用户部分信息。

数据组装(业务层)

在业务层分多次查询组装数据。

ER分片

将有关联度额数据分散在同一分片上,如根据用户id分片(订单中用户id外键),同一用户及其下的订单分散到同一分片。

2.3 夸节点的分页、排序、函数等问题

当分片规则是按照数值范围切分、且排序规则按照分片字段排序,可以快速定位分片。
其他情况都比较复杂,为了保证准确性,需要将所有节点的前N页数据排序后汇总,然后再整体排序后丢弃绝大部分数据,只返回第N页数据,如果N的数值很大,则会造成系统资源、IO/CPU/MEM的极大浪费。Elasticsearch等搜索引擎的深度分页问题与此类似。
当使用Max/Min/Sum/Count等函数时,需要在所有节点执行函数,汇总后再次计算返回结果。

2.4 全局主键

UUID

方案简单、无网络开销,但32位16进制的UUID会占用大量存储空间,且因UUID的无序性,建立索引、及根据索引查询性能较差。

数据库唯维护主键表

如利用数据库唯一索引等。

Snowflake分布式自增ID算法

41位时间戳+10位机器ID+12位序列号,MongoDB的主键生成策略与此类似

毫秒数在高位,生成的ID整体上按时间趋势递增;不依赖第三方系统,稳定性和效率较高
强依赖机器时钟,如果时钟回拨,则可能导致生成ID重复

Redis原子操作生成

自定义规则 + INCR key (对存储在指定key的数值执行原子的加1操作)
如年月日时间戳 + 原子操作自增生成主键,此策略有序且易读,例如用户注册、用户下单等场景,年月日表示注册、下单日期,自增数量表示当日注册数量、下单数量。
依赖Reids,当Redis资源不够时,对主键生成有较大影响。

2.4 数据迁移、扩容

当使用数值范围分片时,扩容只需增加节点即可,无需对原有分片进行迁移
当使用数值取模分片时,扩容需要对原有分片数据重新取模分片。

3.何时考虑切分

3.1 能不切分尽量不要切分
3.2 数据量过大,正常运维影响业务访问
3.3 数据快速增长
3.4 业务发展,需要对某些字段垂直拆分
例如用户表里面有最后登录时间(频繁修改)、和用户信息(text类型,占用较大空间且读取频率较低),此时可以考虑将最后登录时间拆分出去、将用户信息拆分出去。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值