优惠券读服务优化
優(yōu)惠券讀服務(wù)優(yōu)化
前幾天成功解決了發(fā)券時的并發(fā)問題,參考這里。但是用戶領(lǐng)取得卡一多,讀取速度也慢了下來,用戶領(lǐng)取了200張卡以后,取得自己所有的卡需要5s左右。
主要業(yè)務(wù)場景,查詢用戶卡包,得到用戶所有的卡券。
- 用戶根據(jù)id分在不同的表。
- 卡券根據(jù)不同的商戶,分在不同的表。
第一步,建立卡券的緩存系統(tǒng)。
以商戶和卡券Id在添加一個字符前綴做key,把卡緩存在redis里,并在卡券狀 態(tài)變更后,作廢釣redis里對應(yīng)的卡券。
這樣做后,構(gòu)建一個200張卡的卡包,第一次還是需要5秒,第二次訪問只需要1s。大并發(fā)后響應(yīng)時間更長。這顯然不能滿足要求。
第二步,建立用戶的緩存系統(tǒng)。
在有了卡券的緩存后,另外以用戶id為主鍵,對每一個用戶查詢的返回結(jié)果,再做一級緩存,形成二級緩存機制。用戶卡有變動的時候,作廢緩存。
有了二級緩存,還是200張卡,第一次訪問需要5s,第二次響應(yīng)在200ms。100個并發(fā)訪問在400ms左右。
緩存預(yù)刷機制。
有了二級緩存之后,由于每次卡有變動,用戶訪問卡包構(gòu)建cache在1s以上,會影響體驗,我又加入了預(yù)刷緩存機制,在用戶卡變動后,作廢掉用戶級別的緩存,和變動的單個卡的緩存,隨即拋出一個用戶的id進入隊列,隊列的尾端拿到用戶的主鍵后,訪問用戶卡包構(gòu)建緩存,這樣當(dāng)用戶再次訪問卡包的時候,響應(yīng)體驗提升很大。
第三步,并行構(gòu)建卡券。
在沒有任何緩存的情況下,一次訪問200個卡券的卡包在6s以上。
在只有卡券這一級緩存的情況下,構(gòu)建用戶返回結(jié)果,也需要1s以上。
因為取200個卡券的執(zhí)行是單個串行執(zhí)行的,分析下來這里是性能的瓶頸。優(yōu)化方案是: 在構(gòu)建200個卡券的時候,每一批卡開一個線程去構(gòu)建,200個卡,可以同時開20到40個線程去構(gòu)建,來縮短用戶卡多的時候,這里的時間消耗。
這樣做后,先禁用用戶級別的緩存,來壓力測試這里的性能。
在沒有任何cache的情況下,單次訪問200個卡的卡包,響應(yīng) 時間在500ms,在有卡緩存沒有用戶緩存的情況下,單次的響應(yīng)時間在200ms,構(gòu)建用戶級別緩存的時間已經(jīng)大大縮短了。但在高并發(fā)的情況下,會報錯,一查是因為redis的連接不夠了,每個線程需要20-40個redis連接,所以redis要擴大連接數(shù)。
下面是多線程部分代碼,對象的名字做了處理,并除去了業(yè)務(wù)邏輯。
/*** 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());}}
上面用到的子任務(wù)類代碼
/*** 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 ;}}優(yōu)化到現(xiàn)在,不論是第一次還是之后的訪問,都不慢了,在有二級緩存的情況下,cpu和連接資源消耗也不會太大。而且有緩存預(yù)刷機制,卡包數(shù)據(jù)更新后也不會慢了。
總結(jié)
- 上一篇: 7-3 小孩子才做选择,大人全都要 (1
- 下一篇: 17南宁网络赛