天天看点

Redis | 第9章 Lua 脚本与排序《Redis设计与实现》

目录

前言

1. Lua 脚本

1.1 Redis 创建并修改 Lua 环境的步骤

1.2 Lua 环境协作组件

1.3 EVAL 命令的实现

1.4 EVALSHA 命令的实现

1.5 脚本管理命令的实现

1.6 脚本复制

1.6.1 EVAL、SCRIPT FLUSH、SCRIPTLOAD 命令的复制

1.6.2 EVALSHA 命令的复制

2. 排序

2.1 SORT 命令的实现

2.2 SORT 命令的可选项

2.3 多个选项的执行顺序

最后

参考资料:《Redis设计与实现 第二版》;

第三部分为独立功能的实现,主要由以下模块组成:发布订阅、事务、Lua 脚本、排序、二进制位数组、慢查询日志、监视器;

本篇将介绍 Redis 的Lua 脚本与排序。Redis 从 2.6 版本开始可以使用一些命令操作 Lua 脚本,引入相关支持;Redis 的 SORT 命令可以对列表键、集合键或者有序集合键的值进行排序,SORT 命令的一些可选性能让我们得到想要的排序;

与本章相关的 Redis 命令总结在下篇文章,欢迎点击收藏,本篇将不再重复:

《Redis常用命令及示例总结(API)》:https://www.cnblogs.com/dlhjw/p/15639773.html

Lua 脚本的执行的原子性的;

创建 Lua 环境:调用 Lua 的 C API 函数 lua_open;

载入函数库:包括基础库、表格库、字符串库、数学库、调试库、Lua CJSON 库、Struct 库、Lua cmsgpack 库等;

创建 redis 全局表格:表格包含对 Redis 进行操作的函数,如 <code>redis.call</code> 和 <code>redis.pcall</code> 函数等;

使用 Redis 自制的随机函数替换 Lua 原有的随机函数:<code>math.random</code> 和 <code>math.randomseed</code> 函数带有副作用,Redis 对其进行了替换,以避免在脚本中引入副作用;

创建排序辅助函数:有些命令在相同数据集上会产生不同结果,为了消除这些命令带来的不确定性,创建了 <code>redis__compare_helper</code> 排列辅助函数;

创建 redis.pcall 函数的错误报告辅助函数:创建 <code>__redis__err__handler</code> 错误处理函数,提供更详细的出错信息;

保护 Lua 全局环境:防止用户在执行脚本时将额外的全局变量添加到 Lua 环境中,但可以修改已存在的全局变量;

将 Lua 环境保存到服务器状态的 lua 属性里:然后等待服务器传来 lua 脚本;

Redis | 第9章 Lua 脚本与排序《Redis设计与实现》

Redis 服务器创建两个与 Lua 环境进行协作的组件;

伪客户端:

Redis | 第9章 Lua 脚本与排序《Redis设计与实现》

lua_scripts 字典:

所有被 EVAL 和 SCRIPT LOAD 命令执行过的脚本会被保存到 <code>lua_scripts</code> 字典里;

执行命令:<code>EVAL "return 'hello world'" 0</code>,实现步骤如下:

定义脚本函数:给函数命令为 <code>f_SHA1的校验和</code>,函数体为脚本本身<code>return 'hello world'</code>;

将脚本保存到 lua_scripts 字典:

Redis | 第9章 Lua 脚本与排序《Redis设计与实现》

执行脚本函数:先进行设置钩子(hook)、传入参数等准备工作,再执行函数,将执行结果保存到客户端状态的输出缓冲区,执行垃圾回收等操作;

每个被 EVAL 命令成功执行过的 Lua 脚本,在 Lua 环境中有一个与该脚本对应的 Lua 函数,函数名为 <code>f_SHA1的校验和</code>;

因此即使不知道脚本内容,也可以通过 EVALSHA SHA1校验和 命令调用对应函数;

SCRIPT FLUSH:

用于清除服务器中所有和 Lua 脚本有关的信息;

会释放并重建 <code>lua_scripts</code> 字典,关闭现有 Lua 环境并重建一个新的 Lua 环境;

SCRIPT EXISTS:

根通过检查给定校验和 SHA1 是否在的于 <code>lua_scripts</code> 字典的键,判断对应的脚本是否存在服务器中;

SCRIPT LOAD:

所做工作与 EVAL 命令前两步一样:定义脚本函数,将脚本函数保存进 <code>lua_scripts</code> 字典;

SCRIPT KILL:

用于处理钩子(hook)超时的情况;

如果脚本未执行写入操作,客户端可以使用该命令停止执行脚本;

