【项目实战典型案例】01.redis只管存不管删除让失效时间删除的问题

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 MongoDB,通用型 2核4GB
简介: 【项目实战典型案例】01.redis只管存不管删除让失效时间删除的问题

一:背景介绍

此案例是通过Reids查询该课程下所有的班级信息,如果从reids中没有查询到数据,那么就会从数据库中查询并把查询到的数据存入到redis中。

存在的问题:没有再更新课程下的班级数据时删除缓存,这样会导致如果更新了该课程下的班级数据,那么缓存中的数据和数据中的数据出现不一致的情况。

二:redis

1)redis数据类型

redis支持五种数据类型:string(字符串)、hash(哈希)、list(列表)、set(集合)

①String(字符串)

string是redis最基本的类型,可以理解成与Memcached一模一样的类型,一个key对应一个value。redis的string可以包含任何数据,比如jpg图片或者序列化的对象。

redis 127.0.0.1:6379> SET runoob "Hello"
OK
redis 127.0.0.1:6379> GET runoob
"Hello"

以上实例我们使用了Redis的SET和GET命令,键为runoob,对应的值为"Hello"

②Hash(哈希)

Redis hash是一个键值(key=>value)对集合,是一个string类型的field和value的映射表,用于存储对象

DEL runoob用于删除前面测试用过的key,不然会报错:(error) WRONGTYPE Operation against a key holding the wrong kind of value

redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> HMSET runoob field1 "Hello" field2 "World"
"OK"
redis 127.0.0.1:6379> HGET runoob field1
"Hello"
redis 127.0.0.1:6379> HGET runoob field2
"World"

以上实例我们使用了Redis HMSET,HGET命令,HMSET设置了两个field=>value对,HGET获取对应field对应的value。

③List(列表)

Redis列表是简单的字符串列表,按照插入顺序排序

redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> lpush runoob redis
(integer) 1
redis 127.0.0.1:6379> lpush runoob mongodb
(integer) 2
redis 127.0.0.1:6379> lpush runoob rabbitmq
(integer) 3
redis 127.0.0.1:6379> lrange runoob 0 10
1) "rabbitmq"
2) "mongodb"
3) "redis"
redis 127.0.0.1:6379>

④Set(集合)

Redis的Set是string类型的无序集合,集合是通过哈希表实现的

sadd命令

添加一个string元素到key对应的set集合中,成功返回1,如果元素已经在集合中返回0.

sadd key member
redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> sadd runoob redis
(integer) 1
redis 127.0.0.1:6379> sadd runoob mongodb
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabbitmq
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabbitmq
(integer) 0
redis 127.0.0.1:6379> smembers runoob
1) "redis"
2) "rabbitmq"
3) "mongodb"

以上实例中rebbitmq添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略

2)缓存同步

①设置有效期

给缓存设置有效期,到期后自动删除。再次查询时更新。

优点: 简单,方便

缺点: 时效性差,缓存过期之前可能不一致

场景: 更新频率较低,时效性要求低的业务

②同步双写

在修改数据库的同时,直接修改缓存

优点: 时效性强,缓存与数据库强一致

缺点: 有代码侵入,耦合度高

场景: 对一致性,时效性要求较高的缓存数据

③异步通知

修改数据库时发送事件通知,相关服务监听到通知后修改缓存数据

优点: 低耦合,可以同时通知多个缓存服务

缺点: 时效性一般,可能存在中间不一致状态

场景: 时效性一般,有多个服务需要同步

3)key的过期时间

Redis过期时间设置命令有两种:

PEXPIRE:以毫秒为单位设置key的生存时间

EXPIPE:以秒为单位设置key的生存时间

具体设置方式

  • EXPIRE key seconds //将key的生存时间设置为ttl秒
  • PEXPIRE key milliseconds //将key的生成时间设置为ttl毫秒
  • EXPIREAT key timestamp //将key的过期时间设置为timestamp所代表的的秒数的时间戳
  • PEXPIREAT key milliseconds-timestamp //将key的过期时间设置为timestamp所代表的的毫秒数的时间戳

三:问题分析过程

Redis如果只是将数据存入缓存以提高效率并设置缓存时间,带来的问题是如果数据发生变化之后就得等key失效之后查询数据才会得到正确的数据。

