Memcache应用场景介绍,说明[zz]
转于:http://www.cnblogs.com/literoad/archive/2012/12/23/2830178.html
面临的问题
对于高并发高访问的 Web应用程序来说,数据库存取瓶颈一直是个令人头疼的问题。特别当你的程序架构还是建立在单数据库模式,而一个数据池连接数峰 值已经达到500的时候,那你的程序运行离崩溃的边缘也不远了。很多小网站的开发人员一开始都将注意力放在了产品需求设计上,缺忽视了程序整体性能,可扩 展性等方面的考虑,结果眼看着访问量一天天网上爬,可突然发现有一天网站因为访问量过大而崩溃了,到时候哭都来不及。所以我们一定要未雨绸缪,在数据库还 没罢工前,想方设法给它减负,这也是这篇文章的主要议题。
大家都知道,当有一个request过来后,web服务器交给app服务 器,app处理并从db中存取相关数据,但db存取的花费是相当高昂的。特 别是每次都取相同的数据,等于是让数据库每次都在做高耗费的无用功,数据库如果会说话,肯定会发牢骚,你都问了这么多遍了,难道还记不住吗?是啊,如果 app拿到第一次数据并存到内存里,下次读取时直接从内存里读取,而不用麻烦数据库,这样不就给数据库减负了?而且从内存取数据必然要比从数据库媒介取快 很多倍,反而提升了应用程序的性能。
因此,我们可以在web/app层与db层之间加一层cache层,主要目的:1. 减少数据库读取负担;2. 提高数据读取速度。而且,cache存取的媒介是内存,而一台服务器的内存容量一般都是有限制的,不像硬盘容量可以做到TB级别。所以,可以考虑采用分布 式的cache层,这样更易于破除内存容量的限制,同时又增加了灵活性。
Memcached 介绍
Memcached 是开源的分布式cache系统,现在很多的大型web应用程序包括 facebook,youtube,wikipedia,yahoo等等都在使用memcached来支持他们每天数亿级的页面访问。通过把cache层 与他们的web架构集成,他们的应用程序在提高了性能的同时,还大大降低了数据库的负载。 具体的memcached资料大家可以直接从它的官方网站[1]上得到。这里我就简单给大家介绍一下memcached的工作原理:
Memcached处理的原子是每一个(key,value)对(以下简称kv对),key会通过一个hash算法转化成hash-key,便于查找、对比以及做到尽可能的散列。同时,memcached用的是一个二级散列,通过一张大hash表来维护。
Memcached 有两个核心组件组成:服务端(ms)和客户端(mc),在一个memcached的查询中,mc先通过计算key的hash值来 确定kv对所处在的ms位置。当ms确定后,客户端就会发送一个查询请求给对应的ms,让它来查找确切的数据。因为这之间没有交互以及多播协议,所以 memcached交互带给网络的影响是最小化的。
举例说明:考虑以下这个场景,有三个mc分别是X,Y,Z,还有三个ms分别是A,B,C:
设置kv对 X想设置key=”foo”,value=”seattle” X拿到ms列表,并对key做hash转化,根据hash值确定kv对所存的ms位置 B被选中了 X连接上B,B收到请求,把(key=”foo”,value=”seattle”)存了起来
获取kv对 Z想得到key=”foo”的value Z用相同的hash算法算出hash值,并确定key=”foo”的值存在B上 Z连接上B,并从B那边得到value=”seattle” 其他任何从X,Y,Z的想得到key=”foo”的值的请求都会发向B
Memcached服务器(ms)
内存分配
默 认情况下,ms是用一个内置的叫“块分配器”的组件来分配内存的。舍弃c++标准的malloc/free的内存分配,而采用块分配器的主要目的 是为了避免内存碎片,否则操作系统要花费更多时间来查找这些逻辑上连续的内存块(实际上是断开的)。用了块分配器,ms会轮流的对内存进行大块的分配,并 不断重用。当然由于块的大小各不相同,当数据大小和块大小不太相符的情况下,还是有可能导致内存的浪费。
同时,ms对key和 data都有相应的限制,key的长度不能超过250字节,data也不能超过块大小的限制 --- 1MB。 因为mc所使用的hash算法,并不会考虑到每个ms的内存大小。理论上mc会分配概率上等量的kv对给每个ms,这样如果每个ms的内存都不太一样,那 可能会导致内存使用率的降低。所以一种替代的解决方案是,根据每个ms的内存大小,找出他们的最大公约数,然后在每个ms上开n个容量=最大公约数的 instance,这样就等于拥有了多个容量大小一样的子ms,从而提供整体的内存使用率。
缓存策略
当ms的hash表满了之后,新的插入数据会替代老的数据,更新的策略是LRU(最近最少使用),以及每个kv对的有效时限。Kv对存储有效时限是在mc端由app设置并作为参数传给ms的。
同时ms采用是偷懒替代法,ms不会开额外的进程来实时监测过时的kv对并删除,而是当且仅当,新来一个插入的数据,而此时又没有多余的空间放了,才会进行清除动作。
缓存数据库查询 现在memcached最流行的一种使用方式是缓存数据库查询,下面举一个简单例子说明:
App需要得到userid=xxx的用户信息,对应的查询语句类似:
“SELECT * FROM users WHERE userid = xxx”
App先去问cache,有没有“user:userid”(key定义可预先定义约束好)的数据,如果有,返回数据;如果没有,App会从数据库中读取数据,并调用cache的add函数,把数据加入cache中。
当取的数据需要更新,app会调用cache的update函数,来保持数据库与cache的数据同步。
从 上面的例子我们也可以发现,一旦数据库的数据发现变化,我们一定要及时更新cache中的数据,来保证app读到的是同步的正确数据。当然我们可 以通过定时器方式记录下cache中数据的失效时间,时间一过就会激发事件对cache进行更新,但这之间总会有时间上的延迟,导致app可能从 cache读到脏数据,这也被称为狗洞问题。(以后我会专门描述研究这个问题)
数据冗余与故障预防
从设计角度上,memcached是没有数据冗余环节的,它本身就是一个大规模的高性能cache层,加入数据冗余所能带来的只有设计的复杂性和提高系统的开支。
当一个ms上丢失了数据之后,app还是可以从数据库中取得数据。不过更谨慎的做法是在某些ms不能正常工作时,提供额外的ms来支持cache,这样就不会因为app从cache中取不到数据而一下子给数据库带来过大的负载。
同时为了减少某台ms故障所带来的影响,可以使用“热备份”方案,就是用一台新的ms来取代有问题的ms,当然新的ms还是要用原来ms的IP地址,大不了数据重新装载一遍。
另 外一种方式,就是提高你ms的节点数,然后mc会实时侦查每个节点的状态,如果发现某个节点长时间没有响应,就会从mc的可用server列表里 删除,并对server节点进行重新hash定位。当然这样也会造成的问题是,原本key存储在B上,变成存储在C上了。所以此方案本身也有其弱点,最好 能和“热备份”方案结合使用,就可以使故障造成的影响最小化。
Memcached客户端(mc)
Memcached客户端有各种语言的版本供大家使用,包括java,c,php,.net等等,具体可参见memcached api page[2]。 大家可以根据自己项目的需要,选择合适的客户端来集成。
缓存式的Web应用程序架构 有了缓存的支持,我们可以在传统的app层和db层之间加入cache层,每个app服务器都可以绑定一个mc,每次数据的读取都可以从ms中取得,如果 没有,再从db层读取。而当数据要进行更新时,除了要发送update的sql给db层,同时也要将更新的数据发给mc,让mc去更新ms中的数据。
假设今后我们的数据库可以和ms进行通讯了,那可以将更新的任务统一交给db层,每次数据库更新数据的同时会自动去更新ms中的数据,这样就可以进一步减少app层的逻辑复杂度。如下图:
不过每次我们如果没有从cache读到数据,都不得不麻烦数据库。为了最小化数据库的负载压力,我们可以部署数据库复写,用slave数据库来完成读取操作,而master数据库永远只负责三件事:1.更新数据;2.同步slave数据库;3.更新cache。如下图:
以上这些缓存式web架构在实际应用中被证明是能有效并能极大地降低数据库的负载同时又能提高web的运行性能。当然这些架构还可以根据具体的应用环境进行变种,以达到不同硬件条件下性能的最优化。
不太建议在任何情况下使用Memcache替代任何缓存:
1) 如果Value特别大,不太适合。因为在默认编译下Memcache只支持1M的Value(Key的限制到不是最大的问题)。其实从实践的角度来说也不 建议把非常大的数据保存在Memcache中,因为有序列化反序列化的过程,别小看它消耗的CPU。说到这个就要提一下,我一直觉得Memcache适合 面向输出的内容缓存,而不是面向处理的数据缓存,也就是不太适合把大块数据放进去拿出来处理之后再放进去,而是适合拿出来就直接给输出了,或是拿出来不需 要处理直接用。
2) 如果不允许过期,不太适合。Memcache在默认情况下最大30天过期,而且在内存达到使用限制后它也会回收最少使用的数据。因此,如果我们要把它当作 static变量的话就要考虑到这个问题,必须有重新初始化数据的过程。其实应该这么想,既然是缓存就是拿到了存起来,如果没有必定有一个重新获取重新缓 存的过程,而不是想着它永远存在。
在使用Memcache的过程中当然也会有一些问题或者说最佳实践:
1) 清除部分数据的问题。Memcache只是一个Key/Value的池,一个公共汽车谁都可以上。我觉得对于类似的公共资源,如果用的人都按照自己的规则 来的话很容易出现问题。因此,最好在Key值的规范上上使用类似命名空间的概念, 每一个用户都能很明确的知道某一块功能的Key的范围,或者说前缀。带来的好处是我们如果需要清空的话可以根据这个规范找到我们自己的一批Key然后再去 清空,而不是清空所有的。当然有人是采用版本升级的概念,老的Key就让它过去吧,到时候自然会清空,这也是一种办法。不过Key有规范总是有好处的,在 统计上也方便一点。
2) Value的组织问题。也就是说我们存的数据的粒度,比如要保存一个列表,是一个保存在一个键值还是统一保存为一个键值,这取决于业务。如果粒度很小的话 最好是在获取的时候能批量获取,在保存的时候也能批量保存。对于跨网络的调用次数越少越好,可以想一下,如果一个页面需要输出100行数据,每一个数据都 需要获取一次,一个页面进行上百次连接这个性能会不会成问题。
那么Memcache主要用在哪些功能上呢?
其实我觉得平时能想到在内存中做缓存的地方我们都可以考虑下是不是可以去适用分布式缓存,但是主要的用途还是用来在前端或中部挡一下读的需求来释放Web服务器App服务器以及DB的压力。
转于:http://www.ooso.net/archives/306
memcache的优缺点:
1、
当php大量访问数据库对数读操作时,应用memcache会减少数据库的访问次数,大大提升php和数据库的运行效率,而xcache只是用来减少php重新编译的次数以减少系统资源的占用。
xcache不仅仅有着opcode的缓存功能,还提供了一些api用来保存php的变量,如下:
mixed xcache_get(string name)
bool xcache_set(string name, mixed value [, int ttl])
bool xcache_isset(string name)
bool xcache_unset(string name)
int xcache_inc(string name [, int value [, int ttl]])
int xcache_dec(string name [, int value [, int ttl]])
我想david应该说的就是这类功能吧。我个人有在高负载的场合使用memcache来缓存db layer的东东,还有apc缓存php opcdoe,但是还没机会尝试xcache在这种场合下的性能,所以有待试验。
其实APC的功能比xcache强大一点,不仅有opcode级的缓存,还有类似memcache的数据/变量缓存,像 apc_store, apc_fetch 之类的接口做这些事情,也能够控制包括数据的有效时间等,不过一般适合缓存存储周期长,数据量小的数据。memcached比这个要强大的多,把apc和 memcached结合使用是最好的。