1. 程式人生 > >記錄一次效能優化的過程

記錄一次效能優化的過程

效能優化:

一、背景

查詢介面,一個複雜條件查詢,符合條件的記錄有17W,查詢首頁的100條,postman測試介面耗時在1.6-2秒不等。
(重複查詢同一個條件,mongodb會將符合條件的記錄載入到記憶體,因此後面查詢會快一些,然後趨於一個比較穩定
的值1.6s 附近)

1.檢視查詢過程,因為是分頁,需要知道總記錄的條數(countByCondition,這一步耗時0.7-0.9秒,是效能瓶頸)同時
還需要查詢100條記錄(queryByCondition,這裡迭代的時間在400-500ms不等),再加上處理時間,網路傳輸,總共在
1.6左右

二、優化

2.1 優化第一步:非同步處理countByCondition;

每一次查詢都統計一次,開銷很大,序列處理由其明顯;將countByCondition的動作通過另一個執行緒執行。最後的
效果是時間降低到0.9秒左右,因為是並行執行查詢和計數,所以時間基本上接近耗時較久的操作。

2.2 優化第二步:快取countByCondition結果;

countByCondition的結果只是統計的值,這個值用於計算總頁數,而資料部分queryByCondition是從資料庫取得,
不存在快取一致性問題. countByCondition的值在一定範圍內偏差一點點並沒有影響,比如上面的17W資料,算出
來的頁面總共1700多頁,一點誤差幾乎無影響,所以這裡可以快取countByCondition的結果,在redis中有的話,
就從redis取,沒有則按照步驟一去資料庫查詢。這樣的結果是除了第一次沒有快取的時候需要0.9秒左右,後面都只
需要0.40-0.5s左右,測試從redis取count只需要3-6ms,忽略不計。

2.3 優化第三步驟:去除特徵值減少網路傳輸;

大部分查詢的時候,特徵值並不需要參與業務中進行處理,之前沒有遇到效能問題的時候,都是怎麼方便怎麼來,
沒有在意細節,java程式從mongodb載入資料的時候需要走網路傳輸,實測含特徵值的資料大小在35KB左右,去
除特徵值在2kb左右,幾乎95%的大小在特徵值這個絕大部分查詢場景沒有用的欄位上,因此增加欄位過濾,從資料
庫載入資料的時候,就去除對特徵值欄位的載入,到這一步之後,效果是載入耗時在300-400ms左右,大約減少了
100ms左右。到這一步用postman測試介面,首次在0.9s左右,後續的次數都在0.35秒左右

2.4 細節補充

1.count在redis的快取失效時間;因為count只會影響計算得到頁數,不會影響資料,所以這裡一致性不算高,這裡
我設定的是60秒
2.後續對於查詢的資料也可以快取,但是那樣一旦有資料新增進庫,就需要對快取做更新,這裡實現其實並不是很
簡單,暫時沒有做,後續可以做一個優化點,如果實現的話,熱點資料查詢到200ms以內應該可以,不過要保證強一
致性需要考慮的很多。
3.上述還未基於大資料量做驗證,後面還需在真實環境中驗證。
4.另外在做的過程中為了方便記錄方法的耗時和記錄日誌,寫了兩個AOP的工具註解,可以參閱,

2.5 程式碼


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableCountTimeIntellif {

}

@Component
@Aspect
public class EnableCountTimeAspect {

    private static final LoggerUtilI LOG = LoggerUtilI.getLogger(EnableCountTimeAspect.class);

    @Around("@annotation(ect)")
    public Object doAround(ProceedingJoinPoint pjp, EnableCountTimeIntellif ect) throws Throwable {
        long begin = System.currentTimeMillis();
        Object obj = pjp.proceed();
        String methodName = pjp.getSignature().getName();
        String className = pjp.getSignature().getDeclaringTypeName();
        LOG.info(className + "." + methodName + " method elapsed time is: " + (System.currentTimeMillis() - begin) + " ms");
        return obj;
    }
}

三、待續

優化過程中使用的工具

1.非同步處理過程並獲得結果,Callable+FutureTask;
2.執行緒池執行任務,避免重複建立執行緒 。
3.自定義註解增強方法,並將附加功能與方法主流程解耦。
4.