浅谈Redis事务机制

前言:redis是Nosql数据库中使用较为广泛的非关系型内存数据库,常用于数据缓存,共享资源,分布式锁等。Redis使用了单线程架构和I/O多路复用模型来实现高性能的内存数据库服务。

Multi
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。

  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。

  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

  • 一个事务从开始到执行会经历以下三个阶段:

  • 开始事务。

  • 命令入队。

  • 执行事务。

示例:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr num
QUEUED
127.0.0.1:6379> incr num
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 2

Lua脚本
Redis 2.6.0及以后版本支持Lua脚本语言,可以将命令打包执行,实现原子性操作。
Redis 以及Spring等提供的API接口都有一个EVAL命令,用于执行lua脚本。
具体语法:

EVAL  "script"  num  args...

其中字符串script及为lua脚本,num为需要传递的参数,args为参数列表,如果不需要传递参数,可写为:

EVAL  "script"  0

在lua中,对于Redis的操作可以使用命令redis.call()来调用,例如需要返回key为zhangsan:phone的value值,即可输入:

127.0.0.1:6379> EVAL "return redis.call('get',KEYS[1])" 1 zhangsan:phone
"13365378654"

其中call() 函数的第一个参数为命令,第二个为key,第三个可以为value,Redis中,KEYS和ARGV为lua内置全局变量。例如:

127.0.0.1:6379> EVAL "return redis.call('set',KEYS[1],ARGV[1])" 1 zhangsan:phone 17751033130
OK
127.0.0.1:6379> get zhangsan:phone
"17751033130"

因为EVAL命令执行脚本是原子性的,所以有时候会用来做些校验,库存扣减等操作,将命令打包处理,但建议脚本不能过长,否则影响其他Redis命令的执行。
另外,Redis提供了脚本的sha1摘要缓存,这样后续如果script不变的话,只需要调用可以通过 SCRIPT LOAD 命令进行,而不需要重复传递过长的脚本内容。

redis Evalsha 命令基本语法如下:

redis 127.0.0.1:6379> EVALSHA sha1 numkeys key [key ...] arg [arg ...] 

参数说明:

  • sha1 : 通过 SCRIPT LOAD 生成的 sha1 校验码。
  • numkeys: 用于指定键名参数的个数。
  • key [key …]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在
    Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
  • arg [arg …]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1]、 ARGV[2] ,诸如此类)。

Redis脚本常用命令:

EVAL script numkeys key [key ...] arg [arg ...]       ## 执行 Lua 脚本。

EVALSHA sha1 numkeys key [key ...] arg [arg ...]       ## 执行 Lua 脚本。

SCRIPT EXISTS script [script ...]       ## 查看指定的脚本是否已经被保存在缓存当中。

SCRIPT FLUSH      ## 从脚本缓存中移除所有脚本。

SCRIPT KILL       ## 杀死当前正在运行的 Lua 脚本。

SCRIPT LOAD script        ## 将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。

详情可参考:http://www.runoob.com/redis/redis-scripting.html

猜你喜欢

转载自blog.csdn.net/weixin_38422258/article/details/84072835
今日推荐