有關效能優化的感悟.2021

KerryWu發表於2022-02-04

下半年換了個工作環境,然後有了些新的感悟。在除夕這天,總結一下這幾個月為了提高程式效能,而做的那些事。

1. 非同步執行緒

剛入行的時候,很煩面試的時候被考多執行緒,畢竟當時很少在工作中用到。但工作幾年後發現,只要想提高程式的效能,就繞不開它。最常見的場景,就是利用多執行緒併發執行多個區域性事件,縮短總體完成時間。開發中,通常整個專案註冊一個執行緒池的 Bean,各個方法都基於這個執行緒池來拿執行緒執行業務,容易針對執行緒池做引數調優。

有返回引數的非同步,可以試試 CompletableFuture,也是 jdk8 提供的神器,比傳統的 Feature 要好用,用法參考 CompletableFuture 教程。例如:當介面需要從多個外部系統取資料時,都可以非同步執行,然後統一收集返回值。甚至利用 CompletableFuture 中特性,自動執行回撥方法。

但要注意,也不是什麼時候都要用非同步:

  • 機器 CPU 資源有限:單臺機器 CPU 的核心數有限,當多執行緒併發數達到頂點時,再加執行緒,反倒適得其反。此時可以考慮負載均衡,將併發執行緒分攤到多臺機器上。
  • 同步更快:有些程式原本執行就很快,加了非同步後反而慢了,畢竟分配執行緒資源也是需要時間的。例如總共只有十幾條資料,流運算時不用 stream,而是 parallelStream

2. 快取中介軟體

Redis

Redis 是最常用的快取中介軟體了,它支援的常用資料結構挺多,要根據實際應用場景來選擇,我目前用的有String、Hash、Set、List。

原始資料當然落在關聯式資料庫中,但業務上常用的,通常是經過轉化的中間資料。例如:員工登入後所屬部門、角色許可權、積分等資料,來自於不同的微服務,不能每次要用都調各方服務的介面,這些都需要快取起來。

用快取很無腦,但重新整理快取才是難題。像剛剛的例子,不能把所有的快取資料都落到員工的粒度上,要根據業務來分層。例如:機構級別的資料,就每個機構存一份,按照員工所屬機構來查;部門級別的資料,就每個部門存一份,按照員工所屬部門來查。等等。

Elastic Search

用 ES 做快取?估計很多人是大大的問號,誰讓 Redis 當時不支援 Json 呢。從關係型資料庫抽離出來的資料有時比較複雜,例如:人員所屬的部門、角色、崗位等,每一個屬性往下,背後的資料都不少。有人說 Redis 快取個部門ID、角色ID,剩下的根據ID查表。但這些可是基礎資料,且不說會不會把資料庫查出問題,如果想要根據部門屬性、角色屬性、崗位屬性來查人員,這個SQL該咋寫。

最好的方式就是將這些資料從關聯式資料庫中抽出來,以 Json 文件的形式快取下來,MongoDB、ES 都能幹這事。但為了應對後續可能千奇百怪的查詢場景,還是 ES 保險一些。

可 ES 畢竟不是專業的快取中介軟體,我很看好最近新出來的 RedisJson。完全滿足日常工作需求,而且據說查詢效能是 ES 的上百倍。但畢竟剛出來怕有坑,等穩定一段時間再入手吧。

3. 訊息中介軟體

這裡不說 MQ 的其他特性,就聊優化效能吧。前面說單臺機器的 CPU 資源有限,可以考慮將併發執行緒分攤到多臺機器上,具體怎麼做呢?

將需要處理任務通過訊息投遞到 MQ 中,消費者監聽到 MQ 的訊息後實際執行。倘若有1000個任務,單臺機器能處理任務的最大併發執行緒數是100,那麼10臺機器就夠了。

4. 減少系統互動

記憶體運算是最快的,要儘量減少和外部系統的互動次數。

例如:在查詢一批資料時,每條資料需要通過外來鍵,查詢匹配的其他系統資訊。如果其他系統資訊種類不多的話,是不是通過查一次SQL或呼叫一次介面,一次性全拿過來。然後寫程式碼做資料拼接。而不是遍歷出每條資料,都去外部系統查一遍。

還有一些插入 MySQL、Redis 的一批資料,或給 MQ 發一批訊息的,如果可以的話,就在程式內部處理好,然後呼叫批量插入、傳送的方法,效能絕對能省不少。

5. Jvm 調優

這是老生常談啦!我之前文章有寫過,網上資料更多,這就不說了。只說一點,儘量少懷疑 Jvm 調優能提高你的程式效能,多從方案設計和程式碼上反思。

新年快樂~

相關文章