#服务端缓存使用总结

服务端缓存使用总结

缓存类型

  1. localcache
  2. memcache
  3. redis

区别对比

缓存类型使用场景使用示例优点缺点
localcache少量数据,对应用程序只读或读多写少后台配置,分区信息无需网络开心,访问速度最快集群机器数据不同步
memcache海量数据,高并发读写评论内容,账号信息内存占用相对redis少,适合大键存储数据结构单一,不支持备份及持久化,只支持客户端操作
redis海量数据,高并发读写评论id索引,收藏视频信息数据结构丰富,支持备份及持久化,支持服务器操作相对memcache内存效率低

- localcache适用于存储少量数据及对应用程序只读或读多写少的场景,例如后台黑白名单、推广信息等,因为应用程序对这些数据几乎只是只读的,数据的修改主要发生在后台管理员更新配置时,且这些数据量很少,完全可以存储在本地内存当中。应用程序只需要定期从数据库load数据进行更新即可。对于分布式集群的部署,每台机器独自维护一份localcache,单后台数据有变动时,不同机器不可能同时load更新,因此存在集群机器数据不一致的情况。 但是这种情况通常是在可接受范围内的。
- memcache适用于存储大量高并发读写的数据,减轻数据库访问压力。如果没有memcache缓存,所有的访问直接打到db,高并发情况下将立马把数据库打挂,由于是直接存储在内存当中,因此访问速度将大大降低,同时数据缓存在memcache集群当中,可以确保应用集群访问数据的一致性,而不会存在localcache当中的问题。由于memcache不支持持久化,一旦集群机器出现宕机,将导致所有数据丢失,但是memcache本身就不是为了持久化数据而存在的,所以这也不是一个问题,需要注意的是,一旦memcache出现宕机等情况需要服务重启时,需要对缓存进行预热,不然大量miss同样也会打挂数据库。
- redis同样也是为了应对高并发读写而存在的。和memcache一样也是k-v类型,但是redis支持更丰富的数据结构,list,set,sortset,hashes。由于redis数据不是完全存在内存当中,当redis内存耗尽时,长期不使用的value将被转移到磁盘,因此redis可以存储比自身内存大的数据。同时redis支持持久化及master-slave模式数据备份。重启时可以再次加载磁盘的数据到内存当中。redis还具有容灾模式,只需要开启aof,即使服务器宕机也可以通过aof文件进行数据恢复。是否使用持久化及开启aof要根据具体业务场景进行选择。

缓存更新逻辑

cache then db or db then cache?

  • 对于cache和db的操作顺序,网上一直存在不同的观点,到底是先更新数据库再淘汰缓存,还是先淘汰缓存再更新数据库呢。我们的做法是先更新数据库在淘汰缓存。
  • 首先先分析一下两者可能导致的最差情况(不考虑db和cache其中一个操作失败的情况)。

    1. 先淘汰缓存在更新数据库。对于这种做法,如果淘汰了缓存,此时刚好来了一个请求,由于缓存已经被淘汰,新来的请求将从数据库读取信息重新load到缓存,如果先前的写操作还没完成,读操作读到的将是旧的数据,此时重新load到缓存的数据将是脏数据并且后续的请求将都读到脏数据。
    2. 先更新了db,后续cache操作失败,此时将会导致cache中的数据是脏数据。

在考虑了更新失败的情况下,分布式服务中并没有完美的解决方案,方法需要自己根据业务进行权衡,除非你愿意牺牲性能使用事务强一致性

