【讨论帖】控制分布式缓存“及时”过期的一种实现

简介:

大型网站中都会用到分布式缓存,现在经常使用的成熟可靠的分布式缓存产品有Memcached、Redis、Velocity等等。开发中我们在设计实现缓存层的时候,通常会按照业务模块,定义一些有意义的缓存键。比如,在一个非常典型的电子商务网站中,我们会缓存常用的字典表,如省市区县、商品分类、商品等等,一种常用的定义缓存键的方式如下:

    /// <summary>
    /// 缓存键管理
    /// </summary>
    public class CacheKeyManager
    {
        /// <summary>
        /// 区域
        /// </summary>
        public static readonly string Area = "{0}_area_{1}";

        /// <summary>
        /// 商品
        /// </summary>
        public static readonly string Product = "{0}_product_{1}";

        /// <summary>
        /// 商品分类
        /// </summary>
        public static readonly string ProductCatagory = "{0}_productcatagory_{1}";

    }

对于字符串中的第一项{0},我们通常会传入一个用于标识业务的有意义的字符串,比如命名空间、业务部门名称等;第二项{1}则通常对应于标识字典表数据的唯一性的值(如主键等)。这样拼接字符串定义缓存的key可以降低不同业务部门不同开发人员命名相同缓存键的可能,这一点在一个业务部门众多而部署的分布式缓存系统只有一套的环境下显得尤为重要。

实际开发使用缓存键的时候,最终拼接的字符串可能如下面这种形式:

        /// <summary>
        /// 区域
        /// </summary>
        public static readonly string Area = "namespace_area_areaid";

        /// <summary>
        /// 商品
        /// </summary>
        public static readonly string Product = "namespace_product_productid";

        /// <summary>
        /// 商品分类
        /// </summary>
        public static readonly string ProductCatagory = "namespace_productcatagory_productcatagoryid";

正如你所看到的,namespace是对应的命名空间,由不同业务部门的开发自行定义;xxxid则用于标识字典表数据的唯一性。

 

缓存的数据通常都是短时间内相对稳定不会发生变化的,但互联网业务是瞬息万变的,业务人员的要求通常也不那么通情达理,不变的数据往往也会随时需要改变并立刻在生产环境中体现出来。于是,我们在前台站点用上分布式缓存之后,还需要在后台业务系统中写不少代码,用于在改变数据的时候,及时更新缓存。

但在后台代码中,更新缓存往往都是费力不讨好的事情,一个考虑不到就会出现数据不一致的问题。比如现在需要更新一个商品分类的属性,和商品分类相关的很多缓存键必须都要考虑到,比如可能按照商品分类主键或者分类名称缓存了一个商品分类,或者按照某种查询规则缓存了一个商品分类列表或字典,或者商品分类属性的修改直接导致商品信息级联的修改,你又不得不考虑到缓存的商品信息……所以,在后台清理缓存的操作通常不是那么稳定可靠让人放心。

针对上面描述的这种复杂多变的应用场景,有人可能会说适当时候直接让运维介入,对于常用分布式缓存产品,上面这种情形只要一个命令行就把缓存及时清理了。可实际情况是,可能只有一个开发小组的缓存数据需要改变,却把整个公司的缓存数据都清理了,这样显然非常不合理。

求人不如求己,下面就是本文要介绍的一种相对灵活控制缓存版本并“及时”清理缓存的方法,分如下几步:

1、在自己的业务系统中新建一张数据表(比如叫CacheVersion),只有一个字符串Version字段,初始化一条记录,且只有一条记录;

2、将CacheVersion表数据写入分布式缓存系统,按照自己所在业务定义Version对应的缓存键,如xxx_cacheversion,对于你所在的业务部门,这个缓存键字符串可以认为是个常量;

3、重新定义业务需要的缓存键,如下所示:

  /// <summary>
        /// 区域
        /// </summary>
        public static readonly string Area = "{0}_area_{1}_{2}";

其中{2}对应的就是读出的CacheVersion对应的那条数据,CacheVersion那条记录的获取也是先从分布式缓存中取,如没有再读数据库,最后拼好的字符串是如下这个形式:

        /// <summary>
        /// 区域
        /// </summary>
        public static readonly string Area = "namespace_area_areaid_version";

