Redis深度历险-Redis事务

本文大部分内容引自《Redis深度历险:核心原理和应用实践》,感谢作者!!!

数据库事务

事务有begin、commit、rollback操作,begin表示事务的开始,commit表示事务的提交,rollback表示事务的回滚

Redis事务

Redis事务也有multi、exec、discard,multi表示事务的开始,exec表示事务的执行,discard表示丢弃事务中的所有指令;Redis事务在遇到错误的指令时不会自动回滚,而是会继续执行之后的指令;discard指令只能在exec之前执行

> multi
OK
> incr books
QUEUED
> incr books
QUEUED
> exec
(integer) 1
(integer) 2

Redis事务中指令在exec执行前是不会执行的,所有的指令会被缓存在服务器的一个事务队列中,服务器收到exec请求后才开始执行整个事务队列,执行完毕后一次性返回所有指令的的运行结果;Redis是单线程的,所以事务执行是有先后顺序的,Redis事务不具有“原子性”,具有“隔离性--串行化执行”

Redis事务执行优化

Redis事务一次性会执行多条指令,如果一条一条请求的话会经历多次网络读写,所以Redis客户端在执行事务时会强制使用pipeline

Watch

watch会在Redis事务开始执行前监控1个或多个关键变量,watch指令是一种乐观锁,当服务器收到exec指令要开始执行事务时,Redis会检查关键变量从watch开始之后是否被修改。如果关键变量被修改过,那么exec指令将会返回null告诉客户端事务执行失败,这个时候客户端一般会选择重试

> watch books
OK
> incr books # 被修改了
(integer) 1
> multi
OK
> incr books
QUEUED
> exec # 事务执行失败
(nil)

当服务器给exec指令返回一个null回复时,客户端知道了事务执行是失败的,通常客户端 (jedis) 不会抛出异常,而是通过在exec方法里返回一个null,这样客户端就能判断事务是否执行了

 

实现一个对余额加倍的操作

import java.util.List;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class TransactionDemo {
    public static void main(String[] args) {
        Jedis jedis = new Jedis();
        String userId = "abc";
        String key = keyFor(userId);
        jedis.setnx(key, String.valueOf(5)); # setnx 做初始化
        System.out.println(doubleAccount(jedis, userId));
        jedis.close();
    }
    public static int doubleAccount(Jedis jedis, String userId) {
        String key = keyFor(userId);
        while (true) {
            jedis.watch(key);
            int value = Integer.parseInt(jedis.get(key));
            value *= 2; // 加倍
            Transaction tx = jedis.multi();
            tx.set(key, String.valueOf(value));
            List<Object> res = tx.exec();
            if (res != null) {
                break; // 成功了
            }
        }
        return Integer.parseInt(jedis.get(key)); // 重新获取余额
    }
    public static String keyFor(String userId) {
        return String.format("account_{}", userId);
    }
}

注意事项

Redis禁止在multi和exec之间执行watch,必须在multi之前使用watch监控关键变量

为什么Redis不支持回滚

Redis命令在事务中可能会执行失败,但是Redis事务不会回滚,而是继续会执行余下的命令

Redis这样做,主要是因为:

    只有当发生语法错误(这个问题在命令队列时无法检测到)了,Redis命令才会执行失败, 或对keys赋予了一个类型错误的数据:这意味着这些都是程序性错误,这类错误在开发的过程中就能够发现并解决掉,几乎不会出现在生产环境。由于不需要回滚,这使得Redis内部更加简单,而且运行速度更快

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值