Redis学习笔记之八:Redis的事务
来源:互联网 发布:酷听说软件下载 编辑:程序博客网 时间:2024/06/04 19:43
Redis作为数据库,当然也有事务。但它的事务与Mysql等关系型数据库的事务不同。
Redis中的事务同Redis的执行命令一样,都是最小执行单元,这是与Mysql的第一点区别。为什么?因为Mysql默认开启了自动事务提交,每一条Sql语句都会被当做一个事务提交,而关闭Mysql事务后需要使用commit指令提交后才开启下一个事务。但Redis中的指令就是一个最小执行单元,这点可以认为Redis默认开启自动事务提交,且无法关闭。
Redis开启事务使用指令Multi指令开启事务,同时遇到EXEC指令事务结束。演示如下:
第二点区别,Redis事务中提交的命令不会马上执行,会被加载到队列中,然后遇到exec指令时再执行,并返回所有指令的执行结果,但Mysql的事务会执行sql语句。
看看Redis事务的另一个演示:
可以看到使用lpush指令对String类型的Key进行操作出错了,但是两条set指令却执行了。所以第三点不同,Redis并没有像Mysql那样有事务回滚,但的确Redis的事务中的指令都执行了,只不过有的执行出错了,但出错了也会继续执行后面的指令,回想Mysql的事务,如果有一条sql执行出错,后面的sql都不会执行,而且还会数据回滚,这算是Redis与Mysql事务最大的区别。 所以使用Redis的事务出错需要开发人员来回滚数据。
WATCH监控指令,这个指令监控的内容如果有变化,那么指令之后的事务不会执行,演示如下:
可以看到watch指令监控username,username的值发生了变化,所以后面的事务没有执行。
可以看到两点
1) 使用了watch指令监控某个key,只要值发生改变,后面的事务不会执行,无论事务中是否用到了被监控的Key
2) 只有watch指令后的第一个事务不会被执行,这是因为执行了EXEC等于对所有被监控的键取消了监控。
当然你也可以使用UnWatch指令取消对所有键的监控,这个指令与EXEC不同在于,EXEC不能单独使用,要与Multi配合。
如果使用expire指令给键设置了过期时间,到了时间自动删除这种不会被watch指令认为改变了值,演示如下:
当然使用del指令删除Key会被认为值改变了。
ps:expire指令设置时间后,如果使用SET等SET指令更改了value的值,过期时间将被清除。LPUSH等操作不算。
注意:使用expire必须在watch指令前才不算更改,请看下面的演示:
如果使用watch监控不存在的Key时,watch指令后面的事务是会执行的。
Redis的Java SDK使用事务
测试代码如下:
package org.yamikaze.redis.test;import org.junit.After;import org.junit.Before;import org.junit.Test;import redis.clients.jedis.Jedis;import redis.clients.jedis.Transaction;import redis.clients.jedis.exceptions.JedisDataException;import static org.junit.Assert.*;import java.util.List;import java.util.concurrent.TimeUnit;/** * Redis的事务处理 * @author yamikaze */public class TransactionTest { private Jedis jedis; private String key1 = "key1"; private String key2 = "key2"; private String key3 = "key3"; @Before public void setUp() { jedis = MyJedisFactory.defaultJedis(); } /** * 当事务中的指令没有错误时 */ @Test public void testTransactionWithoutError() { Transaction transaction = jedis.multi(); transaction.set(key1, key1); transaction.set(key2, key2); transaction.set(key3, key3); List<Object> objs = transaction.exec(); assertTrue(objs.size() == 3); for(Object obj : objs) { //这儿由于set指令返回的是Ok,且为String //如果事务中Lrange这种指令, if(obj instanceof String) { assertTrue("OK".equals(obj)); } } } /** * 当事务中的指令有错误时 */ @Test public void testTransactionWithError() { jedis.set(key1, key2); Transaction transaction = jedis.multi(); transaction.set(key1, key1); //使用lpush指令操纵String类型 transaction.lpush(key1, key2, key3); List<Object> objs = transaction.exec(); assertTrue(objs.size() == 2); for(Object obj : objs) { /** * 有错时返回的对象是JedisDataException * 这儿的错误是 * WRONGTYPE Operation against a key holding the wrong kind of value */ if(obj instanceof JedisDataException) { System.out.println(((JedisDataException) obj).getMessage()); } if(obj instanceof String) { System.out.println(obj); } } /** * 但事务中的set指令执行了,没有回滚 */ String result = jedis.get(key1); assertTrue(key1.equals(result)); } /** * 使用WATCH指令监控,事务不会执行 */ @Test public void testTransactionWithWatch() { jedis.set(key1, key2); //监控key1 jedis.watch(key1); //改变key1的值 jedis.set(key1, key1); //开启事务 Transaction transaction = jedis.multi(); transaction.set(key1, key3); transaction.set(key1, key2); List<Object> objs = transaction.exec(); //这种watch的情况下没有执行事务所以返回结果为0 assertTrue(objs.size() == 0); String result = jedis.get(key1); assertTrue(key1.equals(result)); } /** * Watch指令后的第二个事务会执行 */ @Test public void testTransactionWithWatch02() { jedis.set(key1, key2); //监控key1 jedis.watch(key1); //改变key1的值 jedis.set(key1, key1); //开启事务,这是第一个事务 Transaction transaction = jedis.multi(); transaction.set(key1, key3); transaction.set(key1, key2); List<Object> objs = transaction.exec(); //这种watch的情况下没有执行事务所以返回结果为0 assertTrue(objs.size() == 0); String result = jedis.get(key1); assertTrue(key1.equals(result)); //第二个事务 jedis.multi(); transaction.set(key1, key3); transaction.set(key1, key2); objs = transaction.exec(); assertTrue(objs.size() == 2); result = jedis.get(key1); assertTrue(key2.equals(result)); } /** * expire的情况 */ @Test public void testTransactionWithExpire() throws InterruptedException{ jedis.set(key1, key2); //expire语句不能放到watch语句后面,否则会被视作改变了值 jedis.expire(key1, 10); //监控key1 jedis.watch(key1); TimeUnit.SECONDS.sleep(11); //开启事务 Transaction transaction = jedis.multi(); transaction.set(key1, key3); transaction.set(key1, key2); List<Object> objs = transaction.exec(); assertTrue(objs.size() == 2); String result = jedis.get(key1); assertTrue(key2.equals(result)); } /** * del的情况 */ @Test public void testTransactionWithDel() { jedis.lpush(key1, "1", "2"); jedis.watch(key1); //删除被监控的键 jedis.del(key1); jedis.set(key2, key3); Transaction transaction = jedis.multi(); transaction.set(key2, key2); transaction.exec(); //事务中的set语句没有执行,key2的值还是key3 String result = jedis.get(key2); assertTrue(key3.equals(result)); } @After public void tearDown() { if(jedis != null) { jedis.del(key1, key2, key3); jedis.close(); } }}
《Redis入门指南》
- Redis学习笔记之八:Redis的事务
- Redis学习笔记八、事务
- JAVAWEB开发之redis学习(八)——redis事务
- [Redis学习笔记]-Redis 事务
- Redis学习笔记(八)redis之lua脚本学习
- Redis 事务学习笔记
- Redis学习笔记:事务
- 八 redis学习笔记之主从复制
- redis学习笔记八之主从复制
- redis学习笔记八之主从复制
- 八 redis学习笔记之主从复制
- redis学习笔记四之事务
- redis学习笔记四之事务
- 四 redis学习笔记之事务
- 四 redis学习笔记之事务
- Redis学习笔记(八)——事务入门
- Redis学习笔记(八)事务 和 连接相关命令
- Redis学习笔记(八)事务 和 连接相关命令
- PHP执行原理
- java1.8中创建hashmap的初始化大小设置标准
- Excel批量导入power designer
- spring boot配置tomcat部署(12.24修改)
- Java并发编程:volatile关键字解析
- Redis学习笔记之八:Redis的事务
- 第5章:基元类型、引用类型和值类型
- 泛型
- SQL 经典五十道题
- Linux串口中的超时设置
- 公司已封装easyUi下拉框,组合框
- [App] rhel7 下 Weblogic12c 集群配置
- 近似算法
- 微服务革命-如何打破传统应用的数据库?