为什么需要分库分表?是因为MySQL等数据库单库单表能支撑的系统并发量和数据存储量存在瓶颈。
中国有超过10亿的网民,单日交易量超过百万、千万单的交易场景越来越多。按照一行数据1K,每日订单量500万计算,一年需要的数据存储为1700G,行数1.7亿。
显然此种情况MySQL单库单表难以保证查询写入性能。一张表一年的数据量恐怖如斯,交易场景成百上千张表,年复一年,所带来的存储和并发量压力何其巨大呢?业务牵引技术的发展,分库分表技术应运而生。
目前分库分表技术似乎成为行业标准,站在鄙视链上游。不分库分表的系统似乎都是毫无亮点的垃圾系统,然而真的如此吗?
分库分表技术是完美的解决方案吗?它有哪些不足之处?
为什么单库单表存在系统瓶颈,就选择分库分表呢?有其他更好的方案吗?
一、分库分表难以解决分布式事务问题
一般情况下通过选择合适的分片属性,可以保证业务在一个数据库内完成事务操作。
如用户领券场景,用户一次领取3张优惠券,系统写入3条优惠券记录和一条领券流水,使用UserId进行分库分表,可以保证同一个用户的4条记录路由到同一数据库,可使用MySQL本地事务即可保证数据一致性。
然而**当同一个事务中多张表的分片属性不同时,难以保证这些表在同一个数据库内完成事务操作,势必会出现难以解决的分布式事务难题**。
如优惠券存在库存,在发券时需同时扣减券库存,库存粒度为券模版ID,用户可同时领取多个券模版的券。如何保证库存扣减、用户发券、领取流水等在同一个数据库内完成呢?难以实现
优惠券和领券流水,基于UserId拆分数据库,但库存表无用户属性,只能使用券模版ID分库。然而不同的分片属性,无法保证在同一个数据库完成事务操作,难以保证数据强一致性。
因此当同一个事务中多张表的分片属性不同时,难以保证这些表在同一个数据库内完成事务操作,势必会出现难以解决的分布式事务难题。即便业务侧使用复杂的业务架构也难以实现强一致性,作出妥协后,可实现最终一致性。
分库分表技术把数据库解决不了的问题推到业务侧,“胁迫”业务侧使用复杂方案参与解决,要求业务侧在数据一致性上作出妥协
二、分库分表难以实现非分片属性的索引查询能力
电商交易场景一般使用UserId作为分片属性,联合索引的前缀都是UserId,但这并不绝对,交易场景中存在品牌、门店、司机、配送等其他维度。
如订单表使用UserId作为分片属性进行分库分表,查询商家在最近1小时的订单如何实现呢?由于使用UserId分库分表,当使用商家ID查询时,需要查询所有的分库分表,这显然不现实。
业务侧有几种实现方案,1)使用商家ID分库分表,异构另一份MySQL存储2)基于ElasticSearch,异构另一种存储支撑其他维度的检索场景。
无论哪一种方案都会导致,数据一致性降低,但系统复杂度增加。
查询商家最近1小时的订单,业务上如此简单清晰的需求,在分库分表后,实现方案竟如此复杂。此外还要求业务在数据准确性上作出妥协。
正是因为分库分表技术的先天不足,所以极大地增加了业务系统架构的复杂性。(甚至很多人以架构复杂为荣,错误的认为系统越复杂越能体现架构能力)
此外还有其他更加tricky的方案,如为了实现订单Id查询,在生成的订单Id中存放UserId后四位(大概意思),用于定位订单在哪个分片。
三、分库分表导致的全局主键问题
MySQL主键支持自增ID,但是这一特性在分库分表后沦为鸡肋功能,系统需要分布式方式的ID生成器。
如优惠券系统在分库分表后,优惠券ID需要借助分布式ID生成器生成全局唯一ID。常见的实现方式包括UUID、雪花算法、美团Leaf、百度UidGenerator等。除维护业务系统外,还需要维护其他纯技术类系统。
毫无疑问,这再次增加架构的复杂性。
四、分库分表难以建立全局唯一键约束
除全局主键问题,全局唯一键也难以实现。当全局唯一键的前缀是非分片属性时,难以实现全局唯一键。
例如订单在履约完成后,会生成一笔履约单。系统明确一笔订单只能有一笔履约单,因此履约单上的订单ID字段应该增加唯一键约束。然而履约单在基于UserID分库分表后,OrderId建立的唯一键约束只在本表内唯一,不能保证在所有的分片内实现全局唯一。
因此分库分表后,数据库无法对OrderId等非分片属性建立全局的唯一性约束。
除以上具体场景外,分库分表面临的难题依然有很多,如扩容难问题、存储成本高、运维成本高、高可用难等等问题。
五、分库分表需要持续造轮子
选择分库分表后,需要业务系统开发和接入很多轮子,包括
ShardingSphere、mycat等方便客户端接入分库分表。如果使用ShardingSphere需考虑多种语言多套轮子,使用Mycat代理层方案又会面临性能风险。
分布式ID生成器生成全局ID
为了支持非分片查询,需要DTS消费binlog,异构存储For查询。
使用ElasticSearch用于其他维度检索。
分库分表管理后台支持查询数据、修改数据
分库分表管理工具支持高效建表、修改表、加索引等DDL操作。(1000张表后,手动建表不现实)
……
层出不穷的问题,需要层出不穷的轮子,有些轮子需要自己造,如分库分表管理工具等。
不禁要问,互联网大厂能承受如此复杂架构方案,所有的公司都能承受吗?导致架构如此复杂的根源是什么呢?分库分表方案上的先天不足
六、分库分表后面临老大难的扩容问题
在系统建设之初,一个合格的架构师必须考虑到:未来数据量庞大后的存储扩容问题。这往往让人很难抉择,因为要权衡当下的硬件成本和未来扩容成本。
使用分库分表后,扩容非常困难。如8个数据库,想继续拆分为16个数据库,一定会对业务造成影响,停机迁移是难以避免的问题,需要极多的运维工具保障扩容过程的安全性和快速性,做到对业务影响程度最低。因此扩容成本和风险都极高。
如果在系统建设初期就拆分为16个库,又会面临硬件和运维成本过高的问题。如果每一个业务在系统建设初期都如此铺张浪费,那么公司的硬件成本将极高。
七、大量分库分表导致运维成本激增
研发视角和DBA视角不同,结论不同。业务研发更多考虑容量不足和并发度不足风险,会倾向于设置较大的分库分表规模,而DBA会考虑运维成本、硬件成本,希望数据库规模在可控范围内。两者站在不同的视角,各有理由,这是一个矛盾。
我相信大多数业务研发不清楚,自家公司MySQL单库单实例的最高并发度,最高容量,在设置分片规模时往往是拍脑袋决定。只要研发定的分片方案不离谱,往往DBA也就接受了。
这些决策中,往往还掺杂历史问题。如团队已经有了10个库,无论业务规模如何,新表默认拆分10个库,而不会考虑是否真的需要10个库。
八、为什么业务系统要替数据库负重前行?
分库分表技术把数据库解决不了的问题推到业务侧,“胁迫”业务侧使用复杂方案参与解决,要求业务侧在数据一致性上作出妥协。
为什么单库单表存在系统瓶颈,就选择分库分表呢?有其他更好的方案吗?分布式数据库。
分布式数据库继承了传统单机数据库的核心特性,同时还拥有分布式系统的处理能力。虽然起步较晚,可以预见的是它将是数据库下一个发展方向。
如阿里的OceanBase基于两阶段提交协议解决了分布式事务问题,提供了比肩本地事务的强一致性,此外还提供以下能力解决了分库分表技术难以解决的问题1、2、3、4。相比分库分表,业务侧使用分布式数据库的门槛更低。
提供了全局索引,可以为非分片属性提供全局的索引和唯一性约束
提供了全局自增主键,无需分布式ID生成器。
OceanBase在性能、扩容能力、运维和硬件成本上,相比分库分表有更优的表现
单机数据库能支撑的并发量和存储量的天花板很低,分库分表和分布式数据库是解决此类问题的两种方案。分库分表通过分库分表中间件及一系列轮子整合了独立的数据库实例,可解决并发量和存储量的难题,但是牺牲了一致性,增加了使用门槛。而分布式数据库则直面困难,使用分布式集群架构在解决并发量和存储量挑战的同时,也提供了类比单机数据库的使用门槛和强一致性事务能力。
就像ElaticSearch替代Lucene,RedisCluster替代单机Redis一样,低门槛、强一致性、支持高并发大数据量存储的分布式数据库一定是数据库的主流发展方向。
分库分表更像是开源数据库技术落后互联网业务发展时,不得已的临时过渡方案。