memcached真实项目中的应用
1 缓存式的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的运行性能。
当然这些架构还可以根据具体的应用环境进行变种,以达到不同硬件条件下性能的最优化。
2 以代码为示例
这一篇将以介绍一个memcached在项目中的应用。
假设我们有一个web应用,里面有商品信息,文章信息,评论信息,其他信息,我们希望对其做缓存,
那么我们在ServiceImpl层就不在调用DAOmpl层,而是调用CacheImpl层,
在CacheImpl层中判断要取出的商品信息是否已经在缓存中,如果在了,那么直接从缓存中去,
如果没有这个时候还是从数据库中取,同时将它放到缓存中,以便下次使用。
2.1 新建一个常量类,用于上面的四种信息的在数组中的索引。
1 public class MemcachedConstant { 2 public static final int MEMCACHED_GOODSDETAIL = 0; 3 public static final int MEMCACHED_ARTICLEDETAIL = 1; 4 public static final int MEMCACHED_COMMENTDETAIL = 2; 5 public static final int MEMCACHED_OTHERDETAIL = 3; 6 }
2.2 加前缀
由于有大量的商品信息,我们在放入缓存时必须给定一个key,那么我们最好规范的命名不同类别的key,如商品的key就是商品的前缀加上商品的编号。
1.public class MemcachedKeyUtil { 2. private static final String GOODS_KEY_PREFIX = "goods_"; 3. 4. public static String getGoodsKey(long goodsId) { 5. return GOODS_KEY_PREFIX + goodsId; 6. } 7.}
.3 建立缓存工具类
我们建一个和上一篇文章中一样的工具类,用于新建pool、client,操作缓存等。
这里再强调一下,一个pool关联多个server(就是会根据权重将缓存放在这些servers上),一个client会通过poolName关联具体的pool。
1.public class MemcachedUtil { 2. private int MEMCACHED_SERVER_NUM = 4; 3. private SockIOPool[] pools = new SockIOPool[MEMCACHED_SERVER_NUM]; 4. private MemCachedClient[] mcs = new MemCachedClient[MEMCACHED_SERVER_NUM]; 5. private final String[] poolNames = new String[] { "GOODSDETAIL_POOL", "", "", "" }; 6. private static MemcachedUtil instance; 7. private MemcachedUtil() { 8. this.init(); 9. } 10. // 单例 11. public static MemcachedUtil getInstance() { 12. if (MemcachedUtil.instance == null) { 13. synchronized (MemcachedUtil.class) { 14. if (MemcachedUtil.instance == null) { 15. MemcachedUtil.instance = new MemcachedUtil(); 16. } 17. } 18. } 19. return MemcachedUtil.instance; 20. } 21. 22. public Object get(int index, String key) { 23. return this.mcs[index].get(key); 24. } 25. 26. public boolean set(int index, String key, Object value) { 27. return this.mcs[index].set(key, value); 28. } 29. 30. public boolean delete(String key) { 31. return this.mcs[index].delete(key); 32. } 33. public MemCachedClient getMemCachedClient(int index) { 34. return this.mcs[index]; 35. } 36. 37. public void init() { 38. for (int i = 0; i < MEMCACHED_SERVER_NUM; ++i) { 39. this.pools[i] = SockIOPool.getInstance(poolNames[i]); 40. this.pools[i].setServers(servers); 41. this.pools[i].setWeights(weights); 42. this.pools[i].setInitConn(initConn); 43. this.pools[i].setMinConn(minConn); 44. this.pools[i].setMaxConn(maxConn); 45. this.pools[i].setMaxIdle(maxIdle); 46. this.pools[i].setMaxBusyTime(maxBusyTime); 47. this.pools[i].setMaintSleep(maintSleep); 48. this.pools[i].setNagle(ifNagle); 49. this.pools[i].setSocketTO(socketTO); 50. this.pools[i].setSocketConnectTO(socketConnectTO); 51. this.pools[i].setFailover(ifFailOver); 52. this.pools[i].setFailback(ifFailback); 53. this.pools[i].setAliveCheck(ifAliveCheck); 54. this.pools[i].initialize(); 55. this.mcs[i] = new MemCachedClient(poolNames[i]); 56. } 57. } 58.}
2.3 与dao一起使用
新建一个基类以供所用继承它的CacheImpl直接调用MemcachedUtil里的方法,如果不写该类那么在CacheImpl中会有很多重复的操作MemcachedUtil的代码。
1.public class MemcachedSupport { 2. public boolean setDetailData(String key, Object value) { 3. return MemcachedUtil.getInstance().set(MemcachedConstant.MEMCACHED_DETAIL, key, value); 4. } 5. 6. public Object getDetailData(String key) { 7. return MemcachedUtil.getInstance().get(MemcachedConstant.MEMCACHED_DETAIL, key); 8. } 9. 10. public boolean deleteDetailData(String key) { 11. return MemcachedUtil.getInstance().delete(MemcachedConstant.MEMCACHED_DETAIL); 12. } 13.}
新建一个GoodsCacheImpl,该类的作用就是一开始所说的,取不到缓存,就调用DAO查询并放入缓存,如果缓存中有就直接从缓存中拿。
1.public class GoodsCacheImpl extends MemcachedSupport{ 2. @Resource(name = "goodsDaoImpl") 3. private GoodsDao goodsDao; 4. 5. public Goods selectGoodsById(long goodsId) { 6. Goods goods = null; 7. String goodsKey = MemcachedKeyUtil.getGoodsKey(goodsId); 8. goods = (Goods) getDetailData(goodsKey); 9. if (goods == null) { 10. goods = goodsDao.selectGoodsById(goodsId, false); 11. if (goods != null) { 12. setDetailData(goodsKey, goods); 13. } 14. } 15. return goods; 16. } 17.}