一、分库分表的背景
在数据爆炸的年代,单表数据达到千万级别,甚至过亿的量,都是很常见的情景。这时候再对数据库进行操作就是非常吃力的事情了,select个半天都出不来数据,这时候业务已经难以维系。不得已,分库分表提上日程,我们的目的很简单,减小数据库的压力,缩短表的操作时间。
二、如何进行数据切分
数据切分(Sharding),简单的来说,就是通过某种特定的条件,将存放在同一个数据库中的数据拆分存放到多个数据库(主机)中,从而达到分散单台机器负载的情况,即分库分表。根据数据切分规则的不同,主要有两种模式,
垂直切分(纵向切分),是对不同的表(或者Schema)进行切分,存储到不同的数据库(主机)之上。
水平切分(横向切分),是对同一个表中的数据进行切分,存储到不同的数据库(主机)之上。规则是根据表中数据的逻辑关系,按照某种条件拆分。
垂直切分
垂直切分,强调的是业务的拆分。一个数据库由多个表构成,每个表对应不同的业务,那么我们可以指按照业务的不同将表进行分类,并将其分布到不同的数据库上,这样就将数据分摊到了不同的库上面,做到专库专用。
举个例子,原数据库中有商品表、交易表、订单表,我们可以按照业务的不同进行垂直切分,把商品表、交易表、订单表分别拆分到商品库、交易库、订单库中去。
垂直拆分的优点:
拆分规则明确,拆分后业务清晰;系统之间进行整合或扩展变的容易;数据维护变的容易;按照成本、应用的等级、应用的类型等将表放到不同的机器上,便于管理。垂直拆分的缺点:
部分业务表无法关联(Join),只能通过接口方式解决,提高了系统的复杂度;受每种业务的不同限制,存在单库性能瓶颈,不易进行数据扩展和提升性能;分布式事务处理复杂。
水平切分(重点)
水平切分,强调的是技术层面的拆分。她是将其按照一定的逻辑规则将一个表中的数据分散到多个库中,在每个表中包含一部分数据,所有表加起来就是全量的数据。简单来说,我们可以将对数据的水平切分理解为按照数据行进行切分,就是将表中的某些行切分到一个数据库表中,而将其他行切分到其他数据库表中。
比如,原数据库有一张交易记录表,数据量非常大,其中表中有个地区字段,经过深入考证符合水平拆分的条件。我们就按照这个字段进行水平拆分,按不同的地区(北京、上海、江苏、浙江、广东等)拆分成10个库。
高峰时段同时有100万次请求,如果是单库,数据库就会承受100万次的请求压力,拆分成100个表分别放入10个库中,每个表进行1万次请求,则每个数据库会承受10万次的请求压力,这样压力就减少了很多,并且是成倍减少的。
水平拆分的优点:
拆分规则抽象好,join 操作基本可以数据库做;不存在单库大数据,高并发的性能瓶颈;应用端改造较少;提高了系统的稳定性跟负载能力。水平拆分的缺点:
拆分规则不好抽象;分片事务一致性难以解决;数据多次扩展难度大;跨库 join 性能较差。
三、数据切分导致的一些问题
上面我们也讲了两种数据切分方式的优点和缺点,但是他们有些共同的缺点,
分布式事务的问题;跨节点 Join 的问题;跨节点合并排序分页的问题;多数据源管理问题。一般来说,业务上存在着复杂 join 的场景是很难切分的,往往业务独立的易于切分。如何切分,我们遵循如下原则,
能不切分尽量不要切分;如果要切分一定要选择合适的切分规则,提前规划好;数据切分尽量通过数据冗余或表分组来降低跨库 Join 的可能;由于数据库中间件对数据 Join 实现的优劣难以把握,而且实现高性能难度极大,业务读取尽量少使用多表 Join。
四、数据源管理的问题
分库分表之后,数据源的管理是系统实现的关键。
系统应用层面系统应用代码层面,目前主要有两种思路,
客户端模式,也就是在每个应用程序模块中配置管理自己需要的一个(或者多个)数据源,直接访问各个数据库,在模块内完成数据的整合。比如可以依赖spring注解实现。中间代理模式,统一管理所有的数据源,后端数据库集群对前端应用程序透明。考虑到系统的复杂性和扩展性,建议第二种中间代理模式。虽然短期内需要付出的成本可能会相对更大一些,但是对整个系统的扩展性来说,是非常实用的。
2. 中间件层面
上面的系统层面,需要的代码实现比较复杂,中间件是在数据集群前面加一层代理,比如Cobar、Mycat等数据库中间件。实用数据库中间件,对代码层面的实现是很大的解放。
五、分布式事务的解决方案
分库分表导致的最突出的问题就是分布式事务的处理。大家如果有兴趣可以参考下之前的文章互联网架构下的分布式技术面试要点概览中的分布式事务部分,后面如果有时间,可以单独再探讨下。