應用性能優化列表

應用開發完了,可是隨着用戶規模的上升,數據量的積累,系統會愈來愈慢,性能優化將會伴隨着項目一直持續下去python

一、總原則

  • 可擴展性架構,堆機器能不能解決問題是最最優先考慮的問題
  • 去中心化的點對點通訊,優於經過中心代理的通訊
  • 池化的長鏈接,優於短鏈接
  • 二進制數據,優於文本數據
  • 儘可能減小交互,一次調用的粗粒度聚合接口 優於 屢次調用的細粒度接口,批量接口優於循環調用
  • 儘可能只交互必要的數據
  • 儘可能就近訪問
  • 儘可能使用緩存
  • 老是設定超時
  • 在合適的場景,並行化執行、異步化執行

二、環境準備

保證開發環境、測試環境、驗證環境、正式環境的配置一致,包括但不限於如下內容:redis

  1. 操做系統版本,儘可能大版本一致,系統基本配置預估(CPU,硬盤,帶寬)
  2. 系統參數配置一致,TCP內核參數,網卡參數及多隊列綁定,IO&Swap內核參數,ulimit資源限制,線程棧大小等
  3. DNS指向,host配置一致
  4. 目錄配置,應用目錄,日誌目錄,數據目錄,依賴包目錄
  5. 軟件配置一致,python版本,python依賴,Java版本,JVM調優參數,Tomcat版本、配置,NodeJs版本,數據庫版本等
  6. 服務器監控,應用監控
  7. 扇入模型:平時與高峯期的流量估算,各接口的流量比例,響應時間要求
  8. 扇出模型:各接口對遠程服務、數據庫、緩存、消息系統的調用比例,響應時間估算。

三、數據庫

  1. 各環境的數據庫配置保持一致
  2. 禁用存儲過程,函數,觸發器,外鍵約束。
  3. 各個環境的數據庫索引保持一致
  4. SQL語句規範
  5. 配置SQL執行的超時
  • 必須使用prepareStatement,提高性能與防注入
  • 根據一切皆有超時的原則,配置SQL執行的超時。可在鏈接池裏設置default值,可在MyBatis的Mapper定義裏可設置每一個請求的超時,惋惜規範是秒級的。
  • 根據儘可能少交互與儘可能少數據的原則,需使用對SQL徹底可控的DAO框架,建議爲MyBatis 或 Spring JDBC Template。

3.1 事務

  • 不使用事務,鏈接池設置autocommit,使用其餘方式來保持數據一致性。
  • 經過Transaction Annotation控制事務,事務跨度儘可能短,把非事務範圍內的業務邏輯剔除到被標註的函數以外。
  • 只讀事務能夠不加事務標註。

3.2 鏈接池

  • 在分庫分表時,根據點對點通訊優先的原則,儘可能使用客戶端分片的實現。功能不知足時才用MyCat中央代理。
  • 推薦使用性能最高HikariCP,或者Druid,不推薦c3p0與DBCP。

鏈接池的配置:數據庫

  • 配置初始值,再聯繫DBA得到線上數據庫支持的鏈接數,計算最大鏈接數。
  • 鏈接有效性檢查,只在鏈接空閒檢測時執行,不在拿出和歸還鏈接時執行,最好是直接使用數據的Ping方案,不要配置檢查SQL。
  • 根據老是設置超時的原則,配置獲取鏈接超時的時間。
  • 配置合理的空閒鏈接回收間隔和空閒時間。
  • 番外篇:在分庫分表時,可考慮基於HikariCP二次開發,減小總的空閒鏈接檢查線程數(好比128個分區,可能有256條線程),重用同一個實例上的庫的鏈接等。

四、緩存

4.1 多級緩存

  • 根據緩存原則, 緩存 > 數據庫/遠程調用
  • 根據就近原則, 堆內緩存 > 堆外緩存 > 集中式緩存
  • 堆內緩存受大小限制,並影響GC
  • 堆內緩存與堆外緩存,分佈在每一臺應用服務器上,刷新方式比集中式緩存複雜
  • 堆外緩存與集中式緩存,須要序列化/反序列化對象
  • 集中式緩存,有網絡傳輸的成本,特別是數據超過一個網絡包的大小。
  • 集中式緩存,一次獲取多個鍵時,在有分區的狀況下,須要收發多個網絡包。
  • 使用上述條件選擇合適的緩存方案,或同時使用多級緩存,逐層回源。

4.2 綜述

  • 須要對回源進行併發控制,當key失效時,只有單一線程對該key回源。
  • 基於二進制優於文本數據的原則,JSON的序列化方案較通用與更高的可讀性。而對於較大,結構較複雜的對象,基於Kyro,PB,Thrift的二進制序列化方案的性能更高,見後面的序列化方案部分。

4.3 堆內緩存

