Redis ~ Lua。

文章探讨了Redis的单线程架构如何确保命令原子性,但可能导致并发问题。通过引入Lua脚本,可以一次性发送整块代码到Redis服务器以解决此类问题。此外,介绍了在Windows环境下安装Lua,使用ZeroBraneIDE进行开发,并展示了Redis中如何使用EVAL命令执行和调试Lua脚本。最后提到了使用Lua和Redis实现分布式锁的可能性。
摘要由CSDN通过智能技术生成

Redis ~ Lua。



发现问题。

Redis 单线程架构,保证命令的原子性,不会产生并发问题。

但 eg.

两个程序用同一个 Redis key(var_number)。

incr var_number(A) -> incr var_number(A) -> get var_number(A)(+2)
incr var_number(B) -> incr var_number(B) -> get var_number(B)(+2)
– ,梅毛冰

incr var_number(A) -> incr var_number(B) -> incr var_number(A) -> get var_number(A)(+3) -> incr var_number(B) -> get var_number(B)(+4)



Lua 解决。

Lua 其实不是发送一条条命令,而是一整块代码。

incr var_number(A) -> incr imooc(A) -> get imooc(A)(+2)(一次性发送到 Redis 服务端)。
incr imooc(B) -> incr imooc(B) -> get imooc(B)(+2)(一次性发送到 Redis 服务端)。



Lua ~ Windows。

http://www.lua.org/home.html

https://github.com/LuaDist/Repository/wiki/LuaDist%3A-Installation



Hello World。

hello.lua

print "hello geek"
G:\lyfGeek\lua-5.3.4_Win64_bin>lua53.exe lua\hello.lua
hello geek


IDE ~ ZeroBrane。

https://studio.zerobrane.com/



文档。

http://www.lua.org/docs.html


Redis ~ Lua。

[geek@localhost redis-4.0.11]$ redis-server redis.conf
2362:C 10 Mar 19:30:52.420 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
2362:C 10 Mar 19:30:52.420 # Redis version=4.0.14, bits=64, commit=00000000, modified=0, pid=2362, just started
2362:C 10 Mar 19:30:52.420 # Configuration loaded
[geek@localhost redis-4.0.11]$ redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> keys *
(empty list or set)

Redis 中调用 Lua 脚本使用 EVAL

script 用双引号。

在这里插入图片描述
“redis.call()” ~ Redis 环境下的 Lua 脚本才有。

-- 不带返回值。
127.0.0.1:6379> EVAL "redis.call('set', 'geek', 'test')" 0
(nil)
127.0.0.1:6379> GET geek
"test"

-- 带返回值。
127.0.0.1:6379> EVAL "return redis.call('set', 'name', 'geek')" 0
OK
127.0.0.1:6379> get name
"geek"
-- Redis 中为 Lua 提供了两个全局变量 KEYS[]、ARGV[]。
-- 索引从 1 开始。
-- KEYS[] ~ table 类型。
-- ARGS[] ~ table 类型。
127.0.0.1:6379> EVAL "return redis.call('set', keys[2], argv[3])" 3 geek1 geek2 geek3 geek4 geek5 geek6 geek7
(error) ERR Error running script (call to f_adc64b4d73b10f4835de46824d792cab13dcba76): @enable_strict_lua:15: user_script:1: Script attempted to access nonexistent global variable 'keys'
127.0.0.1:6379> EVAL "return redis.call('set', KEYS[2], argv[3])" 3 geek1 geek2 geek3 geek4 geek5 geek6 geek7
(error) ERR Error running script (call to f_01319cc4e70f6c5f64409e4b27a42d6b5d60ef9b): @enable_strict_lua:15: user_script:1: Script attempted to access nonexistent global variable 'argv'
-- 区分大小写。
127.0.0.1:6379> EVAL "return redis.call('set', KEYS[2], ARGV[3])" 3 geek1 geek2 geek3 geek4 geek5 geek6 geek7
OK


-- 前 3 个传递给 KEYS[],后面的传递给 ARGV[]。
127.0.0.1:6379> KEYS *
1) "geek2"
127.0.0.1:6379> get geek2
"geek6"

  • 捕获异常。
127.0.0.1:6379> EVAL "redis.call('set', KEYS[2], ARGV[3])" 3 geek1 geek2 geek3 geek4 geek5 geek6 geek7
(nil)
127.0.0.1:6379> get geek2
"geek6"

127.0.0.1:6379> EVAL "redis.call('setr', KEYS[1], ARGV[1]); redis.pcall('set', KEYS[3], ARGV[4])" 3 geek1 geek2 geek3 geek4 geek5 geek6 geek7
(error) ERR Error running script (call to f_39e876ae9c49279ddf9d67f470ce91703131f4b7): @user_script:1: @user_script: 1: Unknown Redis command called from Lua script

pcall(); ~ 异常后面的语句继续执行。



缓存命令。

返回命令的 SHA1 加密后的值。

127.0.0.1:6379> script load "return redis.call('set', KEYS[1], ARGV[1])"
"55b22c0d0cedf3866879ce7c854970626dcef0c3"

