【Redis】五、Redis事务处理

MULTI 、EXEC 、DISCARD 和WATCH 是 Redis 事务的基础

1.MULTI  命令用于开启一个事务,它老是返回 OK 。
MULTI 执行以后,客户端能够继续向服务器发送任意多条命令,这些命令不会当即被执行,而是被放到一个队列中javascript

2.EXEC 命令被调用时,全部队列中的命令才会被执行。css

 

+++++++++++命令 +++++++++++java

redis 192.168.1.53:6379> multi OK redis 192.168.1.53:6379> incr foo QUEUED redis 192.168.1.53:6379> set t1 1 QUEUED redis 192.168.1.53:6379> exec 1) (integer) 2 2) OK

+++++++++++对应的java代码 +++++++++++nginx

Jedis jedis = new Jedis("192.168.1.53", 6379); Transaction tx = jedis.multi(); tx.incr( "foo"); tx.set( "t1", "2"); List<Object> result = tx.exec(); if (result == null || result.isEmpty()) { System. err.println( "Transaction error..."); return ; } for (Object rt : result) { System. out.println(rt.toString()); }

使用事务时可能会赶上如下两种错误:程序员

1.事务在执行EXEC 以前,入队的命令可能会出错。好比说,命令可能会产生语法错误(参数数量错误,参数名错误,等等),或者其余更严重的错误,好比内存不足redis

(若是服务器使用 maxmemory 设置了最大内存限制的话)。编程

2.命令可能在EXEC 调用以后失败。举个例子,事务中的命令可能处理了错误类型的键,好比将列表命令用在了字符串键上面,诸如此类。

第一种错误的状况:
服务器端:
在 Redis 2.6.5 之前,Redis 只执行事务中那些入队成功的命令,而忽略那些入队失败的命令服务器

不过,从 Redis 2.6.5 开始,服务器会对命令入队失败的状况进行记录,并在客户端调用EXEC 命令时,拒绝执行并自动放弃这个事务。ui

+++++++++++命令 +++++++++++spa

 

redis 192.168.1.53:6379> multi OK redis 192.168.1.53:6379> incr foo QUEUED redis 192.168.1.53:6379> set ff 11 22 (error) ERR wrong number of arguments for 'set' command redis 192.168.1.53:6379> exec 1) (integer) 4

由于个人版本是:2.6.4,因此Redis 只执行事务中那些入队成功的命令,而忽略那些入队失败的命令

客户端(jredis):

客户端之前的作法是检查命令入队所得的返回值:若是命令入队时返回 QUEUED ,那么入队成功;不然,就是入队失败。若是有命令在入队时失败,

那么大部分客户端都会中止并取消这个事务。

 

第二种错误的状况:

至于那些在EXEC 命令执行以后所产生的错误,并无对它们进行特别处理:即便事务中有某个/某些命令在执行时产生了错误,事务中的其余命令仍然会继续执行。

+++++++++++命令+++++++++++

redis 192.168.1.53:6379> multi OK redis 192.168.1.53:6379> set a 11 QUEUED redis 192.168.1.53:6379> lpop a QUEUED redis 192.168.1.53:6379> exec 1) OK 2) (error) ERR Operation against a key holding the wrong kind of value

+++++++++++对应的java代码 +++++++++++

 

Jedis jedis = new Jedis("192.168.1.53", 6379); Transaction tx = jedis.multi(); tx.set( "t1", "2"); tx.lpop( "t1"); List<Object> result = tx.exec(); if (result == null || result.isEmpty()) { System. err.println( "Transaction error..."); return ; } for (Object rt : result) { System. out.println(rt.toString()); }

Redis 在事务失败时不进行回滚,而是继续执行余下的命令

这种作法可能会让你以为有点奇怪,如下是这种作法的优势:
1.Redis 命令只会由于错误的语法而失败(而且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来讲,

失败的命令是由编程错误形成的,而这些错误应该在开发的过程当中被发现,而不该该出如今生产环境中。
2.由于不须要对回滚进行支持,因此 Redis 的内部能够保持简单且快速。

 

鉴于没有任何机制能避免程序员本身形成的错误,而且这类错误一般不会在生产环境中出现,因此 Redis 选择了更简单、更快速的无回滚方式来处理事务。

 

3.DISCARD  命令时,事务会被放弃,事务队列会被清空,而且客户端会从事务状态中退出

+++++++++++命令 +++++++++++

 

redis 192.168.1.53:6379> set foo 1 OK redis 192.168.1.53:6379> multi OK redis 192.168.1.53:6379> incr foo QUEUED redis 192.168.1.53:6379> discard OK redis 192.168.1.53:6379> get foo "1"

4.WATCH  命令能够为 Redis 事务提供 check-and-set (CAS)行为
被WATCH 的键会被监视,并会发觉这些键是否被改动过了。若是有至少一个被监视的键在EXEC 执行以前被修改了,那么整个事务都会被取消

+++++++++++第一条命令 +++++++++++

 

redis 192.168.1.53:6379> watch foo OK redis 192.168.1.53:6379> set foo 5 OK redis 192.168.1.53:6379> multi OK redis 192.168.1.53:6379> set foo 9 QUEUED

+++++++++++暂停(执行完第二条命令才执行下面的)+++++++++++

redis 192.168.1.53:6379> exec (nil) redis 192.168.1.53:6379> get foo "8"

+++++++++++第二条命令+++++++++++

redis 192.168.1.53:6379> set foo 8 OK

+++++++++++对应的java代码 +++++++++++

 

Jedis jedis = new Jedis("192.168.1.53", 6379); jedis.watch( "foo"); Transaction tx = jedis.multi(); tx.incr( "foo"); List<Object> result = tx.exec(); //运行时在这边打断点,而后经过命令行改变foo的值 if (result == null || result.isEmpty()) { System. err.println( "Transaction error..."); return; } for (Object rt : result) { System. out.println(rt.toString()); }

若是在WATCH 执行以后,EXEC 执行以前,有其余客户端修改了 mykey 的值,那么当前客户端的事务就会失败。程序须要作的,就是不断重试这个操做,直到没有发生碰撞为止。这种形式的锁被称做乐观锁,它是一种很是强大的锁机制。而且由于大多数状况下,不一样的客户端会访问不一样的键,碰撞的状况通常都不多,因此一般并不须要进行重试。

相关文章
相关标签/搜索