Spiga

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

2010-03-09 21:51 by Jeffrey Zhao, 13405 visits, 收藏, 编辑

假如您有一个应用程序,随着业务越来越有起色,系统所牵涉到的数据量也就越来越大,此时您要涉及到对系统进行伸缩(Scale)的问题了。一种典型的扩展方法叫做“向上伸缩(Scale Up)”,它的意思是通过使用更好的硬件来提高系统的性能参数。而另一种方法则叫做“向外伸缩(Scale Out)”,它是指通过增加额外的硬件(如服务器)来达到相同的效果。从“硬件成本”还是“系统极限”的角度来说,“向外伸缩”一般都会优于“向上伸缩”,因此大部分上规模的系统都会在一定程度上考虑“向外”的方式。由于许多系统的瓶颈都处在数据存储上,因此一种叫做“数据分片(Database Sharding)”的数据架构方式应运而生,本文便会讨论这种数据架构方式的一种比较典型的实现方式。

简介

数据分片,自然便是将整体数据分摊在多个存储设备(下文统称为“数据分区”或“分区”)上,这样每个存储设备的数据量相对就会小很多,以此满足系统的性能需求。值得注意的是,系统分片的策略有很多,例如常见的有以下几种:

  • 根据ID特征:例如对记录的ID取模,得到的结果是几,那么这条记录就放在编号为几的数据分区上。
  • 根据时间范围:例如前100万个用户数据在第1个分区中,第二个100万用户数据放在第2个分区中。
  • 基于检索表:根据ID先去一个表内找到它所在的分区,然后再去目标分区进行查找。
  • ……

在这些数据分片策略之中没有哪个有绝对的优势,选择哪种策略完全是根据系统的业务或是数据特征来确定的。值得强调的是:数据分片不是银弹,它对系统的性能和伸缩性(Scalability)带来一定好处的同时,也会对系统开发带来许多复杂度。例如,有两条记录分别处在不同的服务器上,那么如果有一个业务是为它们建立一个“关联”,那么很可能表示“关联”的记录就必须在两个分区内各放一条。另外,如果您重视数据的完整性,那么跨数据分区的事务又立即变成了性能杀手。最后,如果有一些需要进行全局查找的业务,光有数据分片策略也很难对系统性能带来什么优势。

数据分片虽然重要,但在使用之前一定要三思而后行。一旦踏上这艘贼船,往往不成功便成仁,很难回头。在我的经验里,一个滥用数据分片策略而事倍功半的项目给我留下了非常深刻的印象(当然也有成功的啦),因此目前我对待数据分片策略变得愈发谨慎。

那么现在,我们便来讨论一种比较常见的数据分片策略。

策略描述

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

  1. 系统中有用户,用户可以发表文章,文章会有评论
  2. 可以根据用户查找文章
  3. 可以根据文章查找评论

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

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

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

