专栏名称: 芋道源码
纯 Java 源码分享公众号,目前有「Dubbo」「SpringCloud」「Java 并发」「RocketMQ」「Sharding-JDBC」「MyCAT」「Elastic-Job」「SkyWalking」「Spring」等等
目录
相关文章推荐
芋道源码  ·  16个SpringBoot ... ·  2 天前  
芋道源码  ·  您需要模块,而不是微服务 ·  4 天前  
芋道源码  ·  中国永远不会倒闭的四个行业 ·  1 周前  
51好读  ›  专栏  ›  芋道源码

分库分表也没吹的那么神…… 这 7 个问题难以解决

芋道源码  · 公众号  · Java  · 2024-10-06 16:48

正文

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入芋道快速开发平台知识星球。下面是星球提供的部分资料: 

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、CRM 等等功能:

  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 双版本 

来源:juejin.cn/post/
7407271112995651610


为什么需要分库分表?是因为 MySQL 等数据库单库单表能支撑的系统并发量和数据存储量存在瓶颈。

中国有超过 10 亿的网民,单日交易量超过百万、千万单的交易场景越来越多。按照一行数据 1K ,每日订单量 500 万计算,一年需要的数据存储为 1700G,行数 1.7 亿。

显然此种情况 MySQL 单库单表难以保证查询写入性能。一张表一年的数据量恐怖如斯,交易场景成百上千张表,年复一年,所带来的存储和并发量压力何其巨大呢?业务牵引技术的发展,分库分表技术应运而生。

目前分库分表技术似乎成为行业标准,站在鄙视链上游。不分库分表的系统似乎都是毫无亮点的垃圾系统,然而真的如此吗?

  • 分库分表技术是完美的解决方案吗?它有哪些不足之处?
  • 为什么单库单表存在系统瓶颈,就选择分库分表呢?有其他更好的方案吗?

接下来我将分享自己在电商交易场景实际遇到的痛点

1. 分库分表难以解决分布式事务问题

一般情况下 通过选择合适的分片属性,可以保证业务在一个数据库内完成事务操作。

如用户领券场景,用户一次领取 3 张优惠券,系统写入 3 条优惠券记录和一条领券流水,使用 UserId 进行分库分表,可以保证同一个用户的 4 条记录路由到同一数据库,可使用 MySQL 本地事务即可保证数据一致性。

然而当同一个事务中多张表的分片属性不同时,难以保证这些表在同一个数据库内完成事务操作,势必会出现难以解决的分布式事务难题

如优惠券存在库存,在发券时需同时扣减券库存,库存粒度为券模版ID,用户可同时领取多个券模版的券。如何保证库存扣减、用户发券、领取流水等在同一个数据库内完成呢?难以实现

优惠券和领券流水,基于 UserId 拆分数据库,但库存表无用户属性,只能使用券模版 ID分库。然而不同的分片属性,无法保证在同一个数据库完成事务操作,难以保证数据强一致性。

因此 当同一个事务中多张表的分片属性不同时,难以保证这些表在同一个数据库内完成事务操作,势必会出现难以解决的分布式事务难题。即便业务侧使用复杂的业务架构也难以实现强一致性,作出妥协后,可实现最终一致性。

分库分表技术把数据库解决不了的问题推到业务侧,“胁迫” 业务侧使用复杂方案参与解决,要求业务侧在数据一致性上作出妥协

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

2. 分库分表难以实现非分片属性的索引查询能力

电商交易场景一般使用 UserId 作为分片属性,联合索引的前缀都是UserId,但这并不绝对,交易场景中存在品牌、门店、司机、配送等其他维度。

如订单表使用UserId 作为分片属性进行分库分表,查询商家在最近 1 小时的订单如何实现呢?由于使用 UserId 分库分表,当使用商家 ID 查询时,需要查询所有的分库分表,这显然不现实。

业务侧有几种实现方案,1)使用商家 ID分库分表,异构另一份 MySQL存储 2)基于 ElasticSearch,异构另一种存储支撑其他维度的检索场景。

无论哪一种方案都会导致,数据一致性降低,但系统复杂度增加。

查询商家最近1 小时的订单,业务上如此简单清晰的需求,在分库分表后,实现方案竟如此复杂。此外还要求业务在数据准确性上作出妥协。

