合理使用Memcached进行缓存部署
Memcached是danga.com(运营 LiveJournal的技术团队)开发的一套分布式内存对象缓存系统,用于在动态系统中减少数据库负载,提升性能。关于这个东西,相信很多人都用过,本 文意在通过对memcached的实现及代码分析,获得对这个出色的开源软件更深入的了解,并可以根据我们的需要对其进行更进一步的优化。末了将通过对 BSM_Memcache扩展的分析,加深对memcached的使用方式理解。
本文的部分内容可能需要比较好的数学基础作为辅助。
◎Memcached是什么
在阐述这个问题之前,我们首先要清楚它“不是什么”。很多人把它当作和SharedMemory那种形式的存储载体来使用,虽然memcached 使用了同样的“Key=>Value”方式组织数据,但是它和共享内存、APC等本地缓存有非常大的区别。Memcached是分布式的,也就是说 它不是本地的。它基于网络连接(当然它也可以使用localhost)方式完成服务,本身它是一个独立于应用的程序或守护进程(Daemon方式)。
Memcached使用libevent库实现网络连接服务,理论上可以处理无限多的连接,但是它和Apache不同,它更多的时候是面向稳定的持 续连接的,所以它实际的并发能力是有限制的。在保守情况下memcached的最大同时连接数为200,这和Linux线程能力有关系,这个数值是可以调 整的。关于libevent可以参考相关文档。 Memcached内存使用方式也和APC不同。APC是基于共享内存和MMAP的,memcachd有自己的内存分配算法和管理方式,它和共享内存没有 关系,也没有共享内存的限制,通常情况下,每个memcached进程可以管理2GB的内存空间,如果需要更多的空间,可以增加进程数。
◎Memcached适合什么场合
在很多时候,memcached都被滥用了,这当然少不了对它的抱怨。我经常在论坛上看见有人发贴,类似于“如何提高效率”,回复是“用memcached”,至于怎么用,用在哪里,用来干什么一句没有。memcached不是万能的,它也不是适用在所有场合。
Memcached是“分布式”的内存对象缓存系统,那么就是说,那些不需要“分布”的,不需要共享的,或者干脆规模小到只有一台服务器的应 用,memcached不会带来任何好处,相反还会拖慢系统效率,因为网络连接同样需要资源,即使是UNIX本地连接也一样。 在我之前的测试数据中显示,memcached本地读写速度要比直接PHP内存数组慢几十倍,而APC、共享内存方式都和直接数组差不多。可见,如果只是 本地级缓存,使用memcached是非常不划算的。
Memcached在很多时候都是作为数据库前端cache使用的。因为它比数据库少了很多SQL解析、磁盘操作等开销,而且它是使用内存来管理数 据的,所以它可以提供比直接读取数据库更好的性能,在大型系统中,访问同样的数据是很频繁的,memcached可以大大降低数据库压力,使系统执行效率 提升。另外,memcached也经常作为服务器之间数据共享的存储媒介,例如在SSO系统中保存系统单点登陆状态的数据就可以保存在memcached 中,被多个应用共享。
需要注意的是,memcached使用内存管理数据,所以它是易失的,当服务器重启,或者memcached进程中止,数据便会丢失,所以 memcached不能用来持久保存数据。很多人的错误理解,memcached的性能非常好,好到了内存和硬盘的对比程度,其实memcached使用 内存并不会得到成百上千的读写速度提高,它的实际瓶颈在于网络连接,它和使用磁盘的数据库系统相比,好处在于它本身非常“轻”,因为没有过多的开销和直接 的读写方式,它可以轻松应付非常大的数据交换量,所以经常会出现两条千兆网络带宽都满负荷了,memcached进程本身并不占用多少CPU资源的情况。
使用Memcached来缓存数据是目前最有效的方案,使用Memcached能够有效的降低MySQL的查询压力。Memcached工作的原理是将 MySQL查询出来的结果存储在内存中,下次查询时可以避开MySQL,以此来降低数据库的压力。但是现在的社区网站中,数据的更新十分频繁,这也给数据 缓存带来了不小的难度。如果缓存的更新时间设计不合理,用户不能在数据修改的第一时间看到结果;过于频繁的更新又会丧失缓存的功能。
在万蝶 最原始的缓存策略中,我们采用的是:默认数据缓存1天时间,如果期间数据 有更新,在更新的同时删除原有的缓存然后重新查询数据库并缓存新数据,如果没有没有更新,缓存数据1天后自动过期,下次查询经过数据库后重新缓存。这样的 设计方案保证了数据的及时性,用户的更新在第一时间呈现出来,但是弊端是过于频繁的更新不但没有起到降低数据库压力的要求,同时也给Memcached自 身带来一定的压力。
我们向数据库中插入一条数据,之后在需要的时候再从数据库中把这条数据提取出来,我们好像是在刻意给数据库增加压力?如果我们在把数据插入数据库的 同时,也把这条数据同时让Memecached保存起来呢?这样,在需要查询的时候我们就可以直接从Memcached中得到这条数据了,查询避开 MySQL,这样可以将数据库的压力将到最低。
下一步我们要考虑的就是在数据发生更新时候的操作了,这个时候就该Memcache::replace上场了。在pdx 中, 更新最频繁的就是用户的网志了,每一次的阅读,每一次的评论,数据都需要更新,但是每次更新的也只是数据中的一部分,所以这种情况下,我们只需要将 Memcached中的数据提取出来,将需要更新的部分替换掉,然后使用Memcache::replace替换掉老的缓存,这样我们的需求就基本达到 了:查询避开了数据库,有效的减轻了数据库压力;同时我们又在数据修改的第一时间让用户看到了更新。
最后提出两个使用Memcache的小技巧,第一个是如果是一个有效分布式存储的数据,key的取名是很有学问的,这个可以按照项目需要去做,但是key值 不要太长,不会冲突就行。第二个就是每个保存在memcache中的数据不要超过1MB。第三个就是开启一个Memcache进程设置内存不要太多也不要 太少,按照自己合适设置就行,尽量最大程度提高对硬件的使用,同样可以采取在一台服务器上开启多个memcached来分担一个memcached并发链 接的压力。更多技巧在实际使用长慢慢去总结发现,会发现其实memcahe虽然简单,但是很好用。