分区0 分区1
  • User 4
    • Article 8
    • Article 12
      • Comment 4
      • Comment 16
  • User 12
    • Article 4
  • User 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 4
  • User 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属性为IDENTITY(1, 1),表示ID从1开始,以1为间距自动增长。于是我们在创建数据分区的时候,每个自增列的属性则可以设置为:

  • 分区0:IDENTITY(4, 4)
  • 分区1:IDENTITY(1, 4)
  • 分区2:IDENTITY(2, 4)
  • 分区3:IDENTITY(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的折损很大。为此,我们可以制作一个查询表:例如在查询小于1234567的ID时(这是“老系统”的最大ID),假设是100,则根据查询表得知这条记录的新ID为7654321,再以此去数据源进行查找。解决这类问题的方法还有几种,但无论怎么做都会对新系统带来额外的复杂度。而且,一次扩展也罢,如果以后还要有所扩展呢?

consistent-hahsing

有朋友可能会说,取模自然会带来这样的问题,那么为什么不用一致性哈希(Consistent Hash)呢?现在一致性哈希是个很流行的东西,和Memcached一样,如果不用上就会被一些高级架构师所鄙视。不过在这里一致性哈希也不能解决问题。一致性哈希的目的,是希望“在增加服务器的时候降低数据移动规模,让尽可能多的数据保留在原有的服务器”上。而我们现在的问题却是“在增加服务器的时候,让特征相同的数据同样放在一起”。两个目标不同,这并不是一致性哈希的应用场景。

我在以前的一个项目中曾经用过这样的方法:根据对访问量与数据量的预估,我们认为使用最多24个分区便一定可以满足性能要求(为什么是24个?因为它能被许多数字整除)。于是,从项目第一次在生产环境中部署时便创建了24个数据分区,只不过一开始只用了2台服务器,每台服务器放置12个数据分区。待以后需要扩展时,则将数据分区均匀地迁移到新的服务器上即可。我们团队当时便是用这种方法避免尴尬的数据分配问题。

没错,数据分区的数目是个限制,但您真认为,24个数据分区还是无法满足您的项目需求吗?要知道,需要用上24个数据分区的项目,一般来说本身已经有充分的时间和经济实力进行架构上的重大调整(也该调整了,几乎没有什么架构可以满足各种数据规模的需求)。此外,无论是系统优化还是数据分片都可以同时运用其他手段。

不过,我们目前还是想办法解决这个问题吧。

策略改进

我们之所以会遇到上面这个问题,在于我们没有选择好合适的策略,这个策略把一些重要的“要求”给“具体化”了,导致“具体化”后的结果在外部条件改变的时候,却无法重新满足原有的“要求”。还是以前面的案例来说明问题,其实我们“要求”其实是:

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

而我们“具体化”以后的结果却是:

  • 某个用户的所有文章ID,与这个用户的ID模4后的余数相同。
  • 某篇文章的所有评论ID,与这篇文章的ID模4后的余数相同。

之所以能如此“具体化”,这是因为有“4个分区”这样的前提条件在,一旦这个前提条件发生了改变,则杯具无法避免。因此,我们在制定规则的时候,其实不应该把前提条件给过分的“具体化”——具体化可以,但不能过度,得留有一定空间(这个稍后再谈)。打个比方,还是前面的条件(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-A3
  • User 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-A9
  • User 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-A6
  • User 1
    • Article 1-A4
    • Article 1-A5
      • Comment 1-C3
      • Comment 1-C4
  • User 11
    • Article 11-A12
分区2 分区3
  • User 2
    • Article 2-A7
    • Article 2-A8
      • Comment 2-C5
      • Comment 2-C6
  • User 12
    • Article 12-A3
  • User 7
    • Article 7-A10
    • Article 7-A11
      • Comment 7-C7
      • Comment 7-C8
  • User 8
    • Article 8-A12
    • Article 8-A13
      • Comment 8-C9
      • Comment 7-C10
分区4  
  • User 4
    • Article 4-A1
    • Article 4-A2
      • Comment 4-C1
      • Comment 4-C2
 

是不是很合理?

值得一提的是,只要满足了“特征”这个要求,其实选择分区的方式并没有什么限制。例如,我们可以不用“取模”的方式,而是使用“一致性哈希”——没错,这里就是一致性哈希的使用场景了。在利用“一致性哈希”来选择分区之后,在添加服务器的情况下便可以相对减少数据的迁移数量了。

当然,在实现时还可以运用一些技巧。例如,我们的特征并非一定要“把用户ID作为前缀”——毕竟用户ID可能比较长,作为ID前缀还真有些难看(请想象把GUID作为ID前缀,再加上另一个GUID作为ID主体的情景)。此时,我们可以把前提条件先进行一定程度的“具体化”(但就像之前提到的,不能过度),例如我们可以把用户ID先进行取模,可能是1000万,便可以得到一个落在较大区间范围内的数字。然后,再把这个数字作BASE64编码,一下子前缀就缩小为4个字符以内了。而且,1000万这个区间范围,无论是使用取模还是一致性哈希的方式来选择分区都非常可行,一般不会造成什么问题。

总结

数据分片是系统优化的常用设计方式之一。正如前文所说的那样,数据分片的做法很多,本文提到的方式只是其中一种方式。这种根据ID特征的分片方式比较容易遇到的问题之一,便是在数据分区数量改变时造成的规则冲突,这也正是我这篇文章所讨论的主要内容。从这个角度看来,其他一些分片方式,如创建时间也好,查找表也罢,这样的问题反而不太常见。如果您有这方面的经验或是疑惑,也欢迎与我进行交流。

现在Web 2.0网站越来越热门了,此类项目的数据量也越来越大,从近几年的讨论形式可以看出,越来越多的人在强调什么大规模、高性能、或是海量数据。然后,似乎每个人都会横向切分、纵向切分、缓存、分离。我猜,再接下来,估计又会有许多人以用关系型数据库为耻了吧?但是,想想这样的问题:博客园和JavaEye都是国内技术社区的翘楚,它们都只用了1台数据库服务器。StackOverflow世界上最大的编程网站(它是使用ASP.NET MVC写的,兄弟们记住这个经典案例吧),似乎也只用了1台还是2台数据库服务器(可能配置比较高)及SQL Server。因此,即便是单台服务器,即便是使用关系型数据库,它在性能方面的潜力也是非常之高的。

因此,数据分片应该只在需要的时候才做,因为它带来的复杂度会比中心存储的方式高出很多。这带来的结果是,可能您的应用程序还没有用足架构的能力就已经失败了,这样各种投资也已经浪费了。假如您一开始用最简单的方式去做,可能很快会带来成长所需要空间及资源,此时再做更多投资进行架构优化也不迟——架构不是一蹴而就,而是演变得来的。当然,第一次投入多少复杂度是个需要权衡的东西,这也是考验架构师能力的地方。架构不是空中楼阁,而是各种真实资源调配的结果。

Add your comment

84 条回复

  1. #1楼 麒麟      2010-03-09 21:54
    好长 一时半会消化不了
     回复 引用 查看   
  2. #2楼 麒麟      2010-03-09 21:58
    引用StackOverflow是世界上最大的编程网站

    谢谢老赵,以后多去逛逛
     回复 引用 查看   
  3. #3楼 Shiny Zhu      2010-03-09 22:11
    标题里应该是Sharding吧?
     回复 引用 查看   
  4. #4楼 MicroCoder      2010-03-09 22:11
    What Was Stack Overflow Built With?
    data access layer 是LINQ to SQL
     回复 引用 查看   
  5. #5楼[楼主] Jeffrey Zhao      2010-03-09 22:17
    @Shiny Zhu
    嗯,是的,笔误,谢谢指出。
     回复 引用 查看   
  6. #6楼 yyww1[未注册用户]2010-03-09 22:18
    引用Shiny Zhu:标题里应该是Sharding吧?

     回复 引用   
  7. #7楼 flyingchen      2010-03-09 22:24
    一直被取模所附着
    好文,开阔了思路
     回复 引用 查看   
  8. #8楼 James.Ying      2010-03-09 22:26
    两篇文章跨度好大,这次看懂了些
     回复 引用 查看   
  9. #9楼[楼主] Jeffrey Zhao      2010-03-09 22:37
    @James.Ying
    想到什么写什么,胡思乱想胡思乱写,嘿嘿。
     回复 引用 查看   
  10. #10楼 Cheney Shue      2010-03-09 22:42
    在大型应用中一般使用IBM货Oracle的数据库,这些产品支持集群,不需要操心数据物理的分布。

    之前参加的一个项目中,客户采购的是SQL Server,性能满足不了。就用了这里所谓的“向外伸缩”方法。一开始加了一台数据库,将业务流程分成两部分,数据作垂直拆分。后来还是不行,又加了两台,将之前两个数据库的数据结构复制过来,数据作平行分配,一半的数据由新的数据库处理。

    后来就出问题了,数据的同步成本高,垂直分布的数据库之间耦合太紧,风险非常高,经常因为数据流中的一个小问题,使整个系统瘫痪。
     回复 引用 查看   
  11. #11楼       2010-03-09 22:43
    之前研究过,发现这种技术和现在的磁盘管理是一致的。

    而且磁盘管理更成熟,比如扇区、分页等等理论。可以直接套用。甚至可以考虑RAID之类的策略,和CPU一级、二级内存的命中策略之类的。

    至于取模这种,还是不要去想了,部署已经很费力了,扩展起来也不方便。

    个人感觉,需要中间表去动态映射,这个中间表是变化的,为了提高性能。最终用户数据是静态的,通过主键索引来获取。

    如果这条路走下去,那么自然就会到达DHT的模式,可能会有4、5个服务器做Chord环,里面可能会建立2^n的虚拟数据库节点。节点之间存在备份关系。

    然后在这个chord环上面使用一个比较高性能的数据库做中间映射。这样就会 reduce to google 提出的分布式处理架构了。
     回复 引用 查看   
  12. #12楼 wuxiaoqqqq      2010-03-09 22:47
    这篇文章对我比前面的F#有用,现在的数据增长确实很快,现在数据库都说要不进行关联查询了,做冗余,或者从缓存取关联数据。
     回复 引用 查看   
  13. #13楼       2010-03-09 22:49
    顺便说说cnblogs为啥只有1台db,我个人觉得首先是资金有限,其次是技术局限。

    如果搭建了多个db,就要架设SAN网络,这下子就几十万几百万没有了。

    如果用开源、或者自己开发,这要求技术相当相当的过硬。数据库之间同步备份之类的。 当时看豆瓣的技术文档,他们为了搭建Douban-chord,投了一大堆钱,连续升级了3、4次服务器,对memcached代码修改,一大堆东西。
     回复 引用 查看   
  14. #14楼[楼主] Jeffrey Zhao      2010-03-09 22:52
    @Cheney Shue
    你又来了,找一切机会说SQL Server坏话么,虽然每次都不说清楚,呵呵。
    Oracle集群的能力也是有限的,不能成为数据Sharding的替代方案,你也可以把一个Oracle集群作为一个数据分区。
    另外你是做企业项目的,我是做互联网的,所以你不差钱,上企业产品跟玩儿似的。
    还有就是我这里也不是单只关系型数据库,其实和其他存储方式在相当程度上也是一样的。
     回复 引用 查看   
  15. #15楼[楼主] Jeffrey Zhao      2010-03-09 22:54
    @
    多个DB为什么就要SAN了,随便一个交换机不就可以了,互联网项目不都是这样搞的么,我那24个数据库服务器还没SAN呢,SAN和Sharding是两个不相干的问题。
     回复 引用 查看   
  16. #16楼[楼主] Jeffrey Zhao      2010-03-09 22:56
    引用wuxiaoqqqq:这篇文章对我比前面的F#有用,现在的数据增长确实很快,现在数据库都说要不进行关联查询了,做冗余,或者从缓存取关联数据。

    呵呵,但是这里我可以提出一个问题:由于数据分片了,因此有某个查询需要从多台服务器上找出,那么你如何发出并行、异步的请求并汇总呢?F#在这里简直是无与伦比的方便,它在并行、异步、响应式编程方面支持的非常好。
     回复 引用 查看   
  17. #17楼[楼主] Jeffrey Zhao      2010-03-09 22:58
    @
    我看了看你的话,发现你说的和我这篇文章好像没在谈一个东西。
    其实用取模的方式,只要是我后面改进过的做法,问题也不大,只是扩展时数据迁移会多不少,其他依旧很方便。
    还有便是,你一直提到DHT,然后总是说通过主键取,其实在业务略微复杂一点的时候,用非主键做列表检索是必然会出现的。
    即便有些场景可以转化成用DHT存储,也带来了许多额外的复杂度。
     回复 引用 查看   
  18. #18楼       2010-03-09 23:03
    @Jeffrey Zhao
    主要觉得取模的思路,在部署和日后维护会成本很高。比如 有天发现数据丢失等等,会难找到问题。

    至于SAN之类,IBM等有雄厚的解决方案,只要有钱,这些就不是问题了。
     回复 引用 查看   
  19. #19楼[楼主] Jeffrey Zhao      2010-03-09 23:05
    @
    所以SAN还是企业们玩吧,他们都是花钱买安心,咱和博客园都是互联网玩家,都是还需要人赞助的那种……
     回复 引用 查看   
  20. #20楼 blackcat      2010-03-09 23:47
    @Cheney Shue
    数据横向切片和纵向切片都是要满足一定条件的。
    应用水平不足的话,宕机是一定的。
     回复 引用 查看   
  21. #21楼 徐少侠      2010-03-10 07:40
    还是比较有启发的
    我一直就有按ID分区数据的一个想法,但是SQL Server里面对于水平分区还是有不少限制,调整起来也要用点代码

    在多数情况下老赵这个思路还是可以用的

    比如在我这里的这个场景里,整个系统中其实需要分区的表就只有1到2个。实际业务的流水记录表。
    如果不分区,可能用户数稍多的情况下就要达到数亿行记录,并且伴随用户数线性增长
    即使对用户ID索引,平时的查询还是要进行庞大的扫描,整个表是没有时间字段,且一个用户的所有数据会散布在很宽广的时间范围内

    最理想的是能按用户或一组用户进行水平拆分,其实不需要拆分过细,能拆成10个就能大大降低扫描时间,且可以将时间永远控制在一个可接受的范围内了,因为可以控制一组内的用户数量,间接就控制了一个分组的数据规模了




     回复 引用 查看   
  22. #22楼 Sunny Peng      2010-03-10 09:11
    你的文章至少要认真读才能读懂,涨见识了。
     回复 引用 查看   
  23. #23楼 第一控制.NET      2010-03-10 09:28
    记得某年teched你的session后我问过你这个问题。。
     回复 引用 查看   
  24. #24楼 永不言败      2010-03-10 09:30
    策略改进,4个改为5个后,UserID又要重新调整分区..这个不是意味着要移动数据?
     回复 引用 查看   
  25. #25楼 Cat Chen      2010-03-10 09:36
    将来一般企业需要做这种研究的机会越来越少了,只有 cloud host 需要研究群集的技术,一般企业把应用放上去就可以了。
     回复 引用 查看   
  26. #26楼 妖居      2010-03-10 09:47
    老赵的文风好像有了些许微妙的变化。呵呵。不过数据分片确实是我从来没有想到的内容。现在我在学习Windows Azure,里面有个Table Storage存储,要求每个entity都有个字段partition key用来做Load Balance的。而且为partition key选值很有讲究,相同的partition key会被保留在同一个虚拟机上,感觉微软就是使用了类似1-A2、1-A3……的方式进行分片管理的。
     回复 引用 查看   
  27. #27楼[楼主] Jeffrey Zhao      2010-03-10 09:52
    引用永不言败:策略改进,4个改为5个后,UserID又要重新调整分区..这个不是意味着要移动数据?

    分区数量改变,原来的4个分区的数据要放到5个分区里去,总归要数据移动的啊。
     回复 引用 查看   
  28. #28楼 Kevan      2010-03-10 09:58
    很好,受益匪浅,感谢老赵的分享。
     回复 引用 查看   
  29. #29楼 道法自然      2010-03-10 10:44
    老赵写的不错,只是一旦分片就涉及到分布式,而且,万一有一个节点不工作的话,就有麻烦了(如果考虑可靠性的话,每个分片需要使用2个节点,那问更复杂了)。分片的伸缩性较好,不过都是有所得有所失。是否考虑过数据库复制、集群以及Data Fabric,并对它们进行比较?我觉得从效率、可靠性和伸缩性来讲,或许采用后3者的任何一种就能够满足绝大部分的应用了。
     回复 引用 查看   
  30. #30楼[楼主] Jeffrey Zhao      2010-03-10 10:48
    @道法自然
    分片不代表不需要复制,备份等常见操作啊。
    例如有10台服务器,10个数据分区,每个数据分区会replicate到相邻的服务器上,既是备份又是failover。
    一个“数据分区”不是指一台“服务器”等具体的概念,它是一个“抽象”。
     回复 引用 查看   
  31. #31楼 韦恩卑鄙 v-zhewg @waynebaby      2010-03-10 10:54
    最近老赵发的东西,我都没啥可说的。 - -
     回复 引用 查看   
  32. #32楼 道法自然      2010-03-10 10:59
    @Jeffrey Zhao

    明白你意思了。如果是在同一台服务器的话,分片和索引性能又有什么区别呢?同一台服务器的话,优化的方法不就更多了?如果物理上分片的话,我感觉更多考虑的是硬件的伸缩性,好像性能相比其它架构并不能真正提高。你觉得呢?
     回复 引用 查看   
  33. #33楼 韦恩卑鄙 v-zhewg @waynebaby      2010-03-10 11:03
    @道法自然
    比如瓶颈在io 可以建立多个库 把shards分散到多个物理硬盘啊
    比如瓶颈在并发连接数 可以建立多个数据库instance 每个绑定一块物理网卡 或者多个虚拟机 每个绑定一个物理网卡啊

    分布式运算主要是解决瓶颈的 瓶颈在哪里 就解决哪里
     回复 引用 查看   
  34. #34楼[楼主] Jeffrey Zhao      2010-03-10 11:05
    @道法自然
    分片的目的当然就是放在多台服务器上,为什么要讨论放在一台服务器上的情况……
    如果数据是存在一起的,那么往往只能scale up,而分片之后就可以scale out了。
     回复 引用 查看   
  35. #35楼 韦恩卑鄙 v-zhewg @waynebaby      2010-03-10 11:06
    老赵啊 你只说了怎么分片 没说分片后 用什么样的方式组合都能解决什么问题 所以讨论有点乱哦
     回复 引用 查看   
  36. #36楼 韦恩卑鄙 v-zhewg @waynebaby      2010-03-10 11:07
    引用Jeffrey Zhao:
    @道法自然
    分片的目的当然就是放在多台服务器上,为什么要讨论放在一台服务器上的情况……

    额,讨论一台机器情况的优化方式我刚说了>_<
     回复 引用 查看   
  37. #37楼[楼主] Jeffrey Zhao      2010-03-10 11:08
    @韦恩卑鄙 v-zhewg @waynebaby
    没懂你的意思啊,我讨论的就是一种分片方式,为了能够分离I/O压力,还应该谈哪些东西啊?
     回复 引用 查看   
  38. #38楼[楼主] Jeffrey Zhao      2010-03-10 11:10
    @韦恩卑鄙 v-zhewg @waynebaby
    喔喔,你说的应该也是可以的,总之就是把压力分散开去。
     回复 引用 查看   
  39. #39楼 道法自然      2010-03-10 11:17
    引用Jeffrey Zhao:
    @道法自然
    分片的目的当然就是放在多台服务器上,为什么要讨论放在一台服务器上的情况……
    如果数据是存在一起的,那么往往只能scale up,而分片之后就可以scale out了。


    明白你意思了。其实你的分片最多考虑的还是硬件的成本和伸缩性了。如果这个限制取消掉的话,它的性能应该是比不上复制、集群或者DataFabric了。因为一片数据分到10台机器,相当于建立了一个分布式索引 + 本地索引,如果一片数据只放到一台机器,相当于建立了一个本地索引。对吧?这些纯属于理论上的争辩,我并没有真正实践过,如果显得无知,多多包涵。
     回复 引用 查看   
  40. #40楼 道法自然      2010-03-10 11:27
    引用韦恩卑鄙 v-zhewg @waynebaby:
    @道法自然
    比如瓶颈在io 可以建立多个库 把shards分散到多个物理硬盘啊
    比如瓶颈在并发连接数 可以建立多个数据库instance 每个绑定一块物理网卡 或者多个虚拟机 每个绑定一个物理网卡啊

    分布式运算主要是解决瓶颈的 瓶颈在哪里 就解决哪里


    明白,只是要达到一个目的,方法有很多种。我觉得自己引入分片的话,太复杂了,甚至糟蹋了现有关系数据库的一些功能了。分布式的第一原则就是“尽量不使用分布式”。


    引用Jeffrey Zhao:
    @韦恩卑鄙 v-zhewg @waynebaby
    喔喔,你说的应该也是可以的,总之就是把压力分散开去。


    特别是均衡负载,关系数据库已经提供这种功能了。
     回复 引用 查看   
  41. #41楼 韦恩卑鄙 v-zhewg @waynebaby      2010-03-10 11:27
    @道法自然
    还是那个raid比较能说明问题呢

    "差异分片" 写效率成倍增加 读效率不变
    "映像分片" 写效率不变 读效率成倍增加

    群集么 更像是 "差异分片"在前 "后台同步映像"在后 是一种万用折衷方案吧 导致读写效率都无法提高到一个理想的极限。

    对于web应用 我觉得是并发写入的lock会比较成为负担,而并发读取的每一个请求太小,并不会享受到映像分片的好处 所以我觉得差异分片更适合
     回复 引用 查看   
  42. #42楼[楼主] Jeffrey Zhao      2010-03-10 11:28
    @道法自然
    也没有简单的这种说法吧,如果分片的话,就算在一台机器上,也相当于在一部分数据里建立索引,建立查询,理论上还是比所有数据的索引性能高。
    但是数据分片的缺陷就是会限制可以使用的查询方式,例如一些全局的查询可能就要跨分区或者额外方式了。
     回复 引用 查看   
  43. #43楼 道法自然      2010-03-10 11:30
    引用韦恩卑鄙 v-zhewg @waynebaby:
    @道法自然
    还是那个raid比较能说明问题呢

    "差异分片" 写效率成倍增加 读效率不变
    "映像分片" 写效率不变 读效率成倍增加

    群集么 更像是 "差异分片"在前 "后台同步映像"在后 是一种万用折衷方案吧 导致读写效率都无法提高到一个理想的极限。

    对于web应用 我觉得是并发写入的lock会比较成为负担,而并发读取的每一个请求太小,并不会享受到映像分片的好处 所以我觉得差异分片更适合


    现在又提出了一个Data Fabric技术,据说它的效率是传统OLTP的5倍。Data Fabric是在Cluster前端加一层类似Memcached的Cluster。
     回复 引用 查看   
  44. #44楼[楼主] Jeffrey Zhao      2010-03-10 11:31
    引用道法自然:
    明白,只是要达到一个目的,方法有很多种。我觉得自己引入分片的话,太复杂了,甚至糟蹋了现有关系数据库的一些功能了。分布式的第一原则就是“尽量不使用分布式”。

    我文章里也说了,分片会引入很多问题,采用的话需要谨慎。

    引用特别是均衡负载,关系数据库已经提供这种功能了。

    不一样。
    数据库的那种负载均衡类似于Master-Master,或Master-Slave方式。每台服务器上都放置完整的数据,然后通过复制来同步数据,以此负载均衡。
    而数据分片是每台服务器放置1/n的数据,不需要通过复制来同步数据。
     回复 引用 查看   
  45. #45楼 道法自然      2010-03-10 11:32
    引用Jeffrey Zhao:
    @道法自然
    也没有简单的这种说法吧,如果分片的话,就算在一台机器上,也相当于在一部分数据里建立索引,建立查询,理论上还是比所有数据的索引性能高。
    但是数据分片的缺陷就是会限制可以使用的查询方式,例如一些全局的查询可能就要跨分区或者额外方式了。


    OK,看来我们还是需要好好比较这几个数据库架构的优缺点,并根据实际需求进行选择。
     回复 引用 查看   
  46. #46楼 康康      2010-03-10 11:32
    这种方式还是有较大局限,全局查询会有性能问题。

    不过是否可以这样,我们把关键数据都保存在一个DB上以供查询,把数据量大,但几乎不可能做全局查询的数据按照老赵的方式部署,例如老赵的例子:我们把用户信息、文章关键信息(文章ID、标题、作者等)放在查询DB上,把文章内容,文章评论等附属信息都放在分片DB上,这样各方面的性能可以得到提升。

    当然这种方式在做设计时要多做考虑。
     回复 引用 查看   
  47. #47楼 韦恩卑鄙 v-zhewg @waynebaby      2010-03-10 11:32
    引用道法自然:
    现在又提出了一个Data Fabric技术,据说它的效率是传统的5倍。Data Fabric是在Cluster前端加一层类似Memcached的Cluster。

    但是对这个需求来说他不会比memcache +老赵目前的分片 更优化啊。
    企业级的东西 往往隐藏太多的细节 而无法用最直接的方式满足最简单的需求。 嗯 就好像咏春拳和截拳道。
     回复 引用 查看   
  48. #48楼[楼主] Jeffrey Zhao      2010-03-10 11:33
    引用道法自然:
    现在又提出了一个Data Fabric技术,据说它的效率是传统OLTP的5倍。Data Fabric是在Cluster前端加一层类似Memcached的Cluster。

    我觉得没有什么关系,它应该不是OLTP的替代方案,它只能解决一部分如“根据主键”查询的性能。
     回复 引用 查看   
  49. #49楼[楼主] Jeffrey Zhao      2010-03-10 11:35
    引用康康:
    这种方式还是有较大局限,全局查询会有性能问题。

    这就是数据分片需要面对的问题之一,全局查询需要跨分区,或者使用额外的全局索引。
     回复 引用 查看   
  50. #50楼 韦恩卑鄙 v-zhewg @waynebaby      2010-03-10 11:36
    举一个可能不太恰当的例子
    百度贴吧 生肖吧 和 音乐吧 大可不必在一个数据库,完全没必要。
    一个web应用到底有多少“搜索引擎外”的全局搜索需求呢? 是不是做好seo就没必要提供全站搜索了呢 ^_^
     回复 引用 查看   
  51. #51楼 康康      2010-03-10 11:39
    @Jeffrey Zhao
    所以我提出少量信息置于“单”服务器,把多而复杂的信息置于你说的“分片”服务器上,两者结合起来用是不是会好一点?

    毕竟你这种全局查询是比较耗性能的 :)
     回复 引用 查看   
  52. #52楼[楼主] Jeffrey Zhao      2010-03-10 11:46
    @康康
    具体策略可能还要考虑。
    不过根据我的经验,基本上有分区就会有全局的数据存储,反之则显然……不亦然。
     回复 引用 查看   
  53. #53楼 ITniao      2010-03-10 11:50
    除了给你顶,我还能说什么?
     回复 引用 查看   
  54. #54楼 Robin Zhang      2010-03-10 12:04
    @Cat Chen
    非常不幸的是,目前Cloud技术在数据库存储方面表现是非常差劲的。
    性价比奇差,因为大部分Cloud的磁盘写IO对于顺序写比较友好,而对于有update的io操作性价比奇差的。
     回复 引用 查看   
  55. #55楼 Robin Zhang      2010-03-10 12:07
    当然针对搜索引擎,cloud非常适合,因为正好迎合了cloud中磁盘IO的顺序写的要求,而其他大型企业应用上,在cloud上支持db是不可行的,至少现在目前这些大公司的cloud也很难在这方面有突破。部分厂商只能提供的是存储服务,类似于数据备份这种,而不是db这种
     回复 引用 查看   
  56. #56楼 道法自然      2010-03-10 13:41
    引用Jeffrey Zhao:
    引用道法自然:
    现在又提出了一个Data Fabric技术,据说它的效率是传统OLTP的5倍。Data Fabric是在Cluster前端加一层类似Memcached的Cluster。

    我觉得没有什么关系,它应该不是OLTP的替代方案,它只能解决一部分如“根据主键”查询的性能。


    按我理解来看,它确实是想替代OLTP,因为它基于IMDB,支持读和写,由于数据库的操作是基于IMDB的(前端是基于IMDB的Cluster),因此其性能肯定要更高,但实际实现或者实际应用如何,现在还无法下结论。
     回复 引用 查看   
  57. #57楼 韦恩卑鄙 v-zhewg @waynebaby      2010-03-10 13:42
    引用Cat Chen:将来一般企业需要做这种研究的机会越来越少了,只有 cloud host 需要研究群集的技术,一般企业把应用放上去就可以了。

    一般企业欢迎用 MOSS 哇哈哈哈

    弄个farm 弄个群集 是不错滴
     回复 引用 查看   
  58. #58楼 Kevin Dai      2010-03-10 15:09
    这是老赵在“架构”方面的第一篇文章呐,期待后续啊。
     回复 引用 查看   
  59. #59楼[楼主] Jeffrey Zhao      2010-03-10 15:15
    @Kevin Dai
    嘿嘿,你果然就喜欢这些虚的装逼的东西……
     回复 引用 查看   
  60. #60楼 韦恩卑鄙 v-zhewg @waynebaby      2010-03-10 16:31
    老赵你骂人
     回复 引用 查看   
  61. #61楼 陛下      2010-03-10 16:45
    居然没有人“推荐”,太可耻了!:)
    海量数据、性能瓶颈,这些东西我觉得算是值得讨论的、比较高级的点。总介绍新技术、开发语言,麻木了,呵呵。
    取模分片我总觉得不够自然,不知道从业务逻辑上进行分类是不是好些。比如,搜索中分“博客搜索”、“新闻搜索”等等。
    不过通过你的介绍,看来这是一种比较常用的向外伸缩的方式,值得一试。
     回复 引用 查看   
  62. #62楼[楼主] Jeffrey Zhao      2010-03-10 16:49
    @陛下
    没有好坏之分,都是拆分方式,根据需要来。
    打个比方,你一定知道横向拆分,纵向拆分吧?
    你说的拆分方式和我这里说的数据分片方式,一个就属于横向,一个属于纵向。
    // 到底哪个是哪个我就对应不起来了
     回复 引用 查看   
  63. #63楼 陛下      2010-03-10 17:45
    @Jeffrey Zhao
    多谢指点,学习!
     回复 引用 查看   
  64. #64楼 我是游客[未注册用户]2010-03-10 20:16
    写的很不错,不过好像没有谈到数据库层之上的应用层,怎么去寻找正确的db server,如何去和现有的数据访问技术结合的关键点
     回复 引用   
  65. #65楼 我是游客[未注册用户]2010-03-10 20:19
    关于failover, sqlserver的mirroring不失为一种简单可行的方案
     回复 引用   
  66. #66楼 Kevin Dai      2010-03-10 20:27
    @Jeffrey Zhao
    靠,早知道不评论的……
     回复 引用 查看   
  67. #67楼 zzfff      2010-03-11 00:34
    既然企业不差浅,那就赚企业的钱去:)
    我喜欢举k2.com的例子(也许还有更好的例子)。MOSS牛逼吧?k2“硬”是在MOSS上再造了一层软件/平台,卖给企业。
    其实m$的产品离最终用户还是有距离的,这是3rd party的舞台,这也是微软刻意营造的生态链,3rd party的繁荣对大老板百利无一害。
    3rd party也是有高有低的。
     回复 引用 查看   
  68. #68楼 锦瑟无端五十弦      2010-03-11 04:33
    感觉这样的应用放在云中比较好,Sql Azure。
     回复 引用 查看   
  69. #69楼 Robin Zhang      2010-03-11 09:52
    @锦瑟无端五十弦
    不要迷恋云,云就是个传说!
     回复 引用 查看   
  70. #70楼 黑羽飘舞      2010-03-11 10:49
    以博客园这么大的访问量是如何用一台数据库服务器进行承载的?用的是什么解决方案?老赵清楚么?
     回复 引用 查看   
  71. #71楼[楼主] Jeffrey Zhao      2010-03-11 10:50
    @黑羽飘舞
    没啥解决方案,就是优化优化好吧,加加缓存。
    缓存是个万能狗皮膏药,哪里不爽贴一点就好了。
     回复 引用 查看   
  72. #72楼 黑羽飘舞      2010-03-11 10:57
    @Jeffrey Zhao
    一般的具体实现方法,是否是实现的从缓存读取数据,调整命中率.
    对于写入操作使用信息队列,使用异步模式写入?
     回复 引用 查看   
  73. #73楼 mrxliu      2010-03-11 15:01
    @黑羽飘舞
    我觉得:消息队列对于下订单,发邮件我觉得还不错。但对于论坛发帖子,添加新用户这种,还是直接对数据库访问好。不知道我说得对不对,不对之处,请指出。
     回复 引用 查看   
  74. #74楼[楼主] Jeffrey Zhao      2010-03-11 15:36
    @黑羽飘舞
    都要根据你的业务来。比如,你的一些操作要即时,那么可能就不能用队列了。
     回复 引用 查看   
  75. #75楼 玉开      2010-03-12 16:05
    不够深入,期待分享更深入的研究。

    数据量到了需要sharding的规模,可能就不需要选择数据库了。

    建议你研究google的大表和facebook开源的cassandra。
     回复 引用 查看   
  76. #76楼[楼主] Jeffrey Zhao      2010-03-12 16:10
    @玉开
    呵呵,一直关注中,没看到它们在我博客边栏都有1年多了吗?
    不过,你应该可以发现这里的sharding策略并非只能用在关系型数据库中。
    例如,我现在就是用类似的方式配合MongoDB使用的。
     回复 引用 查看   
  77. #77楼 蛙蛙王子      2010-03-13 09:40
    老赵研究开存储了呀,要抢DBA饭碗呀,呵呵。

    DB规模不断增大时,一般都会用这种分片的方式来提高可扩展性,实际实施的时候还会有一些复杂的问题,在添加分片时往往还需要迁移用户的数据,迁移过程中用户的业务得中断,数据复杂时,迁移的很复杂,得自己实现一套事务。

    现在研究云计算的人越来越多,一些大公司也都考虑不用盘阵,采用分布式存储的方式来提高DB的可伸缩性,降低成本。

    上次谁发的那个no-sqldb,我想了想,感觉没什么使用场景,就用你说的这种分片方式可以解决大多数问题了。

    当时还发了个讨论贴。
    http://www.cnblogs.com/onlytiancai/archive/2010/03/02/1676222.html
     回复 引用 查看   
  78. #78楼 绝代恭敬      2010-03-16 16:44
    可以结合按照使用热度来分,建立存储,关联id和热度值
    把热度高的数据,移动出来到N块固定的区域,这些区域数据比较集中,但不多,热度不高的丢在其他块,这样做是增加了负责度,但是绝对后期维护容易
     回复 引用 查看   
  79. #79楼 田景      2010-03-18 09:13
    请教老赵:
    在“缺陷”这一节有这么一段:

    ...假设我们原有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。
    ...
    我们既然是根据用户ID作为我们分区的依据,那这里为何还要考虑同一个用户(你这里举例为用户1)文章ID的重新计算文章所在的分区呢?文章应该是跟着用户ID走,用户ID到了哪个分区,文章就在哪个分区啊?
    谢谢!
     回复 引用 查看   
  80. #80楼[楼主] Jeffrey Zhao      2010-03-18 09:21
    @田景
    再仔细看一下文章吧。
     回复 引用 查看   
  81. #81楼 刘晓飞      2010-03-29 14:06
    @Jeffrey Zhao
    引用Jeffrey Zhao:
    引用永不言败:策略改进,4个改为5个后,UserID又要重新调整分区..这个不是意味着要移动数据?

    分区数量改变,原来的4个分区的数据要放到5个分区里去,总归要数据移动的啊。


    4->8不用挪动数据,只是这种方案有个缺点是每次增加服务器的数量是原有服务器的2倍
     回复 引用 查看   
  82. #82楼[楼主] Jeffrey Zhao      2010-03-29 14:16
    引用刘晓飞:
    4->8不用挪动数据,只是这种方案有个缺点是每次增加服务器的数量是原有服务器的2倍

    仔细想想,真的不需要移动吗?
     回复 引用 查看   
  83. #83楼 刘晓飞      2010-03-29 17:46
    @Jeffrey Zhao
    真的不需要,将各个slave升级为master,数据自然不用移动,不过每次都有一部分冗余数据,如果不能容忍的话可以写个程序删除掉
     回复 引用 查看   
  84. #84楼[楼主] Jeffrey Zhao      2010-03-29 17:55
    @刘晓飞
    那么你理解错文章内容了,这里没有slave和master,这里不是master-slave的模式。
    我文章里写的很清楚,例子也举的很明白,就算4个扩展到8个,还是有问题的(不光是数据冗余)
     回复 引用 查看   
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1681770 tPYSNnWJMwg=