選型:segmentfault

  • 推薦Guava Cache。後端

    • 正確設置並行度等參數。
    • 重載load()參數,實現單一線程回源。
    • Guava Cache能後臺定時刷新,在刷新的過程當中,依然使用舊數據響應請求,不會形成卡頓,但須要重載實現reload()函數。
    • Guava Cache同時還支持併發安全版的WeakHashMap。
  • Ehcache較重,性能也較差。更不要使用存在嚴重bug的Jodd Cache。

4.4 堆外緩存

選型:緩存

  • 推薦Cassandra的OHC 或者 OpenHFT的Chronical map2。
  • OHC夠簡單,其實R大不喜歡Chronical,玩的太深,換個JDK均可能跑不起來。
  • Chronical map3的license則較不友好,複雜度高且要求JDK8。
  • 其餘的Ehcache的Terracota Offheap 一貫不喜歡。

4.5 Memcached

​客戶端:安全

  • 基於點對點通訊優於網關的原則,使用客戶端一致性哈希分區。
  • 推薦Spymemcached。 XMemcached 過久沒更新,Folsom知名度不高。
  • 注意Spymemcached爲單線程單鏈接架構(一個MemcachedClient只有一條IO線程,與每臺Memcached只有一條鏈接),必要時可多建幾個MemcachedClient隨機選擇,但不要用Commons Pool去封裝它,把Spy本來的設計一筆抹殺。
  • 根據在合適場景使用併發的原則,Spymemcached支持異步API。
  • 根據一切皆設超時的原則,可在鏈接工廠中設置最大超時數,默認值兩秒半太長。

數據結構:性能優化

  • Key必須設置失效時間。
  • Key必須有長度限制。
  • Value長度須要控制,以不超過1個網絡包(MTU,千五字節)爲佳。
  • Value大小差異較大的緩存類型,建議拆分到不一樣MC集羣,不然會形成低使用率而且產生踢出。

4.6 Redis as Cache

Redis拓撲:服務器

  • 基於點對點通訊優於網關的原則,使用以下兩種拓撲網絡

    • 無HA的普通分片:由Jedis客戶端完成分片路由。
    • Redis Cluster:一樣由Jedis客戶端封裝分區,跳轉,重試等邏輯,須要使用最新版的Jedis版本。

服務端:

  • Cache節點與持久化數據節點不要混用。
  • Cache節點是否須要持久化要仔細衡量。
  • 因爲Redis是單線程,使用taskset進行cpu綁定後能夠有效地利用cpu,並在單機上運行多個redis實例。

+對熱鍵進行監控,發現不合理的熱健要進行分拆等處理。

客戶端:

  • Jedis基於Apache Commons Pool進行了多鏈接的封裝,正確配置總鏈接數不超過Redis Server的容許鏈接數。
  • 性能考慮,空閒鏈接檢查不要過於頻繁(建議30秒以上),另不要打開testOnBorrow等測試參數。
  • 根據一切皆有超時的原則,設定統一的調用超時,獲取鏈接的最長等待時間參數,重試次數
  • 根據在合適的地方異步的原則,Jedis自己沒有異步API,只在PipleLine模式下支持。

數據結構:

  • 必須對Key設置失效時間。
  • Key必須有長度限制。
  • Value長度須要控制,不要超過一個網絡包。另外集合的元素不要超過五千個。
  • 除了使用序列化的String,一樣能夠考慮用Hash來存儲對象,注意內部結構爲ZipList與HashTable時,hmget 與hgetall的不一樣複雜度。

命令:

  • 慎用的命令:LANGE(0, -1), HGETALL, SMEMBER
  • 高複雜度的命令: ZINTERSTORE, SINTERSTORE, ZUNIONSTORE, ZREM
  • 儘可能使用多參數的命令:MGET/MSET,HMGET/HMSET, LPUSH/RPUSH, LRANGE
  • 儘可能使用pipeline
  • 根據減小交互的原則,必要時可以使用Redis的Lua腳本

五、服務調用

5.1 接口設計

  1. 儘可能少交互的原則:
  • 支持批量接口,最大的批量,綜合考慮調用者的需求與 後端存儲的能力。
  • 支持粗粒度接口,在支持原子細粒度接口的同時,支持粗粒度接口/聚合層接口,將多個數據源的獲取,多個動做,合併成一個粗粒度接口。
  1. 儘可能少數據的原則:
  • 在提供返回全部數據的大接口的同時,提供只提供知足部分調用者須要的輕量接口。
  • 最好再提供能定製返回字段的接口。
  1. 二進制數據優於文本數據
  • 一樣是一個簡單通用性,與性能的選擇,特別是大數據量時。

