redis学习(九) redis事务和redis脚本的比较

如果想要实现一组命令原子性的执行,一种方法是使用事务,一种方法使用redis脚本,可以对比下两种方式的区别

Redis事务回顾

事务命令:MULTI, EXEC, DIDCARD ,WATCH ,UNWATCH

使用: MULTI : 开启一个事务,并会生成一个任务队列,客户端发送的指令都会放在任务队列中,总是返回OK

         EXEC: 执行任务队列中的命令,成功返回OK,失败返回nil

         DIDCARD: 清空任务队列中的所有命令,并取消事务的执行

          WATCH:监控一个或者多个键,如果键被修改了, 就会阻止后面一个事务的执行,但是不会阻止其他客户端对键的修改(不通过事务),所有的键的监控的取消在exec命令执行后,也可以通过执行UNWATCH命令取消对键的监控,客户端断开连接也会,取消监控

127.0.0.1:6379> get zhouy 
"2222222222"
127.0.0.1:6379> get zhouy
"2222222222"
127.0.0.1:6379> watch zhouy 
OK
127.0.0.1:6379> set zhouy 1993
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set zhouy 1990
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get zhouy
"1993"
127.0.0.1:6379> 

事务没有执行成功,key的值还是1993,而且watch一个键后,对这个键修改,如果另一个客户端不通过事务修改这个键,也是能够执行成功的,这点需要注意

127.0.0.1:6379> watch zhouy
OK
127.0.0.1:6379> set zhouy 11111
OK
127.0.0.1:6379> get zhouy
"9999"

事务的特性: 一致性,原子性(事务中的一组命令,要么全部成功,要么全部失败),持久性,隔离性(多个事务提交不会相互影响,由于redis是单线程的,提交的事务按顺序执行)

使用watch的场景: 一般在多线程环境下,对一个键的更改产生了竞态错误

$value = get key

$value = $value+1;

set key $value 

此时,可以使用watch命令,实现cas的机制。

watch key 

$value = get key 

$value = $value +1

multi

set key $value 

exec

这样,每个连接执行exec命令前,如果key被其他客户端修改了,当前连接执行exec就会失败,可以根据返回值判断是否设置成功,没有成功,再次执行。

事务执行错误的场景

事务中的错误:


  1、错误发生在调用CALL之前,例如语法错误(参数数量错误,命令拼写错误....)。

  2、错误发生在调用CALL之后,参数类型错误,例如对一个string类型的key使用list操作。

  对于第一个错误,客户端可以很容易的避免:向队列中每添加一个命令都会返回“QUEUE”字符串,表示添加成功,否则返回一个错误;客户端可以取消事务的执行。

    从版本2.6.5开始,服务端会记录这种错误,在调用EXEC命令时,会拒绝这次事务的执行,并且自动取消事务。

  对于第二类错误,即使发生了错误,Redis不会取消事务的执行。执行结果以会返回给客户端,由客户端决定。

Redis不支持事务回滚,执行后产生的影响需要程序员,自己去维护

Redis取消事务  DIDCARD命令终止事务

Redis Lua脚本的特性

和redis事务类似,Lua脚本的功能是将redis命令打包为一个Lua脚本,在服务器端原子执行。和redis的事务特性相比,减少了网路开销,一次提交打包的命令;原子性执行避免出现watch的竞态条件;同时可以被复用,存储之后继续使用

在脚本中调用redis命令

call 和 pcall   -- redis.call ('get',key)  ,自动将Redis的数据类型转为lua数据类型

redis学习(九) redis事务和redis脚本的比较_第1张图片

两者的区别在于:call如果执行失败,停止运行,并且返回一个脚本错误,pcall如果执行错误,会记录错误,并继续执行。

从脚本中返回值

在脚本中使用return将脚本的执行结果返回给客户端,如果没有执行return语句默认返回nil,在此过程中也会自动将lua类型转为redis数据类型

EVAL命令执行脚本

执行格式: EVAL 脚本内容   key参数的数量  [key...]  [arg...]

127.0.0.1:6379> eval "return redis.call('set',KEYS[1],argv[1]) "  1 testkey helllo
(error) ERR Error running script (call to f_187b569d90860e5f8c7eda2a3b9139e91f659414): @enable_strict_lua:15: user_script:1: Script attempted to access nonexistent global variable 'argv' 
127.0.0.1:6379> eval "return redis.call('set',KEYS[1],ARGV[1]) "  1 testkey helllo
OK

注意到,KEYS ,ARGV这两个全局变量是需要大写的,(Lua语言是区别大小写的),写Lua脚本的时候需要注意

当脚本不需要任何参数的时候,不能省略这个参数(keynumber需要设置为0)

jedis中如何调用

tring script ="local result={} " +   
                " for i,v in ipairs(KEYS) do " +   
                " result[i] = redis.call('get',v) " +   
                " end " +   
                " return result ";  
  
Jedis jedis = new Jedis(ip,port);  
  
jedis.eval(script,keyCount,String … params);  

注意的是,Lua脚本中不能出现耗时比较长的操作,不能出现死循环,否则redis将不接受其他命令,执行去停止脚本运行了

,redis提供了lua-time-limit参数现在脚本最长运行时间,默认是5秒

其他命令:

1. 将脚本加入缓存  SCRIPT LOAD 

每次执行eval命令时,redis会将脚本的sha1摘要加入到脚本缓存中,以便下次客户端可以使用evalsha命令调用脚本,如果只是加入缓存,使用SCRIPT LOAD

2.  是否缓存脚本  SCRIPT EXISTS +脚本摘要 1:是  0:否

3. SCRIPT FLUSH 清空脚本缓存

4. SCRIPT KILL 终止正在执行的脚本 ,如果当前执行的脚本,修改了redis的数据(修改,删除,更新key),这个命令就不会停止脚本运行,防止脚本只运行了一部分,此时只能使用 SHUTDOWN NOSAVE强制停止.

你可能感兴趣的:(redis,Redis学习笔记)