分布式开发+Linux集群

  • 简单说,分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率。

    例如:

    如果一个任务由10个子任务组成,每个子任务单独执行需1小时,则在一台服务器上执行改任务需10小时。

    采用分布式方案,提供10台服务器,每台服务器只负责处理一个子任务,不考虑子任务间的依赖关系,执行完这个任务只需一个小时。(这种工作模式的一个典型代表就是Hadoop的Map/Reduce分布式计算模型)

    而采用集群方案,同样提供10台服务器,每台服务器都能独立处理这个任务。假设有10个任务同时到达,10个服务器将同时工作,10小后,10个任务同时完成,这样,整身来看,还是1小时内完成一个任务!

     

    以下是摘抄自网络文章:

    集群概念

    1. 两大关键特性 
    集群是一组协同工作的服务实体,用以提供比单一服务实体更具扩展性与可用性的服务平台。在客户端看来,一个集群就象是一个服务实体,但事实上集群由一组服务实体组成。与单一服务实体相比较,集群提供了以下两个关键特性:

    ·  可扩展性--集群的性能不限于单一的服务实体,新的服务实体可以动态地加入到集群,从而增强集群的性能。

    ·  高可用性--集群通过服务实体冗余使客户端免于轻易遇到out of service的警告。在集群中,同样的服务可以由多个服务实体提供。如果一个服务实体失败了,另一个服务实体会接管失败的服务实体。集群提供的从一个出 错的服务实体恢复到另一个服务实体的功能增强了应用的可用性。

    2. 两大能力 
    为了具有可扩展性和高可用性特点,集群的必须具备以下两大能力:

    ·  负载均衡--负载均衡能把任务比较均衡地分布到集群环境下的计算和网络资源。

    ·  错误恢复--由于某种原因,执行某个任务的资源出现故障,另一服务实体中执行同一任务的资源接着完成任务。这种由于一个实体中的资源不能工作,另一个实体中的资源透明的继续完成任务的过程叫错误恢复。

    负载均衡和错误恢复都要求各服务实体中有执行同一任务的资源存在,而且对于同一任务的各个资源来说,执行任务所需的信息视图(信息上下文)必须是一样的。

    3. 两大技术 
    实现集群务必要有以下两大技术:

    ·  集群地址--集群由多个服务实体组成,集群客户端通过访问集群的集群地址获取集群内部各服务实体的功能。具有单一集群地址(也叫单一影像)是集群的一个基 本特征。维护集群地址的设置被称为负载均衡器。负载均衡器内部负责管理各个服务实体的加入和退出,外部负责集群地址向内部服务实体地址的转换。有的负载均 衡器实现真正的负载均衡算法,有的只支持任务的转换。只实现任务转换的负载均衡器适用于支持ACTIVE-STANDBY的集群环境,在那里,集群中只有 一个服务实体工作,当正在工作的服务实体发生故障时,负载均衡器把后来的任务转向另外一个服务实体。

    ·  内部通信--为了能协同工作、实现负载均衡和错误恢复,集群各实体间必须时常通信,比如负载均衡器对服务实体心跳测试信息、服务实体间任务执行上下文信息的通信。

    具有同一个集群地址使得客户端能访问集群提供的计算服务,一个集群地址下隐藏了各个服务实体的内部地址,使得客户要求的计算服务能在各个服务实体之间分布。内部通信是集群能正常运转的基础,它使得集群具有均衡负载和错误恢复的能力。

    集群分类

    Linux集群主要分成三大类( 高可用集群, 负载均衡集群,科学计算集群)

    高可用集群( High Availability Cluster)
    负载均衡集群(Load Balance Cluster)
    科学计算集群(High Performance Computing Cluster)
    ================================================

    具体包括:

    Linux High Availability 高可用集群                                       
    (普通两节点双机热备,多节点HA集群,RAC, shared, share-nothing集群等)

    Linux Load Balance 负载均衡集群                                      
     (LVS等....)

    Linux High Performance Computing 高性能科学计算集群     
     (Beowulf 类集群....)

    分布式存储                                                                         
    其他类linux集群              
    (如Openmosix, rendering farm 等..)

    详细介绍

    1. 高可用集群(High Availability Cluster)
    常见的就是2个节点做成的HA集群,有很多通俗的不科学的名称,比如"双机热备", "双机互备", "双机".
    高可用集群解决的是保障用户的应用程序持续对外提供服务的能力。 (请注意高可用集群既不是用来保护业务数据的,保护的是用户的业务程序对外不间断提供服务,把因软件/硬件/人为造成的故障对业务的影响降低到最小程度)。

    2. 负载均衡集群(Load Balance Cluster)

    负载均衡系统:集群中所有的节点都处于活动状态,它们分摊系统的工作负载。一般Web服务器集群、数据库集群和应用服务器集群都属于这种类型。

    负载均衡集群一般用于相应网络请求的网页服务器,数据库服务器。这种集群可以在接到请求时,检查接受请求较少,不繁忙的服务器,并把请求转到这些服务器上。从检查其他服务器状态这一点上看,负载均衡和容错集群很接近,不同之处是数量上更多。

    3. 科学计算集群(High Performance Computing Cluster)

    高性能计算(High Perfermance Computing)集群,简称HPC集群。这类集群致力于提供单个计算机所不能提供的强大的计算能力。

    高性能计算分类  
     高吞吐计算(High-throughput Computing)
      有一类高性能计算,可以把它分成若干可以并行的子任务,而且各个子任务彼此间没有什么关联。象在家搜寻外星人( SETI@HOME  -- Search for Extraterrestrial Intelligence at Home )就是这一类型应用。这一项目是利用Internet上的闲置的计算资源来搜寻外星人。SETI项目的服务器将一组数据和数据模式发给Internet上 参加SETI的计算节点,计算节点在给定的数据上用给定的模式进行搜索,然后将搜索的结果发给服务器。服务器负责将从各个计算节点返回的数据汇集成完整的 数据。因为这种类型应用的一个共同特征是在海量数据上搜索某些模式,所以把这类计算称为高吞吐计算。所谓的Internet计算都属于这一类。按照 Flynn的分类,高吞吐计算属于SIMD(Single Instruction/Multiple Data)的范畴。

     分布计算(Distributed Computing)
      另一类计算刚好和高吞吐计算相反,它们虽然可以给分成若干并行的子任务,但是子任务间联系很紧密,需要大量的数据交换。按照Flynn的分类,分布式的高性能计算属于MIMD(Multiple Instruction/Multiple Data)的范畴。

    4. 分布式(集群)与集群的联系与区别 
    分布式是指将不同的业务分布在不同的地方。 
    而集群指的是将几台服务器集中在一起,实现同一业务。 
    分布式中的每一个节点,都可以做集群。 
    而集群并不一定就是分布式的。 
    举例:就比如新浪网,访问的人多了,他可以做一个群集,前面放一个响应服务器,后面几台服务器完成同一业务,如果有业务访问的时候,响应服务器看哪台服务器的负载不是很重,就将给哪一台去完成。 
    而分布式,从窄意上理解,也跟集群差不多, 但是它的组织比较松散,不像集群,有一个组织性,一台服务器垮了,其它的服务器可以顶上来。 
    分布式的每一个节点,都完成不同的业务,一个节点垮了,哪这个业务就不可访问了。

    发布于 2个月前, 阅读(16) | 评论(0) | 投票(0) | 收藏(0) 阅读全文...
  • 162013-10

    一致性hash和solr千万级数据分布式搜索引擎中的应用

    互联网创业中大部分人都是草根创业,这个时候没有强劲的服务器,也没有钱去买很昂贵的海量数据库。在这样严峻的条件下,一批又一批的创业者从创业中获得成功,这个和当前的开源技术、海量数据架构有着必不可分的关系。比如我们使用mysql、nginx等开源软件,通过架构和低成本服务器也可以搭建千万级用户访问量的系统。新浪微博、淘宝网、腾讯等大型互联网公司都使用了很多开源免费系统搭建了他们的平台。所以,用什么没关系,只要能够在合理的情况下采用合理的解决方案。

    那怎么搭建一个好的系统架构呢?这个话题太大,这里主要说一下数据分流的方式。比如我们的数据库服务器只能存储200个数据,突然要搞一个活动预估达到600个数据。
    可以采用两种方式:横向扩展或者纵向扩展。
    纵向扩展是升级服务器的硬件资源。但是随着机器的性能配置越高,价格越高,这个代价对于一般的小公司是承担不起的。
    横向扩展是采用多个廉价的机器提供服务。这样一个机器只能处理200个数据、3个机器就可以处理600个数据了,如果以后业务量增加还可以快速配置增加。在大多数情况都选择横向扩展的方式。如下图:
    图1

    图2

    现在有个问题了,这600个数据如何路由到对应的机器。需要考虑如果均衡分配,假设我们600个数据都是统一的自增id数据,从1~600,分成3堆可以采用 id mod 3的方式。其实在真实环境可能不是这种id是字符串。需要把字符串转变为hashcode再进行取模。

    目前看起来是不是解决我们的问题了,所有数据都很好的分发并且没有达到系统的负载。但如果我们的数据需要存储、需要读取就没有这么容易了。业务增多怎么办,大家按照上面的横向扩展知道需要增加一台服务器。但是就是因为增加这一台服务器带来了一些问题。看下面这个例子,一共9个数,需要放到2台机器(1、2)上。各个机器存放为:1号机器存放1、3、5、7、9 ,2号机器存放 2、4、6、8。如果扩展一台机器3如何,数据就要发生大迁移,1号机器存放1、4、7, 2号机器存放2、5、8, 3号机器存放3、6、9。如图:

    图3
    从图中可以看出 1号机器的3、5、9迁移出去了、2好机器的4、6迁移出去了,按照新的秩序再重新分配了一遍。数据量小的话重新分配一遍代价并不大,但如果我们拥有上亿、上T级的数据这个操作成本是相当的高,少则几个小时多则数天。并且迁移的时候原数据库机器负载比较高,那大家就有疑问了,是不是这种水平扩展的架构方式不太合理?

    —————————–华丽分割线—————————————

    一致性hash就是在这种应用背景提出来的,现在被广泛应用于分布式缓存,比如memcached。下面简单介绍下一致性hash的基本原理。最早的版本 http://dl.acm.org/citation.cfm?id=258660。国内网上有很多文章都写的比较好。如: http://blog.csdn.net/x15594/article/details/6270242

    下面简单举个例子来说明一致性hash。

    准备:1、2、3 三台机器
    还有待分配的9个数 1、2、3、4、5、6、7、8、9
    一致性hash算法架构

    步骤
    一、构造出来 2的32次方 个虚拟节点出来,因为计算机里面是01的世界,进行划分时采用2的次方数据容易分配均衡。另 2的32次方是42亿,我们就算有超大量的服务器也不可能超过42亿台吧,扩展和均衡性都保证了。
    一致性hash
    二、将三台机器分别取IP进行hashcode计算(这里也可以取hostname,只要能够唯一区别各个机器就可以了),然后映射到2的32次方上去。比如1号机器算出来的hashcode并且mod (2^32)为 123(这个是虚构的),2号机器算出来的值为 2300420,3号机器算出来为 90203920。这样三台机器就映射到了这个虚拟的42亿环形结构的节点上了。
    图5
    三、将数据(1-9)也用同样的方法算出hashcode并对42亿取模将其配置到环形节点上。假设这几个节点算出来的值为 1:10,2:23564,3:57,4:6984,5:5689632,6:86546845,7:122,8:3300689,9:135468。可以看出 1、3、7小于123, 2、4、9 小于 2300420 大于 123, 5、6、8 大于 2300420 小于90203920。从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个Cache节点上。如果超过2^32仍然找不到Cache节点,就会保存到第一个Cache节点上。也就是1、3、7将分配到1号机器,2、4、9将分配到2号机器,5、6、8将分配到3号机器。
    图6
    这个时候大家可能会问,我到现在没有看见一致性hash带来任何好处,比传统的取模还增加了复杂度。现在马上来做一些关键性的处理,比如我们增加一台机器。按照原来我们需要把所有的数据重新分配到四台机器。一致性hash怎么做呢?现在4号机器加进来,他的hash值算出来取模后是12302012。 5、8 大于2300420 小于12302012 ,6 大于 12302012 小于90203920 。这样调整的只是把5、8从3号机器删除,4号机器中加入 5、6。
    图7
    同理,删除机器怎么做呢,假设2号机器挂掉,受影响的也只是2号机器上的数据被迁移到离它节点,上图为4号机器。
    图8
    大家应该明白一致性hash的基本原理了吧。不过这种算法还是有缺陷,比如在机器节点比较少、数据量大的时候,数据的分布可能不是很均衡,就会导致其中一台服务器的数据比其他机器多很多。为了解决这个问题,需要引入虚拟服务器节点的机制。如我们一共有只有三台机器,1、2、3。但是实际又不可能有这么多机器怎么解决呢?把 这些机器各自虚拟化出来3台机器,也就是 1a 1b 1c 2a 2b 2c 3a 3b 3c,这样就变成了9台机器。实际 1a 1b 1c 还是对应1。但是实际分布到环形节点就变成了9台机器。数据分布也就能够更分散一点。如图:
    图91

    写了这么多一致性hash,这个和分布式搜索有什么半点关系?我们现在使用solr4搭建了分布式搜索,测试了基于solrcloud的分布式平台提交20条数据居然需要几十秒,所以就废弃了solrcloud。采用自己hack solr平台,不用zookeeper做分布式一致性管理平台,自己管理数据的分发机制。既然需要自己管理数据的分发,就需要考虑到索引的创建,索引的更新。这样我们的一致性hash也就用上了。整体架构如下图:

    图10
    建立和更新需要维持机器的位置,能够根据数据的key找到对应的数据分发并更新。这里需要考虑的是如何高效、可靠的把数据建立、更新到索引里。
    备份服务器防止建立服务器挂掉,可以根据备份服务器快速恢复。
    读服务器主要做读写分离使用,防止写索引影响查询数据。
    集群管理服务器管理整个集群内的服务器状态、告警。

    整个集群随着业务增多还可以按照数据的类型划分,比如用户、微博等。每个类型按照上图架构搭建,就可以满足一般性能的分布式搜索。对于solr和分布式搜索的话题后续再聊。

    扩展阅读:
    java的hashmap随着数据量的增加也会出现map调整的问题,必要的时候就初始化足够大的size以防止容量不足对已有数据进行重新hash计算。

    疫苗:Java HashMap的死循环 http://coolshell.cn/articles/9606.html
    一致性哈希算法的优化—-关于如何保正在环中增加新节点时,命中率不受影响 (原拍拍同事scott)http://scottina.iteye.com/blog/650380

    语言实现:
    http://weblogs.java.net/blog/2007/11/27/consistent-hashing java 版本的例子
    http://blog.csdn.net/mayongzhan/archive/2009/06/25/4298834.aspx PHP 版的例子
    http://www.codeproject.com/KB/recipes/lib-conhash.aspx C语言版本例子

    发布于 3个月前, 阅读(150) | 评论(1) | 投票(0) | 收藏(21) 阅读全文...
  • 162013-10

    1)首先在${tomcat目录}/conf/Catalina/localhost 创建两个solr的配置文件。

       可以命名为solr.xml(主服务器配置)内容为:

     <Context docBase="F:/apache-solr-1.4.0/dist/apache-solr-1.4.0.war" reloadable="true" >  
         <Environment name="solr/home" type="java.lang.String" value="F:/apache-solr-1.4.0/example/solr" override="true" />  
     </Context> 

    slaver_solr.xml (从服务器配置)内容为:

     <Context docBase="F:/apache-solr-1.4.0/dist/apache-solr-1.4.0.war" reloadable="true" >  
         <Environment name="solr/home" type="java.lang.String" value="F:/solr分布式/solr" override="true" />  
     </Context> 

     

    可以看到两个配置所引用的后台管理是同一个目录的,但这个没关系,只要solr/home的不一样就行了,接着看主从服务器上solr/home的配置有什么不一样。主要是在solr/home/conf/solrconfig.xml上配置不一样的,其它配置可以互相拷贝。

     

    主要不同的地方为如下:

     

    从服务器的配置

     

    <requestHandler name="/replication" class="solr.ReplicationHandler" >
        <lst name="slave">

    <!--主服务器的url-->


          <str name="masterUrl">http://localhost:8080/solr/replication</str>

    <!--定时去请求主服务器,查看索引是否有改变-->
          <str name="pollInterval">00:00:60</str>
        </lst>
    </requestHandler>

     

    主服务器的配置

     


    <requestHandler name="/replication" class="solr.ReplicationHandler" >
        <lst name="master">
          <str name="replicateAfter">commit</str>
          <str name="replicateAfter">startup</str>
          <str name="confFiles">schema.xml,stopwords.txt</str>
        </lst>

    </requestHandler>

     

    大概这样的。启动 tomcat看看吧。。主服务器建立索引后,从服务器会请求将索引拷贝到从服务器中。

    发布于 3个月前, 阅读(54) | 评论(0) | 投票(0) | 收藏(0) 阅读全文...
  • 162013-04
    时间过得很快,来淘宝已经两个月了,在这两个月的时间里,自己也感受颇深。下面就结合淘宝目前的一些底层技术框架以及自己的一些感触来说说如何构建一个可伸缩,高性能,高可用性的分布式互联网应用。

      一应用无状态淘宝session框架

      俗话说,一个系统的伸缩性的好坏取决于应用的状态如何管理。为什么这么说呢?咱们试想一下,假如我们在session中保存了大量与客户端的状态信息的话,那么当保存状态信息的server宕机的时候,我们怎么办?通常来说,我们都是通过集群来解决这个问题,而通常所说的集群,不仅有负载均衡,更重要的是要有失效恢复failover,比如tomcat采用的集群节点广播复制,jboss采用的配对复制等session状态复制策略,但是集群中的状态恢复也有其缺点,那就是严重影响了系统的伸缩性,系统不能通过增加更多的机器来达到良好的水平伸缩,因为集群节点间session的通信会随着节点的增多而开销增大,因此要想做到应用本身的伸缩性,我们需要保证应用的无状态性,这样集群中的各个节点来说都是相同的,从而是的系统更好的水平伸缩。

      OK,上面说了无状态的重要性,那么具体如何实现无状态呢?此时一个session框架就会发挥作用了。幸运的是淘宝已经具有了此类框架。淘宝的session框架采用的是client cookie实现,主要将状态保存到了cookie里面,这样就使得应用节点本身不需要保存任何状态信息,这样在系统用户变多的时候,就可以通过增加更多的应用节点来达到水平扩展的目的.但是采用客户端cookie的方式来保存状态也会遇到限制,比如每个cookie一般不能超过4K的大小,同时很多浏览器都限制一个站点最多保存20个cookie.淘宝cookie框架采用的是“多值cookie”,就是一个组合键对应多个cookie的值,这样不仅可以防止cookie数量超过20,同时还节省了cookie存储有效信息的空间,因为默认每个cookie都会有大约50个字节的元信息来描述cookie。

      除了淘宝目前的session框架的实现方式以外,其实集中式session管理来完成,说具体点就是多个无状态的应用节点连接一个session服务器,session服务器将session保存到缓存中,session服务器后端再配有底层持久性数据源,比如数据库,文件系统等等。

      二有效使用缓存Tair

      做互联网应用的兄弟应该都清楚,缓存对于一个互联网应用是多么的重要,从浏览器缓存,反向代理缓存,页面缓存,局部页面缓存,对象缓存等等都是缓存应用的场景。

      一般来说缓存根据与应用程序的远近程度不同可以分为:local cache和remote cache。一般系统中要么采用local cache,要么采用remote cache,两者混合使用的话对于local cache和remote cache的数据一致性处理会变大比较麻烦.

      在大部分情况下,我们所说到的缓存都是读缓存,缓存还有另外一个类型:写缓存.对于一些读写比不高,同时对数据安全性需求不高的数据,我们可以将其缓存起来从而减少对底层数据库的访问,比如统计商品的访问次数,统计API的调用量等等,可以采用先写内存缓存然后延迟持久化到数据库,这样可以大大减少对数据库的写压力。

      OK,我以店铺线的系统为例,在用户浏览店铺的时候,比如店铺介绍,店铺交流区页,店铺服务条款页面,店铺试衣间页面,以及店铺内搜索界面这些界面更新不是非常频繁,因此适合放到缓存中,这样可以大大减低DB的负载。另外宝贝详情页面相对也更新比较少,因此也适合放到缓存中来减低DB负载。

      三应用拆分HSF

      首先,在说明应用拆分之前,我们先来回顾一下一个系统从小变大的过程中遇到的一些问题,通过这些问题我们会发现拆分对于构建一个大型系统是如何的重要。

      系统刚上线初期,用户数并不多,所有的逻辑也许都是放在一个系统中的,所有逻辑跑到一个进程或者一个应用当中,这个时候因为比较用户少,系统访问量低,因此将全部的逻辑都放在一个应用未尝不可。但是,兄弟们都清楚,好景不长,随着系统用户的不断增加,系统的访问压力越来越多,同时随着系统发展,为了满足用户的需求,原有的系统需要增加新的功能进来,系统变得越来越复杂的时候,我们会发现系统变得越来越难维护,难扩展,同时系统伸缩性和可用性也会受到影响。那么这个时候我们如何解决这些问题呢?明智的办法就是拆分这也算是一种解耦,我们需要将原来的系统根据一定的标准,比如业务相关性等分为不同的子系统,不同的系统负责不同的功能,这样切分以后,我们可以对单独的子系统进行扩展和维护,从而提高系统的扩展性和可维护性,同时我们系统的水平伸缩性scale out大大的提升了,因为我们可以有针对性的对压力大的子系统进行水平扩展而不会影响到其它的子系统,而不会像拆分以前,每次系统压力变大的时候,我们都需要对整个大系统进行伸缩,而这样的成本是比较大的,另外经过切分,子系统与子系统之间的耦合减低了,当某个子系统暂时不可用的时候,整体系统还是可用的,从而整体系统的可用性也大大增强了。

      因此一个大型的互联网应用,肯定是要经过拆分,因为只有拆分了,系统的扩展性,维护性,伸缩性,可用性才会变的更好。但是拆分也给系统带来了问题,就是子系统之间如何通信的问题,而具体的通信方式有哪些呢?一般有同步通信和异步通信,这里我们首先来说下同步通信,下面的主题“消息系统”会说到异步通信。既然需要通信,这个时候一个高性能的远程调用框架就显得非常总要啦,因此咱们淘宝也有了自己的HSF框架。

      上面所说的都是拆分的好处,但是拆分以后必然的也会带来新的问题,除了刚才说的子系统通信问题外,最值得关注的问题就是系统之间的依赖关系,因为系统多了,系统的依赖关系就会变得复杂,此时就需要更好的去关注拆分标准,比如能否将一些有依赖的系统进行垂直化,使得这些系统的功能尽量的垂直,这也是目前淘宝正在做的系统垂直化,同时一定要注意系统之间的循环依赖,如果出现循环依赖一定要小心,因为这可能导致系统连锁启动失败。

      OK,既然明白了拆分的重要性,我们看看随着淘宝的发展,淘宝本身是如何拆分系统的。

      首先我们来看以下这个图:作者图片已无法打开,请见谅

      从上面的图可以看出淘宝系统的一个演变过程,在这个演变的过程中,我们所说的拆分就出现V2.2和V3.0之间。在V2.2版本中,淘宝几乎所有的逻辑都放在Denali系统中,这样导致的问题就是系统扩展和修改非常麻烦,并且更加致命的是随着淘宝业务量的增加,如果按照V2.2的架构已经没有办法支撑以后淘宝的快速发展,因此大家决定对整个系统进行拆分,最终V3.0版本的淘宝系统架构图如下:作者图片已无法打开,请见谅

      从上图可以看出V3.0版本的系统对整个系统进行了水平和垂直两个方向的拆分,水平方向上,按照功能分为交易,评价,用户,商品等系统,同样垂直方向上,划分为业务系统,核心业务系统以及以及基础服务,这样以来,各个系统都可以独立维护和独立的进行水平伸缩,比如交易系统可以在不影响其它系统的情况下独立的进行水平伸缩以及功能扩展。

      从上面可以看出,一个大型系统要想变得可维护,可扩展,可伸缩,我们必须的对它进行拆分,拆分必然也带来系统之间如何通信以及系统之间依赖管理等问题,关于通信方面,淘宝目前独立开发了自己的高性能服务框架HSF,此框架主要解决了淘宝目前所有子系统之间的同步和异步通信目前HSF主要用于同步场合,FutureTask方式的调用场景还比较少。至于系统间的依赖管理,目前淘宝还做的不够好,这应该也是我们以后努力解决的问题。

      四数据库拆分TDDL

      在前面“应用拆分”主题中,我们提到了一个大型互联网应用需要进行良好的拆分,而那里我们仅仅说了”应用级别”的拆分,其实我们的互联网应用除了应用级别的拆分以外,还有另外一个很重要的层面就是存储如何拆分的。因此这个主题主要涉及到如何对存储系统,通常就是所说的RDBMS进行拆分。

      好了,确定了这个小节的主题之后,我们回顾一下,一个互联网应用从小变大的过程中遇到的一些问题,通过遇到的问题来引出我们拆分RDBMS的重要性。

      系统刚开始的时候,因系统刚上线,用户不多,那个时候,所有的数据都放在了同一个数据库中,这个时候因为用户少压力小,一个数据库完全可以应付的了,但是随着运营那些哥们辛苦的呐喊和拼命的推广以后,突然有一天发现,oh,god,用户数量突然变多了起来,随之而来的就是数据库这哥们受不了,它终于在某一天大家都和惬意的时候挂掉啦。此时,咱们搞技术的哥们,就去看看究竟是啥原因,我们查了查以后,发现原来是数据库读取压力太大了,此时咱们都清楚是到了读写分离的时候,这个时候我们会配置一个server为master节点,然后配几个salve节点,这样以来通过读写分离,使得读取数据的压力分摊到了不同的salve节点上面,系统终于又恢复了正常,开始正常运行了。但是好景还是不长,有一天我们发现master这哥们撑不住了,它负载老高了,汗流浃背,随时都有翘掉的风险,这个时候就需要咱们垂直分区啦也就是所谓的分库,比如将商品信息,用户信息,交易信息分别存储到不同的数据库中,同时还可以针对商品信息的库采用master,salve模式,OK,通过分库以后,各个按照功能拆分的数据库写压力被分担到了不同的server上面,这样数据库的压力终于有恢复到正常状态。但是是不是这样,我们就可以高枕无忧了呢?NO,这个NO,不是我说的,是前辈们通过经验总结出来的,随着用户量的不断增加,你会发现系统中的某些表会变的异常庞大,比如好友关系表,店铺的参数配置表等,这个时候无论是写入还是读取这些表的数据,对数据库来说都是一个很耗费精力的事情,因此此时就需要我们进行“水平分区”了这就是俗话说的分表,或者说sharding.

      OK,上面说了一大堆,无非就是告诉大家一个事实“数据库是系统中最不容易scale out的一层”,一个大型的互联网应用必然会经过一个从单一DB server,到Master/salve,再到垂直分区分库,然后再到水平分区分表,sharding的过程,而在这个过程中,Master/salve以及垂直分区相对比较容易,应用的影响也不是很大,但是分表会引起一些棘手的问题,比如不能跨越多个分区join查询数据,如何平衡各个shards的负载等等,这个时候就需要一个通用的DAL框架来屏蔽底层数据存储对应用逻辑的影响,使得底层数据的访问对应用透明化。

      拿淘宝目前的情况来说,淘宝目前也正在从昂贵的高端存储小型机+ORACLE切换到MYSQL,切换到MYSQL以后,势必会遇到垂直分区分库以及水平分区Sharding的问题,因此目前淘宝根据自己的业务特点也开发了自己的TDDL框架,此框架主要解决了分库分表对应用的透明化以及异构数据库之间的数据复制

      五异步通信Notify

      在”远程调用框架”的介绍中,我们说了一个大型的系统为了扩展性和伸缩性方面的需求,肯定是要进行拆分,但是拆分了以后,子系统之间如何通信就成了我们首要的问题,在”远程调用框架”小节中,我们说了同步通信在一个大型分布式系统中的应用,那么这一小节我们就来说说异步通信.好了,既然说到了异步通信,那么”消息中间件”就要登场了,采用异步通信这其实也是关系到系统的伸缩性,以及最大化的对各个子系统进行解耦.

      说到异步通信,我们需要关注的一点是这里的异步一定是根据业务特点来的,一定是针对业务的异步,通常适合异步的场合是一些松耦合的通信场合,而对于本身业务上关联度比较大的业务系统之间,我们还是要采用同步通信比较靠谱。

      OK,那么下一步我们说说异步能给系统带来什么样子的好处。首先我们想想,假如系统有A和B两个子系统构成,假如A和B是同步通信的话,那么要想使得系统整体伸缩性提高必须同时对A和B进行伸缩,这就影响了对整个系统进行scale out.其次,同步调用还会影响到可用性,从数学推理的角度来说,A同步调用B,如果A可用,那么B可用,逆否命题就是如果B不可用,那么A也不可用,这将大大影响到系统可用性,再次,系统之间异步通信以后可以大大提高系统的响应时间,使得每个请求的响应时间变短,从而提高用户体验,因此异步在提高了系统的伸缩性以及可用性的同时,也大大的增强了请求的响应时间当然了,请求的总体处理时间也许不会变少。

      下面我们就以淘宝的业务来看看异步在淘宝的具体应用。交易系统会与很多其它的业务系统交互,如果在一次交易过程中采用同步调用的话,这就要求要向交易成功,必须依赖的所有系统都可用,而如果采用异步通信以后,交易系统借助于消息中间件Notify和其它的系统进行了解耦,这样以来当其它的系统不可用的时候,也不会影响到某此交易,从而提高了系统的可用性。

      最后,关于异步方面的讨论,我可以推荐大家一些资源:

      1 . J2EE meets web2.0

      2. Ebay架构特点HPTS 2009

      六非结构化数据存储 TFS,NOSQL

      在一个大型的互联网应用当中,我们会发现并不是所有的数据都是结构化的,比如一些配置文件,一个用户对应的动态,以及一次交易的快照等信息,这些信息一般不适合保存到RDBMS中,它们更符合一种Key-value的结构,另外还有一类数据,数据量非常的大,但是实时性要求不高,此时这些数据也需要通过另外的一种存储方式进行存储,另外一些静态文件,比如各个商品的图片,商品描述等信息,这些信息因为比较大,放入RDBMS会引起读取性能问题,从而影响到其它的数据读取性能,因此这些信息也需要和其它信息分开存储,而一般的互联网应用系统都会选择把这些信息保存到分布式文件系统中,因此淘宝目前也开发了自己的分布式文件系统TFS,TFS目前限制了文件大小为2M,适合于一些小于2M数据的存放。

      随着互联网发展,业界从08年下半年开始逐渐流行了一个概念就是NOSQL。我们都知道根据CAP理论,一致性,可用性和分区容错性3者不能同时满足,最多只能同时满足两个,我们传统的关系数据采用了ACID的事务策略,而ACID的事务策略更加讲究的是一种高一致性而降低了可用性的需求,但是互联网应用往往对可用性的要求要略高于一致性的需求,这个时候我们就需要避免采用数据的ACID事务策略,转而采用BASE事务策略,BASE事务策略是基本可用性,事务软状态以及最终一致性的缩写,通过BASE事务策略,我们可以通过最终一致性来提升系统的可用性,这也是目前很多NOSQL产品所采用的策略,包括facebook的cassandra,apache hbase,google bigtable等,这些产品非常适合一些非结构化的数据,比如key-value形式的数据存储,并且这些产品有个很好的优点就是水平伸缩性。目前淘宝也在研究和使用一些成熟的NOSQL产品。

      七监控、预警系统

      对于大型的系统来说,唯一可靠的就是系统的各个部分是不可靠。

      因为一个大型的分布式系统中势必会涉及到各种各样的设备,比如网络交换机,普通PC机,各种型号的网卡,硬盘,内存等等,而这些东东都在数量非常多的时候,出现错误的概率也会变大,因此我们需要时时刻刻监控系统的状态,而监控也有粒度的粗细之分,粒度粗一点的话,我们需要对整个应用系统进行监控,比如目前的系统网络流量是多少,内存利用率是多少,IO,CPU的负载是多少,服务的访问压力是多少,服务的响应时间是多少等这一系列的监控,而细粒度一点的话,我们就需对比如应用中的某个功能,某个URL的访问量是多,每个页面的PV是多少,页面每天占用的带宽是多少,页面渲染时间是多少,静态资源比如图片每天占用的带宽是多少等等进行进一步细粒度的监控。因此一个监控系统就变得必不可少了。

      前面说了一个监控系统的重要性,有了监控系统以后,更重要的是要和预警系统结合起来,比如当某个页面访问量增多的时候,系统能自动预警,某台Server的CPU和内存占用率突然变大的时候,系统也能自动预警,当并发请求丢失严重的时候,系统也能自动预警等等,这样以来通过监控系统和预警系统的结合可以使得我们能快速响应系统出现的问题,提高系统的稳定性和可用性。

      八配置统一管理

      一个大型的分布式应用,一般都是有很多节点构成的,如果每次一个新的节点加入都要更改其它节点的配置,或者每次删除一个节点也要更改配置的话,这样不仅不利于系统的维护和管理,同时也更加容易引入错误。另外很多时候集群中的很多系统的配置都是一样的,如果不进行统一的配置管理,就需要再所有的系统上维护一份配置,这样会造成配置的管理维护很麻烦,而通过一个统一的配置管理可以使得这些问题得到很好的解决,当有新的节点加入或者删除的时候,配置管理系统可以通知各个节点更新配置,从而达到所有节点的配置一致性,这样既方便也不会出错。(编选:中国电子商务研究中心 勇全)

    发布于 9个月前, 阅读(89) | 评论(1) | 投票(0) | 收藏(1) 阅读全文...
  • 262013-03

    基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式。无论是在大型互联网应用还是企业级架构中,我们都见到了越来越多SOA或RESTful的Web API。为什么Web API如此流行呢?我认为很大程度上应归功于简单有效的HTTP协议。HTTP协议是一种分布式的面向资源的网络应用层协议无论是服务器端提供Web服务,还是客户端消费Web服务都非常简单。再加上浏览器、Javascript、AJAX、JSON以及HTML5等技术和工具的发展,互联网应用架构设计表现出了从传统的PHP、JSP、ASP.NET等服务器端动态网页向Web API + RIA(富互联网应用)过渡的趋势。Web API专注于提供业务服务,RIA专注于用户界面和交互设计,从此两个领域的分工更加明晰。在这种趋势下,Web API设计将成为服务器端程序员的必修课。然而,正如简单的Java语言并不意味着高质量的Java程序,简单的HTTP协议也不意味着高质量的Web API要想设计出高质量的Web API,还需要深入理解分布式系统及HTTP协议的特性。

     

    幂等性定义

    本文所要探讨的正是HTTP协议涉及到的一种重要性质:幂等性(Idempotence)。在HTTP/1.1规范中幂等性的定义是:

    Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

    从定义上看,HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。幂等性属于语义范畴,正如编译器只能帮助检查语法错误一样,HTTP规范也没有办法通过消息格式等语法手段来定义它,这可能是它不太受到重视的原因之一。但实际上,幂等性是分布式系统设计中十分重要概念,而HTTP的分布式本质也决定了它在HTTP中具有重要地位。

     

    分布式事务 vs 幂等设计

    为什么需要幂等性呢?我们先从一个例子说起,假设有一个从账户取钱的远程API(可以是HTTP的,也可以不是),我们暂时用类函数的方式记为: 

    bool withdraw(account_id, amount)

    withdraw的语义是从account_id对应的账户中扣除amount数额的钱;如果扣除成功则返回true,账户余额减少amount;如果扣除失败则返回false,账户余额不变。值得注意的是:和本地环境相比,我们不能轻易假设分布式环境的可靠性。一种典型的情况是withdraw请求已经被服务器端正确处理,但服务器端的返回结果由于网络等原因被掉丢了,导致客户端无法得知处理结果。如果是在网页上,一些不恰当的设计可能会使用户认为上一次操作失败了,然后刷新页面,这就导致了withdraw被调用两次,账户也被多扣了一次钱。如图1所示:

    non-idemponent

    图1

    这个问题的解决方案一是采用分布式事务,通过引入支持分布式事务的中间件来保证withdraw功能的事务性。分布式事务的优点是对于调用者很简单,复杂性都交给了中间件来管理。缺点则是一方面架构太重量级,容易被绑在特定的中间件上,不利于异构系统的集成;另一方面分布式事务虽然能保证事务的ACID性质,而但却无法提供性能和可用性的保证。

    另一种更轻量级的解决方案是幂等设计。我们可以通过一些技巧把withdraw变成幂等的,比如:

    int create_ticket() 

    bool idempotent_withdraw(ticket_id, account_id, amount)

    create_ticket的语义是获取一个服务器端生成的唯一的处理号ticket_id,它将用于标识后续的操作。idempotent_withdraw和withdraw的区别在于关联了一个ticket_id,一个ticket_id表示的操作至多只会被处理一次,每次调用都将返回第一次调用时的处理结果。这样,idempotent_withdraw就符合幂等性了,客户端就可以放心地多次调用。

    基于幂等性的解决方案中一个完整的取钱流程被分解成了两个步骤:1.调用create_ticket()获取ticket_id;2.调用idempotent_withdraw(ticket_id, account_id, amount)。虽然create_ticket不是幂等的,但在这种设计下,它对系统状态的影响可以忽略,加上idempotent_withdraw是幂等的,所以任何一步由于网络等原因失败或超时,客户端都可以重试,直到获得结果。如图2所示:

    image

    图2

    和分布式事务相比,幂等设计的优势在于它的轻量级,容易适应异构环境,以及性能和可用性方面。在某些性能要求比较高的应用,幂等设计往往是唯一的选择。

     

    HTTP的幂等性

    HTTP协议本身是一种面向资源的应用层协议,但对HTTP协议的使用实际上存在着两种不同的方式:一种是RESTful的,它把HTTP当成应用层协议,比较忠实地遵守了HTTP协议的各种规定;另一种是SOA的,它并没有完全把HTTP当成应用层协议,而是把HTTP协议作为了传输层协议,然后在HTTP之上建立了自己的应用层协议。本文所讨论的HTTP幂等性主要针对RESTful风格的,不过正如上一节所看到的那样,幂等性并不属于特定的协议,它是分布式系统的一种特性;所以,不论是SOA还是RESTful的Web API设计都应该考虑幂等性。下面将介绍HTTP GET、DELETE、PUT、POST四种主要方法的语义和幂等性。

    HTTP GET方法用于获取资源,不应有副作用,所以是幂等的。比如:GET http://www.bank.com/account/123456,不会改变资源的状态,不论调用一次还是N次都没有副作用。请注意,这里强调的是一次和N次具有相同的副作用,而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次得到不同的结果,但它本身并没有产生任何副作用,因而是满足幂等性的。

    HTTP DELETE方法用于删除资源,有副作用,但它应该满足幂等性。比如:DELETE http://www.forum.com/article/4231,调用一次和N次对系统产生的副作用是相同的,即删掉id为4231的帖子;因此,调用者可以多次调用或刷新页面而不必担心引起错误。

    比较容易混淆的是HTTP POST和PUT。POST和PUT的区别容易被简单地误认为“POST表示创建资源,PUT表示更新资源”;而实际上,二者均可用于创建资源,更为本质的差别是在幂等性方面。在HTTP规范中对POST和PUT是这样定义的:

    The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. ...... If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header.

    The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

    POST所对应的URI并非创建的资源本身,而是资源的接收者。比如:POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子,HTTP响应中应包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源,它们具有不同的URI;所以,POST方法不具备幂等性。而PUT所对应的URI是要创建或更新的资源本身。比如:PUT http://www.forum/articles/4231的语义是创建或更新ID为4231的帖子。对同一URI进行多次PUT的副作用和一次PUT是相同的;因此,PUT方法具有幂等性。

    在介绍了几种操作的语义和幂等性之后,我们来看看如何通过Web API的形式实现前面所提到的取款功能。很简单,用POST /tickets来实现create_ticket;用PUT /accounts/account_id/ticket_id&amount=xxx来实现idempotent_withdraw。值得注意的是严格来讲amount参数不应该作为URI的一部分,真正的URI应该是/accounts/account_id/ticket_id,而amount应该放在请求的body中。这种模式可以应用于很多场合,比如:论坛网站中防止意外的重复发帖。

     

    总结

    上面简单介绍了幂等性的概念,用幂等设计取代分布式事务的方法,以及HTTP主要方法的语义和幂等性特征。其实,如果要追根溯源,幂等性是数学中的一个概念,表达的是N次变换与1次变换的结果相同,有兴趣的读者可以从Wikipedia上进一步了解。

     

    参考

    RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1, Method Definitions

    The Importance of Idempotence

    stackoverflow -  PUT vs POST in REST

    发布于 9个月前, 阅读(9) | 评论(0) | 投票(0) | 收藏(0) 阅读全文...
  • 292012-11
    摘要:HBTC 2012(Hadoop&BigData Technology Conference 2012)即将召开,为了让读者可以提前了解一下目前国内各个公司在Hadoop和大数据方面的运用,CSDN于会前对演讲嘉宾进行了采访。本期采访了阿里巴巴的代志远,他给我们提前分享了一下HBase在阿里巴巴海量数据部门的实践。

    【CSDN报道】代志远早年就职网易研究院从事MapReduce与DFS系统的自主研发,后加入支付宝数据平台负责Hadoop与HBase体系的架构设计与二次研发,支付宝流计算与分布式搜索系统的设计和研发,后成为支付宝海量计算体系架构师兼支付宝三代架构成员。现就转战于阿里巴巴集团-CDO-海量数据部门,负责创新性项目的研究和跟进,目前专注于Google第二代数据库产品MegaStore的研究和在阿里的落地。

    在即将召开的HBTC大会中,我们有幸邀请到代志远作为我们的演讲嘉宾,请他分享下阿里巴巴在海量数据分布式数据库领域的探索。我们也对他提前做了邮件采访,让用户可以更快地了解阿里巴巴海量数据分布式数据库以及在Hadoop应用领域的实践。

    阿里巴巴海量数据部门: 代志远

    CSDN: Hadoop目前是大数据处理领域的王者,你认为中小企业应用Hadoop的瓶颈在哪里?

    代志远:首先因为Hadoop本身机制复杂,所依赖的参数配置颇多,并且Hadoop需要像数据库一样稳定,满足性能的运行,就需要运维人员如同DBA一样要懂网络、磁盘、内核以及其他一些硬件知识,这对于运维人员的要求是比较高的。其次Hadoop社区蓬勃发展,生态圈不断扩大,用户不断增多,规模极限也不断突破,这就促使了Hadoop的架构和代码发展非常快而且变更也比较快,正因为如此,系统在快速发展的时候容易引入很多的Bug和一些缺陷(可能因为稍稍的使用不当或比较小的问题就引起整体性能和稳定性的波动)。更重要的是,Hadoop代码复杂,而且需要与社区接轨,能够找到对Hadoop源码熟悉并能优化升级和bugfix的人才是很难的,这对于一个公司的研发来说是个很大的挑战。最后一点是公司的认知,除了类似Cloudera、MapR之类的软件公司需要对软件技术负责,其他多数公司无论大中小都依赖于公司业务,尤其中小公司业务压力大、人员紧张,能够从业务研发人员中抽调或通过其他方式组建专有的Hadoop运维团队甚至是研发团队,从公司规划与发展上来说是比较困难的事情。

    CSDN: Hadoop的本质是为全量而生,就是说它重吞吐量,响应时间完全没有保障,那么对于像淘宝、天猫在“双11”活动抢购的时候,需要实时处理数据(可能是毫秒级,秒级的响应),是如何进行实现的?

    代志远:Hadoop是离线计算平台,其中包括分布式文件系统(HDFS)和分布式计算(MapReduce),这本身是无法对响应时间做保证的。但是目前在Hadoop之上的生态系统越来越完善,其中HBase就是支持海量数据、高并发的在线数据库,应对这种场景就非常适合。HBase在这次双十一中与MySQL等在线数据库共同作为线上库使用,承担了重要的责任,并创下了并在全天高压力之下无故障的佳绩。另外非Hadoop生态圈的流式计算框架Storm、S4也同样可以为实时计算分担一定的压力。

    CSDN: 你在云计算大会时做的一场有关HBase的报告,主要讲如何用HBase替代MySQL,HBase对比MySQL的优势在哪里?

    代志远:准确来说是HBase替换MySQL的一部分应用,这些应用自然是要符合HBase的应用场景(与MySQL对比),比如数据量大、对线性拓展有需求、对自动化运维(负载均衡)有要求而且应用模式简单。在支付宝中因其增长速度快,业务量大,造成了很多应用都是数据量庞大而且速度增长快,因此有一些应用迫切需要一个数据库能够支撑现在的业务而降低对关系型的需求,所以尝试了HBase的解决方案。

    CSDN: 阿里巴巴在部署Hadoop的过程中有哪些比较好的经验可以跟技术人员分享?

    代志远:最重要的是要有一个完善团队,健全的流程。

    • 集群越来越大,要树立以集群稳定性和性能为要领的工作思路。
    • 现在进入Hadoop应用开发领域的人变多,但本身知识因其入行早晚而积累不同,无法对集群的稳定性负责,常常会写出跑死集群的任务(数据库中SQL使用不善也常会如此)。因此要有一个较好的管理流程约束开发人员做到责任分明,以便促使应用开发不仅要对自己的任务负责还要对集群负责,不断学习和检查减少故障的产生。
    • 要有一个好的运维团队,懂硬件、重流程、负责任。
    • 公司在资源和战略上应有所倾斜,重视研发人员加强在研发的投入,毕竟分布式系统的入行门槛相比应用开发的技术门槛要高,当然有好的应用架构师能够取长补短规避大多数问题也是可行的,但单一系统的稳定性还是需要靠人来保证。

    CSDN: 请您简要介绍一下本次HBTC2012大会上的议题的内容。

    代志远:06年Google发表论文Bigtable,社区随之出现HBase,后Google 08年发表第二代数据库产品MegaStore至今未有社区同类产品出现,现今Google又出现新一代数据库理论Spanner和F1。 而最近几年随之Bigtable和NoSQL的兴起,社区产品HBase逐步走向NoSQL系统的主流产品,优势明显然而缺点也明显,大数据平台下的业务由SQL向NoSQL的迁移比较复杂而应用人员学习成本颇高,并且无法支持事务和多维索引,使得许多业务无法享用来自NoSQL系统中线性拓展能力。

    Google内部MegaStore就作为Bigtable的一个补充而出现,在Bigtable的上层支持了SQL,事务、索引、跨机房灾备,并成为大名鼎鼎的Gmail、Google App Engine、Android Market的底层存储。因此我们决定以MegaStore为理论模型进行探索如何在HBase系统上不牺牲线性拓展能力,同时又能提供跨行事务、索引、SQL的功能。

    HBase系统故障恢复的优化实践

    其实在第四届中国云计算大会上,当时还在支付宝数据平台的架构师代志远就为大家带来了题为“HBase系统故障恢复的优化实践分享”的精彩演讲,他分析了支付宝海量数据在线处理的现状,以HBase解决方案取代传统MySQL解决方案的技术历程,并详尽分享了Region Server的宕机恢复流程(阅读全文)。

    在Hadoop的体系当中,支持实时的一条线,HBase,支持海量数据库初衷的时候,设计为了设计万一级实时数据库,HBase这个东西经过这几年的发展,已经逐渐成为目前业界当中主要的实时数据库,分布式数据库,像支付宝直接上HBase系统,就是考虑到HBase的先进架构,能够帮助支付宝完成现在很多的海量数据的存储以及在线随机读写高性能的访问和存储。

    不过在HBase的系统当中,体现它的可用性有几个风险。第一个是HBase本身在底层依赖的HDFS,加载了唯一一块数据,单台机器保证一致性,HDFS保持了冗余。第二点,恢复过程当中,Failover过程非常复杂,这个时间消耗越长,作为在线系统,这种时间越长可能会影响到在线访问用户体验。第三点它依赖的HDFS,HBase作为在线数据库依赖HDFS有故障的,经过几小时恢复提供生产业务,对业务方没有直接感受,作为在线系统如果挂掉,如果需要经过近小时恢复时间,恐怕就会直接收到自于支付宝外部的用户投诉了。HBase目前它自己的监控体系尚不完善,目前的监控力度非常得粗,只能监控到单台的Region Server的情况,看不到当前用户表有多少读写比例,看不到当前服务结点写作量多少,读出量多少。

    Region Server在恢复过程当中有几个流程这个流程很复杂,流程非常非常多,以当前的系统规模,它凸显出来的问题,这几个流程是影响到它的恢复速度的关键流程。等待时间周期非常长,周期之所以比较长,是因为现在的机器发展速度非常得快,每台机器从两个G到8个G,96G,140G的大层次的机器,Java语言实现了系统当中大内存管理本身存在问题,除非革新这门语言,否则别无他法。如果说在设计的参数不合理,就可能会导致一个问题,有可能这台服务器就会停止运行,发生这么一次情况就非常可怕,几十G的内存这个过程需要几十秒甚至上分钟,通常情况下,我们会设置到3分钟,这就意味着,为了避免出现这种问题,就会同时引入新的问题,宕机之后恢复等待时间需要三分钟。第二个关键流程当中,当它感知到已经挂掉了,在线数据库协助WL数据重新做到存储当中去,以保证实时更新是同步,否则这个数据库肯定要丢出去,重做数据过程当中,会有一个过程,Split Hlog,跟当前数据量有关系,Edit Log数据又比较多,大家在业余时间可以进行测试,数据不以支付宝的为准,以当前数据系统大小为准。

    第三个关键流程,重做完数据之后,这部分重新上线,上线之前进行数据进行二次扫描,告诉系统,Region怎么加入到Region Server当中去,扫描也存在问题,问题可能引发到两分钟到6分钟,这也跟当前系统数据有关。第四部分,这个过程称之为再次上线的过程,这个再次上线,上线时间跟当前这台机器的Region上线有关系。支付宝面对消费记录查询,用户查不出来数据,15分钟之后才能查到,在面临在线问题上这是非常可怕的事情。

    针对Region Server这一关键流程,做了一些优化。这个优化正是提到关键流程第一点,在判断宕机超市的情况下,不强依赖于Zookeeper,支付宝又启动了监控进程Mirror Process,每一台,Region Server当中都会起到PID存不存在,这种检查并非完全可靠,当检查PID不存在,就有理由认为已经挂掉了,要进行可靠检查,通常DBA在线判断数据库是否可用,通常会用PIng连续服务端口,这就弥补了系动中的调用命令不可靠的事情。最后当发现服务端口不可用时,有理由认为当前进程已经死掉了,死掉了之后,那么就按照现有逻辑删除结点,这三分钟的时间就完全省略掉了。(整理/@CSDN王鹏,审校/包研)

    11月30日-12月1日,北京新云南皇冠假日酒店,业内将迎来国内大数据领域最纯粹的技术盛会——HBTC 2012(Hadoop&BigData Technology Conference 2012)。Hadoop及云计算生态系统的力量齐聚北京,欢迎热爱开源的朋友们加入!报名网址参见HBTC 2012

    发布于 1年前, 阅读(112) | 评论(0) | 投票(0) | 收藏(2) 阅读全文...
  • 292012-11

    在前面三篇文章中,介绍了关于分布式系统中数据一致性的问题,这一篇主要介绍CAP定理以及自己对CAP定理的了解。

    CAP定理是2000年,由 Eric Brewer 提出来的

    Brewer认为在分布式的环境下设计和部署系统时,有3个核心的需求,以一种特殊的关系存在。这里的分布式系统说的是在物理上分布的系统,比如我们常见的web系统。

    这3个核心的需求是:ConsistencyAvailabilityPartition Tolerance,赋予了该理论另外一个名字 - CAP

    Consistency:一致性,这个和数据库ACID的一致性类似,但这里关注的所有数据节点上的数据一致性和正确性,而数据库的ACID关注的是在在一个事务内,对数据的一些约束。

    Availability:可用性,关注的在某个结点的数据是否可用,可以认为某一个节点的系统是否可用,通信故障除外。

    Partition Tolerance:分区容忍性,是否可以对数据进行分区。这是考虑到性能和可伸缩性。

    为什么不能完全保证这个三点了,个人觉得主要是因为一旦进行分区了,就说明了必须节点之间必须进行通信,涉及到通信,就无法确保在有限的时间内完成指定的行文,如果要求两个操作之间要完整的进行,因为涉及到通信,肯定存在某一个时刻只完成一部分的业务操作,在通信完成的这一段时间内,数据就是不一致性的。如果要求保证一致性,那么就必须在通信完成这一段时间内保护数据,使得任何访问这些数据的操作不可用。

    如果想保证一致性和可用性,那么数据就不能够分区。一个简单的理解就是所有的数据就必须存放在一个数据库里面,不能进行数据库拆分。这个对于大数据量,高并发的互联网应用来说,是不可接受的。

     

    我们可以拿一个简单的例子来说明:假设一个购物系统,卖家A和卖家B做了一笔交易100元,交易成功了,买家把钱给卖家。

    这里面存在两张表的数据:Trade表Account表 ,涉及到三条数据Trade(100),Account A ,Account B

    假设 trade表和account表在一个数据库,那么只需要使用数据库的事务,就可以保证一致性,同时不会影响可用性。但是随着交易量越来越大,我们可以考虑按照业务分库,把交易库和account库单独分开,这样就涉及到trade库和account库进行通信,也就是存在了分区,那么我们就不可能同时保证可用性和一致性。

    我们假设初始状态

    trade(buyer,seller,tradeNo,status) = trade(A,B,20121001,I)

    account(accountNo,balance) = account(A,300)

    account(accountNo,balance) = account(B,10)

    在理想情况下,我们期望的状态是

    trade(buyer,seller,tradeNo,status) = trade(A,B,20121001,S)

    account(accountNo,balance) = account(A,200)

    account(accountNo,balance) = account(B,110)

     

    但是考虑到一些异常情况

    假设在trade(20121001,S)更新完成之前,帐户A进行扣款之后,帐户A进行了另外一笔300款钱的交易,把钱消费了,那么就存在一个状态

    trade(buyer,seller,tradeNo,status) = trade(A,B,20121001,S)

    account(accountNo,balance) = account(A,0)

    account(accountNo,balance) = account(B,10)

    产生了数据不一致的状态

     

    由于这个涉及到资金上的问题,对资金要求比较高,我们必须保证一致性,那么怎么办,只能在进行trade(A,B,20121001)交易的时候,对于任何A的后续交易请求trade(A,X,X),必须等到A完成之后,才能够进行处理,也就是说在进行trade(A,B,20121001)的时候,Account(A)的数据是不可用的。

     

    任何架构师在设计分布式的系统的时候,都必须在这三者之间进行取舍。首先就是是否选择分区,由于在一个数据分区内,根据数据库的ACID特性,是可以保证一致性的,不会存在可用性和一致性的问题,唯一需要考虑的就是性能问题。对于可用性和一致性,大多数应用就必须保证可用性,毕竟是互联网应用,牺牲了可用性,相当于间接的影响了用户体验,而唯一可以考虑就是一致性了。

     

    牺牲一致性

    对于牺牲一致性的情况最多的就是缓存和数据库的数据同步问题,我们把缓存看做一个数据分区节点,数据库看作另外一个节点,这两个节点之间的数据在任何时刻都无法保证一致性的。在web2.0这样的业务,开心网来举例子,访问一个用户的信息的时候,可以先访问缓存的数据,但是如果用户修改了自己的一些信息,首先修改的是数据库,然后在通知缓存进行更新,这段期间内就会导致的数据不一致,用户可能访问的是一个过期的缓存,而不是最新的数据。但是由于这些业务对一致性的要求比较高,不会带来太大的影响。

     

    异常错误检测和补偿

    还有一种牺牲一致性的方法就是通过一种错误补偿机制来进行,可以拿上面购物的例子来说,假设我们把业务逻辑顺序调整一下,先扣买家钱,然后更新交易状态,在把钱打给卖家

    我们假设初始状态

    account(accountNo,balance) = account(A,300)

    account(accountNo,balance) = account(B,10)

    trade(buyer,seller,tradeNo,status) = trade(A,B,20121001,I)

     

    那么有可能出现

    account(accountNo,balance) = account(A,200)

    trade(buyer,seller,tradeNo,status) = trade(A,B,20121001,S)

    account(accountNo,balance) = account(B,10)

     

    那么就出现了A扣款成功,交易状态也成功了,但是钱没有打给B,这个时候可以通过一个时候的异常恢复机制,把钱打给B,最终的情况保证了一致性,在一定时间内数据可能是不一致的,但是不会影响太大。

     

    两阶段提交协议

    当然,还有一种方式就是我另外一篇文章里面《X/Open DTP-分布式事务模型》里面说的,但是再第一阶段和第二阶段之间,数据也可不能是一致性的,也可能出现同样的情况导致异常。而且DTP的分布式事务模型 限制太多,例如必须有实现其功能的相关的容器支持,并且资源管理器也必须实现了XA规范。限制比较多。

     

    国外有的架构师有两种方案去解决CAP的限制,但是也是比较适合特定的业务,而没有通用的解决方案,

    探知分区->分区内操作->事后补偿

    就是上面介绍的异常检测恢复机制,这种机制其实还是有限制,

    首先对于分区检测操作,不同的业务涉及到的分区操作可能不一样

    分区内操作限制:不同的业务对应的约束不一致

    事后补偿:由于业务约束不一样,补偿方式也不一样。

    所以这只能作为一种思想,不能做一个通用的解决方案

     

     

    转载自:http://www.cnblogs.com/aigongsi/archive/2012/10/15/2721366.html

    发布于 1年前, 阅读(69) | 评论(0) | 投票(0) | 收藏(0) 阅读全文...
  • 292012-11
    关于分布式系统的数据一致性问题(三)

    在我的博文里面 关于分布式系统的数据一致性问题(二) 里面主要介绍了数据分布的情况下保证一致性的情况,在第二篇文章里面,我这里提出了三个问题

    1. 订单系统调用支付系统支付订单,支付成功,但是返回给订单系统数据超时,订单还是I(初始状态),但是此时会员帐户余额100,会员肯定会马上找京东骂京东,为啥不给老子发货,我都付钱了
    2. 订单系统调用支付系统成功,状态也已经更新成功,但是通知仓库发货失败,这个时候订单是P(已支付)状态,此时会员帐户余额是100,但是仓库不会发货。会员也要骂京东。
    3. 订单系统调用支付系统成功,状态也已经更新成功,然后通知仓库发货,仓库告诉订单系统,没有货了。这个时候数据状态和第二种情况一样。

     

    重点分析解决了第一个的问题以及相应的方案,发现在数据分布的环境下,很难绝对的保证数据一致性(任何一段区间),但是有办法通过一种补偿机制,最终保证数据的一致性。

     

    在下面在分析一下第二个问题

    • 订单系统调用支付系统成功,状态也已经更新成功,但是通知仓库发货失败,这个时候订单是P(已支付)状态,此时会员帐户余额是100,但是仓库不会发货。会员也要骂京东。

    通过在上一篇文章里面分析过,这个相对来说是比较简单的,我可以采取重试机制,如果发现通知仓库发货失败,就一致重试,

    这里面有两种方式:

    1 异步方式:通过类似MQ(消息通知)的机制,这个是异步的通知

    2 同步调用:类似于远程过程调用

    对于同步的调用的方式,比较简单,我们能够及时获取结果,对于异步的通知,就必须采用请求,应答的方式进行,这一点在(关于分布式系统的数据一致性问题(一))里面有介绍。这里面就不再阐述。

     

    来看看第三个问题

    • 订单系统调用支付系统成功,状态也已经更新成功,然后通知仓库发货,仓库告诉订单系统,没有货了。这个时候数据状态和第二种情况一样。

    我觉得这是一个很有意思的问题,我们还是考虑几种解决的方案

    1 在会员下单的时刻,就告诉仓库,我要你把货物留下来,

    2 在会员支付订单时候,在支付之前检查仓库有没有货,如果没有货,就告知会员木有货物了

    3 如果会员支付成功,这个时候没有货了,就会退款给用户或者等待有货的时候在发货

     

    正常情况,京东的仓库一般都是有货的,所以影响到的会员很少,但是在秒杀和营销的时候,这个时候就不一定了,我们考虑假设仓库有10台iphone

    如果采用第一种方案,

    1 在会员下单的时候,相当于库存就-1,那么用户恶意拍下来,没有去支付,就影响到了其他用户的购买。京东可以设置一个订单超时时间,如果这段时间内没有支付,就自动取消订单

    2 在会员支付之前,检查仓库有货,这种方案了,对于用户体验不好,但是对于京东比较好,至少我东西都卖出去了。那些没有及时付款的用户,只能投诉了京东无故取消订单

    3 第三种方案,这个方案体验更不好,而且用户感觉受到京东欺诈,但是对于京东来说,比第二种方案更有益,毕竟我还可以多卖出一点东西。

     

    个人觉得,京东应该会采用第二种或者第三种方式来处理这类情况,我在微博上搜索了 “京东 无故取消订单”,发现果真和我预料的处理方式。不过至于这里的无故取消是不是技术上的原因我不知道,如果真的是技术上的原因,我觉得京东可以采用不同的处理方案。对于秒杀和促销商品,可以考虑第一种方案,大多数人都会直接付款,毕竟便宜啊,如果用户抢不到便宜的东西,抱怨当然很大了。这样可以照顾大多数用户的体验。对于一般的订单,可以采用第二种或者第三种方式,这种情况下,发生付款之后仓库没有货的情况会比较少,并且就算发生了,用户也会觉得无所谓,大不了退钱吗,这样就可以实现自己的利益最大化而最低程度的减少用户体验。

     

    而铁道部在这个问题上,采用的是第一种方案,为什么和京东不一样,就是因为用户体验,如果用户把票都买了,你告诉我木有票了,旅客会杀人的。哈哈,不过铁道部不担心票卖不出去,第一种方案对他影响没有什么。

     

    说了这么多,就是说 分布式环境下(数据分布)要任何时刻保证数据一致性是不可能的,只能采取妥协的方案来保证数据最终一致性。这个也就是著名的CAP定理。

     

    转载自:http://www.cnblogs.com/aigongsi/archive/2012/09/25/2701396.html


    发布于 1年前, 阅读(36) | 评论(0) | 投票(0) | 收藏(0) 阅读全文...
  • 292012-11

    分布式系统的数据一致性问题(一)里面,简单的介绍了分布式数据的同步问题,上面的问题比较抽象,在目前的互联网应用中还很少见,这次在通过一个比较常见的例子,让大家更深入的了解一下分布式系统设计中关于数据一致性的问题

     

    这次我们拿我们经常使用的功能来考虑吧,最近网购比较热门,就以京东为例的,我们来看看京东的一个简单的购物流程

     

    用户在京东上下了一个订单,发现自己在京东的账户里面有余额,然后使用余额支付,支付成功之后,订单状态修改为支付成功,然后通知仓库发货。假设订单系统,支付系统,仓库系统是三个独立的应用,是独立部署的,系统之间通过远程服务调用。

    订单的有三个状态:I:初始 P:已支付 W:已出库,订单金额100, 会员帐户余额200

    如果整个流程比较顺利,正常情况下,订单的状态会变为I->P->W,会员帐户余额100,订单出库。

     

    但是如果流程不顺利了?考虑以下几种情况

    1:订单系统调用支付系统支付订单,支付成功,但是返回给订单系统数据超时,订单还是I(初始状态),但是此时会员帐户余额100,会员肯定会马上找京东骂京东,为啥不给老子发货,我都付钱了

    2:订单系统调用支付系统成功,状态也已经更新成功,但是通知仓库发货失败,这个时候订单是P(已支付)状态,此时会员帐户余额是100,但是仓库不会发货。会员也要骂京东。

    3:订单系统调用支付系统成功,状态也已经更新成功,然后通知仓库发货,仓库告诉订单系统,没有货了。这个时候数据状态和第二种情况一样。

     

    对于问题一,我们来分析一下解决方案,能想到的解决方案如下

    1 假设调用支付系统支付订单的时候先不扣钱,订单状态更新完成之后,在通知支付系统你扣钱

    如果采用这种设计方案,那么在同一时刻,这个用户,又支付了另外一笔订单,订单价格200,顺利完成了整个订单支付流程,由于当前订单的状态已经变成了支付成功,但是实际用户已经没有钱支付了,这笔订单的状态就不一致了。即使用户在同一个时刻没有进行另外的订单支付行为,通知支付系统扣钱这个动作也有可能完不成,因为也有可能失败,反而增加了系统的复杂性。

     

    2 订单系统自动发起重试,多重试几次,例如三次,直到扣款成功为止。

    这个看起来也是不错的考虑,但是和解决方案一样,解决不了问题,还会带来新的问题,假设订单系统第一次调用支付系统成功,但是没有办法收到应答,订单系统又发起调用,完了,重复支付,一次订单支付了200。

    假设支付系统正在发布,你重试多少次都一样,都会失败。这个时候用户在等待,你怎么处理?

     

    3 在第二种方案的基础上,我们先解决订单的重复支付行为,我们需要在支付系统上对订单号进行控制,一笔订单如果已经支付成功,不能在进行支付。返回重复支付标识。那么订单系统根据返回的标识,更新订单状态。

    接下来解决重试问题,我们假设应用上重试三次,如果三次都失败,先返回给用户提示支付结果未知。假设这个时候用户重新发起支付,订单系统调用支付系统,发现订单已经支付,那么继续下面的流程。如果会员没有发起支付,系统定时(一分钟一次)去核对订单状态,如果发现已经被支付,则继续后续的流程。

     

    这种方案,用户体验非常差,告诉用户支付结果未知,用户一定会骂你,你丫咋回事情,我明明支付了,你告诉我未知。假设告诉用户支付失败,万一实际是成功的咋办。你告诉用户支付成功,万一支付失败咋办。

     

    4 第三种方案能够解决订单和支付数据的一致性问题,但是用户体验非常差。当然这种情况比较可能是少数,可以牺牲这一部分的用户体验,我们还有没有更好的解决方案,既能照顾用户体验,又能够保证资金的安全性。

    我们再回来看看第一种方案,我们先不扣钱,但是有木有办法让这一部分钱不让用户使用,对了,我们先把这一部分钱冻结起来,订单系统先调用支付系统成功的时候,支付系统先不扣钱,而是先把钱冻结起来,不让用户给其他订单支付,然后等订单系统把订单状态更新为支付成功的时候,再通知支付系统,你扣钱吧,这个时候支付系统扣钱,完成后续的操作。

     

    看起来这个方案不错,我们仔细在分析一下流程,这个方案还存在什么问题,假设订单系统在调用支付系统冻结的时候,支付系统冻结成功,但是订单系统超时,这个时候返回给用户,告知用户支付失败,如果用户再次支付这笔订单,那么由于支付系统进行控制,告诉订单系统冻结成功,订单系统更新状态,然后通知支付系统,扣钱吧。如果这个时候通知失败,木有问题,反正钱都已经是冻结的了,用户不能用,我只要定时扫描订单和支付状态,进行扣钱而已。

     

    那么如果变态的用户重新拍下来一笔订单,100块钱,对新的订单进行支付,这个时候由于先前那一笔订单的钱被冻结了,这个时候用户余额剩余100,冻结100,发现可用的余额足够,那就直接在对用户扣钱。这个时候余额剩余0,冻结100。先前那一笔怎么办,一个办法就是定时扫描,发现订单状态是初始的话,就对用户的支付余额进行解冻处理。这个时候用户的余额变成100,订单数据和支付数据又一致了。假设原先用户余额只有100,被冻结了,用户重新下单,支付的时候就失败了啊,的确会发生这一种情况,所以要尽可能的保证在第一次订单结果不明确的情况,尽早解冻用户余额,比如10秒之内。但是不管如何快速,总有数据不一致的时刻,这个是没有办法避免的。

     

    第二种情况和第三种情况如何处理,下次在分析吧。

    由于互联网目前越来越强调分布式架构,如果是交易类系统,面临的将会是分布式事务上的挑战。当然目前有很多开源的分布式事务产品,例如java JPA,但是这种解决方案的成本是非常高的,而且实现起来非常复杂,效率也比较低下。对于极端的情况:例如发布,故障的时候都是没有办法保证强一致性的。

     

    转载自:http://www.cnblogs.com/aigongsi/archive/2012/09/22/2698055.html

    发布于 1年前, 阅读(41) | 评论(0) | 投票(0) | 收藏(1) 阅读全文...
  • 292012-11
    关于分布式系统的数据一致性问题(一)

     

    先把问题简单化处理,假设A增加一条记录Message_A,发送到M,B增加一条记录 MESSAGE_B发送到M,都是通过MQ服务器进行转发,那么M系统接收到条消息,增加两条数据,那么M在把增加的消息群发给A,B,A和B找到自己缺失的数据,更新数据库。这样就完成了一个数据的同步。

     

    从正常情况下来看,都没有问题,逻辑完全合理,但是请考虑以下三个问题

    1 如何保证A->M的消息,M一定接收到了,同样,如何保证M->A的消息,M一定接收到了

    2 如果数据需要一致性更新,比如A发送了三条消息给M,M要么全部保存,要么全部不保存,不能够只保存其中的几条记录。我们假设更新的数据是一条条发送的。

    3 假设同时A发送了多条更新请求,如何保证顺序性要求?

     

    这两个问题就是分布式环境下数据一致性的问题

    对于第一个问题,比较好解决,我们先看看一个tcp/ip协议链接建立的过程

    我们的思路可以从这个上面出发,在简化一下,就一个请求,一个应答。

    简单的通信模型是这样的

    A->M : 你收到我的一条消息没有,消息的ID是12345

    M->A:  我收到了你的一条消息数据,消息数据是ID;12345

    这样就一个请求,一个应答,就完成了一次可靠性的传输。如果A一致没有收到M的应答,就不断的重试。这个时候M就必须保证幂等性。不能重复的处理消息。那么最极端的情况是,怎么也收不到M的应答,这个时候是系统故障。自己检查一下吧。

    这么设计就要求,A在发送消息的时候持久化这个消息的数据内容,然后不断的重试,一旦接收到M的应答,就删除这条消息。同样,M端也是一样的。不要相信MQ的持久化机制,不是很靠谱的。

    那么M给A发送消息也采取类似的原理就可以了。

     

    下面在看看第二个问题,如何保持数据的一致性更新,这个还是可以参考TCP/IP的协议。

    首先A发送一条消息给M:我要发送一批消息数据给你,批次号是10000,数据是5条。

    M发送一条消息给A:ok,我准备好了,批次号是10000,发送方你A

    接着A发送5条消息给M,消息ID分别为1,2,3,4,5 ,批次号是10000,

    紧接着,A发送一个信息给M:我已经完成5小消息的发送,你要提交数据更新了

     

    接下来可能发送两种情况

    1 那么M发送消息给A:ok,我收到了5条消息,开始提交数据

    2 那么M也可以发送给A:我收到了5条消息,但是还缺少,请你重新发送,那么A就继续发送,直到A收到M成功的应答。

    整个过程相当复杂。这个也就是数据一旦分布了,带来最大的问题就是数据一致性的问题。这个成本非常高。

     

    对于第三个问题,这个就比较复杂了

    这个最核心的问题就是消息的顺序性,我们只能在每个消息发一个消息的序列号,但是还是没有最好解决这个问题的办法。因为消息接收方不知道顺序。因为即使给他了序列号,也没有办法告诉他,这个应该何时处理。最好的办法是在第二种方式的基础作为一个批次来更新。

     

    这个只是以最简单的例子来说明一下分布式系统的要保证数据一致性是一件代价很大的事情。当然有的博主会说,这个何必这么复杂,直接数据库同步不就可以了。这个例子当然是没有问题的,万一这个几个库的模型都不一样,我发送消息要处理的事情不一样的。怎么办?

  • 展开阅读全文

    没有更多推荐了,返回首页