Redis exists命令bug分析(案例详解)

目录
  • 1、复现条件版本:
  • 2、源码分析
  • 3、问题解决

本文基于社区版Redis 4.0.8

1、复现条件版本:

  • 社区版Redis 4.0.10以下版本
  • 使用场景:开启读写分离的主从架构或者集群架构(master只负责写流量,slave负责读流量)

案例:

# 写入一条带过期时间10s的key
10.90.73.147:12345> set luxiu1 1 ex 10
OK
10.90.73.147:12345> get luxiu1
"1"
10.90.73.147:12345> exists luxiu1
(integer) 1
......
#等待10s,待key过期
......

10.90.73.147:12345> ttl luxiu1
(integer) -2 #确定key已经过期了
10.90.73.147:12345> get luxiu1
(nil)  #没有问题,该key不存在了
10.90.73.147:12345> exists luxiu1
(integer) 1 #还能查到
10.90.73.147:12345> exists luxiu1
(integer) 1 #还能查到

2、源码分析

在分析该问题前,需要了解Redis在读写分离模式下读到过期数据的问题:

Redis过期key的删除策略采用惰性删除和定时删除:

惰性删除:主节点每次处理读取命令时,都会检查键是否超时,如果超时则执行del命令删除键对象,之后del命令也会异步发送给从节点。需要注意的是为了保证复制的一致性,从节点自身永远不会主动删除超时数据;

定时删除:Redis主节点在内部定时任务会循环采样一定数量的键,当发现采样的键过期时执行del命令,之后再同步给从节点;

如果此时数据大量过期,主节点采样速度跟不上过期速度且主节点没有读取过期键的操作,那么从节点将无法收到del命令。这时在从节点上可以读取到已经超时的数据。Redis在3.2版本解决了这个问题,在从节点上读取数据之前也会检查键的过期时间来决定是否返回数据。但是,4.0.10版本以下的exists命令实现方式有问题,导致该命令还是查询到过期数据问题。

下面是4.0.10以下版本exists命令实现源码:

问题就在于expireIfNeeded这个函数,它的功能就是惰性删除,判断如果key过期了就进行del,我们是读写分离架构,slave不进行del,如下代码:

直接返回1,并不进行到del操作。

所以exists查询到过期key一直存在。

3、问题解决

在社区版4.0.11以上版本已经修复了该bug:

lookupKeyRead函数调用lookupKeyReadWithFlags(db,key,LOOKUP_NONE)lookupKeyReadWithFlags函数逻辑如下:

最后,可以升级到4.0.12版本解决该问题。

