一种以ID特征为依据的数据分片(Sharding)策略

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

这里我先描述一个极其简单的业务:

1. 系统中有用户,用户可以发表文章,文章会有评论

2. 可以根据用户查找文章

3. 可以根据文章查找评论

那么,如果我要对这样一个系统进行数据分片又该怎么做呢?这里我们可以使用上面提到的第一种方式,即对记录的ID取模,并根据结果选择数据所在的分区。根据后两条业务中描述的查询要求,我们会为分区策略补充这样的规则:

某个用户的所有文章,与这个用户处在同一数据分区内。

某篇文章的所有评论,与这篇文章处在用一数据分区内。

您可能会说,似乎只要保证“相同用户文章在同一个数据分区内”就行了,不是吗?没错,不过我这里让文章和用户在同一个分区内,也是为了方便许多额外的操作(例如在关系数据库中进行连接)。那么假设我们有4个数据分区,那么它们内部的条目可能便是:

分区0分区1

User 4 Article 8 Article 12 Comment 4 Comment 16 User 12 Article 4User 1 Article 5 Article 9 Comment 13 Comment 17 User 5 Article 13

分区2分区3

User 2 Article 10 Article 14 Comment 6 Comment 10 User 10 Article 4User 7 Article 7 Article 11 Comment 3 Comment 15 User 11 Article 4

在ID为0的分区中,所有对象的ID模4均为0,其他分区里的对象也有这样的规律。那么好,在实际应用中,如果我们需要查找“ID为2的用户”,便去第2分区搜索便是;如果要查找“ID为8的文章的所有评论”那么也只要去第0分区进行一次查询即可。既然查询不成问题,那么我们该如何添加新记录呢?其实这也不难,只要:

添加新用户时,随机选择一个数据分区

添加新文章时,选择文章作者所在分区(可根据Article的UserID求模得到)

添加新评论时,选择文章所在分区(可根据Comment的ArticleID求模得到)

但是,我们又如何保证新纪录的ID正好满足我们的分区规律?例如我们向第3分区添加的新数据,则它的ID必须是3、7、11等等。以前,我们可能会使用数据库的自增列作为ID的值,但这似乎不能满足我们“取模”的要求。以前我们可能还会使用GUID,但是我们如何生成一个“被4模于3”的GUID呢?其实我们还是可以使用自增ID来解决这个问题,只不过需要进行一些简单的设置。例如在SQL Server中,默认的自增ID属性为IDENT IT Y(1, 1),表示ID从1开始,以1为间距自动增长。于是我们在创建数据分区的时候,每个自增列的属性则可以设置为:

分区0:IDENT IT Y(4, 4)

分区1:IDENT IT Y(1, 4)

分区2:IDENT IT Y(2, 4)

分区3:IDENT IT Y(3, 4)

这样,ID方面的问题便交由数据库来关心吧,我们的使用方式和以前并没有什么区别。

缺陷

那么这个数据分片策略有什么缺陷呢?当然缺陷还是有很多啦,只是大多数问题可能还是要和业务放在一起考虑时才会凸显出来。不过有一个问题倒和业务关系不大:如果数据继续增长,单个数据分区的数据量也超标了,怎么办?

自然,继续拆分咯。那么我们使用什么分区规则呢?和原先一致吗?我们举个例子便知。假设我们原有4个分区,有一个ID为1的用户落在第1分区里,他的文章也都在这个分区里,ID分别是1、5、9、13、17等等。于是在某一天,我们需要将分区数量提高到5个(财力有限,一台一台来吧),在重新计算每篇文章所在的分区之后,我们忽然发现:

ID为1的文章,模5余1,处在分区1。

ID为5的文章,模5余0,处在分区0。

ID为9的文章,模5余4,处在分区4。

ID为13的文章,模5余3,处在分区3。

ID为17的文章,模5余2,处在分区2。

呼,5个分区都齐了!这说明,如果我们保持记录原来的ID不变,是没有办法直接使用之前的分区规则——无论您扩展成几个分区,(即便是从4个到8个)也只能“缓解”也不能“解决”这个情况。那么这时候该如何是好呢?例如,我们可以重新分配记录,改变原有ID,只是这么做会产生一个问题,便是外部URL可能也会随着ID一起改变,这样对SEO的折损很大。为此,我们可以制作一个查询表

则杯具无法避免。因此,我们在制定规则的时候,其实不应该把前提条件给过分的“具体化”——具体化可以,但不能过度,得留有一定空间(这个稍后再谈)。打个比方,还是前面的条件(XX

和XX处在同一数据分区内),但我们换一种具体化的方式:

某个用户的所有文章ID的前缀,便是这个用户的ID。例如,ID为1的用户的所有文章,其ID便可能是1-A1、1-A2、1-A3……

某篇文章的所有评论ID,与这个文章的ID使用相同前缀。例如,ID为3-A1的文章的所有评论,其ID便可能是3-C1、3-C2、3-C3……

使用这个策略,我们便可以保证与某个用户相关的“所有数据”都共享相同的“特征”(ID的前缀都

相同),然后我们便可以根据这个特征来选择分区——例如,还是以“取模”的方式。此时,我们已经确保了“相同分区内的所有数据都具备相同的特征”,即便分区数量有所调整,我们也只需要根据特征重新计算分区即可,影响不大。而以前为什么不行?因为“模4的余数”只是“结果”而不是“特征”,这里的“特征”应该是“追本溯源后的用户ID相同”,而这一点已经体现在新的策略中了。

还是通过图示来说明问题吧。假设原有4个分区,使用“取模”的策略:

分区0分区1

User 4

Article 4-A1 Article 4-A2 Comment 4-C1 Comment 4-C2 User 12

Article 12-A3User 1

Article 1-A4 Article 1-A5 Comment 1-C3 Comment 1-C4 User 5

Article 5-A6

分区2分区3

User 2

Article 2-A7 Article 2-A8 Comment 2-C5 Comment 2-C6 User 10

Article 10-A9User 7

Article 7-A10 Article 7-A11 Comment 7-C7 Comment 7-C8 User 11

Article 11-A12

当分区数量调整为5个之后(为了避免分区3空缺,我又补充了一些对象):分区0分区1

User 10 Article 10-A9 User 5 Article 5-A6User 1

Article 1-A4 Article 1-A5 Comment 1-C3 Comment 1-C4

相关文档
最新文档