Redis 函数
Redis 7.0 支持
Redis 函数是临时脚本的进化步骤, 函数提供与脚本相同的核心功能但却是数据库的一流软件工件
Redis 将函数作为数据库的一个组成部分进行管理, 并通过数据持久化和复制确保它们的可用性. 由于函数是数据库的一部分, 因此在使用前声明, 因此应用程序不需要在运行时加载它们, 也不必冒事务中止的风险. 使用函数的应用程序仅依赖于它们的 API, 而不依赖于数据库中的嵌入式脚本逻辑
Redis 函数可以将 Lua 的所有可用功能用于临时脚本, 唯一例外的是 Redis Lua 脚本调试器
Redis 函数还通过启用代码共享来简化开发, 每个函数都属于一个库, 任何给定的库都可以包含多个函数, 库的内容是不可变的, 并且不允许对其功能进行选择性更新. 取而代之的是, 库作为一个整体进行更新, 在一个操作中将它们的所有功能一起更新. 这允许从同一库中的其他函数调用函数, 或者通过使用库内部方法中的公共代码在函数之间共享代码, 这些函数也可以采用语言本机参数
Redis 函数也被持久化到 AOF 文件中, 并从主服务器复制到副本服务器, 因此它们与数据一样可以持久化
Redis 函数的执行是原子的, 函数的执行在其整个时间内阻止所有服务器活动, 类似于事务的语义, 已执行函数的阻塞语义始终适用于所有连接的客户端, 因为运行一个函数会阻塞 Redis 服务器
函数都属于一个库, 任何给定的库都可以包含多个函数
库的内容是不可变的, 并且不允许选择性地更新其函数, 只能将库作为一个整体进行更新
函数命令
FUNCTION help 显示 FUNCTION 的帮助信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 127.0.0.1:6379> FUNCTION help 1) FUNCTION <subcommand> [<arg> [value] [opt] ...]. Subcommands are: 2) LOAD [REPLACE] <FUNCTION CODE> 3) Create a new library with the given library name and code. 4) DELETE <LIBRARY NAME> 5) Delete the given library. 6) LIST [LIBRARYNAME PATTERN] [WITHCODE] 7) Return general information on all the libraries: 8) * Library name 9) * The engine used to run the Library 10) * Library description 11) * Functions list 12) * Library code (if WITHCODE is given) 13) It also possible to get only function that matches a pattern using LIBRARYNAME argument. 14) STATS 15) Return information about the current function running: 16) * Function name 17) * Command used to run the function 18) * Duration in MS that the function is running 19) If no function is running, return nil 20) In addition, returns a list of available engines. 21) KILL 22) Kill the current running function . 23) FLUSH [ASYNC|SYNC] 24) Delete all the libraries. 25) When called without the optional mode argument, the behavior is determined by the 26) lazyfree-lazy-user-flush configuration directive. Valid modes are: 27) * ASYNC: Asynchronously flush the libraries. 28) * SYNC: Synchronously flush the libraries. 29) DUMP 30) Return a serialized payload representing the current libraries, can be restored using FUNCTION RESTORE command 31) RESTORE <PAYLOAD> [FLUSH|APPEND|REPLACE] 32) Restore the libraries represented by the given payload, it is possible to give a restore policy to 33) control how to handle existing libraries (default APPEND): 34) * FLUSH: delete all existing libraries. 35) * APPEND: appends the restored libraries to the existing libraries. On collision, abort. 36) * REPLACE: appends the restored libraries to the existing libraries, On collision, replace the old 37) libraries with the new libraries (notice that even on this option there is a chance of failure 38) in case of functions name collision with another library). 39) HELP 40) Prints this help .
FUNCTION DELETE 删除指定的库
FUNCTION LIST 查看所有库和函数
1 2 3 4 5 6 7 8 9 10 11 12 127.0.0.1:6379> FUNCTION LIST 1) 1) "library_name" 2) "mylib" 3) "engine" 4) "LUA" 5) "functions" 6) 1) 1) "name" 2) "knockknock" 3) "description" 4) (nil) 5) "flags" 6) (empty array)
每个 Redis 函数都属于一个加载到 Redis 的库, 使用命令 FUNCTION LOAD 将库加载到数据库, 库必须以 shebang 语句开头 #!<engine name> name=<library name>
1 2 3 127.0.0.1:6379> FUNCTION LOAD "#!lua name=mylib\n" (error) ERR No functions registered
注册调用
注册
redis.register_function(name, callback, flags, description) 注册函数
name 注册的函数名
callback 注册的函数
flags
no-writes 标识脚本只能读取但不能写入
allow-oom 标识允许脚本在服务器内存不足(OOM)时执行
allow-stable
no-cluster 标识脚本在 Redis 集群模式下返回错误, 防止对集群中的节点执行脚本
allow-cross-slot-keys 允许脚本从多个 slot 访问密钥
description 函数描述
调用
FCALL function numkeys [key [key …]] [arg [arg …]] 调用注册的函数
FCALL_RO function numkeys [key [key …]] [arg [arg …]] 调用注册的只读函数
Redis 命令行注册调用 1 2 3 4 5 6 7 8 127.0.0.1:6379> FUNCTION LOAD "#!lua name=mylib\nredis.register_function{function_name='noop', callback=function() end, flags={ 'no-writes' }, description='Does nothing'}" 127.0.0.1:6379> FUNCTION LOAD "#!lua name=mylib\nredis.register_function('knockknock', function() return 'Who\\'s there?' end)" "mylib" 127.0.0.1:6379> FCALL knockknock 0 "Who's there?"
Lua 脚本文件注册调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #!lua name=mylib local function knockknock () return 'Who\'s there?' end local function my_hset (keys, args) local key = keys[1 ] local time = redis.call('TIME' )[1 ] return redis.call('HSET' ,key, '_last_modified_' , time , unpack (args)) end local function my_hgetall (keys, args) redis.setresp(3 ) local key = keys[1 ] local res = redis.call('HGETALL' , key) res['map' ]['_last_modified_' ] = nil return res end redis.register_function('knockknock' , knockknock) redis.register_function('my_hset' , my_hset) redis.register_function('my_hgetall' , my_hgetall) redis.register_function{ function_name='my_hgetall_ro' , callback=my_hgetall, flags={'no-writes' } description='read-only hash getall command' }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 [root@centos7 workspace]# cat mylib.lua | redis-cli -x FUNCTION LOAD REPLACE "mylib" 127.0.0.1:6379> FCALL my_hset 1 hash :zhang name "zhangsan" age 18 addr "beijing" (integer ) 4 127.0.0.1:6379> KEYS * 1) "hash:zhang" 2) "bit:zhang" 3) "xiaoming" 4) "name" 127.0.0.1:6379> FCALL my_hgetall 1 hash :zhang 1) "age" 2) "18" 3) "addr" 4) "beijing" 5) "name" 6) "zhangsan" 127.0.0.1:6379> FCALL my_hgetall_ro 1 hash :zhang 1) "age" 2) "18" 3) "addr" 4) "beijing" 5) "name" 6) "zhangsan" 127.0.0.1:6379> FCALL_RO my_hgetall 1 hash :zhang (error) ERR Can not execute a script with write flag using *_ro command . 127.0.0.1:6379> FCALL_RO my_hgetall_ro 1 hash :zhang 1) "age" 2) "18" 3) "addr" 4) "beijing" 5) "name" 6) "zhangsan"
Lua 脚本 Redis 允许在服务器上上传和执行 Lua 脚本, 脚本可以采用编程控制结构并在执行时使用大部分命令来访问数据库, 因为脚本在服务器中执行, 所以从脚本中读取和写入数据非常高效.
Redis 保证脚本的原子执行, 在执行脚本时, 所有服务器活动在其整个运行期间都被阻止.
Lua 允许在 Redis 中运行部分应用程序逻辑, 这样的脚本可以跨多个键执行条件更新, 可能以原子方式组合几种不同的数据类型
Lua 脚本由嵌入式执行引擎在 Redis 中执行, 尽管服务器执行它们, 但 EVAL 脚本被视为客户端应用程序的一部分, 这就是它们没有命名、版本化或持久化的原因. 因此, 如果所有脚本丢失, 应用程序可能需要随时重新加载
不能在 lua 脚本中声明全局变量, 只能使用 local 声明局部变量, 否则将执行报错
1 2 3 4 127.0.0.1:6379> EVAL "name=10; return name" 0 (error) ERR user_script:1: Attempt to modify a readonly table script: be32aff3876f2170bc8f0e19998edc94924341e1, on @user_script:1. 127.0.0.1:6379> EVAL "local name=10; return name" 0 (integer ) 10
脚本命令
脚本参数化 为了确保在独立部署和集群部署中正确执行脚本, 脚本访问的所有键名都必须作为输入键参数显式提供
EVAL script numkeys key [key …] arg [arg …] 执行 Lua 脚本
script 要执行的脚本语句
numkeys 指定后续的参数有几个 key
key 要操作的键的数量, 在 Lua 脚本中通过 KEYS[1], KEYS[2] 获取
arg 参数, 在 Lua 脚本中通过 ARGV[1], ARGV[2] 获取
EVALSHA sha1 numkeys key [key …] arg [arg …] 使用缓存 Lua 脚本的 sha 执行脚本(SCRIPT LOAD 命令缓存脚本)
EVAL_RO script numkeys [key [key …]] [arg [arg …]] 只读版本的 EVAL 命令, Redis 7.0 支持
EVALSHA_RO sha1 numkeys [key [key …]] [arg [arg …]] 只读版本的 EVALSHA 命令, Redis 7.0 支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 127.0.0.1:6379> EVAL "return 10" 0 (integer ) 10 127.0.0.1:6379> EVAL "return ARGV[1]" 0 100 "100" 127.0.0.1:6379> EVAL "return {ARGV[1], ARGV[2]}" 0 100 101 1) "100" 2) "101" 127.0.0.1:6379> EVAL "return {KEYS[1], KEYS[2], ARGV[1], ARGV[2], ARGV[3]}" 2 name age v1 v2 1) "name" 2) "age" 3) "v1" 4) "v2" 127.0.0.1:6379> EVAL "return {1, 2, { 3, 'hello world' } }" 0 1) (integer ) 1 2) (integer ) 2 3) 1) (integer ) 3 2) "hello world"
每次执行脚本都需要重新加载一遍脚本代码, 浪费资源, 使用 脚本缓存
lua 脚本中的 redis 实例 redis 单例实例, 使脚本能够与运行它的 Redis 服务器进行交互
全局变量
KEYS 获取脚本声明的键参数, 下标从 1 开始
ARGV 获取脚本声明的键参数剩余的参数, 下标从 1 开始
redis.call(command [, arg…]) 执行 redis 命令并返回结果, 如果遇到错误时直接返回给客户端
redis.pcall(command [, arg…]) 执行 redis 命令并返回结果, 如果遇到错误时将返回给脚本的执行上下文
1 2 3 4 5 6 7 8 9 10 11 12 13 127.0.0.1:6379> GET name "hello world" 127.0.0.1:6379> EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 name "hello redis" OK 127.0.0.1:6379> GET name "hello redis" 127.0.0.1:6379> get user:100:score "60" 127.0.0.1:6379> EVAL "if redis.call('EXISTS', KEYS[1]) == 1 then return redis.call('INCRBY', KEYS[1], ARGV[1]) else return nil end" 1 user:100:score 10 (integer ) 70 127.0.0.1:6379> get user:100:score "70"
分布式接口限流,每秒钟限制接口的最大请求次数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 local key = KEYS[1 ]local limit = tonumber (ARGV[1 ])local expire_time = tonumber (ARGV[2 ])local current = redis.call('GET' , key)if current and tonumber (current) >= limit then return 0 end current = redis.call('INCR' , key) if tonumber (current) == 1 then redis.call('EXPIRE' , key, expire_time) end return 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@centos7 workspace]# cat rate_limit.lua | redis-cli -x SCRIPT LOAD "688faba01b26f6e54969ba7b9d0ce340b4c298c0" 127.0.0.1:6379> SCRIPT EXISTS 688faba01b26f6e54969ba7b9d0ce340b4c298c0 1) (integer ) 1 127.0.0.1:6379> EVALSHA 688faba01b26f6e54969ba7b9d0ce340b4c298c0 1 rate:limit :192.168.1.1 5 100 (integer ) 1 127.0.0.1:6379> EVALSHA 688faba01b26f6e54969ba7b9d0ce340b4c298c0 1 rate:limit :192.168.1.1 5 100 (integer ) 1 127.0.0.1:6379> EVALSHA 688faba01b26f6e54969ba7b9d0ce340b4c298c0 1 rate:limit :192.168.1.1 5 100 (integer ) 1 127.0.0.1:6379> EVALSHA 688faba01b26f6e54969ba7b9d0ce340b4c298c0 1 rate:limit :192.168.1.1 5 100 (integer ) 1 127.0.0.1:6379> EVALSHA 688faba01b26f6e54969ba7b9d0ce340b4c298c0 1 rate:limit :192.168.1.1 5 100 (integer ) 1 127.0.0.1:6379> EVALSHA 688faba01b26f6e54969ba7b9d0ce340b4c298c0 1 rate:limit :192.168.1.1 5 100 (integer ) 0 127.0.0.1:6379> GET rate:limit :192.168.1.1 "5" 127.0.0.1:6379> TTL rate:limit :192.168.1.1 (integer ) 66
redis.error_reply(x) 辅助函数, 返回一个错误信息
redis.status_reply(x) 辅助函数, 可以修改 Redis 命令的默认返回值 OK
1 2 3 4 5 6 7 8 9 127.0.0.1:6379> EVAL "return redis.error_reply('ERR This is a special error')" 0 (error) ERR This is a special error 127.0.0.1:6379> EVAL "return { ok = 'TICK' }" 0 "TICK" 127.0.0.1:6379> EVAL "return redis.status_reply('TOCK')" 0 "TOCK"
redis.sha1hex(x) 返回单个字符串参数的 SHA1 十六进制摘要信息
redis.log(level, message) 写入 Redis 服务器日志
redis.LOG_DEBUG 日志级别
redis.LOG_VERBOSE 日志级别
redis.LOG_NOTICE 日志级别
redis.LOG_WARNING 日志级别
1 2 3 127.0.0.1:6379> EVAL "return redis.sha1hex('')" 0 "da39a3ee5e6b40d3255bfef95601890afd80709" 127.0.0.1:6379> EVAL "return redis.log(redis.LOG_WARNING, 'Something is terribly wrong')" 0
redis.setresp(x) 设置执行脚本和服务器之间的请求应答协议, 默认值 2. Redis 6.0 支持
redis.set_repl(x)
redis.replicate_commands()
redis.breakpoint() 在使用 Redis Lua 调试器时触发断点
redis.debug(x) 在 Redis Lua 调试器控制台中打印其参数
redis.acl_check_cmd(command [,arg…]) 用于检查运行脚本的当前用户是否具有使用给定参数执行给定命令的 ACL 权限, 返回值布尔类型. Redis 7.0 支持
redis.register_function(name, callback, flags, description) Redis 7.0 支持
redis.REDIS_VERSION 以字符串形式返回当前 Redis 服务器版本, 格式 MM.mm.PP. Redis 7.0 支持
redis.REDIS_VERSION_NUM 以数字形式返回当前 Redis 服务器版本, 格式为十进制值. Redis 7.0 支持
1 2 3 4 127.0.0.1:6379> EVAL "return redis.REDIS_VERSION" 0 "7.0.5" 127.0.0.1:6379> EVAL "return redis.REDIS_VERSION_NUM" 0 (integer ) 458757
脚本缓存 存储在服务器的脚本专用缓存中, 缓存内容由脚本的 SHA1 摘要作为缓存中的唯一标识
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 127.0.0.1:6379> SCRIPT help 1) SCRIPT <subcommand> [<arg> [value] [opt] ...]. Subcommands are: 2) DEBUG (YES|SYNC|NO) 3) Set the debug mode for subsequent scripts executed. 4) EXISTS <sha1> [<sha1> ...] 5) Return information about the existence of the scripts in the script cache. 6) FLUSH [ASYNC|SYNC] 7) Flush the Lua scripts cache. Very dangerous on replicas. 8) When called without the optional mode argument, the behavior is determined by the 9) lazyfree-lazy-user-flush configuration directive. Valid modes are: 10) * ASYNC: Asynchronously flush the scripts cache. 11) * SYNC: Synchronously flush the scripts cache. 12) KILL 13) Kill the currently executing Lua script. 14) LOAD <script> 15) Load a script into the scripts cache without executing it. 16) HELP 17) Prints this help .
SCRIPT FLUSH 从脚本缓存中移除所有脚本, 返回 ok
SCRIPT KILL 杀死系统当前正在运行的 Lua 脚本(又名慢脚本)
SCRIPT DEBUG 设置脚本内执行时的模式
SCRIPT LOAD <script> 将脚本加载到服务器缓存中, 并不立即执行
1 2 3 4 5 6 127.0.0.1:6379> SCRIPT LOAD "return redis.call('GET', KEYS[1])" "d3c21d0c2b9ca22f82737626a27bcaf5d288f99f" 127.0.0.1:6379> EVALSHA d3c21d0c2b9ca22f82737626a27bcaf5d288f99f 1 name "hello redis"
SCRIPT EXISTS <script> [script …] 查看缓存中是否存在 sha 对应的脚本, 1 表示存在, 0 表示不存在
1 2 3 4 127.0.0.1:6379> SCRIPT EXISTS d3c21d0c2b9ca22f82737626a27bcaf5d288f99f 1) (integer ) 1 127.0.0.1:6379> SCRIPT EXISTS d3c21d0c2b9ca22f82737626a27bcaf5d288f99g 1) (integer ) 0
脚本复制 一般在集群部署环境下, Redis 确保脚本执行的所有写操作也被发送到副本以保持一致性, 脚本复制有两种概念
逐字复制: master 将脚本的源代码发送到 slave, 然后 slave 执行脚本并写入效果.
在短脚本生成许多命令的情况下, 可以节省资源, 但意味着 slave 会重做 master 完成的相同工作而浪费资源
效果复制: 仅复制脚本的数据修改命令, slave 然后执行命令而不执行任何脚本, 从 redis 5.0 开始为默认模式
脚本效果复制 —— 复制命令
在这种模式下,在执行 Lua 脚本的同时, Redis 会收集 Lua 脚本引擎执行的所有实际修改数据集的命令, 当脚本执行完成时, 脚本生成的命令序列被包装到一个 事务 中并发送到副本和 AOF
Lua API
使用未声明为本地的变量和函数会引起 Redis 的报错
沙盒执行上下文不支持使用导入的 Lua 模块
数据类型转换 RESP2
RESP2 -> Lua
RESP2 整数 -> Lua 数
RESP2 批量字符串 -> Lua 字符串
RESP2 数组 -> Lua 表(可能嵌套额其他 Redis 数据类型)
RESP2 状态 -> 包含状态字符串的单个 ok 字段的 Lua 表
RESP2 错误 -> 包含错误字符串的单个 err 字段的 Lua 表
RESP 空批量|空多批量 -> Lua false 布尔类型
Lua -> RESP2
Lua 数字 -> RESP2 整数(数字转为整数, 舍去小数部分)
Lua 字符串 -> RESP 批量字符串
Lua 表(索引, 非关联数组) -> RESP2 数组(在表中遇到第一个 nil 时截断)
带有单个 ok 字段的 Lua 表 -> RESP2 状态
带有单个 err 字段的 Lua 表 -> RESP2 错误
Lua false 布尔类型 -> RESP2 空批量
Lua true 布尔类型 -> RESP2 整数 1
1 2 3 4 5 6 7 8 9 10 11 12 127.0.0.1:6379> EVAL "return {1, 2, {3, 'hello world'}, 'bar'}" 0 1) (integer ) 1 2) (integer ) 2 3) 1) (integer ) 3 2) "hello world" 4) "bar" 127.0.0.1:6379> EVAL "return {1, 2, 3.33, somekey = 'somevalue', 'foo', nil, 'bar'}" 0 1) (integer ) 1 2) (integer ) 2 3) (integer ) 3 4) "foo"
RESP3
一旦 Redis 的回复采用 RESP3 协议, 所有 RESP2 到 Lua 的转换规则都适用, 并添加以下内容
RESP3 -> Lua
RESP3 map -> 带有单个映射字段的 Lua 表, 其中包含表示映射字段和值的 Lua 表
RESP3 set -> 具有单个集合字段的 Lua 表
RESP3 null -> Lua nil
RESP3 true -> Lua true 布尔类型
RESP3 false -> Lua false 布尔类型
RESP3 浮点数 -> 带有一个浮点数字段的 Lua 表
RESP3 大数字 -> 带有单个大数字字段的 Lua 表. Redis 7.0 支持
RESP3 逐句逐字字符串 -> Lua 表, 其中包含单个 verbatim_string 字段的 Lua 表, 其中包含两个字段 string 和 format,分别表示 verbatim string 和它的格式. Redis 7.0 支持
Lua -> RESP3
Lua Boolean -> RESP3 Boolean
将单个映射字段设置为关联 Lua 表的 Lua 表 -> RESP3 map
将单个集合字段设置为关联 Lua 表的 Lua 表 -> RESP3 set, 值可以为任何值, 都会被丢弃
带有单个浮点数字段的 Lua 表到关联的 Lua 表 -> RESP3 浮点数
Lua nil -> RESP3 null
外部库 struct
struct.pack(x) 返回一个结构编码的字符串, 接收一个结构格式字符串作为第一个参数, 后面是要编码的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 127.0.0.1:6379> EVAL "return struct.pack('bb', 1, 2)" 0 "\x01\x02" 127.0.0.1:6379> EVAL "return struct.pack('BB', 1, 2)" 0 "\x01\x02" 127.0.0.1:6379> EVAL "return struct.pack('B', 1, 2)" 0 "\x01" 127.0.0.1:6379> EVAL "return struct.pack('xB', 1, 2)" 0 "\x00\x01" 127.0.0.1:6379> EVAL "return struct.pack('xBx', 1, 2)" 0 "\x00\x01\x00" 127.0.0.1:6379> EVAL "return struct.pack('xBxx', 1, 2)" 0 "\x00\x01\x00\x00" 127.0.0.1:6379> EVAL "return struct.pack('xBxxH', 1, 2)" 0 "\x00\x01\x00\x00\x02\x00" 127.0.0.1:6379> EVAL "return struct.pack('BxxH', 1, 2)" 0 "\x01\x00\x00\x02\x00" 127.0.0.1:6379> EVAL "return struct.pack('Bxxh', 1, 2)" 0 "\x01\x00\x00\x02\x00" 127.0.0.1:6379> EVAL "return struct.pack('BxxB', 1, 2)" 0 "\x01\x00\x00\x02" 127.0.0.1:6379> EVAL "return struct.pack('Bxxl', 1, 2)" 0 "\x01\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00"
struct.unpack(x) 返回结构的解码值, 接收一个结构格式字符串作为第一个参数, 然后是编码结构的字符串
1 2 3 4 5 6 7 8 127.0.0.1:6379> EVAL "return {struct.unpack('BxxH', ARGV[1])}" 0 "\x01\x00\x00\x02\x00" 1) (integer ) 1 2) (integer ) 2 3) (integer ) 6 127.0.0.1:6379> EVAL "return {struct.unpack('BB', ARGV[1])}" 0 "\x01\x02" 1) (integer ) 1 2) (integer ) 2 3) (integer )
struct.size(x) 返回结构的大小(以字节为单位), 接收结构格式字符串作为唯一参数
1 2 3 4 5 6 7 8 9 10 11 12 127.0.0.1:6379> EVAL "return struct.size('b')" 0 (integer ) 1 127.0.0.1:6379> EVAL "return struct.size('B')" 0 (integer ) 1 127.0.0.1:6379> EVAL "return struct.size('h')" 0 (integer ) 2 127.0.0.1:6379> EVAL "return struct.size('H')" 0 (integer ) 2 127.0.0.1:6379> EVAL "return struct.size('l')" 0 (integer ) 8 127.0.0.1:6379> EVAL "return struct.size('L')" 0 (integer ) 8
结构格式
> 大端
< 小端
![num] 结盟
x 填充
b/B 有/无符号字节
h/H 有/无符号短
l/L 有/无符号长
T 大小
i/In 大小为 n 的有/无符号整数(默认为 int 的大小)
cn n 个字符的序列, 打包时, n ==0 表示整个字符串, 解包时, n == 0 表示使用先前读取的数字作为字符串的长度
s 零终止字符串
f float
d double
(space) 忽略
cjson cjson 库提供了来自 Lua 的快速 JSON 编码和解码
cjson.encode(x) 返回作为其参数提供的 Lua 数据类型的 JSON 编码字符串
cjson.decode(x) 从作为其参数提供的 JSON 编码字符串返回 Lua 数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 127.0.0.1:6379> EVAL "return cjson.encode({ 1, 2, 'foo', 'bar' })" 0 "[1,2,\"foo\",\"bar\"]" 127.0.0.1:6379> EVAL "return cjson.encode({ 1, 2, 3.33, 'foo', 'bar' })" 0 "[1,2,3.33,\"foo\",\"bar\"]" 127.0.0.1:6379> EVAL "return cjson.encode({ ['foo'] = 'bar' })" 0 "{\"foo\":\"bar\"}" 127.0.0.1:6379> EVAL "return cjson.encode({ ['foo'] = 'bar', ['fov'] = 'baz' })" 0 "{\"fov\":\"baz\",\"foo\":\"bar\"}" 127.0.0.1:6379> EVAL "return cjson.decode(ARGV[1])[4]" 0 "[1,2,3.33,\"foo\",\"bar\"]" "foo" 127.0.0.1:6379> EVAL "return cjson.decode(ARGV[1])['fov']" 0 "{\"fov\":\"baz\",\"foo\":\"bar\"}" "baz"
cmsgpack cmsgpack 库提供了来自 Lua 的快速 MessagePack 编码和解码
cmsgpack.pack(x) 返回作为参数给出的 Lua 数据类型的压缩字符串编码
cmsgpack.unpack(x) 返回解码其输入字符串参数的解压缩值
1 2 3 4 5 6 7 127.0.0.1:6379> EVAL "return cmsgpack.pack({'foo', 'bar', 'baz', 'hello'})" 0 "\x94\xa3foo\xa3bar\xa3baz\xa5hello" 127.0.0.1:6379> EVAL "return cmsgpack.unpack(ARGV[1])" 0 "\x94\xa3foo\xa3bar\xa3baz\xa5hello" 1) "foo" 2) "bar" 3) "baz" 4) "hello"
bit bit 提供对数字的按位运算
bit.tobit(x)` 将数字格式化为位运算的数值范围并返回
bit.tohex(x [, n]) 将第一个参数转换为十六进制并返回, 第二个参数的绝对值控制返回值的数量
1 2 3 4 5 127.0.0.1:6379> EVAL "return bit.tobit(1)" 0 (integer ) 1 127.0.0.1:6379> EVAL "return bit.tohex(422342)" 0 "000671cd"
bit.bnot(x) 返回其参数的按位非运算
bit.bor(x1 [, x2…]) 返回其所有参数的按位或运算
bit.band(x1 [, x2…]) 返回其所有参数的按位与运算
bit.bxor(x1 [, x2…]) 返回其所有参数的按位异或运算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 127.0.0.1:6379> EVAL "return bit.bnot(12)" 0 (integer ) -13 127.0.0.1:6379> EVAL "return bit.bnot(32)" 0 (integer ) -33 127.0.0.1:6379> EVAL "return bit.bor(1,2,4,8,16,32,64)" 0 (integer ) 127 127.0.0.1:6379> EVAL "return bit.band(12, 74)" 0 (integer ) 8 127.0.0.1:6379> EVAL "return bit.bxor(12, 74)" 0 (integer ) 70
bit.lshift(x, n) 返回第一个参数按位左移 n 位的结果
bit.rshift(x, n) 返回第一个参数按位右移 n 位的结果
bit.arshift(x, n) 返回第一个参数按位算术右移 n 位的结果, 不改变符号位的移位操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 127.0.0.1:6379> EVAL "return bit.lshift(1, 3)" 0 (integer ) 8 127.0.0.1:6379> EVAL "return bit.lshift(2, 1)" 0 (integer ) 4 127.0.0.1:6379> EVAL "return bit.lshift(3, 2)" 0 (integer ) 12 127.0.0.1:6379> EVAL "return bit.rshift(1, 1)" 0 (integer ) 0 127.0.0.1:6379> EVAL "return bit.rshift(2, 1)" 0 (integer ) 1 127.0.0.1:6379> EVAL "return bit.rshift(3, 1)" 0 (integer ) 1 127.0.0.1:6379> EVAL "return bit.arshift(10, 1)" 0 (integer ) 5 127.0.0.1:6379> EVAL "return bit.arshift(128, 1)" 0 (integer ) 64 127.0.0.1:6379> EVAL "return bit.rshift(-12, 1)" 0 (integer ) 2147483642 127.0.0.1:6379> EVAL "return bit.arshift(-12, 1)" 0 (integer ) -6
bit.rol(x, n) 按第二个参数给定的位数返回其第一个参数的按位左旋转
bit.ror(x, n) 按第二个参数给定的位数返回其第一个参数的按位右旋转
1 2 3 4 5 6 7 8 9 10 11 12 13 127.0.0.1:6379> EVAL "return bit.rol(12, 1)" 0 (integer ) 24 127.0.0.1:6379> EVAL "return bit.rol(12, 2)" 0 (integer ) 48 127.0.0.1:6379> EVAL "return bit.rol(12, 6)" 0 (integer ) 768 127.0.0.1:6379> EVAL "return bit.ror(12, 1)" 0 (integer ) 6 127.0.0.1:6379> EVAL "return bit.ror(12, 4)" 0 (integer ) -1073741824 127.0.0.1:6379> EVAL "return bit.ror(12, 6)" 0 (integer ) 805306368
bit.bswap(x) 交换其参数的字节并返回它, 可用于将小端 32 位数字转换位大端 32 位数字, 反之亦然
1 2 3 4 5 6 127.0.0.1:6379> EVAL "return bit.bswap(1)" 0 (integer ) 16777216 127.0.0.1:6379> EVAL "return bit.bswap(2)" 0 (integer ) 33554432 127.0.0.1:6379> EVAL "return bit.bswap(12)" 0 (integer ) 201326592