redis编写lua脚本

1.4k 词

由于redis并没有类似mysql或者mongo的乐观锁机制,并发控制成了一个棘手的问题
这块是可以用redis的watch来做,但是如果能实现乐观锁,那就非常方便了

redis使用lua的基础语法

1
2
3
4
5
> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

lua可以调用的redis函数

1
2
redis.call()
redis.pcall()

简单例子

1
2
3
4
5
6
7
8
9
local version = redis.call("INCR", KEYS[2])
if version == 1 then
redis.call("EXPIRE", KEYS[2], ARGV[3])
end;

if tostring(version) ~= ARGV[2] then
return {-1, version}
end;
return redis.call("SET", KEYS[1], ARGV[1])

运行方式:

1
redis-cli EVAL "$(cat ./helloWorld.lua)" 3 mykey version1 expire 1 1 100

redis并发控制

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

func (db *RedisDB) (key string, val interface{}, versionKey string, versionVal int, versionExpire int) (err error) {
if versionExpire == 0 {
versionExpire = 100
}
storeValue, err := db.getStoreValue(val)
if err != nil {
return
}

conn := db.Option.Pool.Get()
defer conn.Close()
if err = conn.Err(); err != nil {
return err
}

script := `
local version = redis.call("INCR", KEYS[2])
if version == 1 then
redis.call("EXPIRE", KEYS[2], ARGV[3])
end;

if tostring(version) ~= ARGV[2] then
return {-1, version}
end;
return redis.call("SET", KEYS[1], ARGV[1])
`
getScript := redis.NewScript(3, script)
res, err := redis.String(getScript.Do(conn, key, versionKey, "expire", storeValue, versionVal, versionExpire))

if err != nil {
utils.Error(err)
return
}
if res == "-1" {
//err = ErrConflict
}redis
return
}

相关文档

Redis 命令参考 · lua 脚本