解決數據一致性方案
本文只講述原理,均爲僞代碼,具體實現還得小夥伴實現。
Redis
Redis中第一種庫存存儲方式
- hset(key,value,goods) == set(key,goods)
- 秒殺業務下單
代碼:
//查詢商品
goods = redis.get(key);
//判斷庫存
if (goods.getStockCount <= 0) {
//已售馨
} else {
//減redis庫存
//下單業務
}
-
問題:
- 在第一步執行,如果有多個線程一起進來,都會拿到相同數量庫存,會出現超賣現象。
Redis中第二種方式
- hset(key,value,goods) == set(key,goods)
- 庫存:hset(key, value, stockCount)
//減庫存
Long result = redis.increment(key, value, -1);
if (result <= 0) {
//已售馨
}
//下單業務
Redis中第三種方式
- hset(key,value,goods) == set(key,goods)
- lpush(key, ltemid) //使用隊列存儲商品id
隊列
扣減緩存庫存時候,發送消息通知數據庫庫存也進行扣減,從而到達緩存數據和數據庫數據一致性。
//從redis扣減庫存
Long increment = redisTemplate.opsForValue().increment(商品id, -1);
//判斷扣減庫存是否成功
if (increment > 0) {
//發送下單信息
boolean res = mq.send(商品id);
if (!res) {
//消息發送失敗,庫存回滾
redisTemplate.opsForValue().increment(商品id, 1);
}
} else if (increment == 0) {
//商品已售馨
} else {
//扣減庫存失敗,庫存回滾
redisTemplate.opsForValue().increment(商品id, 1);
}
//下單業務
問題:
- 異步消息發送失敗——已解決(上述代碼)
- 扣減操作失敗——已解決(上述代碼)
- 下單失敗(數據無法正確回滾)發送事務型消息(半消息,提交)
事務消息
如果下單業務處理失敗,那麼數據庫庫存已經扣減,需要回滾消息。
Long increment = redisTemplate.opsForValue().increment(商品id, -1);
//判斷扣減庫存是否成功
if (increment > 0) {
//庫存扣減成功
} else if (increment == 0) {
//商品已售馨
} else {
//扣減庫存失敗,庫存回滾
redisTemplate.opsForValue().increment(商品id, 1);
}
//下單業務
//發送事務消息,沒有發送成功拋異常回滾事務
boolean res = Mq.sendTransaction(消息);
if (!res) {
new RunnTimeException(“下單失敗”);
}