美文网首页技术分享
redis秒杀系统数据同步

redis秒杀系统数据同步

作者: 追梦人在路上不断追寻 | 来源:发表于2020-10-07 15:12 被阅读0次

Redis中的Watch刚好可以实现一点。首先我们需要获取当前库存,只有库存中的食物小于购物车的数目才能对库存进行减。在高并发的情况下会出现某时刻查询库存够的,但下一时刻另外一个线程下单了,对库存进行减操作,刚好小于上个线程的购物车数目。照理现在的状态是不能下单成功的,因为库存已经不够了,但上一线程仍然认为数量还够,对库存进行减操作,从而导致库存出现负数的情况。如何避免?

Redis 中的watch可以在事务前对数据进行监控,如果在事务执行前,该数据发生改变,则事务不执行。刚好能满足我们的要求。

public class Main {
 
    public static void main(String[] args) {
            new MyThread().start();
            new MyThread().start();
            new MyThread().start();
            new MyThread().start();
            new MyThread().start();
            new MyThread().start();
            new MyThread().start();
            new MyThread().start();
            new MyThread().start();
            new MyThread().start();
    }
}
 
class MyThread extends Thread {
    Jedis jedis = null;
 
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while (true) {
            System.out.println(Thread.currentThread().getName());
            jedis = RedisUtil.getJedis();
            try {
                int stock = Integer.parseInt(jedis.get("mykey"));
                if (stock > 0) {
                    jedis.watch("mykey");
                    Transaction transaction = jedis.multi();
                    transaction.set("mykey", String.valueOf(stock - 1));
                    List<Object> result = transaction.exec();
                    if (result == null || result.isEmpty()) {
                        System.out.println("Transaction error...");// 可能是watch-key被外部修改,或者是数据操作被驳回
                    }
                } else {
                    System.out.println("库存为0");
                    break;
                }
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
                RedisUtil.returnResource(jedis);
            }finally{
                RedisUtil.returnResource(jedis);
            }
 
        }
    }
 
}


redis.watch('balance')
balance = redis.get('balance')
if (balance < amtToSubtract) {
    redis.unwatch()
} else {
    redis.multi()
    redis.decrby('balance', amtToSubtract)
    redis.incrby('debt', amtToSubtract)
    redis.exec()
}

通俗点讲,watch命令就是标记一个键,如果标记了一个键,在提交事务前如果该键被别人修改过,那事务就会失败,这种情况通常可以在程序中重新再尝试一次。像上面的例子,首先标记了键balance,然后检查余额是否足够,不足就取消标记,并不做扣减;足够的话,就启动事务进行更新操作,如果在此期间键balance被其它人修改,那在提交事务(执行exec)时就会报错,程序中通常可以捕获这类错误再重新执行一次,直到成功。

Redis事务失败后不支持回滚 与数据库事务很重要的一个区别是Redis事务在执行过程中出错后不会回滚。在exec命令后,Redis Server开始一个个的执行被缓存的命令,如果其中某个命令执行出错了,那之前的命令并不会被回滚。

相关文章

网友评论

    本文标题:redis秒杀系统数据同步

    本文链接:https://www.haomeiwen.com/subject/mwvfpktx.html