抢购功能:在极短的时间内有大量的并发请求,出现因为并发所引起的问题。
实例:
一个商品100件,一个用户一次购买3件,100个用户同时购买。
使用redis的String类型实现:
通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃
第一种:String
/*
redis实现,String方式
*/
public void redis(int num,int i){
Jedis jedis = new JedisUtil().getJedis();
try{
jedis.watch("product");
int stock = Integer.parseInt(jedis.get("product"));
if (stock>=num){
Transaction transaction = jedis.multi();
transaction.decrBy("product",num);
List<Object> result=transaction.exec();
if (result!=null){
jedis.set("user::"+String.valueOf(i),"3");
}
}
}finally {
jedis.unwatch();
jedis.close();
}
}
第二种:list
/*
redis实现,String方式。
*/
public void redisList(int num,int i){
Jedis jedis = new JedisUtil().getJedis();
try{
jedis.watch("product");
String product =jedis.lpop("product");
if (product!=null){
Transaction transaction = jedis.multi();
List<Object> result=transaction.exec();
if (result!=null){
jedis.set("user::"+String.valueOf(i),"1");
}
}
}finally {
jedis.unwatch();
jedis.close();
}
}
使用redisTemplate实现:
因为redisTemplate.multi并没有开启一个事务,需要SessionCallback去执行,否则会报错:No ongoing transaction. Did you forget to call multi?
@Autowired
private RedisTemplate redisTemplate;
/*
不加金额简单测试了一下使用redisTemplate解决超发现象
*/
@Override
public void red(int redId, int userId) {
int num = (int)redisTemplate.opsForHash().get("red","num");
redisTemplate.watch("red");
SessionCallback sessionCallback = new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
if (num>0){
operations.multi();
operations.opsForHash().increment("red","num",-1);
List<Object> result = operations.exec();
if (result!=null){
operations.opsForHash().put("redId::"+redId,"userId::"+userId,"value");
System.out.println("用户:"+userId+"抢到了");
return true;
}
}
return null;
}
};
redisTemplate.unwatch();
redisTemplate.execute(sessionCallback);
}