分布式缓存的策略
非原创,
转载自:http://blog.163.com/minecz@126/blog/static/62590769201014111316768/
由于工作需要,2年前开始接触分布式的缓存--memcache。2年过去了,memcahce也发展了不少,同时也出现许多新星--TT、redis。但万变不离其宗,现将心得总结如下。
先说最早的memcache
这个东西感觉就是个BDB的内存版,就因为是纯内存的程序,所以性能异常的高。现在好像有自己的主页了(http://memcached.org/),记得最早的时候就是danga的个人主页,而且除了一个readme之外就没什么资料了,不过现在这个东西已经遍地开花了。
关于这个东西里面到底应该存放数据网上一直有很多种说法,有的说sql进行md5之后作为键值,结果作为内容存放,也有人说按照业务逻辑错放,反正是炒的不亦乐乎。
本人经过将近2年的实践,最后还是觉得要根据业务逻辑来存放,不能将sql加密然后对应结果集存放。这样做,基本上无法实现数据的及时更新,只能依靠memcahce的过期时间来更新。资讯类的静态数据比较合适,不过这种网站一般会做静态化的处理,所以memcache也发挥不了太大用途。真正有用武之地的地方是社区类网站,这类网站大部分是动态数据,而且性能要求还高,所以memcahce比较合适(貌似memcache本来就为了解决这个问题才被创造出来的)。
如果按照业务来进行存储,很容易控制数据的准确性,一般的程序每种业务都会有自己的底层数据,memcahce部分的操作何以放在这部分代码里。这样对上层程序是透明的,不会将上层的开发工程师搞的晕头转向。
memcache操作的基本逻辑:先检查在mem中是否存在,如果存在,则返回mem中的数据,如果不存在,根据条件到db中查找相应的结果,然后放入mem中,返回结果(如果mem写入失败,需要记录下日志)。这个逻辑有个弊端就是当缓存失效的时候上层程序没有感觉,结果底层程序一直会访问db,很可能导致db宕机,这就是为什么在mem写入失败的时候要记录日志的原因。以上实现了数据的存储,下面讲数据的更新,当db有insert和update操作的时候,需要根据相应的条件找到与之有关的mem键值,然后删除掉(当然您也可以在这个时候直接将修改后的数据放入mem,不过这样做性能就下降了一倍),当下次再有程序检查这个键值的时候,就会发现键值不存在,然后它会从db中再取一份新的数据出来放入mem。这样就实现了数据的被动更新。
为什我要把memcache设计成这样一个被动式更新的方式呢?因为这样做最节省资源。看看cdn的缓存方式就明白了,cdn也是一种被动式的缓存,只有当有用户访问到对应的资源是,cdn才会去检查相应的资源是否存在,不存在了去源站取回资源并缓存在自己这里。如果把memcache设计成主动更新的形式,实在是太浪费资源,因为这样做会有很多基本上没有访问的数据被放入了mem,如果想优化,还需要统计数据的使用率,然后才能决定哪部分数据不放入缓存。实现难度太高。
下面继续深入。存储逻辑清楚了,下面就开始看数据了。大家都知道,不同的业务数据的大小也是不一样的(session的数据基本上都是很小的值,基本上就几十字节,单条用户数据基本不会很大,也就1-3k,资源类的就比较大一点了,相片,投票,分享等基本在1k以上)。为什么说这个呢?这是由memcache的存储机制决定的,memcache存储在内存中的数据并不是有多长存多长的,而是预先定义小的大小,数据优先存放在能够存放的最小的空间里,至于为什么要这样做,大家可以看看memcache的帮助。由于这个实际情况,所以我们必须考虑要存放的数据的实际大小,以免内存被浪费掉(虽然内存都白菜价了,但那也是钱啊)。memcache有个启动参数-f用来控制最小空间的大小,基础空间大小是80b,然后按照80*(1+f)^n这个公式一次计算以后的空间大小。f的默认值是0.25。所以如果存储session数据,那么这个值可以适当的改小,如果存资源类的数据,这个值可以适当的改大,不建议改的太大,那样太浪费空间。其他的参数大家可以看网上的资料就可以学到了,基本上没什难得。
下面再深入,逻辑清楚了,单个进程也完美的启动了,下面是分布式的问题。memcahce的优势除了性能高之外就是分布式的结构了,这个结构可以让性能进一步的提升。既然要分布式,必然要进行hash运算,确定数据的实际存储位置。最初的memcahce只有一个余数的算法,虽然均匀性很好但是在增加以及减少机器的时候,命中率实在是低的可怜,所以后来出现了ConsistentHashing算法。这部分没什么说的,网上资料一大堆(http://ispring.javaeye.com/blog/221355)
到这里基本上程序已经运行起来了,剩下的是维护工作,经常去看看memcache服务器的状态啦,命中率啦,lru值啦,是否内存块存满啦等等,这个基本上是要和运维配合来做的事情了(因为他们有cacti,^_^)。
memcache基本上没什么了,后来出现TT,也是个很不错的东西,和memcahce一样的协议,还能定时将数据写到硬盘上,很好的解决了memcache宕机丢数据的问题。原来的session数据完全就可以挪到TT里了。
redis,这个是个更不错的东西,memcahce,TT的优点它都有,而且据说还更好。而且还有list和set,list和set解决了很多memcahe解决不了的问题,原来做list只能用memcahce做个伪的,要不然就弄个m4q,再不然找个queue软件,还要装,性能还没有redis好。要不是它现在还不太成熟,就可以将大部分缓存迁移到redis上了。不过redis的作者也偷懒,php的客户端竟然有严重bug!!!而且貌似redis的分布式也没有memcahce好。希望今年redis可以多进步点。