分库分表:提升数据库性能的必备技巧

我们为什么需要分库分表

在讨论分库分表之前,我们需要明确为什么要进行这样的操作。我们从两个维度来分析:为什么要分库,为什么要分表。

1. 为什么要分库

  1. 磁盘存储:当业务量剧增,单机MySQL的磁盘容量可能会达到极限。通过将数据拆分到多个数据库中,可以大幅降低单库的磁盘使用率。
  2. 并发连接支撑:数据库连接数是有限的。在高并发场景下,单机MySQL无法承受大量请求,可能会出现“too many connections”报错。为了应对高并发,可以采用微服务架构,将不同模块拆分为多个应用,并将单一数据库拆分为多个功能模块的数据库(如订单库、用户库、商品库),以分散读写压力。

2. 为什么要分表

当单表数据量非常大时,存储和查询性能会遇到瓶颈。即使做了很多优化,仍然可能无法提升效率。这时需要考虑分表。一般来说,当单表数据量达到千万级别时,就需要分表。

这是因为,即使SQL命中了索引,如果表的数据量超过千万,查询速度也会明显变慢。B+树索引结构的高度会随着数据量的增加而增加,从而导致查询速度变慢。假设一棵高度为3的B+树,可以存放约两千万条记录。如果B+树高度达到4层,查询时需要多次查找磁盘,SQL执行速度会变慢。

什么时候考虑分库分表?

对于MySQL的InnoDB存储引擎,单表最多可以存储10亿级数据,但性能会很差。阿里巴巴《Java开发手册》建议:

单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。

但是我们不应等到数据量达到500万才开始分库分表,而是应该提前规划。如果估算3年后,表数据量不会达到500万,则不需要分库分表。如果持续发展,仍需考虑分库分表。一般流水表、用户表等类型的业务表需要考虑分库分表,而配置类的表则不需要。

如何选择分表键

分表键即用来分库/分表的字段,如用户ID、时间、地区等。数据库表拆分时,需先找到业务的主题。例如企业客户信息表可以使用客户号作为分表键,以将同一客户的信息存储在一个表中,避免全表路由。

非分表键如何查询

分库分表后,有时需要通过非分表键来查询。例如按userId分表,但用户登录时需要根据手机号查询用户信息。非分表键查询方案包括:

  1. 遍历所有表(不推荐)
  2. 将用户信息冗余同步到ES,通过ES查询(推荐)
  3. 基因法:如订单号包含客户号,通过订单号解析客户号(不适用于手机号)

分库后,事务问题如何解决

分库分表后,本地事务无效,需要使用分布式事务。常用分布式事务解决方案包括:

  • 两阶段提交
  • 三阶段提交
  • TCC
  • 本地消息表
  • 最大努力通知
  • saga

跨节点Join关联问题

分库分表后,跨库join操作可以通过以下方式解决:

  1. 字段冗余:将关联字段放入主表,避免关联操作。
  2. 全局表:基础表在每个数据库中均保存一份。
  3. 数据抽象同步:定时将关联表数据同步,生成新表(借助ETL工具)。
  4. 应用层代码组装:分开多次查询,代码层进行字段拼装。

order by、group by等聚合函数问题

跨节点的countorder bygroup by等聚合函数问题,可以分别在各个节点上得到结果后,再在应用程序端合并。

分库分表后的分页问题

方案1(全局视野法)

在各个数据库节点查询结果后,在代码端汇聚再分页。优点是精准返回所需数据,缺点是返回数据多,增大网络传输,造成空查。

方案2(业务折衷法-禁止跳页查询)

业务妥协,只允许上一页和下一页,不允许跳页查询。查询第一页时,与全局视野法相同。下一页时,将当前最大创建时间传递,每个节点查询大于该时间的一页数据,汇总后返回。

分库分表选择哪种中间件

常见分库分表中间件包括:

  • Sharding-JDBC:当当开源,建议使用
    在这里插入图片描述

  • cobar:阿里巴巴产品,不支持读写分离

  • Mycat:建议使用,功能强大

  • Atlas:360开源产品,不支持分布式分表

  • TDDL(淘宝):阿里巴巴产品,不支持读写分离

  • vitess:谷歌产品,支持高并发,使用较少

如何评估分库数量

单库处理记录能力决定分库数量。一般单库超过5000万记录时,DB压力大。分库数量应在4~10个之间,一般建议10个库以下,便于管理。

垂直分库、水平分库、垂直分表、水平分表的区别

  • 水平分库:以字段为依据,将数据拆分到多个库中。
  • 水平分表:以字段为依据,将数据拆分到多个表中。
  • 垂直分库:以表为依据,将不同表拆分到不同库中。
  • 垂直分表:以字段为依据,将表中字段拆到不同表(主表和扩展表)中。

分表要停服吗?不停服怎么做?

分表不需要停服。具体步骤如下:

  1. 编写代理层,加开关控制访问新旧DAO,灰度发布期间访问旧DAO。
  2. 发版全量后,开启双写,同时写入新旧表,记录新表ID起始值,旧表中小于该值的数据为存量数据。
  3. 通过脚本将旧表存量数据写入新表。
  4. 停读旧表改读新表,保持双写一段时间。
  5. 一段时间后,无业务问题,停写旧表。
  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖的小熊猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值