?
博主介紹:?全網粉絲5W+,全棧開發工程師,從事多年軟件開發,在大廠呆過。持有軟件中級、六級等證書。可提供微服務項目搭建與畢業項目實戰,博主也曾寫過優秀論文,查重率極低,在這方面有豐富的經驗?
博主作品:《Java項目案例》主要基于SpringBoot+MyBatis/MyBatis-plus+MySQL+Vue等前后端分離項目,可以在左邊的分類專欄找到更多項目。《Uniapp項目案例》有幾個有uniapp教程,企業實戰開發。《微服務實戰》專欄是本人的實戰經驗總結,《Spring家族及微服務系列》專注Spring、SpringMVC、SpringBoot、SpringCloud系列、Nacos等源碼解讀、熱門面試題、架構設計等。除此之外還有不少文章等你來細細品味,更多驚喜等著你哦
🍅uniapp微信小程序🍅面試題軟考題免費使用,還可以使用微信支付,掃碼加群。由于維護成本問題得不到解決,可能將停止線上維護。
🍅文末獲取聯系🍅精彩專欄推薦訂閱👇🏻👇🏻 不然下次找不到喲
Java項目案例《100套》
https://blog.csdn.net/qq_57756904/category_12173599.html
uniapp小程序《100套》https://blog.csdn.net/qq_57756904/category_12173599.html
有需求代碼永遠寫不完,而方法才是破解之道,抖音有實戰視頻課程,某馬某千等培訓都是2萬左右,甚至廣東有本科院校單單一年就得3萬4年就12萬學費,而且還沒有包括吃飯的錢。所以很劃算了。另外博客左側有源碼閱讀專欄,對于求職有很大幫助,當然對于工作也是有指導意義等。在大城市求職,你面試來回一趟多多少少都在12塊左右,而且一般不會一次性就通過,還得面試幾家。而如果你對源碼以及微服務等有深度認識,這無疑給你的面試添磚加瓦更上一層樓。
最后再送一句:最好是學會了,而不是學廢了!!
2
在秒殺系統中,庫存鎖定是防止超賣的關鍵環節。以下是幾種高效可靠的庫存鎖定方案:
一、Redis原子操作方案(推薦)
1. 基于DECR的原子扣減
public boolean lockStockWithRedis(Long itemId, int quantity) {String key = "seckill:stock:" + itemId;// Lua腳本保證原子性String script ="local stock = tonumber(redis.call('get', KEYS[1])) " +"if stock >= tonumber(ARGV[1]) then " +" return redis.call('decrby', KEYS[1], ARGV[1]) " +"else " +" return -1 " +"end";Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),Collections.singletonList(key),String.valueOf(quantity));return result != null && result >= 0;
}
2. 分布式鎖+庫存扣減
public boolean lockStockWithDistributedLock(Long itemId, int quantity) {String lockKey = "seckill:lock:" + itemId;RLock lock = redissonClient.getLock(lockKey);try {// 嘗試加鎖,等待時間100ms,鎖持有時間30sif (lock.tryLock(100, 30000, TimeUnit.MILLISECONDS)) {try {String stockKey = "seckill:stock:" + itemId;Long stock = redisTemplate.opsForValue().get(stockKey);if (stock != null && stock >= quantity) {redisTemplate.opsForValue().decrement(stockKey, quantity);return true;}} finally {lock.unlock();}}} catch (InterruptedException e) {Thread.currentThread().interrupt();}return false;
}
二、數據庫方案
1. 樂觀鎖實現
@Update("UPDATE item_stock SET stock = stock - #{quantity}, version = version + 1 " +"WHERE item_id = #{itemId} AND stock >= #{quantity} AND version = #{version}")
int deductStockWithOptimisticLock(@Param("itemId") Long itemId, @Param("quantity") int quantity,@Param("version") int version);
2. 悲觀鎖實現
@Select("SELECT * FROM item_stock WHERE item_id = #{itemId} FOR UPDATE")
ItemStock selectForUpdate(Long itemId);public boolean lockStockWithPessimisticLock(Long itemId, int quantity) {// 在事務中執行ItemStock stock = stockMapper.selectForUpdate(itemId);if (stock.getStock() >= quantity) {stock.setStock(stock.getStock() - quantity);stockMapper.update(stock);return true;}return false;
}
三、預扣庫存方案
1. 兩階段庫存鎖定
public boolean twoPhaseLockStock(Long itemId, int quantity, String orderNo) {// 第一階段:預扣Redis庫存if (!lockStockWithRedis(itemId, quantity)) {return false;}// 第二階段:異步持久化mqTemplate.send("stock_prelock_topic", new StockLockEvent(itemId, quantity, orderNo));return true;
}// 消費者處理
public void handleStockLock(StockLockEvent event) {try {// 數據庫最終扣減int updated = stockMapper.realDeduct(event.getItemId(), event.getQuantity());if (updated <= 0) {// 回滾RedisredisTemplate.opsForValue().increment("seckill:stock:" + event.getItemId(), event.getQuantity());// 標記訂單失敗orderService.markAsFailed(event.getOrderNo());}} catch (Exception e) {// 異常處理}
}
四、庫存分段方案(應對熱點商品)
public boolean segmentLockStock(Long itemId, int quantity) {// 將庫存分成16個段final int SEGMENTS = 16;int segment = (int) (itemId % SEGMENTS);String segmentKey = "seckill:stock:seg:" + itemId + ":" + segment;// 每個段維護部分庫存Long remain = redisTemplate.opsForValue().decrement(segmentKey, quantity);if (remain != null && remain >= 0) {return true;} else {// 回滾redisTemplate.opsForValue().increment(segmentKey, quantity);return false;}
}
五、庫存回滾機制
public boolean rollbackStock(Long itemId, int quantity) {String key = "seckill:stock:" + itemId;Long result = redisTemplate.opsForValue().increment(key, quantity);// 同時記錄回滾流水stockFlowMapper.insert(new StockFlow(itemId, "ROLLBACK", quantity));return result != null;
}
六、庫存狀態檢查
public StockLockResult checkStockLock(String orderNo) {// 檢查訂單狀態Order order = orderMapper.selectByNo(orderNo);if (order == null) {return StockLockResult.notFound();}// 檢查庫存鎖定狀態String lockKey = "seckill:lock:" + order.getItemId() + ":" + orderNo;boolean locked = redisTemplate.hasKey(lockKey);if (order.getStatus() == OrderStatus.PAID) {return StockLockResult.success();} else if (locked) {return StockLockResult.locking();} else {return StockLockResult.failed();}
}
七、方案對比
方案 | 優點 | 缺點 | 適用場景 |
---|---|---|---|
Redis原子操作 | 性能高,實現簡單 | 需要保證Redis高可用 | 絕大多數秒殺場景 |
樂觀鎖 | 無額外依賴 | 高并發下重試率高 | 并發量適中的場景 |
悲觀鎖 | 強一致性 | 性能差,可能死鎖 | 對一致性要求極高的場景 |
預扣庫存 | 用戶體驗好 | 實現復雜 | 需要快速響應的場景 |
庫存分段 | 減少競爭 | 庫存分配需合理 | 超級熱點商品 |
最佳實踐建議
-
組合使用:Redis原子操作 + 異步數據庫更新 + 定時核對
-
監控報警:
-
庫存鎖定成功率
-
Redis庫存與數據庫庫存差異
-
鎖定/釋放比例異常
-
-
壓測驗證:模擬峰值流量驗證鎖庫存邏輯
-
兜底方案:
// 庫存鎖定兜底檢查 @Scheduled(fixedRate = 60000) public void checkStockConsistency() {List<Item> items = itemMapper.selectAll();items.forEach(item -> {Long redisStock = redisTemplate.opsForValue().get("seckill:stock:" + item.getId());if (redisStock != null && !redisStock.equals(item.getStock())) {alarmService.notify("庫存不一致:" + item.getId());}}); }
通過以上方案,可以有效解決秒殺系統中的庫存鎖定問題,在保證系統高并發的條件下防止超賣現象。
3