Aurora Cache???? ???Cache Server??

2011-12-26 12:33:30by SeaCat

AuroraCacheDesign1

Cache机制概述

使用Cache server是提高系统性能及并发能力的一个重要手段。理论上说,一切获取代价超过内部网络数据传输的操作,如数据库查询、文件访问、复杂数据的构造等等,都可以通过应用cache server来提高性能。并且,由于cache server减少了对数据库这一类更难扩展的关键节点的直接访问,这种架构也能提高整个系统应对并发访问的能力。

一般而言,对cache server的使用很像HashMap。你用一个具有唯一性的key放入某种数据,比如根据用户id放入该用户最近上传的5张照片的html片段,下次你可以再根据这个key来取回数据,不需要重新查询数据库,这样就能加速页面的创建过程。

Cache可以在系统架构的任何一层进行。数据库、操作系统通常有自己的cache,Web server也有内部cache。除此之外,还有memcached这样的独立cache server,或者JBoss cache这样的运行于JVM内部的cache服务。相对来说,一般来说,应用系统根据自己的业务逻辑,使用独立的cache组件来管理cache,能获取比数据库cache这样对应用透明的cache机制更好的性能。设想一下,数据库的SQL cache有非常多的限制条件,任何一个细微的原始输入改变,都可能导致cache失效。而应用系统的设计者,通常对于“什么时候需要更新cache”有着更精准的控制。比如,一个电子商务网站,可以接受更新产品数据之后,消费者不是马上看到变化,而是有几秒钟的延迟。这样,就可以通过一个后台线程,定期将更新后的产品数据写入cache server,而不是对产品的任何信息更改后都导致cache失效。

Cache server的特性

以下探讨主要针对memcached进行,因为memcached是目前全世界应用最广泛、认可度最高的产品。

进程内 vs 进程外

对于Java应用来说,独立于JVM进程的独立cache server,尤其是memcached这类使用C开发的非VM机制的cache server,具有更好的可扩展性。Cache server本身就是内存消耗大户,如果由于cache server自己的内存垃圾回收导致应用的性能下降,那就有点得不偿失了。

分布式存储 vs 共享式存储

当系统需要cache的数据大到一定程度时,单台服务器可能到达内存物理上限。于是,cache server集群的架构就成为自然的选择。Facebook构造了成千上万的memcached server集群,数据量达到了TB级。一旦使用cache server集群,一个很重要的考虑就是,数据如何分布于各台server节点中? memcached选择分布式存储,也就是说,一个节点中的数据不会在任何一个其它节点中存在。另外一些产品,使用共享式存储,在节点之间有一些复制服务,使得同样的数据可能存在于多个节点中。前者使得集群的架构非常简单,带来更好的性能和更低的管理成本。后者使得数据更加可靠,不会由于一个节点的失效导致cache数据丢失。

那么,问题的关键就在于:cache数据需要reliable吗?

Reliable vs Unreliable

memcached中的数据是不具备容错能力的。你放进一个节点的数据,很可能由于这个节点的失效而丢失。如果这种情况发生,memcached的客户端程序会对该数据的请求返回空,并记得这个节点已经不可用。这是一个问题吗?如果你从一开始就认为cache数据是不可靠的,并基于这种假设去设计程序,那么这就不会是一个问题。当你去获取数据的时候,数据由于之前没有放进去而不存在,和由于某个server节点宕机而不存在,没有任何区别。你尝试获从cache取数据,如果它不存在,就用常规手段去获取,并通过某种机制把数据写入cache。如果你有10个节点,那么1个节点宕机的代价就是在一段时间内损失了大约1/10的性能。但很快宕机的节点中原本存在的数据,会被重新获取,分别写入其它的9个节点,系统逐渐自我恢复正常。

基于这样的理念,memcached的server节点是完全不知道其它节点的存在的,也没有一个“中央调度器”来将客户端的请求分发给各个节点,也没有一个“中央控制器”来将失效的节点中的数据主动转移到其它节点。它的逻辑非常简单:客户端程序从一开始就知道所有节点的ip,通过一个Hash算法将某个key唯一地分配到某个节点。如果这个节点宕机,客户端程序重新更新自己的可用server节点表,再将这个key分配到其它的节点。这样的机制使得memcached具有超强的可扩展性,不存在任何瓶颈节点或全局关键节点,以某一节点宕机时损失部分性能的代价,来换取整体更好的性能。

但总有一些场合,我们希望cache数据是reliable的,如果你放进去了,它就在那里,除非所有的节点都玩完。这时候,具有冗余备份能力的cache server就很有意义。可靠的cache机制有多种方式实现,从天生具有该特性的独立产品,到memcached+数据库这样的组合方案。

Lockable vs Unlockable

设想这样的场景:多个线程同时向cache server写入同一个key,你希望cache server能像数据库一样,先锁定这个key,防止其它线程写入吗?

例如,你放在cache server中的数据是一个list,这个list里面的数据是全局共享的(如,当前在线用户清单)。你有多个线程可能会修改这个list,修改的逻辑是先通过key将list取到程序内存中(server向client程序传递该list序列化后的二进制数据,client再通过反序列化在本地进程构造list的一份copy),向list中写入数据,再将list通过client程序序列化成二进制数据,写回cache server。

如果A线程用key1获取list,B线程之后同样用key1获取list,B线程在自己的list中写入自己的数据,B线程先写回list,A线程再向list写入自己的数据,A线程写回list,这时候B线程写入的数据就会丢失。除非,A线程能事先“锁住”key1,避免B线程在自己操作的同时获取list,等A线程操作完毕后,B线程再获得的list就已经包含了A写入的数据,这样大家都不会丢失自己写入的数据。世界从此完美了。

但是,很遗憾,memcached不提供这样的机制。理由是:为了性能。所以,从一开始,就要避免程序中出现这样的设计。

Demo
    Attachments

      Comments

      0 Responses to the article

      暂时没有评论。

      发表评论