Redis lua脚本简要学习


背景

上周督促客户从Windows平台升级到了Linux平台.
redis一周相安无事.
但是这周一突然又出现了卡断和慢的情况.
只能继续进行分析.

分析思路

现场日志里面出现了大量的错误提示:
BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. 不管用chatGPT后者是其他工具都可以查询出来:
Redis执行lua脚本时间太长了. 导致其他客户端无法获取结果集. 然后计划通过redis的slowlog 查询验证一下自己的想法

分析过程


我在自己的测试机器上面执行了 slowlog get 获取如下:

10) 1) (integer) 996
2) (integer) 1685314080
3) (integer) 10513
4) 1) "EVALSHA"
2) "7ae4b3e7f4bf3b0c3470baf33dd1b4941599b520"
3) "879" 发现最近十条慢命令全是id为: 7ae4b3e7f4bf3b0c3470baf33dd1b4941599b520 的lua脚本导致的. 督促现场抓紧获取指令:
现场反馈的图片与我这边一致
但是不太一样的是:
现场有四万个键值对耗时 接近 9 秒钟.
比我这边有非常大的差距. 我这边又让研发同事去另外一个大项目查询了下: 2万个参数. 执行时间为:58毫秒左右 389654
1685325926
57581
EVALSHA
7ae4b3e7f4bf3b0c3470baf33dd1b4941599b520
20855 基本上明确是此lua脚本导致的卡顿

问题再现以及缓解

网上查了一点简单资料, 准备做一下实验
在一个redis的客户端上面你执行命令
eval "while 0==0 do end" 0
然后在另外的redis客户端验证
redis-cli
get 1
get 2
会提示: "BUSY Redis is busy running a script."
然后执行: script KILL
再执行
get 1 就可以正常给出结果了. 说明问题基本上可以再现. 缓解方式: 如果不是一个很需要数据一致性的场景, 可以进行一下SCRIPT KILL
后期解决方式, 应该可以分组,每组不超过1万或者是1000 进行处理, 循环进行清理操作.

未解决的问题

翻找了一早上资料, 没有解决如何根据evalsha 的sha1值反向获取具体的lua脚本.
chatgpt给的结果就是垃圾, 一个get 一个list命令 script 根本没有这些参数. 没办法只能求助于开发:
private final static DefaultRedisScript SessionExistRedisScript = new DefaultRedisScript<>(
"local expires = ''\n" +
"for i=1, #KEYS do\n" +
" if(redis.call('EXISTS','caf-session:sessions:'..KEYS[i]) == 0)\n" +
" then\n" +
" expires = expires..';'..KEYS[i]\n" +
" end\n" +
"end\n" +
"return expires", String.class
);

问题思考

前端时间刚整理了因为Redis的scan导致机器卡顿宕机的问题
今天早上在看ergonomics导致full GC无法提供服务的现象. 我突然感觉redis因为单线程的处理逻辑, 如果大家都需要同步的获取redis的结果
那么每次redis执行吗命令都相当于在做一次minor GC
如果是一个slowlog. 那么就相当于所有连接着一个redis的所有应用服务器一起在做STW.的full GC
所以从时间链上分析. 任何single point 都需要非常严格的重视. 所以研发写的那一套lua脚本非常值得研究与修改.

lua脚本的优点

(1) 减少网络开销: 在Redis操作需求需要向Redis发送5次请求,
而使用脚本功能完成同样的操作只需要发送一个请求即可,减少了网络往返时延。
(2) 原子操作: Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。
换句话说在编写脚本的过程中无需担心会出现竞态条件,也就无需使用事务。事务可以完成的所有功能都可以用脚本来实现。
(3) 复用: 客户端发送的脚本会永久存储在Redis中,
这就意味着其他客户端(可以是其他语言开发的项目)可以复用这一脚本而不需要使用代码完成同样的逻辑。
(4) 速度快:见 与其它语言的性能比较, 还有一个 JIT编译器可以显著地提高多数任务的性能;
对于那些仍然对性能不满意的人, 可以把关键部分使用C实现, 然后与其集成, 这样还可以享受其它方面的好处。

