分库分表就能无限扩容吗

随着业务发展,数据库成为性能瓶颈。文章探讨了从单体应用到RPC服务,再到分库分表的演进,指出分库分表无法解决所有扩容问题,因为会遭遇数据库连接数限制。为解决此问题,引入了单元化概念,通过限制每个应用仅连接部分数据库,避免连接数过多。单元化需要确保请求与特定数据库的对应关系,实现扩容的同时保持数据库连接的可控性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

像我这样的菜鸟,总会有各种疑问,刚开始是对 JDK API 的疑问,对 NIO 的疑问,对 JVM 的疑问,当工作几年后,对服务的可用性,可扩展性也有了新的疑问,什么疑问呢?其实是老生常谈的话题:服务的扩容问题。

正常情况下的服务演化之路

让我们从最初开始。

  1. 单体应用 每个创业公司基本都是从类似 SSM 和 SSH 这种架构起来的,没什么好讲的,基本每个程序员都经历过。
  2. RPC 应用 当业务越来越大,我们需要对服务进行水平扩容,扩容很简单,只要保证服务是无状态的就可以了,如下图:

当业务又越来越大,我们的服务关系错综复杂,同时,有很多服务访问都是不需要连接 DB 的,只需要连接缓存即可,那么就可以做成分离的,减少 DB 宝贵的连接。如下图:

我相信大部分公司都是在这个阶段。Dubbo 就是为了解决这个问题而生的。

如果你的公司产品很受欢迎,业务继续高速发展,数据越来越多,SQL 操作越来越慢,那么数据库就会成为瓶颈,那么你肯定会想到分库分表,不论通过 ID hash 或者 range 的方式都可以。如下图:

这下应该没问题了吧。任凭你用户再多,并发再高,我只要无限扩容数据库,无限扩容应用,就可以了。

这也是本文的标题,分库分表就能解决无限扩容吗?

实际上,像上面的架构,并不能解决。

其实,这个问题和 RPC 的问题有点类似:数据库连接过多!

通常,我们的 RPC 应用由于是使用中间件进行访问数据库,应用实际上是不知道到底要访问哪个数据库的,访问数据库的规则由中间件决定,例如 sharding JDBC。这就导致,这个应用必须和所有的数据库连接,就像我们上面的架构图一样,一个 RPC 应用需要和 3 个 mysql 连接,如果是 30 个 RPC 应用,每个 RPC 的数据库连接池大小是8 ,每个 mysql 需要维护 240 个连接,我们知道,mysql 默认连接数是 100,最大连接数是 16384,也就是说,假设每个应用的连接池大小是 8 ,超过 2048 个应用就无法再继续连接了,也就无法继续扩容了。

注意,由于每个物理库有很多逻辑库,再加上微服务运动如火如荼, 2048 并没有看起来那么大。

也许你说,我可以通过前面加一个 proxy 来解决连接数的问题,实际上,代理的性能也会成为问题,为什么?代理的连接数也是不能超过 16384 的,如果并发超过 16384,变成 163840,那么 proxy 也解决不了问题。

怎么办?让我们再看看上面的架构图:

我们发现,问题是出在“每个 RPC 应用都要连所有的库”,导致扩容应用的同时,每个数据库连接数就要增加。就算增加数据库,也不能解决连接数的问题。

那怎么办呢?

单元化

单元化,听起来高大上,通常在一些 XXX 大会上,分享“关于两地三中心”,“三地五中心”,“异地多活”等等牛逼的名词的时候,单元化也会一起出现。

这里我们不讨论那么牛逼的,就只说“数据库连接数过多” 的问题。

实际上,思路很简单:我们不让应用连接所有的数据库就可以了。

假设我们根据 range 分成了 10 个库,现在有 10 个应用,我们让每个应用只连一个库,当应用增多变成 20个,数据库的连接不够用了,我们就将 10 个库分成 20 个库,这样,无论你应用扩容到多少个,都可以解决数据库连接数过多的问题。

注意:做这件事的前提是:你必须保证,访问你这个应用的 request 请求的数据库一定是在这个应用的。s

换个说法,当用户从 DNS 那里进来的时候,就知道自己要去那个应用了,所以,规则在 DNS 之前就定好了,虽然这有点夸张,但肯定在进应用之前就知道要去哪个库了。

所以,这通常需要一个规则,例如通过用户 ID hash,由配置中心广播 hash 规则。这样,所有的组件都能保持一致的规则,从而正确的访问到数据库。如下图:

