重置Redis中某个key的值的时候,另外的线程继续获取这个key的时候会发生什么

本文通过实验探讨了Redis在并发场景下对同一key进行读写操作的行为,验证了即使在重置key值的过程中,其他线程也能立即获取到最新值,而非null。测试通过定时任务模拟并发读写,揭示了Redis内部可能的加锁机制。

今天写 Redis 定时任务的时候,突然想到一个问题:当我定时任务在重置某个 key 的值的时候,而别的线程继续执行并且要获取这对 key-value 的时候,有没有可能获取回来的是 null?答案是不会的。

所以写了测试代码,大家一起来看看先。

// 每 20 秒执行 execut 方法,该方法主要是重置 key 的值。
@Scheduled(cron = "0/20 * * * * ?")
public void execut() throws Exception {
	UUID id = UUID.randomUUID();
	System.out.println("******create:" + id.toString() + "---time:" + new Date());
	
	Thread.sleep(5000);
	System.out.println("******sleep 5 second" + "---time:" + new Date());
	
	RedisValueOps.setValue("test", id.toString());
	System.out.println("******put:" + id.toString() + "---time:" + new Date());
}

// 每秒执行 execut2 方法,该方法主要是获取 key 的值。
@Scheduled(cron = "0/1 * * * * ?")
public void execut2() {
	String value = (String) RedisValueOps.getValue("test");
	System.out.println("======get:" + value + "---time:" + new Date());
}

下面是结果图

在这里插入图片描述
可以看到在 17分20秒 的时候生成了uuid,紧接着睡了 5 秒,再把 value put 到 redis。

然后紧跟着 execut2 方法就能获取到 value 值了。

我们再看 ======get: 的打印记录,在17分19秒执行了一次并获取到了 value 值,等再次获取到值的时候,是在17分25秒,中间有 5 秒钟的时间没有输出日志,说明是在堵塞中。


这个睡眠时间不够明显,我又让 execut 再睡 5 秒。

// 每 20 秒执行 execut 方法,该方法主要是重置 key 的值。
@Scheduled(cron = "0/20 * * * * ?")
public void execut() throws Exception {
	UUID id = UUID.randomUUID();
	System.out.println("******create:" + id.toString() + "---time:" + new Date());
	
	Thread.sleep(5000);
	System.out.println("******sleep 5 second" + "---time:" + new Date());
	
	RedisValueOps.setValue("test", id.toString());
	System.out.println("******put:" + id.toString() + "---time:" + new Date());
	
	Thread.sleep(5000);
	System.out.println("******sleep 5 second again" + "---time:" + new Date());
}

在这里插入图片描述
为什么上面说不明显呢?

因为第一张结果图把 value 值 put 进 redis 后,紧跟着就能 get 到 value 了,以为一 put ,另外的程序就能获取得到,所以我让 execut 再睡5秒。

当 value 被 put 到 redis 的时候,又继续睡了 5 秒,然而看 get 的输出日志,并没能获取到值,整个 execut 方法执行了 10 秒,而每秒执行的 execut2 却在外面堵塞了 10 秒

由此猜测 redis 在读写的时候对这个 key 加锁了。


以下应该说一下原理的,由于知识有限,以后回来补充。

