什么是redis事物
Redis的事务是下面4个命令来实现
1.multi,开启Redis的事务,置客户端为事务态。
2.exec,提交事务,执行从multi到此命令前的命令队列,置客户端为非事务态。
3.discard,取消事务,置客户端为非事务态。
4.watch,监视键值对,作用时如果事务提交exec时发现监视的监视对发生变化,事务将被取消。
redis事物和数据库事物不一样,可以理解成是一串命令的集合 要么一起执行 要么都不执行
执行图
非cas事物使用案例
正常执行
本地:0>multi //标识连接为事物连接 OK
本地:0>get test //命令入队 QUEUED
本地:0>set test2 1 //命令入队 QUEUED
本地:0>set test 2 //命令入队 QUEUED 本地:0>get test //命令入队 QUEUED 本地:0>exec //执行命令 并重置连接状态为事物连接 1) 1 2) OK 3) OK 4) 2 本地:0>
放弃事物
本地:0>get test //修改前test数据值 3 本地:0>get test2 //修改前test2数据值 4 本地:0>multi //标识连接为事物连接 OK 本地:0>set test 30 //命令入队 QUEUED 本地:0>set test2 40 //命令入队 QUEUED 本地:0>discard //丢失事物队列命令,并重置连接为非事物连接 OK 本地:0>get test //test数据未发送改变 3 本地:0>get test2 //test2数据未发生改变 4
命令编辑错误提交事物
本地:0>get test //test修改前的值 3 本地:0>get test2 //test修改前的值 4 本地:0>multi //表示连接为事物连接 OK 本地:0>set test 3333 //修改test QUEUED 本地:0>sett tset2 2 //编译错误,不是正确的命令格式 ERR unknown command `sett`, with args beginning with: `tset2`, `2`, 本地:0>set test2 4444 //修改test2 QUEUED 本地:0>exec //提交报错 并重置事物连接为非事物 EXECABORT Transaction discarded because of previous errors. 本地:0>get test //test数据未修改 3 本地:0>get test2 //test2数据未修改 4
命令运行时错误
本地:0>get test f 本地:0>get test2 5 本地:0>get test3 2 本地:0>multi //开启事物 OK 本地:0>set test2 6 //修改test2 QUEUED 本地:0>incr test //对值为f的执行+1操作 QUEUED 本地:0>set test3 8 //修改test3 QUEUED 本地:0>exec 1) OK 2) ERR value is not an integer or out of range //除了修改test失败其他都修改成功 3) OK 本地:0>get test f 本地:0>get test2 6 本地:0>get test3 8
CAS事物案例
什么是CAS
参考:点击跳转
正常执行
本地:0>get test //监控的key值 f 本地:0>watch test //监控test key的值 也可以监控多个 OK 本地:0>multi //开启事物 OK 本地:0>set test2 1 //修改test2 QUEUED 本地:0>set test 1 //修改test1 QUEUED 本地:0>exec //提交事物 并自动取消watch监控 1) OK 2) OK 本地:0>get test //修改成功 1 本地:0>get test2 //成功 1
异常提交
本地:0>get test //test修改前的值 1 本地:0>get test2 //test2修改前的值 1 本地:0>watch test //监控test 也可以监控多个 OK 本地:0>multi //开启事物 OK 本地:0>set test 33 //修改test的值 QUEUED 本地:0>set test2 33 //修改test2的值 QUEUED 本地:0>exec //提交事物,提交之前我使用另外一个连接改成了555 所以返回null修改失败 并取消test监控 本地:0>get test //另外一个连接 改的值 555 本地:0>get test2 //未修改成功 1
银行转账例子
public static void main(String[] args) throws Exception { Jedis conn = new Jedis("127.0.0.1", 6379); //=====================删除历史测试数据====================== //转入方 String inputUserIdKey=String.format("user:%s", "1"); //转出方 String outputUserIdKey=String.format("user:%s", "2"); conn.del(inputUserIdKey); conn.del(outputUserIdKey); //设置默认值 conn.hset(outputUserIdKey,"money","100"); conn.hset(inputUserIdKey,"money","50"); boolean processStatus=false; int index=0; do { index++;//重试3次 processStatus= transaction(conn, "1", "2", 100); }while (!processStatus&&index<=3);//cas重试 //打印转出后金额 System.out.println(processStatus?"转账成功!":"转账失败!"); System.out.println("转出方余额:"+conn.hget(outputUserIdKey,"money")); System.out.println("转入方余额:"+conn.hget(inputUserIdKey,"money")); } public static boolean transaction(Jedis conn, String inputUserId, String outUserId, Integer tranMoney) { String outUserKey = String.format("user:%s", outUserId); String inputUserKey = String.format("user:%s", inputUserId); //监控转出方金额 防止金额变化 不足以扣除 conn.watch(String.format("user:%s", outUserId)); //分为单位 Long money = Long.valueOf(conn.hget(outUserKey, "money")); if (money < tranMoney) { System.out.println("余额不足"); return false; } Transaction transaction= conn.multi(); transaction.hincrBy(outUserKey, "money", -money); transaction.hincrBy(inputUserKey, "money", money); List<Object> result= transaction.exec(); //如果监控值改变返回的是空集合 return result!=null&&result.size()>0; }