如果脚本已经进行过写入操作,客户端只能使用 SHUTDOWN nosave 命令来停止服务器,防止不合法的数据被写入到数据库中;

Redis | 第9章 Lua 脚本与排序《Redis设计与实现》

当服务器运行在复制模式下时,具有写性质的脚本命令会被复制到从服务器里,如:EVAL、EVALSHA、SCRIPT FLUSH、SCRIPTLOAD;

当主服务器执行完 EVAL、SCRIPT FLUSH、SCRIPTLOAD 命令时,会直接将被执行的命令传播给所有从服务器;

Redis | 第9章 Lua 脚本与排序《Redis设计与实现》

EVALSHA 命令接受一个校验和 SHA1 作为参数传入;

每个服服务器间的脚本校验和 SHA1 可能不同,导致在不同的服务器间执行 EVALSHA 命令情况可能也不同;

Redis 复制 EVALSHA 命令的方法:

判断传播 EVALSHA 命令是否安全的方法:主服务器使用 <code>repl_scriptcache_dict</code> 字典记录哪些脚本传播给了所有从服务器。跟 <code>lua_scripts</code> 字典对比即可得知哪些校验和还未传播;

清空 repl_scriptcache_dict 字典:每当主服务器添加一个新的从服务器时,主服务器会清空自己的 <code>repl_scriptcache_dict</code> 字典;

EVALSHA 命令转换成 EVAL 命令的方法:根据 sha1 校验和在 <code>lua_scripts</code> 字典中查找对应 Lua脚本 script;然后改写命令,用 script 替换 sha1;其他参数不变;

EVAL命令:<code>EVAL script numkeys key [key ...] arg [arg ...]</code>

EVALSHA命令:<code>EVALSHA sha1 numkeys key [key ...] arg [arg ...]</code>

传播 EVALSHA 命令的方法:当主服务器成功执行完一个 EVALSHA 命令后,会根据该命令指定的 SHA1 校验和是否存在于 <code>repl_scriptcache_dict</code> 字典里来决定是否向从服务器传播 EVALSHA 命令还是 EVAL 命令;

Redis | 第9章 Lua 脚本与排序《Redis设计与实现》

服务器执行 SORT numbers 命令的详细步骤:

1)创建一个和 numbers 列表长度相同的数组,数组的每个项都是 <code>redis.h/redisSortObject</code> 结构;

2)遍历数组,将各个数组项的 obj 指针指向 numbers 列表的各个项,构成 obj 指针和列表项的一对一关系;

Redis | 第9章 Lua 脚本与排序《Redis设计与实现》

3)遍历数组,将各个 obj 指针所指向的列表项转换成一个 double 类型的浮点数,并将浮点数保存到相应数组项 <code>u.score</code> 属性里;

Redis | 第9章 Lua 脚本与排序《Redis设计与实现》

4)根据数组项 <code>u.score</code> 属性的值,对数组进行数字值排序;

Redis | 第9章 Lua 脚本与排序《Redis设计与实现》

5)遍历数组,将各个数组项的 obj 指针所指向的列表项作为排序结果返回给客户端;

ALPHA 选项可以对包含字符串的键进行排列;

ASC 选项执行升序排序;

DESC 选项执行降序排序;

升序和降序都采用快速排序算法;

BY 选项可以指定某些字符串键,或者某个哈希键说包含的某些域来作为权重,对一个键排序;

LIMIT 选项可以让结果只返回其中部分已排序的元素;

GET 选项可以在键被排序后,根据被排序的元素,以及 GET 选项指定的模式,查找并返回某些键的值;

STORE选项可以将排序结果保存在指定的键里面;

选项的执行顺序:

1)排序:会先使用 ALPHA、ASC、DESC、BY 这几个选项,对输入键进行排序,并得到一个排序结果集;

2)限制排序结果集的长度:使用 LIMIT 选项,对排序结果集的长度进行限制;

3)获取外部键:使用 GET 选项,根据结果集的元素,以及 GET 指定的模式,查找并获取指定键的值,并用这些值构成新的结果集;

4)保存排序结果集:使用 STORE 选项,将排序结果集保存到指定的键上;

5)向客户端返回排序结果集:遍历排序结果集,并依次向客户端返回排序结果集中的元素;

除了 GET 选项之外,改变选项的摆放顺序不会影响 SORT 命令执行这些选项的顺序;

新人制作,如有错误,欢迎指出,感激不尽!

欢迎关注公众号,会分享一些更日常的东西!

如需转载,请标注出处!

Redis | 第9章 Lua 脚本与排序《Redis设计与实现》