高并发,原子性缓存递增递减
import com.kaying.luck.dao.gift.GiftTypeDao;
import com.kaying.luck.exception.ServiceException;
import com.kaying.luck.pojo.gift.dos.GiftTypeDO;
import com.kaying.luck.response.enums.ApiResponseCodeEnum;
import com.kaying.luck.service.redis.RedisService;
import com.kaying.luck.utils.redis.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author yt
* @create 2023/3/3 15:31
*/
@Service
public class RedisServiceImpl implements RedisService {
@Autowired
private RedisUtils redisUtils;
@Autowired
private GiftTypeDao giftTypeDao;
/**
* 缓存递减库存
* @param giftTypeId
* @return
*/
@Override
public Integer redisDecrAsGiftCount(Integer giftTypeId) {
return redisAsInvitedCount(giftTypeId, 2);
}
/**
* 缓存递增库存
* @param giftTypeId
* @return
*/
@Override
public Integer redisIncrAsGiftCount(Integer giftTypeId) {
return redisAsInvitedCount(giftTypeId, 1);
}
/**
* @param giftTypeId 商品id
* @param operation 操作类型(0=无;1=递增;2=递减)
* @return
*/
private Integer redisAsInvitedCount(Integer giftTypeId, Integer operation) {
//是否查询数据库,true=查询了,false=未查询
boolean queryDatabase = false;
//redis获取商品库存
String key = redisUtils.AS_INVITED_COUNT_BY_GIFT_TYPE_ID + giftTypeId;
Integer dayGiftTyprCount = (Integer) redisUtils.get(key);
if (dayGiftTyprCount == null) {
// 锁 根据商品id加锁,保证一个商品只能有一个人来查询并同步到数据库
String lockKey = redisUtils.LOCK_AS_INVITED_COUNT_BY_AS_ID + giftTypeId;
// 获取锁 超时时间10秒 重试6次 重试睡眠时间0.25秒
String time = redisUtils.getLock(lockKey, 10L, 6, 250L);
if (time == null) {
throw new ServiceException(ApiResponseCodeEnum.FAIL, "网络繁忙,请稍后再试");
}
try {
dayGiftTyprCount = (Integer) redisUtils.get(key);
if (dayGiftTyprCount == null) {
GiftTypeDO giftTypeDO = giftTypeDao.getById(giftTypeId);
if (giftTypeDO == null) {
throw new ServiceException(ApiResponseCodeEnum.FAIL, "未找到该商品");
}
dayGiftTyprCount = giftTypeDO.getDayRemainInventory();
queryDatabase = true;
// 7天
long expireTime = 7 * 24 * 60 * 60L;
redisUtils.set(key, dayGiftTyprCount, expireTime);
}
} finally {
// 解锁
redisUtils.unlock(lockKey, time);
}
}
/**
* 操作类型(0=无;1=递增;2=递减)
* 递增递减之间会有一个超时的情况,如果超时,或者redis被清空没必要进行递增操作了,因为以数据库的数据为准
* 如果先递减就要把 queryDatabase 判断放在递减上,如果先递增就放在递增上
*/
if (operation == 1) {
if (!queryDatabase) {
return redisUtils.incr(key, 1).intValue();
}
} else if (operation == 2) {
return redisUtils.decr(key, 1).intValue();
}
return dayGiftTyprCount;
}
}