关于lua脚本的继续学习

前端时间想学习 openresty 使用lua脚本.
但是非常可耻的立了flag没有学习深入下去.
所以发现还是遇到问题督促学习更好.
需要拥抱问题, 不能害怕问题, 通过问题来学习和提高自己. lua脚本的基础知识
lua脚本是解释性的语言, 跟python和shell 都比较类似.
print("hello 济南小老虎!")
就是一个最简单的 hello world的脚本. 数据类型:
nil,布尔,浮点数,字符串,函数,线程,关联数组(table等) 循环判断
while( true )
do
print("测试循环")
os.execute("sleep " .. 2)
end

Redis里面eval等学习

eval 其实一个非常不安全的命令
很多一句话木马基本上都是基于这个吗精灵来做的
在js,php,等语言里面都有存在.
redis 里面 eval 是用于调用lua脚本的命令. eval以及script脚本可以实现很多lua脚本的调用和使用.
学习和举例如下:
script load "while (true) do end"
会返回一个hash值如下: 127.0.0.1:6379> script load "while (true) do end"
"f926d8b19784f2c8c63b04288b32cb8de66070ba" 我理解此处就像是数据库绑定变量后可以避免hard parse 的方式一样.
下次执行时可以使用 evalsha f926d8b19784f2c8c63b04288b32cb8de66070ba 0 注意需要加最后一个0 用于指代参数个数
此时就可以执行命令了
等效率执行
eval "while (true) do end" 0 # 注意执行此命令会阻塞产品的使用, 必须尽早执行kill处理.
# 因为redis默认的lua脚本超时时间是 5 seconds 所以只会kill 5s 以上的script.
# 举例如下
# 我执行删除操作. 其实一直没有返回, 因为还没到五秒
127.0.0.1:6379> script kill
OK
(2.23s)
# 执行 lua脚本的返回情况如下:
(error) ERR Error running script (call to f_f926d8b19784f2c8c63b04288b32cb8de66070ba): @user_script:1: Script killed by user with SCRIPT KILL...
(5.00s)

Redis lua脚本简要学习的更多相关文章

  1. Redis Lua脚本完全入门

    1. 前言 Redis是高性能的KV内存数据库,除了做缓存中间件的基本作用外还有很多用途,比如胖哥以前分享的Redis GEO地理位置信息计算.Redis提供了丰富的命令来供我们使用以实现一些计算.R ...

  2. Redis Lua脚本调试

    从版本3.2开始,Redis包含一个完整的Lua调试器,可以用来使编写复杂Redis脚本的任务更加简单. 由于Redis 3.2仍处于测试阶段,请unstable从Github 下载Redis 的分支 ...

  3. Redis Lua 脚本使用

    本文转载自Redis Lua 脚本使用 Lua 简介 Lua语言提供了如下几种数据类型:booleans(布尔).numbers(数值).strings(字符串).tables(表格). 下面是一些 ...

  4. redis lua脚本学习

    语法格式(常见) a = 5 -- 全局变量 local b = 5 -- 局部变量 Eval的使用 EVAL script numkeys key [key ...] arg [arg ...] 首 ...

  5. Redis Lua脚本原理

    2.6版本之后支持嵌入Lua脚本,客户端使用Lua脚本,直接在服务器端原子的执行多条命令 Lua脚本执行过程 创建并修改Lua环境 1 创建基础Lua环境 2 载入函数库 3 创建全局表格Lua 4 ...

  6. 在Spring中使用Redis Lua脚本批量删除缓存

    背景 之前分享了一篇利用lua脚本批量删除redis的key的文章.现在项目中我打算使用spring的缓存,而Spring缓存以前我是用ehcache来做实现的.没发现什么问题..这次我换成redis ...

  7. 【连载】redis库存操作,分布式锁的四种实现方式[四]--基于Redis lua脚本机制实现分布式锁

    一.redis lua介绍 Redis 提供了非常丰富的指令集,但是用户依然不满足,希望可以自定义扩充若干指令来完成一些特定领域的问题.Redis 为这样的用户场景提供了 lua 脚本支持,用户可以向 ...

  8. redis --- lua 脚本实现原子操作

    如题, 楼主的想法很简单, lua 脚本本身支持原子性, 所以把命令写进一个脚本就行, 当然后续还会优化才能放到生产上,例如缓存脚本 ,redis 本身会缓存执行过的脚本 ,这样速度更快, 再优化, ...

  9. Redis进阶应用:Redis+Lua脚本实现复合操作

    一.引言 Redis是高性能的key-value数据库,在很大程度克服了memcached这类key/value存储的不足,在部分场景下,是对关系数据库的良好补充.得益于超高性能和丰富的数据结构,Re ...

  10. redis>lua脚本

    String lua="local num=redis.call('incr',KEYS[1])\n"+"if tonumber(num)==1 then\n" ...

