http://my.oschina.net/u/142836/blog/171196
memcached是一款高性能的分布式缓存系统,凭借其简单方便的操作,稳定可靠的性能广泛应用于互联网应用中,这篇文章里,我主要去总结一些常见应用场景问题以及解决办法。

memcached 优化 批量删除 multiget

目录[-]

  • 1. 缓存的存储设计
  • 2. 缓存更新策略
  • 3. 批量删除(或更新)问题
  • 4. 故障转移和扩容的问题
  • 5. 与优化有关的一些小细节

memcached是一款高性能的分布式缓存系统,凭借其简单方便的操作,稳定可靠的性能广泛应用于互联网应用中,网上关于memcached介绍的资料也很多,最经典的资料就是《memcached全面剖析》这个文档,原文链接:http://gihyo.jp/dev/feature/01/memcached/0001,中文翻译网上很多:http://tech.idv2.com/2008/08/17/memcached-pdf/,这个文档写的很好,也很容易读懂。接下来我主要去总结一些常见应用场景问题以及解决办法。

1. 缓存的存储设计

按应用场景的不同一般有以下两种设计方案:

  • 方案一:把数据库的SQL查询结果缓存到memcached,读取数据的时候优先从memcached读取,挡住数据库查询请求。

    优点:我们可以在开发框架上做一些统一的缓存处理,对业务开发透明,减少对业务逻辑代码的侵入。这种情况下缓存的预热也比较方便,我们可以借助数据库的存储日志(eg:mysql的binlog)来预热缓存。

    缺点:这种方式有个隐患就是如果前端的一次请求需要涉及到多个SQL查询结果,这时候memcached需要取多次数据,在高并发的情况下网络io的开销和memcached的并发压力有可能成为瓶颈。

  • 方案二:把业务处理的最终结果进行缓存,客户端来请求时可以直接返回这个缓存的结果。

    优点:可以快速返回数据,只取一次memcache就可以了,减少了网络io消耗和运算处理消耗。

    缺点:需要在业务逻辑里显式处理缓存,同时存储的数据结构较复杂,当我们有数据更新时,重新生成缓存会比较麻烦。这种情况比较适用于计算密集型的高并发应用场景。

2. 缓存更新策略

两种常见的方案,也各有优缺点和应用场景:

  • 方案一:懒惰式加载,客户端先查询memcached,如果命中,返回结果,如果没命中(没有数据或已经过期),则从数据库中加载最新数据,并写回到memcached中,最后返回结果。

    优点:使用方便,简单;

    缺点:高并发的情况下如果缓存失效,将对后端数据库造成瞬时压力。当然,我们可以在应用里加锁来控制并发,但是这样也会对应用程序造成影响。

  • 方案二:主动更新策略,缓存里的数据永不失效,当有数据更新的时候,由单独程序来更新这个缓存。

    优点:缓存数据总是可靠的(没有LRU的情况下),前端可以快速响应,后端的数据库也没有并发查询的压力。

    缺点:程序结构变复杂了,需要维护单独的程序来完成更新,两套程序要共享一套缓存配置。(ps:其实有一些业务场景本来就是这样的,比如门户网站的内容发布系统和网站系统就需要共享一份数据,一个负责写数据,一个负责展示数据)

3. 批量删除(或更新)问题

在memcached中,我们的绝大部分操作都是基于单个key的add/set/del/get操作,用起来很方便,但是呢,有些时候我们会碰到批量删除(或更新)的问题。比如某手机App应用因为出现了敏感内容,网络监管部门要求删除所有跟这条内容有关的信息,这个时候因为手机机型、版本不同,这个内容在缓存里的key有多种多样。我们不能方便地拿到所有的key,或者可以枚举出所有的key,但是memcached并不支持批量删除操作,这就麻烦了,怎么解决这种问题呢?下面我以某门户网站删除敏感新闻来举例,我们假设每条新闻都有很多维度的内容,新闻以newsid标识,每个维度以prop来老相识,再加一个通用前缀,这样,完整的key应该是这样的格式:key{newsid}{prop}

  • 方案一:

    用一个单独的集合(Set)把一类key维护起来。当需要批量删除(或更新)时只需要取出这个集合里的所有key进行相应的操作即可。这样做起来比较简单:

    首先,我们往memcached里面添加一个新的k,v时,就往那个set里加一个key,比如一条新闻在memcached里面有下面这些 对:

    key_{newsid}_{prop1}:value1
    key_{newsid}_{prop2}:value2
    key_{newsid}_{prop3}:value3
    ……
    key_{newsid}_{propn}:valuen
    

    在我们的集合里面,就要存放所有跟这条新闻有关key的集合:

    keyset_{newsid}:key_{newsid}_{prop1},key_{newsid}_{prop2},……,key_{newsid}_{propn})
    

    这样,当我们要清除这条新闻的缓存时,就可以取出这个key的集合,然后遍历这些key,到memcached里面逐个删除,这样就达到了批量删除的目的。

    在这里,我们提到的这个key set具体怎么存放和维护呢?

    一种方式是,在memcached里面把所有key用逗号拼接成一个大字符串构成keyset的value或者借助开发语言提供的集合结构(set)来组织数据,系列化到memcached中。

    另一种方式是,借助更方便的存储结构来保存这个key,比如redis的set结构,当然了,这种方式并不推荐,会给现有系统带来复杂度。

  • 方案二:

    通过动态更新key的方式来实现,这种方式是给每一个key都在原来key的基础上加一个版本号来组成,当需要批量删除或更新时只需升级版本号即可,具体怎么做呢?

    首先,我们在memcached给这条新闻维护一个版本号,这样:

    key_version_{newsid}:v1.0 (版本号可以用时间戳或其它任何有意义的内容代替)
    // 伪代码
    $memcacheClient->setVersion(key_version{newsid}, "v1.0");
    

    然后,当我们要保存或读取这条新闻相关的数据时,先取出这个版本号来生成新的key,如下:

    //伪代码
    $version = getVersion(key_version_{newsid});
    $key = "key_{newsid}_{prop}_" + $version;
    

    再用这个新的key来保存(或读取)真正的内容,这样在memcached里面保存的跟这条新闻有关的 对就是下面这样了:

    key_{newsid}_{prop1}_v1.0:value1
    key_{newsid}_{prop2}_v1.0:value2
    key_{newsid}_{prop3}_v1.0:value3
    ……
    key_{newsid}_{propn}_v1.0:valuen
    

    当我们需要删除(或更新)这条新闻相关的所有key时,只需要升级版本号即可,如下:

    //伪代码
    $memcacheClient->updateVersion(key_version_{newsid},"v2.0");
    

    这样的话,当我们下次访问这条新闻的缓存时,由于版本号升级,新的key下所有内容都为空,需要从数据库加载新的内容,或者是返回空的结果。而旧的key在过期时间到了以后也就可以回收利用了。这样就达到了我们批量删除或更新的目的。

