Redis中Lua脚本的常见场景
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">1. 分布式锁</a></li><ul class="second_class_ul"><li><a href="#_lab2_0_0">示例:分布式锁的获取与释放</a></li></ul><li><a href="#_label1">2. 计数器</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_1">示例:原子性的自增操作</a></li></ul><li><a href="#_label2">3. 事务性操作</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_2">示例:转账操作</a></li></ul><li><a href="#_label3">4. 排行榜</a></li><ul class="second_class_ul"><li><a href="#_lab2_3_3">示例:获取排行榜前 N 名</a></li></ul><li><a href="#_label4">5. 队列操作</a></li><ul class="second_class_ul"><li><a href="#_lab2_4_4">示例:推送和弹出任务</a></li></ul><li><a href="#_label5">6. 限流器</a></li><ul class="second_class_ul"><li><a href="#_lab2_5_5">示例:限流脚本</a></li></ul><li><a href="#_label6">总结</a></li><ul class="second_class_ul"></ul></ul></div><p>Redis 的 Lua 脚本可以极大提升操作的原子性和效率,特别适用于需要多个 Redis 命令组合执行的场景。以下是一些常见的使用场景,并结合代码进行详细说明。</p><p class="maodian"><a name="_label0"></a></p><h2>1. 分布式锁</h2>
<p>Redis 的 Lua 脚本常用于实现分布式锁,以确保多个客户端在并发访问时的互斥性。</p>
<p class="maodian"><a name="_lab2_0_0"></a></p><h3>示例:分布式锁的获取与释放</h3>
<div class="jb51code"><pre class="brush:sql;">-- 获取锁
local lock_key = KEYS
local lock_value = ARGV
local ttl = tonumber(ARGV)
if redis.call("SETNX", lock_key, lock_value) == 1 then
redis.call("PEXPIRE", lock_key, ttl)
return 1
else
return 0
end
</pre></div>
<div class="jb51code"><pre class="brush:sql;">redis-cli EVAL "local lock_key = KEYS; local lock_value = ARGV; local ttl = tonumber(ARGV); if redis.call('SETNX', lock_key, lock_value) == 1 then redis.call('PEXPIRE', lock_key, ttl); return 1; else return 0; end" 1 mylock lock_value 30000
</pre></div>
<div class="jb51code"><pre class="brush:sql;">-- 释放锁
local lock_key = KEYS
local lock_value = ARGV
if redis.call("GET", lock_key) == lock_value then
redis.call("DEL", lock_key)
return 1
else
return 0
end
</pre></div>
<div class="jb51code"><pre class="brush:sql;">redis-cli EVAL "local lock_key = KEYS; local lock_value = ARGV; if redis.call('GET', lock_key) == lock_value then redis.call('DEL', lock_key); return 1; else return 0; end" 1 mylock lock_value
</pre></div>
<p class="maodian"><a name="_label1"></a></p><h2>2. 计数器</h2>
<p>实现自增、自减等计数器功能。</p>
<p class="maodian"><a name="_lab2_1_1"></a></p><h3>示例:原子性的自增操作</h3>
<div class="jb51code"><pre class="brush:sql;">local key = KEYS
local increment = tonumber(ARGV)
local current = redis.call("GET", key)
if current == false then
current = 0
else
current = tonumber(current)
end
local new_value = current + increment
redis.call("SET", key, new_value)
return new_value
</pre></div>
<div class="jb51code"><pre class="brush:sql;">redis-cli EVAL "local key = KEYS; local increment = tonumber(ARGV); local current = redis.call('GET', key); if current == false then current = 0; else current = tonumber(current); end; local new_value = current + increment; redis.call('SET', key, new_value); return new_value;" 1 mycounter 1
</pre></div>
<p class="maodian"><a name="_label2"></a></p><h2>3. 事务性操作</h2>
<p>Lua 脚本可以确保多条命令的原子性,避免使用事务的复杂性。</p>
<p class="maodian"><a name="_lab2_2_2"></a></p><h3>示例:转账操作</h3>
<div class="jb51code"><pre class="brush:sql;">local from_account = KEYS
local to_account = KEYS
local amount = tonumber(ARGV)
local from_balance = tonumber(redis.call("GET", from_account))
local to_balance = tonumber(redis.call("GET", to_account))
if from_balance >= amount then
redis.call("DECRBY", from_account, amount)
redis.call("INCRBY", to_account, amount)
return 1
else
return 0
end
</pre></div>
<div class="jb51code"><pre class="brush:sql;">redis-cli EVAL "local from_account = KEYS; local to_account = KEYS; local amount = tonumber(ARGV); local from_balance = tonumber(redis.call('GET', from_account)); local to_balance = tonumber(redis.call('GET', to_account)); if from_balance >= amount then redis.call('DECRBY', from_account, amount); redis.call('INCRBY', to_account, amount); return 1; else return 0; end" 2 account1 account2 100
</pre></div>
<p class="maodian"><a name="_label3"></a></p><h2>4. 排行榜</h2>
<p>操作有序集合(sorted sets)实现排行榜功能。</p>
<p class="maodian"><a name="_lab2_3_3"></a></p><h3>示例:获取排行榜前 N 名</h3>
<div class="jb51code"><pre class="brush:sql;">local key = KEYS
local limit = tonumber(ARGV)
return redis.call("ZRANGE", key, 0, limit - 1, "WITHSCORES")
</pre></div>
<div class="jb51code"><pre class="brush:sql;">redis-cli EVAL "local key = KEYS; local limit = tonumber(ARGV); return redis.call('ZRANGE', key, 0, limit - 1, 'WITHSCORES');" 1 leaderboard 10
</pre></div>
<p class="maodian"><a name="_label4"></a></p><h2>5. 队列操作</h2>
<p>通过列表(list)实现任务队列。</p>
<p class="maodian"><a name="_lab2_4_4"></a></p><h3>示例:推送和弹出任务</h3>
<div class="jb51code"><pre class="brush:sql;">-- 推送任务到队列
local queue_key = KEYS
local task = ARGV
redis.call("RPUSH", queue_key, task)
return redis.call("LLEN", queue_key)
</pre></div>
<div class="jb51code"><pre class="brush:sql;">redis-cli EVAL "local queue_key = KEYS; local task = ARGV; redis.call('RPUSH', queue_key, task); return redis.call('LLEN', queue_key);" 1 task_queue "task1"
</pre></div>
<div class="jb51code"><pre class="brush:sql;">-- 弹出任务
local queue_key = KEYS
local task = redis.call("LPOP", queue_key)
if not task then
return nil
else
return task
end
</pre></div>
<div class="jb51code"><pre class="brush:sql;">redis-cli EVAL "local queue_key = KEYS; local task = redis.call('LPOP', queue_key); if not task then return nil; else return task; end" 1 task_queue
</pre></div>
<p class="maodian"><a name="_label5"></a></p><h2>6. 限流器</h2>
<p>实现简单的限流器,用于控制请求频率。</p>
<p class="maodian"><a name="_lab2_5_5"></a></p><h3>示例:限流脚本</h3>
<div class="jb51code"><pre class="brush:sql;">local key = KEYS
local limit = tonumber(ARGV)
local interval = tonumber(ARGV)
local current = tonumber(redis.call("GET", key) or "0")
if current + 1 > limit then
return false
else
redis.call("INCR", key)
if current == 0 then
redis.call("EXPIRE", key, interval)
end
return true
end
</pre></div>
<div class="jb51code"><pre class="brush:sql;">redis-cli EVAL "local key = KEYS; local limit = tonumber(ARGV); local interval = tonumber(ARGV); local current = tonumber(redis.call('GET', key) or '0'); if current + 1 > limit then return false; else redis.call('INCR', key); if current == 0 then redis.call('EXPIRE', key, interval); end; return true; end" 1 rate_limit_key 10 60
</pre></div>
<p class="maodian"><a name="_label6"></a></p><h2>总结</h2>
<p>Redis 的 Lua 脚本强大且灵活,适用于多种场景。通过合理使用 Lua 脚本,可以确保操作的原子性、减少网络开销和提高系统性能。上述示例涵盖了常见的分布式锁、计数器、事务性操作、排行榜、队列操作和限流器等场景,为这些应用场景提供了高效、可靠的解决方案。</p>
頁:
[1]