使用memcache缓存数据,减少对数据库的直接访问,提高网站性能已经成了各大网站最基本的技术.如何更好的提高memcache缓存的利用率及命中次数会在后面的blog中单独介绍,本文主要探讨为何及如何使用本地缓存(java localcache)提高网站性能.
localcache与memcache性能比较
先来个本地缓存与memcache缓存的性能比较,有个直观上的概念
Cache |
请求方式 |
次数 |
时间 |
平均 |
Localcache |
hashmap中get请求 |
1亿 |
1344ms |
0.00001344ms |
Memcache |
简单的get请求,不做序列化 |
1万 |
4437ms |
0.4437ms |
Db |
单表查询(有索引) |
1-2ms |
以上测试在开发机器.生产环境采集的数据显示memcache的一次请求大约在0.2ms左右,如果存储的是java object,那算上发序列化的时间在0.5ms以上.与测试数据在同一个数量级上.
通过以上数据对比,可以得知localcache的效率比memcache高1万倍以上.这个数字让我对使用本地缓存充满了极大的兴趣.
使用localcache会带来哪些问题
localcache有着极大的性能优势,单机情况下,适当使用localcache会使程序的效率得到很大的提升.但在集群环境下localcache就存在很多问题了,主要体现在多个jvm之间cache的同步问题.
有很多框架在这上面做了很多工作,比如ehcache ,主要是通过cache复制(copy或invalidate)来解决,大概的思路是使用消息多播机制,当一个jvm中的数据做了更新操作后,首先更新本jvm内的localcache,然后广播消息,其他jvm接收到消息后更新自己的localcache. 但这种机制可能带来并发操作时出现脏数据的问题,具体见Potential Issues with Replicated Caching.
其他cache产品也遇到类似的问题,不再一一介绍.
那有没有很好的方法来解决localcache的同步问题,从而可以放心的品尝localcache这块"甜饼"呢?
这个问题我也很纠结,通过多种方案的组合及补偿机制似乎可以实现一个完美的方案.但也注定成为了一个复杂的方案.类似的方案可以有如下几种:
1.localcache作为一级缓存,通过广播的方式同步缓存,同时设置缓存过期时间,以达到数据同步和出现脏数据后自动修复的功能.
2.localcache作为一级缓存,数据更新后发送异步消息(MQ等),其余localcache订阅异步消息,并根据消息来同步缓存.
3.localcache作为一级缓存,memcache中存放缓存变更的信息,定时任务定时获取memcache的信息,并决定是否更新localcache.
4.localcache作为一级缓存,每次从memcache中获取数据更改的标记位,如果标记发生变化,更新localcache
以上的这些实现方案,都在一定程度上加大了架构的复杂性,当localcache中数据出现脏数据时,排查问题及清理数据都会变得复杂.
他人经验之谈
1.sohu早期使用广播的方式(jgroup)同步localcache,结果经常会出现脏数据的问题,在后来的架构设计上干脆摒弃了localcache(即使使用,也不再作数据同步),全部使用memcache.
2.taobao在生产环境也很少使用localcache同步,对于非敏感性数据,只是通过简单的过期策略,来保证数据的一致性.
总结
集群环境下对于敏感性要求不高的数据可以使用localcache,只配置简单的失效机制来保证数据的一致性.
对敏感性高的数据直接使用集中式缓存,减低复杂度.
复杂方案看似完美的解决了问题,实际上性能和稳定性却很可能大打折扣.
感谢William 、放翁提出的宝贵建议。