上面提到的两种方案其实都比较简单和实用,当然也各有缺点,方案一的key set维护需要额外的消耗,方案二的老版本数据不能及时清理,造成缓存垃圾。我们在实际应用场景中可以灵活选择,两者在效果上其实不会有太大区别。

4. 故障转移和扩容的问题

memcached它不是一个分布式的系统,严格来说是个单点系统,所谓的分布式只是借助客户端来实现的。所以它没有那些开源分布式系统那样的高可用性,我们这里来讨论一下memcached怎么去避免单点故障,以及在线扩容的问题。(ps:memcached做得真省事儿,最大的特点就是简单,好多辅助功能都要依赖于客户端自己去实现)。

  • 一致性哈希:好吧,这应该算是最简单常见的一种机制了,依赖于一致性哈希的特点,节点故障或扩容加节点时对集群影响较小,基本上可以满足大部分应用场景了。但是要注意:节点调整的最初一段时间内,会有一部分缓存丢失,穿透到后端的数据库上,在高并发的应用里,要做好并发控制,以免对数据库造成压力。
  • 双写机制:客户端维护两个集群,每次更新数据的时候同时更新两份,读取的时候随机(或固定)读取一份,这种情况下集群的可用性和稳定性是很高的,可以无痛变更,节点故障或扩容对缓存和后端数据库都没有影响。当然,这样做也是有代价的:一是两份数据的一致性问题,不过对缓存来说,这种极少数的不一致情况是可以容忍的;另一个是内存浪费的问题,通过冗余一份数据来减少故障率,代价还是挺大的,并不适合大型的互联网应用。
  • Twemproxy:这是twitter开源的一个代理程序,可以给redis和memcached作代理,有了这个东西可以减少好多维护成本(主要是客户端的)。对于故障转移和在线扩容也很方便。具体可以参考:https://github.com/twitter/twemproxy

5. 与优化有关的一些小细节

  • 批量读取(multiget):有些较复杂的业务请求可能一次请求要进行多次memcached操作,其中的网络往返的消耗以及对memcached节点施加的并发压力还是比较可观的,这种情况下我们可以考虑进行批量读取来减少网络io往返的次数,一次把数据返回,同时还能减轻客户端的业务处理逻辑。

    这里有一个著名的multiget无底洞问题,在facebook的应用中发现了这个问题,请参考:http://highscalability.com/blog/2009/10/26/facebooks-memcached-multiget-hole-more-machines-more-capacit.html,这篇文章中已经提出了解决方案。但其实我们也可以考虑把multiget的key分布到一个节点上,来避免这个问题,这样就需要自己定制memcache的客户端,按一定的规则(比如:相同的前缀)把一类key分布到同一个节点上,来避免这个问题,同时这样也可以提高性能,不用在多个节点之间等待数据。

  • 改变系列化方式:不使用java的对象序列化方式(哈哈,我这里只针对java来说),自己实现序列化,把要缓存的对象序列化成字节数组或者string进行保存。这样在内存节省和网络传输上都有不错的效果。

  • 数据预热:一些场景下我们需要为应用预热缓存数据(比如节点扩容需要重新分布数据),在前面说缓存设计的时候提出过,可以借助数据库的更新日志来预热缓存,这主要依赖于缓存的内容是跟数据库存储一致。其它情况下我们可以考虑在现有缓存前面挡一层空内容的集群节点,逐步把旧缓存读取到新缓存集群中来达到数据预热的目的,这样做就是有一点麻烦,需要应用端配合。

  • 增长因子:合理调整memcached的增长因子,可以有效控制内存的浪费。

  • 空结果的处理:有些场景下我们数据库里没有查到数据,缓存里也是空的,这时候需要在缓存里存放一个短时效的空结果来挡住前端的频繁请求,以免对数据库造成压力。