随机推荐

  1. Kmesh内核级流量治理,服务转发性能提升50%+

    本文分享自华为云社区<DTSE Tech Talk | 第49期:Kmesh内核级流量治理,服务转发性能提升50%+!>,作者:华为云社区精选. 数据面时延开销,无法满足应用SLA诉求?内 ...

  2. 秋风到,ModelArts“ AI市场算法Fast-SCNN指南”秋膘贴起来

    本文分享自华为云社区<带你来秋日尝鲜 | ModelArts AI市场算法Fast-SCNN使用指导>,作者:Tianyi_Li 摘要:送小伙伴们一份新鲜出炉的ModelArts AI市场 ...

  3. 论文解读:ACL2021 NER | 基于模板的BART命名实体识别

    摘要:本文是对ACL2021 NER 基于模板的BART命名实体识别这一论文工作进行初步解读. 本文分享自华为云社区<ACL2021 NER | 基于模板的BART命名实体识别>,作者: ...

  4. vue2升级vue3:vue3创建全局属性和方法

    vue2.x挂载全局是使用Vue.prototype.$xxxx=xxx的形式来挂载,然后通过this.$xxx来获取挂载到全局的变量或者方法 在vue3.x这种方法显然是不行了,vue3中在setu ...

  5. 一文读懂火山引擎A/B测试的实验类型(1)——编程实验

    一. 概述 编程实验:指的是通过代码编程进行AB实验,广泛使用于前端优化.策略优化和后端算法优化多种实验场景,包含客户端和服务端实验. 前置条件:接入客户端SDK或者服务端SDK,详见:应用接入 二. ...

  6. 火山引擎 DataLeap:3 小时分享,体系化讲透企业数据治理如何做?

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 数据治理伴随着数据全生命周期的进程,涉及事前规范检查.事中监控管理.事后优化复盘等过程,关键重点领域包括数据质量的 ...

  7. python发送邮件+多人+附件 !!!!

    import smtplib import os from email.header import Header from email.mime.text import MIMEText # shen ...

  8. redis之列表 redis之hash redis其他操作 redis管道 django中使用redis celery介绍和安装 celery快速使用 celery包结构

    目录 昨日回顾 今日内容 1 redis之列表 2 redis之hash 3 redis其他操作 4 redis 管道 5 django中使用redis 6 celery介绍和安装 7 celery快 ...

  9. CS01 BOM客制化屏幕增强

    一.BOM行项目新增定制字段 效果如下 二.前台增强实现步骤 1.行项目表新增字段 2.CMOD,增强项目PCSD0002:在行项目中增强(PCSD0003:在抬头增强) 3.双击创建定制化屏幕 4. ...

  10. AtCoder Beginner Contest 199 游记(AB水题,C字符串操作,D搜索,E状压)

    A - Square Inequality 水题 B - Intersection 水题,就是找公共区间,维护一下 Lmax,Rmin即可 void solve() { int n, a, b; in ...