RedLock实现Redis值锁定失效求助:锁成功但数据仍可读取
问题核心:误解了RedLock的运作机制
RedLock不会自动锁定Redis中的数据,它只是提供一个分布式互斥锁的标识,用来协调多进程/服务间的互斥操作。你创建的refId:1234锁是Redis中独立的一个key,和你的events列表没有任何关联,所以lpop操作完全不会去检查这个锁的状态,自然能直接弹出数据。
你的代码问题拆解
- 你成功获取了锁,但后续的
listPop函数没有做任何锁校验逻辑,直接执行了弹出操作,完全忽略了锁的存在。 - 锁实例的
expiration字段是锁的到期时间戳(不是剩余TTL),你可以通过expiration - Date.now()计算锁的剩余有效时间。
正确的实现方式
要实现“锁定数据后需等待锁释放才能访问”的效果,必须在操作数据的逻辑前主动检查锁的状态,只有获取到锁(或锁已释放)才能执行数据操作。
修改后的示例代码
const redis = require('ioredis'); const RedLock = require('redlock'); // Connect to Redis const client = redis.createClient({ port: 6379, host: '127.0.0.1', }); const redlock = new RedLock([client]); async function listPush() { const value = 'refId:1234'; await client.rpush('events', value); const time = Date.parse(Date()); console.log('Successfully pushed : ', time); } async function listPop() { const targetValue = 'refId:1234'; try { // 尝试获取锁:设置重试5次,每次间隔1秒,模拟等待锁释放 const lock = await redlock.lock(targetValue, 1000, { retryCount: 5, retryDelay: 1000 }); // 成功获取锁,执行弹出操作 const reply = await client.lpop('events'); const time = Date.parse(Date()); console.log(`Data read from list: ${reply}`, ' ', time); // 操作完成后主动释放锁 await lock.unlock(); } catch (err) { // 多次重试仍无法获取锁,说明锁仍被持有 const time = Date.parse(Date()); console.log(`Failed to pop data, lock is still held`, ' ', time); } } async function lockValue(value, ttl) { const lock = await redlock.lock(value, ttl); const time = Date.parse(Date()); console.log("Successfully acquired lock on : ", time); return lock; } // 用async/await保证异步顺序 async function runTest() { await listPush(); await lockValue("refId:1234", 4000); setTimeout(listPop, 1000); } runTest();
关键逻辑说明
- 在
listPop中,我们先尝试获取锁,如果锁还被持有(前4秒内),会自动重试,直到锁释放或重试次数耗尽。 - 锁的作用是保护代码执行的互斥性,而不是直接锁定Redis数据。如果要保护整个
events列表的弹出操作,也可以把锁key设置为events-list-lock,让所有操作该列表的进程都先获取这个锁。
内容的提问来源于stack exchange,提问作者fbyself2011
相关产品推荐
相关产品推荐