update cache or delete cache ?

  • 写操作发生时,是更新缓存还是删除缓存呢。facebook在 Scaling Memcache at Facebook中使用的策略是更新数据库后删除memcache缓存。为什么是删除缓存而不是更新缓存呢?假设并发写更新完数据库后同时去更新缓存。此时两个写操作可能都从缓存中取到了数据A,此时将导致并发写导致脏数据。
  • 并发写操作导致脏数据的情况是因为必须先从memcache中取出数据修改完在写回。但是在redis中,由于redis支持服务器操作,INCE,HINCR等操作都可以直接在服务器当中操作完成,对于这类操作不存在并发写的问题,因此可以选择更新db后更新缓存。直接update将减少一次cache miss。
  • 在我们的大多数业务场景中,我们使用的是更新db后更新缓存。虽然更新缓存可能导致并发写脏数据。但是由于我们使用了kafka消息队列,并发写操作经过kafka后是可以转化为顺序写的。比如对于评论。同一个视频下面可能有多条并发写评论,视频收到评论后需要更新视频的评论数信息,如果直接更新缓存有很大的几率导致并发写脏数据,但是我们使用视频aid作为key,通过kafka消息队列异步处理,这样对于同一个视频的写操作都会发送到同一个kafka分区,同时对于consumer来说,同一个视频的写操作都会由同一个consumer消费,通过消息队列异步化处理,即可把并发写转化为顺序写,此时更新缓存就不会存在写竞争。
  • 到底delete还是update,还是得自己根据业务场景进行权衡。

redis使用

  • 业务中,redis的使用占绝大部分,且相对于memcache,由于丰富的数据结构,redis的使用也相对比较复杂。因此着重讲讲redis使用的一些注意事项
    redis数据类型选择

Use hashes when possible

  • redis的内存效率是比较低的,尤其是使用k-v的时候,在http://redis.io/topics/memory-optimization这这篇博客中明确说明。能使用hashes的地方尽量使用hashes,在使用k-v结构的时候,每一个value都是一个redisObject,当使用hashes的时候,redis使用的压缩列表和字典两种存储方式,使用压缩列表的时候,由于压缩列表内存的优化,将大大节省内存空间。 redisObject typedef struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:LRU_BITS; int refcount; void *ptr; } robj;
  • 例如视频下评论信息,可以使用 aid-rpid =》 replyinfo,即aid-rpid作为key,replyinfo作为value,也可以使用aid =》rpid-replyifo,即aid作为key,value是hashes,hashes的key是rpid,value是replyinfo。经过测试,在同样100W个key的情况下使用后一种方法可以比第一种节约大量的内存。同时,第二种方法还可以通过HGET aid 获取指定评论,也可以HGETALL aid获取所有评论。第一种则只能先 KEYS aid*获取rpid在去查询info。
sortset
  • sortset的实现采用了跳表和字典两种数据结构,跳表保证rank及范围查询时O(logN)的复杂度,字典则使得score查询时能达到O(1)的时间复杂度. 使用sortset时需要注意的一点是sortset的大小,由于sortset是插入排序,如果sortset里数据量太大,可能导致插入排序速度太慢。
set
  • set底层使用的其实是dict,其实就是一个value为null的hashes。需要注意的是,在某些场景下,可以使用二进制数组来替代set节省内存空间,通过GETBIT,SETBIT设置member判断member是否存在。
    redis底层图解
redis expire
  • 不同于memcache必须在set key的时候指定expire time,redis可以在set的时候指定expiretime,也可以在使用途中在设置expiretime。
  • 那么何时设置expire呢,是在set数据前之前还是set数据之后呢。在业务中曾经犯过这样一个错误。用户查询收藏夹信息的时候会先查询缓存,如果缓存miss则从db中加载。收藏夹信息在redis中是以sortset方式存储的。score表示收藏时间。当用户添加收藏的时候,就把新的数据ZADD进去,然后expire 增加过期时间。粗看是没什么问题,但是这个时候忽略了一个问题,就是zadd前缓存过期了,下次查询的时候由于zadd数据存在将不会miss导致读到的只是最新zadd的数据!
  • 因此在执行类似zadd sadd hset等操作的时候,一定要先进行expire,如果miss则不执行,等到查询miss的时候在从db中load 。
hashes 拆分
  • 是否有必要对hashes进行拆分?答案是肯定的。原因如下
    减小hashes粒度,增加查询效率。
    分散存储空间,redis当中,hashes的k-v只会保存在一个节点,如果所有的数据全部存在一个hashes,将导致节点数据不均衡。相反,把hashes进行拆分,每个hashes保存的只是部分数据,不同的hashes也会被分配到集群的不同节点。均衡集群的内存负载。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值