使用 Lua 脚本在 Redis 中可以实现一些复杂的原子操作,比如分布式锁、计数器以及排行榜等。
1. 分布式锁
分布式锁是一种常用的机制,用于控制分布式系统中多个客户端对共享资源的访问。下面是一个简单的基于 Lua 脚本的分布式锁实现:
-- 设置锁的超时时间(秒)
local lockTimeout = tonumber(ARGV[1])
-- 当前请求者的唯一标识
local requestId = ARGV[2]
-- 尝试获取锁
local lockAcquired = redis.call('setnx', KEYS[1], requestId)
if lockAcquired == 1 then
-- 如果获取锁成功,设置过期时间
redis.call('expire', KEYS[1], lockTimeout)
else
-- 检查锁是否已经过期
local currentTime = redis.call('time')
local ttl = redis.call('ttl', KEYS[1])
if ttl == -2 then
-- 锁不存在,尝试获取锁
lockAcquired = redis.call('setnx', KEYS[1], requestId)
if lockAcquired == 1 then
redis.call('expire', KEYS[1], lockTimeout)
end
elseif (currentTime[1] + currentTime[2]/1000000) > lockTimeout then
-- 锁已过期,尝试清除
redis.call('del', KEYS[1])
lockAcquired = redis.call('setnx', KEYS[1], requestId)
if lockAcquired == 1 then
redis.call('expire', KEYS[1], lockTimeout)
end
end
end
return lockAcquired
2. 原子计数器
原子计数器可以在并发情况下保证计数的准确性。以下是一个简单的计数器实现:
local countKey = KEYS[1]
local increment = tonumber(ARGV[1])
-- 增加计数器
redis.call('incrby', countKey, increment)
-- 返回当前计数值
return redis.call('get', countKey)
3. 排行榜
排行榜功能可以通过在 Redis 中使用有序集合(Sorted Set)来实现。下面是一个简单的 Lua 脚本,它允许添加用户分数,并返回排名前 N 的用户:
local leaderboardKey = KEYS[1]
local userId = ARGV[1]
local userScore = tonumber(ARGV[2])
local topN = tonumber(ARGV[3])
-- 更新用户的分数
redis.call('zadd', leaderboardKey, userScore, userId)
-- 获取排名前 N 的用户
local topUsers = redis.call('zrevrange', leaderboardKey, 0, topN-1, 'WITHSCORES')
return topUsers
在 Redis 客户端中执行 Lua 脚本
在 Python 的 Redis 客户端中执行上述脚本的一个例子如下:
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
# 分布式锁
lock_script = """
-- 脚本代码...
"""
lock_timeout = 300 # 5分钟
request_id = "unique_request_id"
lock_result = r.eval(lock_script, 1, 'my_lock', lock_timeout, request_id)
# 计数器
counter_script = """
-- 脚本代码...
"""
increment = 1
counter_result = r.eval(counter_script, 1, 'my_counter', increment)
# 排行榜
leaderboard_script = """
-- 脚本代码...
"""
top_n = 10
leaderboard_result = r.eval(leaderboard_script, 1, 'my_leaderboard', 'user1', 1234, top_n)