到这里,我们终于解决了无限扩容的问题。

最后

本文从单体应用开始,逐步讲述了一个正常后台的演进历程,知道了分库分表并不能解决“无限扩容” 的问题,只有单元化才能解决这问题。而单元化则带来更多的复杂性。但是好处不言而喻。

单元化带来的更多的思路。

有了单元化,解决了无限扩容的问题,但是我们还没有考虑单点的问题,即服务的可用性。要知道,我们这里的数据库都是单点的。

<think>好的,用户现在问的是“为什么要做分库分表?怎么做?”,之前他们已经问过分布式ID生成方式,现在这个问题可能是在进一步探讨分布式系统中的数据存储问题。我需要先明确用户的需求,他们可能是在设计一个高并发的系统,遇到了数据库性能瓶颈,或者想要提前了解分库分表的知识来规划架构。 首先,回答“为什么要做分库分表”时,应该从性能瓶颈、数据量过大、高并发访问、可用性等方面入手。用户可能已经遇到了单库单表的性能问题,或者预期数据增长很快,需要提前规划。这里需要强调单库单表的限制,比如连接数不够、磁盘IO压力大、查询速度变慢等。 然后是“怎么做”,这部分需要结构清晰,分步骤讲解。可能需要包括分库分表的方式(垂直和水平拆分)、分片策略(范围、哈希、一致性哈希等)、中间件选择(如ShardingSphere、MyCat)、数据迁移和扩容问题,以及带来的挑战比如分布式事务、跨库查询等。 用户可能还关心实际操作中的注意事项,比如如何选择分片键,如何避免热点问题,扩容时如何平滑迁移数据。此外,他们可能想知道有没有什么工具或框架可以简化分库分表的工作,或者是否有成功案例可以参考。 需要注意的是,用户之前的问题是关于分布式ID生成的,所以分库分表后ID生成可能会更复杂,可能需要结合之前提到的Snowflake或号段模式来确保ID的唯一性,这点可以适当联系起来,帮助用户形成完整的知识体系。 另外,用户可能对分库分表的优缺点不太清楚,需要指出虽然分库分表能解决扩展性问题,但也会带来复杂性,比如跨库join困难,事务管理复杂,维护成本增加等。需要权衡利弊,是否真的有必要进行分库分表,或者是否有其他优化手段如读写分离、缓存等可以先尝试。 最后,可能需要给出一些实际的应用场景,比如电商平台的订单表分库分表,用户量大的社交平台分库分表,帮助用户更好地理解何时以及如何实施分库分表。</think>以下是关于分库分表的详细介绍,从核心原因到实施方法,结合场景和案例进行说明: --- ### **一、为什么要做分库分表?** #### **1. 解决单机数据库的性能瓶颈** - **存储瓶颈**:单机磁盘容量有限(如MySQL单表数据量超过5000万行,性能明显下降)。 - **计算瓶颈**:高并发查询导致CPU过载(如电商大促时每秒数十万请求)。 - **连接数瓶颈**:单机数据库最大连接数有限(如MySQL默认151连接,超出后拒绝服务)。 #### **2. 提升系统扩展性** - 单机数据库无法通过无限升级硬件来应对数据量增长。 - 分库分表允许通过增加节点横向扩展,支持业务持续增长。 #### **3. 提高可用性与容灾能力** - 单机故障会导致全库不可用,分库分表后故障影响范围缩小(如仅部分分片受影响)。 - 天然支持多机房部署,实现异地容灾。 #### **4. 优化数据管理效率** - 按业务拆分后,不同库表可独立维护(如订单库和用户库分开备份、升级)。 --- ### **二、分库分表的核心策略** #### **1. 垂直拆分** - **分库**:按业务模块拆分(如订单库、用户库、商品库)。 **场景**:不同业务团队独立管理数据库,减少跨库事务。 - **分表**:将宽表拆分为多个字段较少的表(如订单表拆分为订单基础表 + 订单详情表)。 **场景**:减少单行数据大小,提升查询效率。 #### **2. 水平拆分** - **分库分表**:将同一表的数据按规则分散到多个库/表中。 **分片键选择**: - **用户ID**:适合用户维度查询(如用户订单)。 - **订单ID**:适合全局唯一ID查询(如订单详情页)。 - **时间**:适合按时间范围查询(如日志表按月分片)。 --- ### **三、分库分表的具体实现方法** #### **1. 分片策略** | **策略** | **原理** | **优缺点** | **场景** | |----------------|-----------------------------------------|----------------------------------------------|----------------------------| | **范围分片** | 按分片键范围划分(如1~100万在DB1,100万+在DB2) | 易扩容,但数据分布不均(可能热点集中在最新分片) | 时间序列数据(日志、订单按年分片) | | **哈希取模** | 对分片键哈希取模(如 `user_id % 4`) | 数据分布均匀,但扩容需迁移大量数据 | 用户ID、订单ID等离散值分片 | | **一致性哈希** | 哈希环动态分配节点,减少扩容时数据迁移量 | 扩容灵活,但实现复杂 | 动态调整分片数量的场景 | #### **2. 分库分表中间件** - **客户端SDK**: - **ShardingSphere**(Apache开源):通过JDBC驱动或Proxy代理实现分片。 - **TDDL**(阿里开源):基于JDBC的轻量级分库分表框架。 - **代理层**: - **MyCat**:通过独立代理服务路由SQL(需维护中间件高可用)。 - **Vitess**(YouTube开源):针对MySQL的集群管理方案,支持Kubernetes部署。 #### **3. 分库分表示例(以订单表为例)** - **分片规则**:按 `order_id` 哈希取模分4个库,每个库分8张表。 - **路由计算**: ```python db_index = (hash(order_id) % 4) # 确定库编号(DB0~DB3) table_index = (hash(order_id) // 4) % 8 # 确定表编号(table0~table7) ``` - **真实表名**:`order_db_{db_index}.order_table_{table_index}` --- ### **四、分库分表的挑战与解决方案** #### **1. 分布式事务** - **问题**:跨库操作无法保证ACID(如扣库存和生成订单需跨库)。 - **解决方案**: - **最终一致性**:通过消息队列异步补偿(如库存服务扣减后发MQ通知订单服务)。 - **Seata**(阿里开源):基于两阶段提交(2PC)的分布式事务框架。 #### **2. 跨分片查询** - **问题**:分页、排序、聚合计算需合并多个分片结果(如查询“用户最近3月订单”)。 - **解决方案**: - **冗余数据**:建立全局二级索引表(如ES维护用户ID与订单ID的映射)。 - **并行查询**:中间件向所有分片广播查询请求,内存中合并结果(性能较差,慎用)。 #### **3. 扩容与数据迁移** - **扩容流程**: 1. 双写旧库和新库,确保新旧数据同步。 2. 迁移历史数据到新库(使用工具如`pt-online-schema-change`)。 3. 切换流量到新库,停用旧库。 - **工具支持**: - **ShardingSphere-Scaling**:自动化分片扩容工具。 - **数据管道**:通过Kafka同步增量数据。 #### **4. 全局唯一ID** - **问题**:分库分表后无法依赖数据库自增ID。 - **解决方案**:使用分布式ID生成方案(如Snowflake、Leaf)。 --- ### **五、分库分表的适用场景** | **场景** | **拆分方式** | **示例** | |---------------------------|---------------------|---------------------------------------| | 单表数据量超过5000万行 | 水平分表 | 用户表按 `user_id` 哈希分64张表 | | 业务模块耦合度高 | 垂直分库 | 电商系统拆分为订单库、商品库、支付库 | | 高并发读写 | 分库分担连接数压力 | 社交App用户动态表按 `user_id` 分16个库 | | 数据冷热分离 | 按时间范围分库分表 | 日志表按月分片,旧数据归档到廉价存储 | --- ### **六、分库分表的最佳实践** 1. **避免过度拆分**: - 单库不超过500张表,单表数据量控制在千万级。 - 拆分前优先优化索引、缓存、SQL性能。 2. **分片键设计原则**: - 选择高频查询字段(如`user_id`)。 - 避免热点问题(如按时间分片导致新库压力大)。 3. **灰度与监控**: - 逐步迁移数据,观察慢查询和连接数变化。 - 监控分片负载均衡(如Prometheus + Grafana)。 --- ### **总结** 分库分表是应对海量数据和高并发的终极手段,但其复杂度极高。**建议优先尝试读写分离、缓存优化、数据库参数调优等方案**,仅在单机无法支撑时再考虑分库分表。拆分后需配套解决分布式ID、事务、查询等问题,可借助成熟中间件降低开发成本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值