2017年7月15日星期六

DB 分库分表(1):拆分实施策略和示例演示


Linuxeden 开源社区 --

第一部分:实施策略

图 1. 数据库分库分表 (sharding) 实施策略图解 ( 点击查看大图 )

1. 准备阶段

对数据库进行分库分表 (Sharding 化) 前,需要开发人员充分了解系统业务逻辑和数据库 schema. 一个好的建议是绘制一张数据库 ER 图或领域模型图,以这类图为基础划分 shard, 直观易行,可以确保开发人员始终保持清醒思路。对于是选择数据库 ER 图还是领域模型图要根据项目自身情况进行选择。如果项目使用数据驱动的开发方式,团队以数据库 ER 图作为业务交流的基础,则自然会选择数据库 ER 图,如果项目使用的是领域驱动的开发方式,并通过 OR-Mapping 构建了一个良好的领域模型,那么领域模型图无疑是最好的选择。就我个人来说,更加倾向使用领域模型图,因为进行切分时更多的是以业务为依据进行分析判断,领域模型无疑更加清晰和直观。

2. 分析阶段

1. 垂直切分
垂直切分的依据原则是:将业务紧密,表间关联密切的表划分在一起,例如同一模块的表。结合已经准备好的数据库 ER 图或领域模型图,仿照活动图中的泳道概念,一个泳道代表一个 shard,把所有表格划分到不同的泳道中。下面的分析示例会展示这种做法。当然,你也可以在打印出的 ER 图或模型图上直接用铅笔圈,一切取决于你自己的喜好。

2. 水平切分
垂直切分后,需要对 shard 内表格的数据量和增速进一步分析,以确定是否需要进行水平切分。

2.1 若划分到一起的表格数据增长缓慢,在产品上线后可遇见的足够长的时期内均可以由单一数据库承载,则不需要进行水平切分,所有表格驻留同一 shard, 所有表间关联关系会得到最大限度的保留,同时保证了书写 SQL 的自由度,不易受 join、group by、order by 等子句限制。

2.2 若划分到一起的表格数据量巨大,增速迅猛,需要进一步进行水平分割。进一步的水平分割就这样进行:

2.2.1. 结合业务逻辑和表间关系,将当前 shard 划分成多个更小的 shard, 通常情况下,这些更小的 shard 每一个都只包含一个主表(将以该表 ID 进行散列的表)和多个与其关联或间接关联的次表。这种一个 shard 一张主表多张次表的状况是水平切分的必然结果。这样切分下来,shard 数量就会迅速增多。如果每一个 shard 代表一个独立的数据库,那么管理和维护数据库将会非常麻烦,而且这些小 shard 往往只有两三张表,为此而建立一个新库,利用率并不高,因此,在水平切分完成后可再进行一次“反向的 Merge”, 即:将业务上相近,并且具有相近数据增长速率(主表数据量在同一数量级上)的两个或多个 shard 放到同一个数据库上,在逻辑上它们依然是独立的 shard,有各自的主表,并依据各自主表的 ID 进行散列,不同的只是它们的散列取模(即节点数量)必需是一致的。这样,每个数据库结点上的表格数量就相对平均了。

2.2.2. 所有表格均划分到合适的 shard 之后,所有跨越 shard 的表间关联都必须打断,在书写 sql 时,跨 shard 的 join、group by、order by 都将被禁止,需要在应用程序层面协调解决这些问题。

特别想提一点:经水平切分后,shard 的粒度往往要比只做垂直切割的粒度要小,原单一垂直 shard 会被细分为一到多个以一个主表为中心关联或间接关联多个次表的 shard,此时的 shard 粒度与领域驱动设计中的“聚合”概念不谋而合,甚至可以说是完全一致,每个 shard 的主表正是一个聚合中的聚合根!

3. 实施阶段
如果项目在开发伊始就决定进行分库分表,则严格按照分析设计方案推进即可。如果是在中期架构演进中实施,除搭建实现 sharding 逻辑的基础设施外 (关于该话题会在下篇文章中进行阐述),还需要对原有 SQL 逐一过滤分析,修改那些因为 sharding 而受到影响的 sql.

第二部分:示例演示

本文选择一个人尽皆知的应用:jpetstore 来演示如何进行分库分表 (sharding) 在分析阶段的工作。由于一些个人原因,演示使用的 jpetstore 来自原 ibatis 官方的一个 Demo 版本,SVN 地址为:http://ift.tt/2tUGivX jpetstore 的业务逻辑这里不再介绍,这是一个非常简单的电商系统原型,其领域模型如下图:

图 2. jpetstore 领域模型

由于系统较简单,我们很容易从模型上看出,其主要由三个模块组成:用户,产品和订单。那么垂直切分的方案也就出来了。接下来看水平切分,如果我们从一个实际的宠物店出发考虑,可能出现数据激增的单表应该是 Account 和 Order, 因此这两张表需要进行水平切分。对于 Product 模块来说,如果是一个实际的系统,Product 和 Item 的数量都不会很大,因此只做垂直切分就足够了,也就是(Product,Category,Item,Iventory,Supplier)五张表在一个数据库结点上(没有水平切分,不会存在两个以上的数据库结点)。 但是作为一个演示,我们假设产品模块也有大量的数据需要我们做水平切分 ,那么分析来看,这个模块要拆分出两个 shard: 一个是(Product(主),Category),另一个是(Item(主),Iventory,Supplier), 同时,我们认为:这两个 shard 在数据增速上应该是相近的,且在业务上也很紧密 ,那么我们可以 把这两个 shard 放在同一个数据库节点上,Item 和 Product 数据在散列时取一样的模 。根据前文介绍的图纸绘制方法,我们得到下面这张 sharding 示意图:

图 3. jpetstore sharding 示意图

对于这张图再说明几点:
1. 使用泳道表示物理 shard(一个数据库结点)

2. 若垂直切分出的 shard 进行了进一步的水平切分,但公用一个物理 shard 的话,则用虚线框住,表示其在逻辑上是一个独立的 shard。

3. 深色实体表示主表

4.X 表示需要打断的表间关联

转自 http://ift.tt/2ukwwUE

The post DB 分库分表(1):拆分实施策略和示例演示 appeared first on Linuxeden开源社区.

http://ift.tt/2tUPQqQ

没有评论:

发表评论