命令缓存到 Redis 中后可以直接通过加密后的值调用。

127.0.0.1:6379> script load "return redis.call('set', KEYS[1], ARGV[1])"
"55b22c0d0cedf3866879ce7c854970626dcef0c3"
127.0.0.1:6379> EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 2 key1 key2 value1
OK
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> get key2
(nil)

  • script flush ~ 删除所有缓存的脚本。

在这里插入图片描述

127.0.0.1:6379> SCRIPT EXISTS 55b22c0d0cedf3866879ce7c854970626dcef0c3
1) (integer) 1
127.0.0.1:6379> SCRIPT FLUSH
OK
127.0.0.1:6379> SCRIPT EXISTS 55b22c0d0cedf3866879ce7c854970626dcef0c3
1) (integer) 0


Lua 脚本调试。

http://www.redis.cn/topics/ldb.html

local i = 0
while true
do
	i = i + 1
	redis.debug(i)
end
return "ok"

[geek@localhost geek]$ redis-cli --ldb --eval loop.lua keys1 keys2, argv1 argv2

在这里插入图片描述
h ~ 帮助。

lua debugger> h
Redis Lua debugger help:
[h]elp               Show this help.
[s]tep               Run current line and stop again.
[n]ext               Alias for step.
[c]continue          Run till next breakpoint.
[l]list              List source code around current line.
[l]list [line]       List source code around [line].
                     line = 0 means: current position.
[l]list [line] [ctx] In this form [ctx] specifies how many lines
                     to show before/after [line].
[w]hole              List all source code. Alias for 'list 1 1000000'.
[p]rint              Show all the local variables.
[p]rint <var>        Show the value of the specified variable.
                     Can also show global vars KEYS and ARGV.
[b]reak              Show all breakpoints.
[b]reak <line>       Add a breakpoint to the specified line.
[b]reak -<line>      Remove breakpoint from the specified line.
[b]reak 0            Remove all breakpoints.
[t]race              Show a backtrace.
[e]eval <code>       Execute some Lua code (in a different callframe).
[r]edis <cmd>        Execute a Redis command.
[m]axlen [len]       Trim logged Redis replies and Lua var dumps to len.
                     Specifying zero as <len> means unlimited.
[a]bort              Stop the execution of the script. In sync
                     mode dataset changes will be retained.

Debugger functions you can call from Lua scripts:
redis.debug()        Produce logs in the debugger console.
redis.breakpoint()   Stop execution like if there was a breakpoing.
                     in the next line of code.
lua debugger>

在这里插入图片描述
去掉 --ldb 参数。

[geek@192 ~]$ redis-cli --eval loop.lua keys1 keys2, argv1 argv2

进入了阻塞。

另一个客户端操作会提示

127.0.0.1:6379> keys *
(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.

使用 script kill。

127.0.0.1:6379> SCRIPT KILL
OK
127.0.0.1:6379> keys *
1) "geek2"
2) "key1"
3) "geek3"
4) "geek"
5) "name"

https://github.com/pkulchenko/ZeroBranePackage/blob/master/redis.lua

在 ZeroBrane 文件目录 packages 下新建 redis.lua。

G:\lyfGeek\ZeroBraneStudioEduPack-1.90-win32\packages

重启 ZeroBrane。

在 project -> Lua Interpreter 中会多出一个 redis 选项。



Redis ~ 分布式锁。

RedisLua脚本是一种在Redis中执行操作的脚本语言。它允许用户通过Lua脚本执行一系列的Redis操作,包括读取、写入和删除数据等。RedisLua脚本具有以下特点: 1. 原子性:RedisLua脚本在执行过程中是原子的,它们要么全部执行成功,要么全部不执行。 2. 可重用性:Lua脚本可以被缓存和重复使用,提高执行效率。 3. 灵活性:Lua脚本支持逻辑控制语句、循环和函数等高级特性,可以实现复杂的业务逻辑。 4. 安全性:Lua脚本可以通过Redis的安全机制进行权限控制,确保脚本的执行安全。 5. 扩展性:RedisLua脚本可以通过调用Redis提供的API扩展其功能。 RedisLua脚本执行流程包括加载脚本、编译脚本和执行脚本三个步骤。在加载脚本阶段,Redis会将脚本加载到内存中,并对其进行缓存,以提高执行效率。在编译脚本阶段,Redis会对脚本进行语法检查和编译。在执行脚本阶段,Redis会按照预定的执行流程执行脚本中的命令,并返回执行结果。 RedisLua脚本在实际应用中有许多场景,包括但不限于: 1. 原子性操作:通过Lua脚本可以实现多个Redis操作的原子性,例如加锁、解锁、事务操作等。 2. 复杂数据处理:通过Lua脚本可以对Redis中的数据进行复杂的处理和计算,例如排序、过滤、聚合等。 3. 分布式锁:通过Lua脚本可以实现分布式锁,保证多个客户端之间的数据的一致性和原子性。 4. 实时计算:通过Lua脚本可以进行实时计算,例如统计、推荐算法等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lyfGeek

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值