public List<TbContent> getContentListByCid(long cid) {
    //查询缓存
    try {
      //如果缓存中有直接响应结果
      String json = jedisClient.hget(CONTENT_LIST, cid + "");
      if (StringUtils.isNotBlank(json)) {
        //json转列表,TbContent.class是list中每个元素的类型
        List<TbContent> list = JsonUtils.jsonToList(json, TbContent.class);
        return list;
      }
    } catch (Exception e) { 
      e.printStackTrace();
    }
    //如果缓存没有就查询数据库
    TbContentExample example = new TbContentExample();
    Criteria criteria = example.createCriteria();
    //设置查询条件
    criteria.andCategoryIdEqualTo(cid);
    //执行查询
    List<TbContent> list = contentMapper.selectByExampleWithBLOBs(example);
    //把结果添加到缓存
    try {
      jedisClient.hset(CONTENT_LIST, cid + "", JsonUtils.objectToJson(list));
      jedisClient.expire(CONTENT_LIST, 3600);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return list;
  } 

应该采取的做法是在对数据进行添加、修改和删除操作时删除数据,查询数据时发现缓存中已删除就从数据库中查询得到最新的数据,将最新的数据重新插入到缓存保证缓存中数据的准确性。

public E3Result addContent(TbContent content) {
      content.setCreated(new Date());
      content.setUpdated(new Date());     
      contentMapper.insert(content);
      //缓存同步
      jedisClient.hdel(CONTENT_LIST, content.getCategoryId().toString());
      return E3Result.ok();
  }

四:总结

1、如果开发人员要开发已有的代码,需要和写此代码的开发人员进行沟通,避免出现问题。

2、对于redis如何在项目中应用,要及时查阅,做总结。建议至少看三遍redis官网并画思维导图。

五:升华

不怕不知道,就怕不知道。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2天前
|
缓存 NoSQL Java
Redis7的10大应用场景和案例解析
你在项目中使用 Redis 实现了什么应用场景,欢迎一起跟 V 哥讨论。同时也做个小调查,朋多少兄弟是需要了解 Redis 核心源码的,人多的话,下一篇 V 哥写 Redis7的源码分析,人少的话就算了,感谢。
|
2天前
|
存储 监控 NoSQL
【Redis技术专区】「优化案例」谈谈使用Redis慢查询日志以及Redis慢查询分析指南
【Redis技术专区】「优化案例」谈谈使用Redis慢查询日志以及Redis慢查询分析指南
42 0
|
2天前
|
缓存 NoSQL 前端开发
【Redis技术专区】「实战案例」谈谈使用Redis缓存时高效的批量删除的几种方案
【Redis技术专区】「实战案例」谈谈使用Redis缓存时高效的批量删除的几种方案
48 0
|
2天前
|
NoSQL Java 数据库
优惠券秒杀案例 - CAS、Redis+Lua脚本解决高并发并行
优惠券秒杀案例 - CAS、Redis+Lua脚本解决高并发并行
109 0
|
2天前
|
NoSQL Redis
Redis企业项目实战--登录校验拦截器
Redis企业项目实战--登录校验拦截器
|
2天前
|
消息中间件 存储 NoSQL
【Redis项目实战】使用Springcloud整合Redis分布式锁+RabbitMQ技术实现高并发预约管理处理系统
【Redis项目实战】使用Springcloud整合Redis分布式锁+RabbitMQ技术实现高并发预约管理处理系统
|
2天前
|
存储 NoSQL 关系型数据库
Redis系列-8.Redis案例实战之Bitmap、Hyperloglog、GEO(下)
Redis系列-8.Redis案例实战之Bitmap、Hyperloglog、GEO
32 0
|
2天前
|
存储 NoSQL 算法
Redis系列-8.Redis案例实战之Bitmap、Hyperloglog、GEO(上)
Redis系列-8.Redis案例实战之Bitmap、Hyperloglog、GEO
53 0
|
10月前
|
存储 缓存 NoSQL
案例01-修改数据redis没有同步更新
修改数据redis没有同步更新
|
6月前
|
消息中间件 缓存 NoSQL
阿里云国际站代理商:Redis实现分布式配置管理的方法与应用案例
@luotuoemo飞机@TG阿里云国际站代理商:Redis实现分布式配置管理的方法与应用案例,为了实现高可用和负载均衡,可以将Redis部署成哨兵集群或集群模式。哨兵负责监控主从节点的状态,发现故障时自动进行故障转移。集群模式可以提高系统的可扩展性,通过添加更多的从节点来分摊负载压力。