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 选项。