背景
当今社会是一个信息大爆炸的社会,大家都在用各种应用软件,也因此产生了大量的数据,企业把这些数据当做宝贝,然而这些被视为宝贝的数据往往是我们技术人员的烦恼,这些海量的数据存储和访问成为了系统设计与使用的瓶颈,而这些数据往往存储在数据库中,然后传统的数据库又是存在不足的。单个数据库是存在性能瓶颈的,并且扩展起来十分困难,在当今这个大数据的时代,我们就必须要解决这样的问题。如果单机数据库易于扩展,数据可切分,就可以避免这些问题,但是当前的这些数据库厂商,包括开源的数据库MySQL在内,提供这些服务都是要收费的。所以我们一般转向第三方的软件,使用这些软件来给我们的数据做数据切分,将原本一台数据库上的数据,分散到多台数据库中,降低每一个单体数据库的负载。那么我们如何做数据切分呢?接下来,跟着老猫来看一下切分的方案。
数据切分
所谓的数据的切分其实就是按照某种规则将一台机器上的数据分配到多台数据库中,从而达到降低单台数据库压力的效果。我们所说的数据库切分,大致其实分为两类,一类是垂直切分,另外一类是水平切分。下面我们一一来看一下这两种的方案。
垂直切分
所谓的垂直切分就是按照不同的表或者schema切分到不同的数据库中。我们就简单举例电商产品中的订单表(order)以及商品信息表(product)以及会员(member),初期的时候我们可能放在同一个数据库中,现在我们就要对其进行拆分,拆分的规则就是讲不同的业务线表分别落到不同的物理机的不同的数据中心,使他们完全隔离,从而达到降低数据库负载的效果。如下示例图
垂直切分的特点其实就是规则比较简单,比较容易实施,可以根据不同的业务类型进行模块的划分,这样的话各个业务耦合性降低,相互影响也小。
老猫觉得一个架构设计比较好的应用系统,总体功能应该是有不同的业务模块组成的。每个不同的业务模块对应着数据里面的一系列的表。就刚才我们举例的三个业务模块,如果我们稍微扩展一下的话。应该会是这样的:
订单模块:订单、订单明细、订单收货地址、订单日志等等表
商品模块:类目、属性、属性值、商品、sku等等表
会员模块:会员基本信息表、会员信息操作日志表等等
这样我们进行一下扩展应该就会变成如下图示
在我们对一个系统进行架构设计的时候,各个模块之间的交互越统一越好,越少越好。这样,系统模块之间的耦合度会很低,各个系统模块的可扩展性以及可维护性会大大提高,这样的系统如果后续数量级大的时候,我们实现数据的垂直切分就相当容易。
但是,在我们实际的系统架构设计中,往往很难做到完全的独立,表和表之间存在夸库join的查询还是会有的。比如我们要查询一个类目下面产生了多少个订单,如果是单个数据库的话,我们直接连表查询即可,但是现在垂直切分成两块数据的话,这时候就要通过调用接口的方式去查询,这样系统的复杂程度就会提高。所以此时就要去平衡,是数据库让步于业务,将这些表放在一个数据库中,还是拆分成多个数据库,然后通过接口的方式来调用。如何去切分,切分到什么程度其实对于架构师来说是一个考验。
关于垂直气氛最终优缺点的整理:
优点:
拆分之后,我们的业务更加地清晰了,拆分规则也比较明确。
数据维护变得简单。
系统之间更加容易扩展和整合。
缺点:
业务中表和表无法做join查询,只能通过接口调用,提升了系统的复杂度。
如果涉及到事务,跨库事务比较难处理。
虽然进行了垂直切分,但是有些业务数据还是会过于庞大,例如订单,其实依旧存在着单体性能的瓶颈。
以上我们讲述了垂直切分的缺点,然而最后一点我们如何才能解决呢?这个时候其实我们就需要用到水平切分。
水平切分
水平切分其实会比垂直切分更加复杂,它需要根据特定的规则将一张表中的数据拆分到不同的数据库中。例如,举个比较简单的例子,我们之前说的订单垂直切分之后业务数据依旧很大,那么我们可以根据某种规则进行水平切分,比方说根据订单编号的奇数或者是偶数,将编号为奇数的订单存放到数据库A中,将编号为偶数的订单存放到数据库B中。但是给我们带来的麻烦就是查询的时候需要根据奇偶性去不同的库中查询数据。我们来看一下水平切分的架构图,如下:
在进行水平拆分的时候,我们需要定义数据具体按照什么维度去拆分,前面的订单中,我们提及说按照尾号的奇偶去拆分,我们想一下会有什么问题呢?我是一个用户,我下了两单,一单订单编号为奇数,另外一单是偶数,这时候我们查看自己下单记录的时候,就需要根据用户的ID去两个不同的库中分别查询两单数据,可想而知这种是相当麻烦的。
所以我们在进行水平拆分的时候需要结合具体的业务场景。如果我们按照用户的ID去拆是不是就OK了呢?其实也不一定,我们换个角度,如果我们站在不是用户的立场而是站在商户的立场。在商户后台也会有很多订单,商户需要管理自己的订单,订单拆分的时候我们根据用户的ID,这就意味着很多商户在获取订单的时候还是要去不同的订单表中查询,然后聚合成一张订单表给商户,此时我们用用户ID去拆分显然是不合理的。
我们看看场景的几种水平拆分的方法:
用户ID求模法,上述已经提及。
按照日期去拆分数据。
按照其他字段进行求模拆分数据。
上述用户ID求模法示意图如下:
综上我们再看一下水平切分的优点和缺点,
优点:
解决了单库大数据、高并发的性能瓶颈。
拆分规则封装完毕之后对应用层透明,开发人员无需关心拆分细节。
提高了系统的稳定性以及负载能力。
缺点:
拆分规则很难定义。
事务一致性问题难解决。
二次扩展的时候(例如根据模3扩展成模5的时候,历史数据的处理,此时由三台数据库变成5台数据库),数据迁移,维护的难度比较大。
写在最后
其实世界上就没有完美的事情,有利也有弊。大数据切分也是一样,无论是垂直切分还是水平切分。这两种虽然都解决了海量数据的存储以及访问的性能问题,但是同时又会产生很多新的问题。共同的问题:
分布式的事务问题。
跨库连接查询问题。
多个数据源的管理问题。
针对最后一种情况,多源数据的管理问题,其实有两种思路:
客户端模式——在每个应用模块内,配置自己需要的数据源,然后进行数据库的访问。
中间代理模式——中间代理统一管理所有的数据源,数据库层对开发人员透明,开发人员无需关注拆分细节。
根据上述两种模式,其实市面就有成熟的第三方的软件。MyCat(中间代理模式)和sharding-jdbc(客户端模式)。
由于篇幅的限制,后续老猫会详细针对这两种软件的实际落地做举例。
有任何疑问的地方欢迎大家留言,更多纯技术干货,各个大厂的面试题。也可以搜索关注“程序员老猫”公众号~