高併發系統設計五-池化技術

池化技術-如何減少頻繁創建數據庫連接的性能損耗

1、池化技術

池化技術的核心思想是空間換時間,期望使用預先創建好的對象來減少頻繁創建對象的性能開銷,同時還可以對對象進行統一的管理,降低了對象的使用成本。

用連接池預先建立數據庫連接

  • 數據庫連接池
  • HTTP連接池
  • Redis連接池

2、數據庫連接池

兩個最重要的配置:最小連接數、最大連接數

2.1、流程

  • 如果當前連接數小於最小連接數,則創建新的連接數處理數據庫請求
  • 如果連接池中有控件連接則複用空閒連接
  • 如果空閒池中沒有連接並且當前連接數小於最大連接數,則創建新的連接處理請求
  • 如果當前連接數大於等於最大連接數,則按照配置中設定的時間等等舊的連接可用
  • 如果超過了這個設定的時間,則向外拋出錯誤

2.2、線上設置多少合適

  • 對於數據庫連接池的設置,線上最小連接數控制在10左右,最大連接數控制在20-30左右即可。
  • 啓動一個線程來定期檢測連接池中的連接是否可用,SELECT 1 FROM DUAL
  • testOnBorrow 設置爲 false

啓動一個線程來定期檢測連接池中的連接是否可用,比如使用連接發送“select 1”的命令給數據庫看是否會拋出異常,如果拋出異常則將這個連接從連接池中移除,並且嘗試關閉。目前 C3P0 連接池可以採用這種方式來檢測連接是否可用

在獲取到連接之後,先校驗連接是否可用,如果可用纔會執行 SQL 語句。比如 DBCP 連接池的 testOnBorrow 配置項,就是控制是否開啓這個驗證。這種方式在獲取連接時會引入多餘的開銷,在線上系統中還是儘量不要開啓,在測試服務上可以使用。

3、線程池

使用 ThreadPoolExecutor 創建線程池

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {}

各個參數說明

  • corePoolSize:線程池中的常駐核心線程數

  • maximumPoolSize:線程池中能夠容納同時執行的最大線程數,此值必須大於等於1

  • keepAliveTime:多餘的空閒線程的存活時間 當前池中線程數量超過corePoolSize時,當空閒時間達到keepAliveTime時,多餘線程會被銷燬,直到只剩下corePoolSize個線程爲止

  • unit:keepAliveTime的單位

  • workQueue:任務隊列,被提交但尚未被執行的任務

  • threadFactory:表示生成線程池中工作線程的線程工廠,用於創建線程,一般默認的即可

  • Handler:拒絕策略,表示當隊列滿了,並且工作線程大於線程池的最大線程數時如何來拒絕請求執行的runnable的策略

4、需要注意

4.1、優先將任務放入隊列中暫存起來,而不是創建更多的線程。

它比較適用於執行CPU密集型的任務,因爲執行CPU密集型的任務時CPU比較繁忙,因爲只需要和CPU核心數相當的線程,避免過多的線程上下文切換。

在Web系統中有大量的IO操作,比如說查詢數據庫,查詢緩存等,任務在執行IO操作的時候,CPU就空閒下來,這時如果增加執行任務的線程數而不是把任務暫存在隊列中,就可以在單位時間內執行更多的任務。Tomcat 使用的線程池就不是 JDK 原生的線程池,而是做了改造,當線程數超過 coreThreadCount 時,就會優先創建線程,直到線程數達到 maxThreadCount。這種比較適合 Web系統大量的 IO 操作的場景。

4.2、線程池中使用的隊列的堆積量也是我們需要監控的重要指標。

我在實際項目中就曾經遇到過任務被丟給線程池之後,長時間都沒有被執行的詭異問題。最初,我認爲這是代碼的 Bug 導致的,後來經過排查發現,是因爲線程池的 coreThreadCount 和 maxThreadCount 設置得比較小,導致任務在線程池裏面大量的堆積,在調大了這兩個參數之後問題就解決了。跳出這個坑之後,我就把重要線程池的隊列任務堆積量,作爲一個重要的監控指標放到了系統監控大屏上

4.3、使用線程池請一定記住不要使用無界隊列(即沒有設置固定大小的隊列)

大量的任務堆積會佔用大量的內存空間,一旦內存空間被佔滿就會頻繁地觸發 Full GC,造成服務不可用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章