redis lua执行原理,主从同步,动态变量

[Redis Slowlog]是排查性能问题关键监控指标。它是记录Redis queries运行时间超时特定阀值的系统。
这类慢查询命令被保存到Redis服务器的一个定长队列,最多保存slowlog-max-len(默认128)个慢查询命令。
当慢查询命令达到128个时,新产生的慢查询被加入前,会从队列中删除最旧的慢查询命令。

执行原理

创建并修改Lua环境

  • 1 创建基础Lua环境
  • 2 载入函数库
  • 3 创建全局表格Lua
  • 4 替换随机函数
  • 5 创建排序辅助函数
  • 6 创建redis.pcall函数
  • 7 全局环境保护
  • 8 修改后的Lua环境保存到服务器状态的Lua属性,等待脚本执行

Redis中带有不确定性的命令:

1

2

3

4

5

6

7

SINTER

SUNION

SDIFF

SMEMEBERS

HKEYS

HVALS

KEYS

注意:

  • Redis提供了排序函数,使用上述命令后,会返回相同的排序结果
  • Redis确保检查创建变量时,添加local等参数,但是没有禁止修改已经存在的全局变量,因此使用时应谨慎。
  • 由于Redis使用串行化的方式执行,因此某一特定时间,只有一个脚本能被放进Lua环境里面运行。

环境协作组件

环境组件:

  • 一个是用于执行Lua脚本的伪客户端
  • 一个用于保存Lua脚本的字典。

执行Lua脚本的伪客户端

使用redis.call或者redis.pcall执行Redis命令:

  • 1 将redis.call或者redis.pcall传给伪客户端
  • 2 伪客户端将执行的命令传给执行器
  • 3 执行器执行命令,返回给伪客户端
  • 4 伪客户端把结果返回给Lua环境
  • 5 Lua环境把结果返回给redis.call或者redis.pcall函数
  • 6 redis.call或者redis.pcall返回结果给调用者

lua_s字典

保存Lua脚本的SHA1【校验和】checksum,值是对应的脚本。Redis会把EVAL命令执行过的脚本,或者 LOAD加载的脚本都保存在字典中。

主从同步

### redis 主从数据不一致导致的bug
1 用户在绑定手机号时,验证码逻辑涉及验证码校验(验证码有效性)和验证码校验验证(校验结果验证)两个过程,发生问题的用户请求涉及的这两个过程时间间隔比较小,缓存主节点和从节点中的“验证码校验结果”状态还未达到一致,所以导致了问题发生。  
2 根据代码发现,通过个人身份认证会产生两个认证:身份认证 + 实名认证,缓存会在短时间内 读写、读写两次,假设系统出现大key类可能会造成主从延迟,导致第二次读的数据为空,所以写入最后的认证信息。  
读数据会从master和slave随机选取  
写数据会写master  

字典的作用:实现 EXISTS命令;实现脚本的复制

EVAL命令的实现

  • 1 根据客户端给定的脚本,在Lua环境中定义Lua函数
  • 2 将客户端给定的脚本保存到lua_s字段,进一步使用
  • 3 执行刚刚在lua中定义的函数,执行客户端给定的lua脚本

通过函数保存传入的脚本好处:

  • 1 执行方便
  • 2 保持局部性
  • 3 如果定义过一次,只要使用校验和即可。

准备执行脚本:

  • 1 将EVAL命令传入的键名参数和脚本参数保存在KEYS数组和ARGV数组中
  • 2 装载超时钩子
  • 3 执行脚本函数
  • 4 移除钩子
  • 5 结果放入缓冲区
  • 5 垃圾回收

使用EVALSHA “xxx校验和”0 就可执行EVAL “return ‘hello world!’” 0

脚本管理命令实现

1

2

3

4

FLUSH 用于清除服务器中lua有关的脚本,释放lua_s字典,关闭现有的lua环境,并重新创建

EXISTS 输入SHA1校验和,判断是否存在

