Redis命令问题
“线程安全”问题
单进程多线程
模型内部多个线程
操作进程内共享内存
导致的数据资源充突。而 Redis 的线程安全问题的产生,并不是来自于 Redis 服务器内部。
-
Redis 内存储了一个用户的状态:
user5277=idle
;
-
客户端连接 A 读取了用户状态,获取到用户的空闲状态
status = get("user5277")
;
-
客户端连接 B 也同样读取了用户状态;
-
客户端连接 A 给用户安排了一个任务,并将 Redis 内用户状态置为忙碌
set("user5277", "busy")
;
-
客户端连接 B 同样设置用户为忙碌状态。
-
可是此时用户却被同时分配了两个任务。
效率问题
100ns
,而不同机房之间来回一次约需要
500000ns
,其中的差距可想而知。
pipeline
的特性,但它需要多个命令的请求和响应之间没有依赖关系。想简化多个相互依赖的命令就只能将数据拉回客户端,由客户端处理后再请求 Redis。
内嵌Lua的执行
Lua
-
轻量:源码包只有核心库,编译后体积很小。
-
高效:由 ANSI C 写的,启动快、运行快。
-
内嵌:可内嵌到各种编程语言或系统中运行,提升静态语言的灵活性。如 OpenResty 就是将 Lua 嵌入到 nginx 中执行。
执行步骤
redis.pcall
等 Redis 命令,以准备 Lua 脚本的执行。
-
检查脚本是否执行过,没执行过使用脚本的 sha1 校验和生成一个 Lua 函数;
-
为函数绑定超时、错误处理勾子;
-
创建一个伪客户端,通过这个伪客户端执行 Lua 中的 Redis 命令;
-
处理伪客户端的返回值,最终返回给客户端;
使用
EVAL
和
EVALSHA
命令。
EVAL
适用于单次执行 Lua 脚本,执行脚本前会由脚本内容生成 sha1 校验和,在函数表内查询函数是否已定义,如未定义执行成功后 Redis 会在全局表里缓存这个脚本的校验和为函数名,后续再次执行此命令就不会再创建新的函数了。
EVALSHA
命令,就得先使用
SCRIPT LOAD
命令先将函数加载到 Redis,Redis 会返回此函数的 sha1 校验和, 后续就可以直接使用这个校验和来执行命令了。
127.0.0.1:6379> EVAL "return 'hello'" 0 0
"hello"
127.0.0.1:6379> SCRIPT LOAD "return redis.pcall('GET', ARGV[1])"
"20b602dcc1bb4ba8fca6b74ab364c05c58161a0a"
127.0.0.1:6379> EVALSHA 20b602dcc1bb4ba8fca6b74ab364c05c58161a0a 0 test
"zbs"
EVAL script numkeys key [key ...] arg [arg ...]
,在 Lua 函数内部可以使用
KEYS[N]
和
ARGV[N]
引用键和参数,需要注意 KEYS 和 ARGV 的参数序号都是从
1
开始的。
false
,而
不是 nil
;
Lua 脚本实例
// 使用: EVAL script 2 A B
local tmpKey = redis.call('HGET', KEYS[1], KEYS[2]);
return redis.call('GET', tmpKey);
// 使用: EVAL script 2 list count
local list = {};
local item = false;
local num = tonumber(KEYS[2]);
while (num > 0)
do
item = redis.call('LPOP', KEYS[1]);
if item == false then
break;
end;
table.insert(list, item);
num = num - 1;
end;
return list;
local elements = redis.call('ZRANK', KEYS[1], 0, KEY[2]);
local detail = {};
for index,ele in elements do
local info = redis.call('HGETALL', ele);
table.insert(detail, info);
end;
return detail;
一些思考
使用场景
-
可以使用 Lua 脚本实现原子性操作,避免不同客户端访问 Redis 服务器造成的数据冲突。
-
在前后多次请求的结果有依赖时,可以使用 Lua 脚本把多个请求整合为一个请求。
注意点
-
要保证安全性,在 Lua 脚本中不要使用全局变量,以免污染 Lua 环境,虽然使用全局变量全报错,Lua 脚本停止执行,但还是在定义变量时添加
local
关键字。
-
要注意 Lua 脚本的时间复杂度,Redis 的单线程同样会阻塞在 Lua 脚本的执行中。
-
使用 Lua 脚本实现原子操作时,要注意如果 Lua 脚本报错,之前的命令同样无法回滚。
-
一次发出多个 Redis 请求,但请求前后无依赖时,使用
pipeline
,比 Lua 脚本方便。
如果看到这里,说明你喜欢这篇文章,请 转发、点赞。微信搜索「web_resource」,关注后回复「进群」或者扫描下方二维码即可进入无广告交流群。
↓扫描二维码进群↓
推荐阅读
1.
GitHub 上有什么好玩的项目?
2.
Linux 运维必备 150 个命令汇总
3.
SpringSecurity + JWT 实现单点登录
4. 100 道 Linux 常见面试题
本文分享自微信公众号 - Java后端(web_resource)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
相关文章
暂无评论...