引言
本文主要描述,服務端做相關秒殺活動的時候,對應的解決方案,即高並發下的數據安全。
優化方案
樂觀鎖思路
Redis中的watch,請求時,通過Redis查詢當前搶購數據,如果當前搶購數據已經到達臨界值,則直接提示相應的頁面/信息,如返回已搶購完的頁面。
分布式限流
當然,對於很大量的秒殺,可以准備多個Redis實例,用戶請求時,可以隨機數或者散列取模,找對應實例來進行搶購。
采用Redis有一個好處,比如支持很多應用服務器一起搶……
提高吞吐量
Nigix反向代理+負載均衡
實現流程
其實拋開秒殺這個場景來說正常的一個下單流程可以簡單分為以下幾步:
- 校驗庫存
- 扣庫存
- 創建訂單
- 支付
Transactional(rollbackFor = Exception.class)
@Service(value = "DBOrderService")
public class OrderServiceImpl implements OrderService {
@Resource(name = "DBStockService")
private com.crossoverJie.seconds.kill.service.StockService stockService;
@Autowired
private StockOrderMapper orderMapper;
@Override
public int createWrongOrder(int sid) throws Exception{
//校驗庫存
Stock stock = checkStock(sid);
//扣庫存
saleStock(stock);
//創建訂單
int id = createOrder(stock);
return id;
}
private Stock checkStock(int sid) {
Stock stock = stockService.getStockById(sid);
if (stock.getSale().equals(stock.getCount())) {
throw new RuntimeException("庫存不足");
}
return stock;
}
private int saleStock(Stock stock) {
stock.setSale(stock.getSale() + 1);
return stockService.updateStockById(stock);
}
private int createOrder(Stock stock) {
StockOrder order = new StockOrder();
order.setSid(stock.getId());
order.setName(stock.getName());
int id = orderMapper.insertSelective(order);
return id;
}
}
其實其他的都沒怎么改,主要是 Service 層。
@Override
public int createOptimisticOrder(int sid) throws Exception {
//校驗庫存
Stock stock = checkStock(sid);
//樂觀鎖更新庫存
saleStockOptimistic(stock);
//創建訂單
int id = createOrder(stock);
return id;
}
private void saleStockOptimistic(Stock stock) {
int count = stockService.updateStockByOptimistic(stock);
if (count == 0){
throw new RuntimeException("並發更新庫存失敗") ;
}
}
對應的 XML:
<update id="updateByOptimistic" parameterType="com.crossoverJie.seconds.kill.pojo.Stock">
update stock
<set>
sale = sale + 1,
version = version + 1,
</set>
WHERE id = #{id,jdbcType=INTEGER}
AND version = #{version,jdbcType=INTEGER}
</update>
如何保持高並發下數據一致性
對多個更新操作的業務加事物注解。在數據庫表中加一個vesion版本控制字段(初始值為0)在更新操作前查詢並記錄該字段,更新操作完成vesion+1,再次查詢vesion與更新操作前記錄的值相差1說明前后數據一致,否則回滾更新操作
