秒殺系統效能優化
優化之前的準備
在吞吐量、延遲和記憶體這三點中選擇我們業務的著重點。
例如本次這個系統,我認為應該更加註重吞吐量。(PS:我也是第一次,憑感覺來的......)
為什麼更加註重吞吐量?
延遲:像我們大家平時去進行商品搶購的時候,應該都會覺得現在人太多了,卡一卡沒什麼問題(但是也不能太久,你給我直接來個10s,肯定給你差評),只關心的是最後我有沒有搶到商品。所以我認為延遲這方面應該是其次,主要的是系統處理請求的數量。
記憶體佔用:一般秒殺活動都會有時間限制,這段時間也不會佔用太大的記憶體。我電腦16G也不用擔心,如果真的記憶體很缺少,可以考慮考慮。
程式碼優化
將寫入Redis快取程式碼與資料庫事務分開
防止網路抖動可能導致寫快取響應時間較慢,阻塞資料庫事務。
原始程式碼:
/**
* 下訂單、減庫存事務(原始程式碼)
*/
@Transactional
public void seckillTransaction(SeckillDto seckillDto, GoodDto goodDto){
//新增訂單操作......
//減庫存操作......
//寫入快取,不用再呼叫資料庫判斷是否還能繼續搶購
if (goodDto.getSurplusCount() <= 0){
redisTemplate.delete(seckillDto.getGoodId().toString());
redisTemplate.opsForValue().set(seckillDto.getGoodId().toString(), JSONObject.toJSONString(goodDto), 320, TimeUnit.SECONDS);
}
}
修改後:
public boolean seckillGood(SeckillDto seckillDto) {
// ......
if (goodDto.getSurplusCount()>0){
//執行下單 、 減庫存事務
seckillTransaction(seckillDto, goodDto);
//寫入快取,不用再呼叫資料庫判斷是否還能繼續搶購
if (goodDto.getSurplusCount() - 1 <= 0){
redisTemplate.delete(seckillDto.getGoodId().toString());
redisTemplate.opsForValue().set(seckillDto.getGoodId().toString(), JSONObject.toJSONString(goodDto), 320, TimeUnit.SECONDS);
}
}
// ......
}
/**
* 下訂單、減庫存事務(修改後)
*/
@Transactional
public void seckillTransaction(SeckillDto seckillDto, GoodDto goodDto){
log.info("seckillTransaction已呼叫");
//新增訂單操作......
//減庫存操作......
}
資料庫相關優化
因為沒弄很多資料,SQL語句也挺簡單的,就不考慮那麼多了。重點考慮連線池的引數設定。
連線池優化
我的電腦CPU是8核的。因為資料庫連線算IO操作,所以我們將連線數在16左右進行調節。
使用的是dbcp2連線池:
max-wait-millis:最大等待時間
initial-size:連線池啟動時建立的初始化連線數量
max-total:最大連線數
min-idle:最小空閒連線
max-idle:最大空閒連線
場景:100個請求同時對資料庫進行搶購(下訂單、減庫存)操作,10個商品。開啟了事務。
預設情況下,不配置連線引數。initialSize = 0,minIdle = 0。
連線數為16。initialSize = 16,minIdle = 16。
連線數為10。initialSize = 10,minIdle = 10。
連線數12。initialSize = 12,minIdle = 12。
連線數13。initialSize = 13,minIdle = 13。
TP95 | TP99 | 吞吐量 | |
---|---|---|---|
預設 | 2671 | 2694 | 104.2 |
連線數12 | 2408 | 2431 | 114.5 |
上面的資料可以測試資料可以看出:配置了連線數之後整體操作TP99下降了260ms,吞吐量也增加了10/s。