5.2 RESTful

  • 僅以Apache HttpClient爲例,大部分Restful框架都是對Apache HttpClient的封裝。另外OkHttp也值得看看。

    • 不要重複建立ApacheClient實例,使用鏈接池,正確配置鏈接池的鏈接數。
    • 鏈接池老是有鎖,針對不一樣的服務,使用不一樣的Apache HttpClient實例,將鎖分散開來。在高併發時比使用全局單例的ApacheClient,有很大的性能提高。
    • 根據一切調用皆有超時的原則,每次調用均設置超時時間。RequestConfig裏共有Connect Timeout, Socket Timout 和 從Pool中獲取鏈接的Timeout三種超時。
    • 須要異步或並行的場景,使用Apache AsyncHttpClient項目。但要注意AsyncHttpClient項目,檢查調用超時的週期默認爲1秒。

5.3 自家RPC框架

每家的RPC框架特性不一樣,但考慮點都相似。

六、消息異步

6.1 選型

  • 根據就近原則,能夠先嚐試用JVM內的隊列來解決,而後再考慮中央消息系統。
  • 可靠性要求極高的選擇RabbitMQ,可支持單條消息確認。
  • 海量消息場景,容許極端狀況下少許丟失則使用Kafka。

6.2 Kafka

  • 在同步和異步之間作好權衡,異步批量發送能夠極大的提升發送的速度。
  • 關注消費者以下參數:commitInterval(自動提交offset間隔),prefetchSize(指單次從服務器批量拉取消息的大小),過大和太小都會影響性能,建議保持默認。

6.3 RabbitMQ

  • 根據擴展性原則,RabbitMQ自己沒有分片功能,但能夠在客戶端自行分片。
  • 如非必要狀況,應該保持默認的同步發送模式。
  • 關注消費者以下參數:autocommit(自動提交確認,默認false) ,在消息拉取到本地即認爲消費成功,而不是真正消費成功後提交。prefetchCount(預取消息條數,默認64條)
  • 生產者在必要時也能夠臨時降級不進行confirm。

7. 日誌

7.1 綜述

  • Log4j2或logback,不要再使用Log4j。
  • 除了應用啓停日誌,不容許使用超慢的System.out.println() 或 e.printStack();
  • 嚴格控制日誌量避免太高IO,對海量日誌,應該有開關能夠動態關停。
  • 若是可能出現海量異常信息,可仿效JDK的優化,用RateLimiter進行限流,丟棄過多的異常日誌。

7.2 內容

  • 嚴格控制日誌格式,避免出現消耗較大的輸出如類名,方法名,行號等。
  • 業務日誌不要濫用toJSONString()來打印對象,儘可能使用對象自身的toString()函數,由於JSON轉換的消耗並不低。
  • 在生產環境一定輸出的日誌,不要使用logger.info("hello {}", name)的模式,而是使用正確估算大小的StringBuilder直接拼裝輸出信息。

7.3 異步日誌

  • 同步日誌的堵塞很是嚴重,特別是發生IO的時候,所以儘可能使用異步日誌。
  • Logback的異步方案存在必定問題,須要正確配置Queue長度,閥值達到多少時丟棄Warn如下的日誌,最新版還能夠設置若是隊列已滿,是等待仍是直接丟棄日誌。
  • 若是以爲Logback的異步日誌每次插入都要詢問隊列容量太過消耗,可重寫一個直接入列,不成功則直接丟棄的版本。

八、工具類

8.1 JSON

  • 使用Jackson 或 FastJSON。GSON的性能較前二者爲差,尤爲是大對象時。
  • 超大對象可使用Jackson或FastJSON的流式 API進行處理。
  • 將不須要序列化的屬性,經過Annotation排除掉。

FastJson:

  • 儘可能使用最新的版本。
  • SerializerFeature.DisableCircularReferenceDetect 關閉循環引用檢查。

Jackson:

  • 設置參數,不序列化爲空的屬性,等於默認值的屬性。
  • 除了jackson-databinding,可試用簡化版沒那麼多花樣的jackon-jr。

8.2 二進制序列化

須要定義IDL的PB與Thrift,不須要定義的Storm等用的Kyro 均可選擇,其餘一些比較舊就算了。

8.3 Bean複製

在VO,BO之間複製時,使用Orika(生成代碼) 或 Dozer(緩存反射),不要使用須要每次進行反射的Apache BeanUitls,Spring BeanUtils。

8.4 日期

  • JDK的日期類與字符串之間的轉換很慢且非線程安全。
  • 繼續用Java日期不想大動做的,就用CommonsLang的FastDateFormat。
  • 能大動做就用joda time,或者JDK8的新日期API。

九、Java代碼優化 與 業務邏輯優化

  • 規則前置,將消耗較大的操做放後面,若是前面的條件不知足時可。
  • 另外前面提到的一堆原則,好比儘可能緩存,儘可能少交互,儘可能少數據,並行,異步等,均可在此使用。



歡迎訂閱「K叔區塊鏈」 - 專一於區塊鏈技術學習

博客地址: http://www.jouypub.com
簡書主頁: https://www.jianshu.com/u/756c9c8ae984
segmentfault主頁: https://segmentfault.com/blog/jouypub
騰訊雲主頁: https://cloud.tencent.com/developer/column/72548
相關文章
相關標籤/搜索