redis同步效率秒_redis秒杀系统数据同步(保证不多卖)

东西不多卖

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

Redis 中的watch可以在事务前对数据进行监控,如果在事务执行前,该数据发生改变,则事务不执行。刚好能满足我们的要求。看了很多代码,对watch功能还不是很理解,因为网上很多写的帖子都没有明确指出多客户端(理解之后发现还是有写的),所以不明白的可以参见下面的例子,是用Java写的。以下代码可以保证库存不多卖。

在redis中设置一个键为mykey,值为1000的变量,

public classMain {public static voidmain(String[] args) {newMyThread().start();newMyThread().start();newMyThread().start();newMyThread().start();newMyThread().start();newMyThread().start();newMyThread().start();newMyThread().start();newMyThread().start();newMyThread().start();

}

}class MyThread extendsThread {

Jedis jedis= null;

@Overridepublic voidrun() {//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 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事务来说行不通,因为在exec命令之前,所有的命令都被Redis缓存起来了,根本就拿不到balance的值。那类似这种需要基于已经存在的某个值的事务在Redis中如何实现呢?答案是Watch命令:

redis.watch('balance')

balance= redis.get('balance')if (balance

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保证从数据只加载一次

我这里碰到的需求是一开始要从MySQL数据库中导入数据到Redis,由于有多台服务器,不进行控制会对数据进行多次加载,所以我们可以设置一个键值进行控制。

importredis.clients.jedis.Jedis;importredis.clients.jedis.Response;importredis.clients.jedis.Transaction;public classMain {public static voidmain(String[] args) {newlock().start();newlock().start();newlock().start();newlock().start();newlock().start();newlock().start();newlock().start();newlock().start();newlock().start();newlock().start();

}

}class lock extendsThread{

@Overridepublic voidrun() {//TODO Auto-generated method stub

System.out.println(Thread.currentThread().getName());

Jedis jedis= null;try{

jedis=RedisUtil.getJedis();if(jedis.setnx("look", "1") == 1){

jedis.set("food", Thread.currentThread().getName());

}else{

System.out.println(Thread.currentThread().getName()+ "未访问");

}

}catch(Exception e) {//TODO: handle exception

e.printStackTrace();

RedisUtil.returnResource(jedis);

}finally{

RedisUtil.returnResource(jedis);

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值