4、后台业务数据发生改变,直接更新业务数据对应表记录,不需要写任何代码用于更新缓存;

5、在一个独立的管理模块,如业务需更新缓存数据,则更新CacheVersion表的唯一记录,并重置分布式缓存中的CacheVersion对应数据。

上面的5步思路其实很简单,就是由原来的维护多个缓存键改为集中维护一个CacheVersion,“及时”过期策略其实就是换一个缓存版本而已。到这里分布式缓存数据的版本控制就大功告成了。

当然,这种方式也有缺点,主要包括:

1、新增了一个数据表CacheVersion需要维护,Version字段需考虑数据唯一的生成策略;

2、多了一个CacheVersion操作模块;

3、前台站点缓存键字符串构造多了一个Version参数,读取Version也需要先从分布式缓存中取,如没有再读数据库,多了网络传输和IO;

4、每次更新版本,依赖这个版本的分布式缓存都会“被过期”,如在并发访问高峰,可能得不偿失。

 

最后欢迎大家讨论:你是如何控制和实现缓存有效期过期策略的?CacheVersion这张表是不是可以不需要?如果是你来控制缓存版本,缓存Version该如何生成?






本文转自JeffWong博客园博客,原文链接:http://www.cnblogs.com/jeffwongishandsome/archive/2013/06/09/3129431.html,如需转载请自行联系原作者

目录
相关文章
|
5月前
|
存储 缓存 安全
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(存穿透、缓存击穿和缓存雪崩)
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(存穿透、缓存击穿和缓存雪崩)
102 1
|
5月前
|
存储 缓存 算法
缓存大作战:强缓存 vs. 协商缓存,谁是王者?
缓存大作战:强缓存 vs. 协商缓存,谁是王者?
|
存储 设计模式 缓存
缓解缓存击穿的大杀器之---singleflight深入浅出
缓解缓存击穿的大杀器之---singleflight深入浅出
394 0
缓解缓存击穿的大杀器之---singleflight深入浅出
|
11月前
|
存储 缓存 监控
10 分钟搞懂缓存设计策略
10 分钟搞懂缓存设计策略
779 0
|
存储 缓存 NoSQL
【Redis从头学-15】三个通俗例子带你理解Redis缓存击穿、缓存穿透、缓存雪崩并从思路引导三者的解决方案
【Redis从头学-15】三个通俗例子带你理解Redis缓存击穿、缓存穿透、缓存雪崩并从思路引导三者的解决方案
87 0
|
缓存 数据库
【有奖征文】高并发场景下的缓存穿透、失效和雪崩问题及解决方案
高并发场景下的缓存穿透、失效和雪崩问题及解决方案
56 0
|
存储 缓存 NoSQL
Redis缓存穿透和雪崩相关概念(面试高频,工作常用)
Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面,但同时,它也带来了一些问题,其中,最重要的问题,就是数据的一致性问题。从严格意义上讲,这个无解。如果对数据的一致性要求很高,那么就不能使用缓存。
150 0
Redis缓存穿透和雪崩相关概念(面试高频,工作常用)
|
缓存
GoFrame gcache使用实践 | 缓存控制 淘汰策略
gcache模块默认提供的是一个高速的内存缓存,操作效率非常高效,CPU性能损耗在ns纳秒级别。使用简单易上手,非常适合单机应用使用。
243 0
GoFrame gcache使用实践 | 缓存控制 淘汰策略
|
设计模式 存储 缓存
再有人问你数据库缓存一致性的问题,直接把这篇文章发给他
在之前的一篇文章《为什么会出现数据库和缓存不一致的问题》中,我们介绍过缓存和数据库会出现数据不一致的几种情况。 我们提到过,在数据库和缓存的操作过程中,可能存在”先写数据库,后删缓存”、”先写数据库,后更新缓存”、”先删缓存库,后写数据库”以及”先更新缓存库,后写数据库”这四种。 那么,到底是应该删
|
数据采集 NoSQL 算法
上车上车,快速搞懂Redis 过期策略和内存淘汰策略
上车上车,快速搞懂Redis 过期策略和内存淘汰策略
125 0
上车上车,快速搞懂Redis 过期策略和内存淘汰策略