从程序员到架构师:大数据量、缓存、高并发、微服务、多团队协同等核心场景实战
上QQ阅读APP看书,第一时间看更新

3.2 拆分存储的技术选型

拆分存储常用的技术解决方案目前主要分为4种:MySQL的分区技术、NoSQL、NewSQL、基于MySQL的分表分库。

3.2.1 MySQL的分区技术

图3-1所示为MySQL官方文档中的架构图。MySQL的分区技术主要体现在图3-1中的文件存储层File System,它可以将一张表的不同行存放在不同的存储文件中,这对使用者来说比较透明。

在以往的项目中,项目组不使用它的原因主要有3点。

1)MySQL的实例只有一个,它仅仅分摊了存储,无法分摊请求负载。

2)正是因为MySQL的分区对用户透明,所以用户在实际操作时往往不太注意,如果SQL跨了分区,那么操作就会严重影响系统性能。

3)MySQL还有一些其他限制,比如不支持query cache、位操作表达式等。感兴趣的读者可以查看官方文档中的相关内容https://dev.mysql.com/doc/refman/5.7/en/partitioning-limitations.html。

• 图3-1 MySQL架构图

3.2.2 NoSQL

比较典型的NoSQL数据库就是MongoDB。MongoDB的分片功能从并发性和数据量这两个角度已经能满足一般大数据量的需求,但是还需要注意下面3点。

1)约束考量:MongoDB不是关系型数据库而是文档型数据库,它的每一行记录都是一个结构灵活可变的JSON,比如存储非常重要的订单数据时,就不能使用MongoDB,因为订单数据必须使用强约束的关系型数据库进行存储。举个例子,订单里面有金额相关的字段,这是系统里面的核心数据,所以必须保证每个订单数据都有这些金额相关的字段,并且不管是怎样的业务逻辑修改,这些字段都要保存好,这时可以通过数据库的能力加一层校验,这样即使业务代码出了问题,导致这些字段存储不正确,也可以在数据库这一层面阻隔问题。

当然,MongoDB 3.2版以后也支持Schema Validation(模式验证),可以制订一些约束规则。不过项目组使用MongoDB的原因之一就是看重它灵活的Schema(模式)。

2)业务功能考量:订单这种跟交易相关的数据肯定要支持事务和并发控制,而这些并不是MongoDB的强项。而且除了这些功能以外,多年来,事务、锁、SQL、表达式等各种各样的操作都在MySQL身上一一实践过,MySQL可以说是久经考验,因此在功能上MySQL能满足项目所有的业务需求,MongoDB却不一定能,且大部分的NoSQL也存在类似复杂功能支持的问题。

3)稳定性考量:人们对MySQL的运维已经很熟悉了,它的稳定性没有问题,然而MongoDB的稳定性无法保证,毕竟很多人不熟悉。

基于以上的原因,当时项目组排除了MongoDB。

3.2.3 NewSQL

NewSQL技术还比较新,笔者曾经想在一些不重要的数据中使用NewSQL(比如TiDB),但从稳定性和功能扩展性两方面考量后,最终没有使用,具体原因与MongoDB类似。

3.2.4 基于MySQL的分表分库

最后说一下基于MySQL的分表分库:分表是将一份大的表数据进行拆分后存放至多个结构一样的拆分表中;分库就是将一个大的数据库拆分成类似于多个结构的小数据库。场景介绍里就举了个简单的例子,这里不再赘述。

项目组没有选用前面介绍的3种拆分存储技术,而是选择了基于MySQL的分表分库,其中有一个重要考量:分表分库对于第三方依赖较少,业务逻辑灵活可控,它本身并不需要非常复杂的底层处理,也不需要重新做数据库,只是根据不同逻辑使用不同SQL语句和数据源而已,因此,之后出问题的时候也能够较快地找出根源。

如果使用分表分库,有3个通用技术需求需要实现。

1)SQL组合:因为关联的表名是动态的,所以需要根据逻辑组装动态的SQL。比如,要根据一个订单的ID获取订单的相关数据,Select语句应该针对(From)哪一张表?

2)数据库路由:因为数据库名也是动态的,所以需要通过不同的逻辑使用不同的数据库。比如,如果要根据订单ID获取数据,怎么知道要连接哪一个数据库?

3)执行结果合并:有些需求需要通过多个分库执行后再合并归集起来。假设需要查询的数据分布在多个数据库的多个表中(比如在order1里面的t_order_1,order2里面的t_order_9中),那么需要将针对这些表的查询结果合并成一个数据集。

而目前能解决以上问题的中间件分为两类:Proxy模式、Client模式。

1)Proxy模式:图3-2所示为ShardingSphere官方文档中的Proxy模式图,重点看中间的Sharding-Proxy层。

这种设计模式将SQL组合、数据库路由、执行结果合并等功能全部放在了一个代理服务中,而与分表分库相关的处理逻辑全部放在了其他服务中,其优点是对业务代码无侵入,业务只需要关注自身业务逻辑即可。

2)Client模式:ShardingSphere官方文档中的Client模式如图3-3所示。这种设计模式将分表分库相关逻辑放在客户端,一般客户端的应用会引用一个jar,然后在jar中处理SQL组合、数据库路由、执行结果合并等相关功能。

• 图3-2 Proxy模式图

• 图3-3 Client模式图

这两种模式的中间件见表3-2。

表3-2 常见分表分库中间件

这两种开源中间件的设计模式该如何选择呢?先简单对比一下它们的优缺点,见表3-3。

表3-3 Client模式与Proxy模式的优缺点

因为看重“代码灵活可控”这个优势,项目组最终选择了Client模式里的Sharding-JDBC来实现分表分库,如图3-3所示。

当然,关于拆分存储选择哪种技术合适,在实际工作中需要根据具体情况来定。