3.4.1 事务

事务命令:

  • multi
  • exec
  • discard

1. 命令错误

例如语法错误, 整个事务无法执行:

image.png

2. 运行时错误

没有问题的命令执行成功, 有问题的命令执行失败, 无法回滚.

image.png
image.png

有些应用场景需要在事务之前, 确保事务中的 key 没有被其他客户端修改过, 才执行事务, 否则不执行 (类似乐观锁)。Redis 提供了 watch 命令来解决这类问题:

image.png

这时候 exec 会返回 nil, 该事务没有执行:

image.png
image.png

3.4.2 Lua 用法简述

1. 数据类型及其逻辑处理

  • booleans
  • numbers
  • strings
  • tables

(1) 字符串

定义:

  1. local strings val = "world"
-- 结果是 "world"
print(hello)

(2) 数组
  • 下标从1开始
local tables myArray = {"redis", "jedis", true, 88.0}
--true
print(myArray[3])

(a) for
local int sum = 0
for i = 1, 100
do
    sum = sum + i
end
-- 输出结果为 5050
print(sum)
  • : 获取长度

for i = 1, #myArray
do
    print(myArray[i])
end
  • 带索引的遍历
for index,value in ipairs(myArray)
do
    print(index)
    print(value)
end

(b) while
local int sum = 0
local int i = 0
while i <= 100
do
    sum = sum +i
    i = i + 1
end
-- 输出结果为 5050
print(sum)

(c) if else
local tables myArray = {"redis", "jedis", true, 88.0}
for i = 1, #myArray
do
    if myArray[i] == "jedis"
    then
        print("true")
        break
    else
        --do nothing
    end
end

(3) 哈希
local tables user_1 = {age = 28, name = "tome"}
--user_1 age is 28
print("user_1 age is " .. user_1["age"])

遍历:

for key,value in pairs(user_1)
do print(key .. value)
end

2. 函数定义

function funcName()
...
end
contact 函数将两个字符串拼接:
function contact(str1, str2)
    return str1 .. str2
end
--"hello world"
print(contact("hello ", "world"))

3.4.3 Redis 与 Lua

1. 在 Redis 中使用 Lua

有两种方法.

(1) eval
eval 脚本内容 key个数 key列表 参数列表

例子:

127.0.0.1:6379> eval 'return "hello " .. KEYS[1] .. ARGV[1]' 1 redis world
"hello redisworld"

eval 命令和 —eval 参数本质是一样的, 客户端如果想执行 Lua 脚本, 首先在客户端编写好 Lua 脚本代码, 然后把脚本作为字符串发送给服务端, 服务端会将执行结果返回给客户端

image.png

(2) evalsha

首先要将 Lua 脚本加载到 Redis 服务端, 得到该脚本的 SHA1 校验和, evalsha 命令使用 SHA1 作为参数可以直接执行对应 Lua 脚本, 避免每次发送 Lua 脚本的开销。

image.png

加载脚本:

$ redis-cli script load "$(cat lua_get.lua)"
"7413dc2440db1fea7c0a0bde841fa68eefaf149c"

执行脚本:

evalsha 脚本SHA1值 key个数 key列表 参数列表
127.0.0.1:6379> evalsha 7413dc2440db1fea7c0a0bde841fa68eefaf149c 1 redis world
"hello redisworld"

2. Lua 的 Redis API

Lua 可以使用 redis.call 函数实现对 Redis 的访问, 例如下面代码是 Lua 使用 redis.call 调用了 Redis 的 set 和 get 操作:

redis.call("set", "hello", "world")
redis.call("get", "hello")

放在 Redis 的执行效果如下:

127.0.0.1:6379> eval 'return redis.call("get", KEYS[1])' 1 hello
"world"

Lua 还可以使用 redis.pcall 函数实现对 Redis 的调用, redis.call 和 redis.pcall 的不同在于, 如果 redis.call 执行失败, 那么脚本执行结束会直接返回错误, 而 redis.pcall 会忽略错误继续执行脚本

  • Lua 可以使用 redis.log 函数将 Lua 脚本的日志输出到 Redis 的日志文件中, 但是一定要控制日志级别
  • Redis3.2 提供了 Lua Script Debugger 功能用来调试复杂的 Lua 脚本, 具体可以参考: http://redis.io/topics/ldb

3.4.4 案例

使用 Lua 脚本的好处:

  • 原子的
  • 定制命令
  • 将多条命令打包, 减少网络开销

3.4.5 Redis 如何管理 Lua 脚本

(1) script load

加载脚本到内存.

script load script

(2) script exists

判断 sha1 是否已经加载到 Redis 内存中.

scripts exists sha1 [sha1 ... ]

scripts 拼写错误?

127.0.0.1:6379> script exists a5260dd66ce02462c5b5231c727b3f7772c0bcc5
1) (integer) 1

(3) script flush

清除已加载的 Lua 脚本.

script flush

(4) script kill

杀掉正在执行的 Lua 脚本.

script kill
  • lua-time-limit 参数: 默认 5s, 超时后脚本不会被 kill, 会向其它客户端返回 busy 信号
    • Busy Redis is busy running a script
  • 如果脚本在执行写操作, 那么 kill 不会生效