分布式系统之缓存的微观应用经验谈(一) 【基础细节篇】 (3)

      如果没有特殊业务需求(如上面提到的),插入必须设置过期时间。同时,尽量保证过期随机性。如果是进行批量缓存,则个人的做法是保证设置的过期时间上至少是分散的,目的是为了降低缓存雪崩等风险和影响(关于这些我会在以后的扩展篇里尝试阐述)。

      如,批量缓存的对象是一个结果集,条目有10万条,缓存时间基础为 60*60*2(sec),现在需要同时进行缓存。我的做法是默认生成一个随机数,如random(范围 0 - 1000),过期时间则设置为( 60*60*2 + random ) 。

 

    3.2 修改(Update)

 

      更新一条缓存的数据,注意是否需要重新调整过期时间。同时在很多场合,如多个缓存间同步时,建议直接删除该缓存,而不是更新缓存。修改操作很多时候是关联到DB间的同步操作的,相对考究的多一些,需要权衡分布式事务上的问题,后续文章里会写到。

 

    3.3 读取(Read)

 

      查找缓存时,如果存在多条,并确定数据量不大,务必使用严格匹配key的模式,而尽量不要使用通配符方式。虽然发送指令的key数据变长了,但却避免了不必要的缓存内的搜索性能损耗。
      例如单纯相信Redis里自身的存储优化,无限制的使用 keys pattern而不考虑时间复杂度,同时造成大量线程阻塞(这里与主从复制无关)。如果折中使用scan分页替代,也并非一种“无忧”的实现,一是需要在程序代码的封装里设置较低的容量,二是请务必在程序逻辑里对数据幻读等潜在问题做相关的管控处理。

      另外可以额外类比一种场景,操作DB中的大表,命中的热点数据分布靠后。

 

    3.4 删除 / 清空(Delete / Clear)

      删除缓存,一般有直接移除和设置时间过期(并不是任何时候都是滑动增加过期)两种方式,没什么细节上的说明。(倒是听过一种特殊业务场合,批量请求同类数据,并且即时性没有很高要求,设置过期时间并将时间稍作分散。)

      清空缓存,我在项目里目前并未应用,甚至也不提倡直接使用。但是假如在应用时,需要慎重考虑两个地方。一是清理时机,二是清理时效(若在Redis里,无论是flushdb或者flushall,都会形成一定阻塞)


    3.5 锁/信号(Locking)

      本身无关缓存,属于一些并发特性实现,有一定的适用场景。这在Redis中有一些基于原子的实现,但与本系列讨论无关。本人去年写过一篇与之相关的分享,详见:商城系统下单库存管控系列杂记(二)https://www.cnblogs.com/bsfz/p/7824428.html),但这里不赘述。

 

    3.6 发布-订阅(Publish-Subscribe)

      为什么提到这个跟生产消费(Produce-Consume)相关的动作呢?这个机制本身是不属于缓存自身的范畴的,而是更相关于消息队列(Message Queue)。之所以提到,是因为如今主流的缓存产品都自带这一特性,很多场景使用起来较方便,配置也简单,效率也够快。只是,往往会造成滥用。最关键是不必要的强耦合也降低了整体灵活性和性能,扩展性也实在有限。当然,这是我目前的看法。

      个人建议是这样的:如果没有特殊的场景应用,尽量不使用。至少本人是不会优先推荐使用缓存自身的发布订阅的,甚至在缓存集群系统中,需要考究的细节更多。而推荐的方式是,使用其他专业中间件解决,如基于MQ的产品替代方案。具体的候选有优秀的开源作品如RabbitMQ、Kafka等,包括有朋友提到的近两年国内阿里研发的RocketMQ等等,但是个人目前使用较多的依然是RabbitMQ。当然,这里不去过多赘述了,根据场景选择,合适的场景选用最合适的技术方案即可吧。

 

 

结语

 

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppwxd.html