优惠券读服务优化
優惠券讀服務優化
前幾天成功解決了發券時的并發問題,參考這里。但是用戶領取得卡一多,讀取速度也慢了下來,用戶領取了200張卡以后,取得自己所有的卡需要5s左右。
主要業務場景,查詢用戶卡包,得到用戶所有的卡券。
- 用戶根據id分在不同的表。
- 卡券根據不同的商戶,分在不同的表。
第一步,建立卡券的緩存系統。
以商戶和卡券Id在添加一個字符前綴做key,把卡緩存在redis里,并在卡券狀 態變更后,作廢釣redis里對應的卡券。
這樣做后,構建一個200張卡的卡包,第一次還是需要5秒,第二次訪問只需要1s。大并發后響應時間更長。這顯然不能滿足要求。
第二步,建立用戶的緩存系統。
在有了卡券的緩存后,另外以用戶id為主鍵,對每一個用戶查詢的返回結果,再做一級緩存,形成二級緩存機制。用戶卡有變動的時候,作廢緩存。
有了二級緩存,還是200張卡,第一次訪問需要5s,第二次響應在200ms。100個并發訪問在400ms左右。
緩存預刷機制。
有了二級緩存之后,由于每次卡有變動,用戶訪問卡包構建cache在1s以上,會影響體驗,我又加入了預刷緩存機制,在用戶卡變動后,作廢掉用戶級別的緩存,和變動的單個卡的緩存,隨即拋出一個用戶的id進入隊列,隊列的尾端拿到用戶的主鍵后,訪問用戶卡包構建緩存,這樣當用戶再次訪問卡包的時候,響應體驗提升很大。
第三步,并行構建卡券。
在沒有任何緩存的情況下,一次訪問200個卡券的卡包在6s以上。
在只有卡券這一級緩存的情況下,構建用戶返回結果,也需要1s以上。
因為取200個卡券的執行是單個串行執行的,分析下來這里是性能的瓶頸。優化方案是: 在構建200個卡券的時候,每一批卡開一個線程去構建,200個卡,可以同時開20到40個線程去構建,來縮短用戶卡多的時候,這里的時間消耗。
這樣做后,先禁用用戶級別的緩存,來壓力測試這里的性能。
在沒有任何cache的情況下,單次訪問200個卡的卡包,響應 時間在500ms,在有卡緩存沒有用戶緩存的情況下,單次的響應時間在200ms,構建用戶級別緩存的時間已經大大縮短了。但在高并發的情況下,會報錯,一查是因為redis的連接不夠了,每個線程需要20-40個redis連接,所以redis要擴大連接數。
下面是多線程部分代碼,對象的名字做了處理,并除去了業務邏輯。
/*** Created by haoli*/if (itemsList != null&& CollectionUtils.isNotEmpty(itemsList)) {int totalCount = itemsList.size();int taskNumber = (totalCount%GROUP_COUNT == 0 ? totalCount/GROUP_COUNT:totalCount/GROUP_COUNT+1);List<FutureTask<List<YourObject>>> ft = new ArrayList<FutureTask<List<YourObject >>>();for(int i=0; i<taskNumber ; i++) {int toSize = ((i+1)*GROUP_COUNT>totalCount-1 ?totalCount : (i+1)*GROUP_COUNT);List<ItemObject> tempList = itemsList.subList(i*GROUP_COUNT,toSize);SubQueryYourObjectTask task = new SubQueryYourObjectTask (tempList,this.yourService);FutureTask<List<YourObject >> ftItem = new FutureTask<List<YourObject >>(task);ft.add(ftItem);ThreadPoolExeService.execute(ftItem);}for(FutureTask<List<YourObject >> ftItem : ft){try {totals.addAll(ftItem.get());} catch (InterruptedException e) {log.error(e.toString());} catch (ExecutionException e) {log.error(e.toString());}}
上面用到的子任務類代碼
/*** Created by haoli*/public class SubQueryYourObjectTask implements Callable<List<YourObject >> {private List<ItemObject > parameter;private YourService yourService ;public SubQueryYourObjectTask (List<ItemObject > rlist, YourService yours){parameter = rlist;this.yourService = yours ;}@Overridepublic List<YourObject > call() throws Exception {List<YourObject > results= new ArrayList<YourObject >();for(ItemObject cbur :parameter ) {results .add(this.yourService .getXXXById(cbur ));}return results ;}}優化到現在,不論是第一次還是之后的訪問,都不慢了,在有二級緩存的情況下,cpu和連接資源消耗也不會太大。而且有緩存預刷機制,卡包數據更新后也不會慢了。
總結
- 上一篇: 7-3 小孩子才做选择,大人全都要 (1
- 下一篇: 17南宁网络赛