<think>首先,用户的问题是:“我想查找在Redis中清理指定key的方法 请问Redis清理指定key的方法”。所以,我需要提供关于如何在Redis中清理指定key的方法。 我有三个引用: - 引用[1]:提到使用`flushall`和`flushdb`命令。`flushall`清空整个Redis服务器的数据,`flushdb`清空当前数据库中的所有key。但用户想清理指定key,而不是全部key。 - 引用[2]:讨论Redis过期key的清理策略和方法。 - 引用[3]:给出了清理大量key的方法,使用Lua脚本结合`keys`和`del`命令,例如针对匹配特定模式的key。 用户的需求是清理指定key的方法。所以,我应该聚焦在如何删除单个或多个指定key上。 在Redis中,清理指定key的基本命令是`DEL`命令。`DEL key [key ...]`可以删除一个或多个key。例如: - `DEL mykey` 删除名为"mykey"的key。 - `DEL key1 key2 key3` 删除多个key。 此外,如果key有特定的模式,可以使用`KEYS`命令来匹配key,然后用`DEL`删除。但`KEYS`命令在生产环境中不建议使用,因为它会阻塞Redis服务器。更好的方法是使用`SCAN`命令进行迭代匹配。 引用[3]提到了一个Lua脚本,用于批量删除匹配模式的key,例如`EVAL "local keys = redis.call('keys', ARGV[1]) for i=1,#keys,5000 do redis.call('del', unpack(keys, i, math.min(i+4999, #keys))) end return #keys" 0 'gameBillRecord*'`。这可以安全地删除大量key。 用户的问题是关于清理指定key,所以我应该涵盖: 1. 删除单个key使用`DEL`命令。 2. 删除多个指定的key使用`DEL` with multiple keys. 3. 删除匹配模式的key使用`KEYS` + `DEL`(但警告阻塞问题),或使用`SCAN` + `DEL`(推荐)。 引用[2]谈到了过期key,但用户是手动清理,不是设置过期时间。所以,我应该区分主动删除和被动过期。 现在,系统级指令: - 所有行内数学表达式用$...$格式。但在这个上下文中,主要是Redis命令,可能不需要数学表达式。 - 独立公式用$$...$$,但同样,可能不适用。 - LaTeX语法正确:使用`$...$` for commands if needed, but commands like `DEL` can be written in code blocks. - 使用中文回答。 - 生成相关问题:在回答后生成相关问题。 - 引用的段落末尾添加引用标识:[^1], [^2], [^3],但我需要基于引用内容使用。 用户说“参考站内引用”,但我应该基于这些引用内容来回答。 用户可见层指令:回答结构清晰,帮助用户逐步解决问题,保证真实可靠。 结构化回答: 1. 介绍:简述Redis中清理指定key的方法。 2. 方法步骤: - 方法1:删除单个key - 使用`DEL`命令。 - 方法2:删除多个key - 使用`DEL` with multiple keys. - 方法3:删除匹配模式的key - 使用`SCAN`或Lua脚本(如引用[3])。 警告:`KEYS`命令可能阻塞Redis,推荐使用`SCAN`。 3. 提及过期策略:如果需要自动清理,参考设置过期时间(如引用[2])。 4. 代码示例:提供Redis命令行例子。 5. 结尾:总结。 生成相关问题:在回答后添加。 添加引用标识:当引用内容时添加[^1], etc. 例如,提到`flushdb`时引用[^1]。 最终回答格式: - 用中文。 - 结构: - 标题或开头:Redis清理指定key的方法。 - 分点解释。 - 代码块显示命令。 - 提醒注意事项。 - 相关问题。 </think>### Redis清理指定key的方法 在Redis中清理指定key(即删除特定键对)是常见的操作,尤其在管理缓存或临时数据时。核心方法是使用`DEL`命令删除单个或多个key,但如果key符合特定模式(如通配符匹配),则需结合`SCAN`或Lua脚本避免性能问题。以下是逐步详解方法,确保操作安全可靠。请注意,所有命令均在Redis命令行接口(redis-cli)中执行。在执行删除操作前,建议先通过`KEYS`或`SCAN`预览匹配的key,以免误删数据。 #### 1. **删除单个key** - **方法**:使用`DEL key_name`命令删除指定名称的key。 - **步骤**: - 打开Redis命令行工具(redis-cli)。 - 输入命令`DEL your_key_name`,其中`your_key_name`是要删除的key名称。 - 命令返回删除的key数量(如`(integer) 1`表示成功删除)。 - **示例**: ```bash # 删除名为"user:1001"的key DEL user:1001 ``` - **适用场景**:已知key确切名称时使用此方法。简单高效,时间复杂度O(1)。 - **注意事项**:如果key不存在,Redis会返回0,不影响服务器运行。 #### 2. **删除多个指定key** - **方法**:使用`DEL key1 key2 key3 ...`命令一次性删除多个key。 - **步骤**: - 在redis-cli中,列出所有要删除的key名称,以空格分隔。 - 输入命令`DEL key1 key2 key3`。 - 命令返回实际删除的key数量。 - **示例**: ```bash # 删除多个key:user:1001, cache:item, temp:lock DEL user:1001 cache:item temp:lock ``` - **适用场景**:需要批量删除少量固定名称的key。 - **注意事项**: - 每个`DEL`命令最多支持删除数百万key,但大量删除可能阻塞Redis(单线程模型)。建议分批操作(如一次不超过1000个)。 - 如果key已设置过期时间,`DEL`会立即删除,而不等待过期策略[^2]。 #### 3. **删除匹配特定模式的key** 如果需要清理符合通配符模式(如`gameBillRecord*`)的key,直接使用`KEYS`命令可能阻塞Redis。推荐以下两种安全方法: - **方法A(推荐):使用`SCAN`迭代删除(避免阻塞)** - **原理**:`SCAN`命令分批次扫描key,减少服务器阻塞风险。结合`DEL`逐批删除。 - **步骤**: 1. 通过`SCAN`获取匹配key的游标列表。 2. 对每个批次执行`DEL`。 3. 重复直到所有key被处理。 - **示例脚本**(在redis-cli中逐行执行): ```bash # 使用SCAN遍历匹配"gameBillRecord*"的key,每次迭代100个 SCAN 0 MATCH gameBillRecord* COUNT 100 # 根据返回的keys列表执行删除,例如返回 keys = ["gameBillRecord:1", "gameBillRecord:2"] DEL gameBillRecord:1 gameBillRecord:2 # 重复SCAN直到游标为0 ``` - **适用场景**:大量key(如万级以上),生产环境首选。 - **注意事项**:`SCAN`是非阻塞的,但多次执行可能耗时较长;COUNT可调整(如100-1000)以平衡性能。 - **方法B:使用Lua脚本批量删除(高效但需谨慎)** - **原理**:通过`EVAL`执行Lua脚本,一次性匹配并分块删除key(如引用[3]所示)。脚本内部使用`KEYS`但限制批量大小,减少阻塞。 - **步骤**: 1. 在redis-cli中输入Lua脚本命令。 2. 脚本自动匹配模式并分批删除(如每批5000个key)。 - **示例**(删除所有以`gameBillRecord*`开头的key): ```bash EVAL "local keys = redis.call('keys', ARGV[1]) for i=1,#keys,5000 do redis.call('del', unpack(keys, i, math.min(i+4999, #keys))) end return #keys" 0 'gameBillRecord*' ``` - 解释:此脚本先匹配`gameBillRecord*`模式,然后每5000个key分批删除,避免大操作阻塞。返回删除的总key数量。 - **适用场景**:快速清理大量pattern匹配的key,适合脚本自动化。 - **注意事项**: - Lua脚本在Redis中是原子操作,但错误使用`KEYS`可能导致短暂性能下降。建议在低峰期执行。 - 脚本中的`5000`可调整为更小(如1000)以适配服务器负载。 #### 4. **附加说明:过期key的自动清理** - 如果用户希望key在一定时间后自动删除(而非手动清理),可设置过期时间:使用`EXPIRE key seconds`命令(如`EXPIRE user:1001 3600`让key在1小时后过期)。Redis会自动清理过期key,策略包括惰性删除(访问时检查)和定期删除(后台线程扫描)[^2]。 - 手动清理过期key:如果过期key未及时删除,可结合上述删除方法操作。 #### 操作前检查和安全提示 - **预览key**:执行删除前,先用`KEYS pattern`预览匹配结果(如`KEYS gameBillRecord*`),确认无误后再删除。但避免在生产环境频繁使用`KEYS`。 - **权限**:确保redis-cli有足够权限(如管理员账号)。 - **备份**:重要数据建议先执行`BGSAVE`备份。 - **性能影响**:删除操作通常快速,但大量删除时监控Redis内存和延迟(如使用`INFO memory`)。 - 引用[1]提到`flushdb`会清空当前数据库所有key,`flushall`清空所有数据库,但**不适用于指定key清理**,仅用于重置环境[^1]。 通过以上方法,您可以高效安全地清理Redis中的指定key。如有疑问,可提供具体key模式或场景,我进一步优化建议。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值