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

简介:

大型网站中都会用到分布式缓存,现在经常使用的成熟可靠的分布式缓存产品有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,如需转载请自行联系原作者

目录
相关文章
|
存储 缓存 负载均衡
Tair分布式缓存
Redis很好用,相比memcached多了很多数据结构,支持持久化。但是在很长一段时间里,原生是不支持分布式的。后来就出现了很多redis集群类产品,Tair是其中胜出的优秀作品之一。 所以Tair的特性都是一些集群的特性,比如:容错、解决单点故障、跨机房管理、多集群管理、支持副本等。总而言之,是redis的高可用版本。
1918 1
Tair分布式缓存
|
安全 PHP
【PHP开发专栏】PHP文件上传与下载实现
【4月更文挑战第30天】本文介绍了在PHP中实现文件上传和下载的详细步骤。文件上传通过`$_FILES`全局数组处理,包括设置HTML表单、PHP脚本处理上传及安全性考虑。文件下载则可使用`readfile()`、`fpassthru()`或`file_get_contents()`函数,同时关注安全问题,如防止目录遍历漏洞。文中还提供了一个简单的图片分享网站案例,演示了上传和下载功能的集成。
230 1
|
NoSQL 关系型数据库 MySQL
Redis 主从同步实现过程
Redis 主从同步实现过程
169 1
|
机器学习/深度学习
「机器学习」机器学习工程师的2020 路线图
「机器学习」机器学习工程师的2020 路线图
|
缓存 算法 NoSQL
阿里华为等大厂的本地缓存、分布式缓存解决方案详解(上)
阿里华为等大厂的本地缓存、分布式缓存解决方案详解
479 0
阿里华为等大厂的本地缓存、分布式缓存解决方案详解(上)
|
7天前
|
存储 关系型数据库 分布式数据库
PostgreSQL 18 发布,快来 PolarDB 尝鲜!
PostgreSQL 18 发布,PolarDB for PostgreSQL 全面兼容。新版本支持异步I/O、UUIDv7、虚拟生成列、逻辑复制增强及OAuth认证,显著提升性能与安全。PolarDB-PG 18 支持存算分离架构,融合海量弹性存储与极致计算性能,搭配丰富插件生态,为企业提供高效、稳定、灵活的云数据库解决方案,助力企业数字化转型如虎添翼!
|
18天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1327 8
|
5天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
本文讲解 Prompt 基本概念与 10 个优化技巧,结合学术分析 AI 应用的需求分析、设计方案,介绍 Spring AI 中 ChatClient 及 Advisors 的使用。
308 130
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话