LOAD 与EVAL相同,创建对应的lua函数,存放到字典中

KILL 使用钩子定期检查脚本运行时间,如果没有执行过,可以使用 KILL杀掉;如果执行过,只能使用SHUTDOWN nosave停止服务器

 

动态变量

https://my.oschina.net/wangxu3655/blog/3125737?from=singlemessage

这个文章介绍了,当lua脚本出现动态变量时的处理方式

-- 当前时间

local now_time = redis.call('TIME');

-- 设置OPERATE_TIME值为当前秒数

redis.call('SET','OPERATE_TIME',now_time[1]);

 

Write commands not allowed after non deterministic commands.

Call redis.replicate_commands() at the start of your script in order to switch to single commands replication mode. 

翻译过来就是说, 写命令不被允许出现在‘非确定性命令’的后面, 请在脚本开始时调用redis.replicate_commands()来切换到命令复制模式.

修改后的lua脚本:

-- 开启单命令复制模式

redis.replicate_commands();

-- 当前时间

local now_time = redis.call('TIME');

-- 设置OPERATE_TIME值为当前秒数

redis.call('SET','OPERATE_TIME',now_time[1]);

再执行就不会报错, 然后我们到AOF文件下去查看一下, 我们发现追加的不是脚本内容, 而是MULTI...EXEC命令:

$1

0
*1
$5
MULTI
*3
$3
SET
$12
OPERATE_TIME
$10
1572855544
*1
$4
EXEC

回到脚本上来, 我们的脚本包含了TIME命令来获取系统时间, 这个命令在不同时间去执行返回的值肯定不一样,  如果在主从复制和AOF追加时, 直接复制整个脚本的内容, 那么肯定会造成执行时数据的不一致性. 

所以, 在执行Lua时Redis默认不允许动态的不确定性的变量存在. 如果存在, 则需要开启命令复制模式, 即只复制Lua脚本里包含的写命令, 所有的写命令会被包装在MULTI ... EXEC里.

Redis官方, 把默认的复制整个脚本内容的模式定义为whole scripts replication, 把只复制脚本里写命令的模式定义为 script effects replication. 

在命令复制模式下, 还可以选择是否对某个命令进行目标复制, 即是否需要复制到‘从服务器’和‘AOF文件’ , 如下:

redis.set_repl(redis.REPL_ALL) -- Replicate to AOF and slaves.
redis.set_repl(redis.REPL_AOF) -- Replicate only to AOF.
redis.set_repl(redis.REPL_SLAVE) -- Replicate only to slaves.
redis.set_repl(redis.REPL_NONE) -- Don't replicate at all.

下面这个slowlog的时间讲解很到位

https://my.oschina.net/u/3023401/blog/2231347

Redis Slowlog只计算命令的执行时间

如MySQL/MongoDB等常见数据库,慢查询的query_time都会包含命令所有耗时,包含锁等待这类时间; 而Redis的慢查询query_time只记录自己“被cpu服务的时间”,不包含排队等待、IO等待(如AOF SYNC)这类时间。
理解这点非常重要

参考:
    The Redis Slow Log is a system to log queries that exceeded a 
specified execution time. The execution time does not include I/O 
operations like talking with the client, sending the reply and so forth,
but just the time needed to actually execute the command (this is the only
stage of command execution where the thread is blocked and can not serve
other requests in the meantime).

Redis Single-threads的问题

Redis Server是单线程的处理(bgsave或aof重写时会Fork子进程处理),同一时间只能处理一个命令,并且是同步完成的。
所以RD和DBA在设计keyspace和访问模式时,应尽量避免使用耗时较大的命令。
在理想状态下,Redis单实例能处理8~10w的QPS, 如果大量的redis命令大量耗时大于1ms, 其实QPS只能达到1000基于几百。
Redis出现耗时大的命令,导致其他所有请求被阻塞等待,redis处理能力急剧退化,易导致整个服务链雪崩。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值