到此这篇关于Redis exists命令bug分析的文章就介绍到这了,更多相关Redis exists命令内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Redis基本命令与使用场景

    Redis和Memcached对比 其中有一个比较重要的区别是关于其提供的数据结构区别 Memcached 在其数据结构中仅使用字符串和整数.因此,您保存的所有内容都可以是字符串或整数.它很复杂,因为对于整数,您可以做的唯一数据操作是添加或减去它们.如果需要保存数组或对象,则必须先将它们序列化然后保存.要阅读它们,您需要取消序列化. Redis 具有更强大的数据结构,它不仅可以处理字符串整数,还可以处理二进制安全字符串,二进制安全字符串列表,二进制安全字符串集和有序集. 关于Redis的数据结构

  • 解析redis hash应用场景和常用命令

    存储对象类数据 hmset user name huyongjian age 23 height 170 常用命令 将多个 field-value对设置到哈希表 key 中 hmset user name huyongjian age 23 height 170 将哈希表 key 中的字段 field 的值设为 value hset user weight 70 只有在字段 field 不存在时,设置哈希表字段的值 hsetnx user address china 获取存储在哈希表中指定字段的

  • Redis exists命令bug分析(案例详解)

    目录 1.复现条件版本: 2.源码分析 3.问题解决 本文基于社区版Redis 4.0.8 1.复现条件版本: 社区版Redis 4.0.10以下版本 使用场景:开启读写分离的主从架构或者集群架构(master只负责写流量,slave负责读流量) 案例: # 写入一条带过期时间10s的key 10.90.73.147:12345> set luxiu1 1 ex 10 OK 10.90.73.147:12345> get luxiu1 "1" 10.90.73.147:12

  • Java struts2请求源码分析案例详解

    Struts2是Struts社区和WebWork社区的共同成果,我们甚至可以说,Struts2是WebWork的升级版,他采用的正是WebWork的核心,所以,Struts2并不是一个不成熟的产品,相反,构建在WebWork基础之上的Struts2是一个运行稳定.性能优异.设计成熟的WEB框架. 我这里的struts2源码是从官网下载的一个最新的struts-2.3.15.1-src.zip,将其解压即可.里面的目录页文件非常的多,我们只需要定位到struts-2.3.15.1\src\core

  • Java ThreadLocal原理解析以及应用场景分析案例详解

    目录 ThreadLocal的定义 ThreadLocal的应用场景 ThreadLocal的demo TheadLocal的源码解析 ThreadLocal的set方法 ThreadLocal的get方法 ThreadLocalMap的结构 ThreadLocalMap的set方法 ThreadLocalMap的getEntry方法 ThreadLocal的内存泄露 如何避免内存泄露呢 应用实例 实际应用二 总结 ThreadLocal的定义 JDK对ThreadLocal的定义如下: The

  • 基于Redis结合SpringBoot的秒杀案例详解

    目录 1.构建SpringBoot项目 2.启动类 3.在Controller层里定义秒杀接口 4.在Service层里通过lua脚本实现秒杀效果 5.配置redis连接参数 6.演示秒杀效果 6.1 准备redis环境 6.2 启动项目 6.3 多线程形式发起秒杀请求 1.构建SpringBoot项目 搭建名为quickbuy的springboot项目,相关的依赖包如下所示: <?xml version="1.0" encoding="UTF-8"?>

  • Python threading Local()函数用法案例详解

    目录 前言 local() 函数是什么? local()函数如何用? 1. 不做标记,不做隔离 2.使用local()函数加以控制 3. 模拟实现local()的功能,创建一个箱子 4. 简化代码操作,进一步模拟实现local()函数 总结 前言 当多线程访问同一个公共资源时,如果涉及到修改该公共资源的操作就可能会出现由于数据不同步导致的线程安全问题.一般情况下我们可以通过给公共资源加互斥锁的方式来处理该问题. 当然,除非必须将多线程使用的资源设置为公共资源的情况.如果一个资源不需要在多个线程之

  • springBoot整合redis使用案例详解

    一.创建springboot项目(采用骨架方式) 创建完成: 我们分析下pom文件中内容: 所使用到的关键依赖: <!--springBoot集成redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.5.4<

  • Go逃逸分析示例详解

    目录 引言大纲 逃逸分析 内存管理 栈 堆 堆和栈的对比 加锁 性能 缓存策略 逃逸分析优势 逃逸分析原则 逃逸分析举例 1.参数是interface类型 2. 变量在函数外部有引用 3. 变量内存占用较大 4. 变量大小不确定时 思考题 总结 引言大纲 这个月我会整理分享一系列后端工程师求职面试相关的文章,知识脉络图如下: JAVA/GO/PHP 面试常问的知识点 DB:MySql PgSql Cache: Redis MemCache MongoDB 数据结构 算法 微服务&高并发 流媒体

  • Linux下tcpdump命令解析及使用详解

    简介 用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者的定义对网络上的数据包进行截获的包分析工具.tcpdump可以将网络中传送的数据包的"头"完全截获下来提供分析.它支持针对网络层.协议.主机.网络或端口的过滤,并提供and.or.not等逻辑语句来帮助你去掉无用的信息. 实用命令实例 默认启动 tcpdump 普通情况下,直接启动tcpdump将监视第一个网络接口上所有流过的数据包. 监视指定网络接口的数据包 tcpdum

  • spring security在分布式项目下的配置方法(案例详解)

    分布式项目和传统项目的区别就是,分布式项目有多个服务,每一个服务仅仅只实现一套系统中一个或几个功能,所有的服务组合在一起才能实现系统的完整功能.这会产生一个问题,多个服务之间session不能共享,你在其中一个服务中登录了,登录信息保存在这个服务的session中,别的服务不知道啊,所以你访问别的服务还得在重新登录一次,对用户十分不友好.为了解决这个问题,于是就产生了单点登录: **jwt单点登录:**就是用户在登录服务登录成功后,登录服务会产生向前端响应一个token(令牌),以后用户再访问系

  • C# CultureInfo类案例详解

    c#中的CultureInfo类 CultureInfo类位于System.Globalization命名空间内,这个类和命名空间许多人都不是很熟悉,实际我们在写程序写都经常间接性的接触这个类,当进行数字,日期时间,字符串匹配时,都会进行CultureInfo的操作,也就是说,也就是不同的CultureInfo下,这些操作的结果可能会不一样,由于我们大部分开发部署都是在同一种语言环境中,平日里可能没有感觉到它的用处,如果你的开发的项目是给国外用户用的,有可能在你机器上运行输出是一种效果,在客户机

随机推荐