正是因为分库分表技术的先天不足,所以极大地增加了业务系统架构的复杂性。(甚至很多人以架构复杂为荣,错误的认为系统越复杂越能体现架构能力

此外还有其他更加 tricky的方案,如为了实现订单 Id 查询,在生成的订单Id中存放UserId后四位(大概意思),用于定位订单在哪个分片。

分库分表技术把数据库解决不了的问题推到业务侧,“胁迫” 业务侧使用复杂方案参与解决,要求业务侧在数据一致性上作出妥协

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud
  • 视频教程:https://doc.iocoder.cn/video/

3. 分库分表导致的全局主键问题

MySQL 主键支持自增 ID,但是这一特性在分库分表后沦为鸡肋功能,系统需要分布式方式的 ID 生成器。

如优惠券系统在分库分表后,优惠券 ID 需要借助分布式ID生成器生成全局唯一 ID。常见的实现方式包括 UUID、雪花算法、美团 Leaf、百度 UidGenerator等。除维护业务系统外,还需要维护其他纯技术类系统。

毫无疑问,这再次增加架构的复杂性。

4. 分库分表难以建立全局唯一键约束

除全局主键问题,全局唯一键也难以实现。当全局唯一键的前缀是非分片属性时,难以实现全局唯一键。

例如订单在履约完成后,会生成一笔履约单。系统明确一笔订单只能有一笔履约单,因此履约单上的订单 ID 字段应该增加唯一键约束。然而履约单在基于 UserID 分库分表后,OrderId建立的唯一键约束只在本表内唯一,不能保证在所有的分片内实现全局唯一。

因此 分库分表后,数据库无法对 OrderId等非分片属性建立全局的唯一性约束。

除以上具体场景外,分库分表面临的难题依然有很多,如扩容难问题、存储成本高、运维成本高、高可用难等等问题。

5. 分库分表需要持续造轮子

选择分库分表后,需要业务系统开发和接入很多轮子,包括

  • ShardingSphere、mycat等方便客户端接入分库分表。如果使用 ShardingSphere需考虑多种语言多套轮子,使用 Mycat代理层方案又会面临性能风险。
  • 分布式 ID 生成器生成全局 ID
  • 为了支持非分片查询,需要 DTS 消费binlog,异构存储For 查询。
  • 使用 ElasticSearch用于其他维度检索。
  • 分库分表管理后台支持查询数据、修改数据
  • 分库分表管理工具支持高效建表、修改表、加索引等 DDL 操作。(1000 张表后,手动建表不现实)
  • ……

层出不穷的问题,需要层出不穷的轮子,有些轮子需要自己造,如分库分表管理工具等。

不禁要问,互联网大厂能承受如此复杂架构方案,所有的公司都能承受吗?导致架构如此复杂的根源是什么呢?分库分表方案上的先天不足

6. 分库分表后面临老大难的扩容问题

在系统建设之初,一个合格的架构师必须考虑到:未来数据量庞大后的存储扩容问题。这往往让人很难抉择,因为要权衡当下的硬件成本和未来扩容成本。

使用分库分表后,扩容非常困难。如8 个数据库,想继续拆分为 16 个数据库,一定会对业务造成影响,停机迁移是难以避免的问题,需要极多的运维工具保障扩容过程的安全性和快速性,做到对业务影响程度最低。因此扩容成本和风险都极高。

如果在系统建设初期就拆分为 16 个库,又会面临硬件和运维成本过高的问题。如果每一个业务在系统建设初期都如此铺张浪费,那么公司的硬件成本将极高。

7. 大量分库分表导致运维成本激增

仅一个业务团队就16 个数据库,公司那么多业务将会有多么庞大规模的数据库实例。有 DBA 曾分享经历,他一天新部署了120 套 mysql实例,可想而知,DBA 们面临了多么庞大的运维压力。

研发视角和 DBA视角不同,结论不同。业务研发更多考虑容量不足和并发度不足风险,会倾向于设置较大的分库分表规模,而 DBA 会考虑运维成本、硬件成本,希望数据库规模在可控范围内。两者站在不同的视角,各有理由,这是一个矛盾。

我相信大多数业务研发不清楚,自家公司MySQL单库单实例的最高并发度,最高容量,在设置分片规模时往往是拍脑袋决定。只要研发定的分片方案不离谱,往往 DBA也就接受了。

这些决策中,往往还掺杂历史问题。如团队已经有了 10 个库,无论业务规模如何,新表默认拆分10 个库,而不会考虑是否真的需要 10 个库。

8. 为什么业务系统要替数据库负重前行?

分库分表技术把数据库解决不了的问题推到业务侧,“胁迫” 业务侧使用复杂方案参与解决,要求业务侧在数据一致性上作出妥协。

为什么单库单表存在系统瓶颈,就选择分库分表呢?有其他更好的方案吗?分布式数据库。

分布式数据库继承了传统单机数据库的核心特性,同时还拥有分布式系统的处理能力 。虽然起步较晚,可以预见的是它将是数据库下一个发展方向。

如阿里的 OceanBase基于两阶段提交协议解决了分布式事务问题https://www.oceanbase.com/docs/enterprise-oceanbase-database-cn-10000000000358802,提供了比肩本地事务的强一致性,此外还提供以下能力解决了分库分表技术难以解决的问题 1、2、3、4。相比分库分表,业务侧使用分布式数据库的门槛更低。

  • 提供了全局索引,可以为非分片属性提供全局的索引和唯一性约束
  • 提供了全局自增主键,无需分布式 ID 生成器。

OceanBase 在性能、扩容能力、运维和硬件成本上,相比分库分表有更优的表现:深入OceanBase内部机制:高性能分布式(实时HTAP)关系数据库概述https://cloud.tencent.com/developer/article/2403999

单机数据库能支撑的并发量和存储量的天花板很低,分库分表和分布式数据库是解决此类问题的两种方案。分库分表通过分库分表中间件及一系列轮子整合了独立的数据库实例,可解决并发量和存储量的难题,但是牺牲了一致性,增加了使用门槛。而分布式数据库则直面困难,使用分布式集群架构在解决并发量和存储量挑战的同时,也提供了类比单机数据库的使用门槛和强一致性事务能力。

就像 ElaticSearch 替代 Lucene ,RedisCluster 替代单机Redis一样,低门槛、强一致性、支持高并发大数据量存储的分布式数据库一定是数据库的主流发展方向。

分库分表更像是开源数据库技术落后互联网业务发展时,不得已的临时过渡方案。


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。

谢谢支持哟 (*^__^*)