memcached的使用其实非常简单,性能也很出色,上面这些就是我们在实际业务开发中会碰到的一些场景,根据实际场景去选择合适的解决方案,可以给以后的开发维护带来不少便利。

Memcached应用总结相关推荐

  1. 常用的高性能 KV 存储 Redis、Memcached、etcd、Zookeeper 区别

    1. 什么是 KV 存储 KV 是 Key-Value 的缩写,KV 存储也叫键值对存储.简单来说,它是利用 Key 做索引来实现数据的存储.修改.查询和删除功能. 常用的高性能 KV 存储主要有 R ...

  2. Magent搭建Memcached集群

    原文地址:http://ultrasql.blog.51cto.com/9591438/1636374 Memcached集群介绍 由于Memcached服务器与服务器之间没有任何通讯,并且不进行任何 ...

  3. memcached和redis的区别和应用场景

    一:特性和对比 1.性能上: 性能上都很出色,具体到细节,由于Redis只使用单核,而Memcached可以使用多核,所以平均每一个核上Redis在存储小数据时比 Memcached性能更高.而在10 ...

  4. 为何Redis要比Memcached好用(转)

    转载链接:http://blog.csdn.net/renfufei/article/details/40598889 GitHub版本地址: https://github.com/cncounter ...

  5. Memcached安装使用和源码调试

    memcached官网:http://memcached.org/ 一.安装 下载 # wget http://www.memcached.org/files/memcached-1.4.25.tar ...

  6. memcache和memcached安装

    首先要明确  memcache不是memcached 第一步安装libevent #wget  https://github.com/downloads/libevent/libevent/libev ...

  7. 分享memcache和memcached安装过程

    Memcache是什么? Memcache是一个自由和开放源代码.高性能.分配的内存对象缓存系统.用于加速动态web应用程序,减轻数据库负载. 它可以应对任意多个连接,使用非阻塞的网络IO.由于它的工 ...

  8. Memcached安装以及PHP的调用

    一:安装libevent 由于memcached安装时,需要使用libevent类库,所以先安装libevent 1.下载 #wget   http://www.monkey.org/~provos/ ...

  9. Linux(centOS)手动安装Apache+MySQL+PHP+Memcached+Nginx原创无错版

    最后更新时间:2012.3.21 =================== 第一步:Apache安装(已更新到2.2.22) =================== 下载 去 http://www.ap ...

  10. Memcached在大型网站中应用

    memcached是一个高性能的分布式的内存对象缓存系统,通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像.视频.文件以及数据库检索的结果等.最初为了加速 LiveJ ...

最新文章

  1. 使用docker部署mysql 并持久化到宿主机本地
  2. 47. Permutations II
  3. HDLBits 系列(16)Something about Counter
  4. 球30家厂商角逐自动驾驶汽车 四年后1000万辆上路
  5. php编译减少大小,C++_减小VC6编译生成的exe文件的大小的方法,1、减小VC6编译生成的exe文件的 - phpStudy...
  6. POJ2762(判断无向图的弱连通)
  7. 元宇宙行业深度研究报告:为什么元宇宙是下一代互联网?
  8. linux 二进制差分工具,打造Android万能的软件更新库
  9. css 背景图怎么设置自动填充满_CSS属性设置 -- 背景样式
  10. 1016.外网资源下载神器
  11. python的自省基础
  12. Android中缓存记忆
  13. 骑在银龙的背上歌词(带罗马音)
  14. 20145322何志威 《Java程序设计》第8周学习总结
  15. 傅里叶级数与傅里叶变换公式推导
  16. 360浏览器(极速)如何导出保存的账号密码
  17. Visual Studio 增加每行最多字符数限制参考线
  18. 【NOIP2009PJ】细胞分裂
  19. 各类文件应该存在哪种数据库中
  20. fpga nvme 寄存器

热门文章

  1. 领土局本副局长蒙贿获刑5年
  2. 苹果付费app共享公众号_【苹果iOS付费游戏应用帐号分享】新增一款25元iOS游戏应用共享帐号...
  3. 手机电视机屏幕分辨率
  4. 分享 java 基础 + 进阶精简资料(视频 + 源码 + 就业项目 + 面试报装)
  5. win7系统如何搜索计算机,win7怎样搜索文件?win7系统准确搜索文件的方法
  6. 图解刘谦如何手穿玻璃桌(详细图文说明)
  7. 潮汐计算php源码,潮汐选股公式
  8. 【有利可图网】PS实战系列:简单易学的PS把照片转素描效果
  9. 传统企业:转型互联网,机遇大,挑战也大
  10. 海南信用社计算机试题,2015年海南农村信用社考试试题——计算机基础知识一...