池化技術 (pool) 是一種很常見的程式設計技巧,在請求量大時能明顯優化應用效能,降低系統頻繁建連的資源開銷。我們日常工作中常見的有資料庫連線池、執行緒池、物件池等,它們的特點都是將 「昂貴的」、「費時的」 的資源維護在乙個特定的 「池子」 中,規定其最小連線數、最大連線數、阻塞佇列等配置,方便進行統一管理和復用,通常還會附帶一些探活機制、強制**、監控一類的配套功能。
提前儲存大量的資源,以備不時之需以及重複使用。池化技術應用廣泛,如記憶體池,執行緒池,連線池等等。記憶體池相關的內容,建議看看 apache、nginx 等開源 web 伺服器的記憶體池實現。
由於在實際應用當做,分配記憶體、建立程序、執行緒都會設計到一些系統呼叫,系統呼叫需要導致程式從使用者態切換到核心態,是非常耗時的操作。因此,當程式中需要頻繁的進行記憶體申請釋放,程序、執行緒建立銷毀等操作時,通常會使用記憶體池、程序池、執行緒池技術來提公升程式的效能。
對連線或執行緒的復用,並對復用的數量、時間等進行控制,從而使得系統的效能和資源消耗達到最優狀態。
程序池與執行緒池同理。
執行緒池的原理很簡單,類似於作業系統中的緩衝區的概念,它的流程如下:先啟動若干數量的執行緒,並讓這些執行緒都處於睡眠狀態,當需要乙個開闢乙個執行緒去做具體的工作時,就會喚醒執行緒池中的某乙個睡眠執行緒,讓它去做具體工作,當工作完成後,執行緒又處於睡眠狀態,而不是將執行緒銷毀。
記憶體池是指程式預先從作業系統申請一塊足夠大記憶體,此後,當程式中需要申請記憶體的時候,不是直接向作業系統申請,而是直接從記憶體池中獲取;同理,當程式釋放記憶體的時候,並不真正將記憶體返回給作業系統,而是返回記憶體池。當程式退出(或者特定時間)時,記憶體池才將之前申請的記憶體真正釋放。
資料庫連線池
資料庫連線池的基本思想是在系統初始化的時候將資料庫連線作為物件儲存在記憶體中,當使用者需要訪問資料庫的時候,並非建立乙個新的連線,而是從連線池中取出乙個已建立的空閒連線物件。在使用完畢後,使用者也不是將連線關閉,而是將連線放回到連線池中,以供下乙個請求訪問使用。這些連線的建立、斷開都由連線池自身來管理。
同時,還可以設定連線池的引數來控制連線池中的初始連線數、連線的上下限數和每個連線的最大使用次數、最大空閒時間等。當然,也可以通過連線池自身的管理機制來監視連線的數量、使用情況等。
httpclient 連線池
httpclient 我們經常用來進行 http 服務訪問。我們的專案中會有乙個獲取任務執行狀態的功能使用 httpclient,一秒鐘請求一次,經常會出現 conection reset 異常。經過分析發現,問題是出在 httpclient 的每次請求都會新建乙個連線,當建立連線的頻率比關閉連線的頻率大的時候,就會導致系統中產生大量處於 time_closed 狀態的連線。這個時候使用連線池復用連線就能解決這個問題。
減少記憶體碎片的產生
提高記憶體的使用頻率
造成記憶體的浪費
池化技術:把一些能夠復用的東西(比如說資料庫連線、執行緒)放到池中,避免重複建立、銷毀的開銷,從而極大提高效能。
在開發過程中我們會用到很多的連線池,像是資料庫連線池、http 連線池、redis 連線池等等。而連線池的管理是連線池設計的核心,我就以資料庫連線池為例,來說明一下連線池管理的關鍵點。
資料庫連線池有兩個最重要的配置:最小連線數和最大連線數,它們控制著從連線池中獲取連線的流程:
資料庫故障的原因可能有下面幾種:
那麼怎麼去避免這種錯誤呢?
jdk 1.5 中引入的 threadpoolexecutor 就是一種執行緒池的實現,它有兩個重要的引數:corethreadcount 和 maxthreadcount,這兩個引數控制著執行緒池的執行過程。它的執行原理類似如下:
這個任務處理流程看似簡單,實際上有很多坑,你在使用的時候一定要注意。
首先, jdk 實現的這個執行緒池優先把任務放入佇列暫存起來,而不是建立更多的執行緒,**它比較適用於執行 cpu 密集型的任務,也就是需要執行大量 cpu 運算的任務。**這是為什麼呢?因為執行 cpu 密集型的任務時 cpu 比較繁忙,因此只需要建立和 cpu 核數相當的執行緒就好了,多了反而會造成執行緒上下文切換,降低任務執行效率。所以當當前執行緒數超過核心執行緒數時,執行緒池不會增加執行緒,而是放在佇列裡等待核心執行緒空閒下來。
但是,我們平時開發的 web 系統通常都有大量的 io 操作,比方說查詢資料庫、查詢快取等等。任務在執行 io 操作的時候 cpu 就空閒了下來,這時如果增加執行任務的執行緒數而不是把任務暫存在佇列中,就可以在單位時間內執行更多的任務,大大提高了任務執行的吞吐量。所以你看 tomcat 使用的執行緒池就不是 jdk 原生的執行緒池,而是做了一些改造,當執行緒數超過 corethreadcount 之後會優先建立執行緒,直到執行緒數到達 maxthreadcount,這樣就比較適合於 web 系統大量 io 操作的場景了,你在實際運用過程中也可以參考借鑑。
其次,執行緒池中使用的佇列的堆積量也是我們需要監控的重要指標,對於實時性要求比較高的任務來說,這個指標尤為關鍵。
最後,如果你使用執行緒池請一定記住不要使用無界佇列(即沒有設定固定大小的佇列)。也許你會覺得使用了無界佇列後,任務就永遠不會被丟棄,只要任務對實時性要求不高,反正早晚有消費完的一天。但是,大量的任務堆積會占用大量的記憶體空間,一旦記憶體空間被佔滿就會頻繁地觸發 full gc,造成服務不可用,我之前排查過的一次 gc 引起的宕機,起因就是系統中的乙個執行緒池使用了無界佇列。
這時,你回顧一下這兩種技術,會發現它們都有乙個共同點:它們所管理的物件,無論是連線還是執行緒,它們的建立過程都比較耗時,也比較消耗系統資源。所以,我們把它們放在乙個池子裡統一管理起來,以達到提公升效能和資源復用的目的。
這是一種常見的軟體設計思想,叫做池化技術,它的核心思想是空間換時間,期望使用預先建立好的物件來減少頻繁建立物件的效能開銷,同時還可以對物件進行統一的管理,降低了物件的使用的成本,總之是好處多多。
不過,池化技術也存在一些缺陷,比方說儲存池子中的物件肯定需要消耗多餘的記憶體,如果物件沒有被頻繁使用,就會造成記憶體上的浪費。再比方說,池子中的物件需要在系統啟動的時候就預先建立完成,這在一定程度上增加了系統啟動時間。
可這些缺陷相比池化技術的優勢來說就比較微不足道了,只要我們確認要使用的物件在建立時確實比較耗時或者消耗資源,並且這些物件也確實會被頻繁地建立和銷毀,我們就可以使用池化技術來優化。
連線池和執行緒池你並不陌生,不過你可能對它們的原理和使用方式上還存在困惑或者誤區,我在面試時,就發現有很多的同學對執行緒池的基本使用方式都不了解。借用這節課,我想再次強調的重點是:
池化技術
什麼是稀疏資料?什麼是池化?
一 稀疏資料 在資料庫中,稀疏資料是指在二維表中含有大量空值的資料 即稀疏資料是指,在資料集中絕大多數數值缺失或者為零的資料。稀疏資料絕對不是無用資料,只不過是資訊不完全,通過適當的手段是可以挖掘出大量有用資訊。稀疏資料是指,資料框中絕大多數數值缺失或者為零的資料。在現代社會中,隨著資訊的 式增長,...
什麼是虛擬化技術?
什麼是虛擬化技術?這是個廣而範的問題,今天我就以我自己的見解給大家談談虛擬化技術。關於虛擬化,這些年來被炒的沸沸揚揚,其實簡單用一句話來概括來說,虛擬化就是模擬實際的硬體環境或者共享硬體環境。虛擬化計算機的本質 虛擬化,原本是指資源的抽象化,也就是單一物理資源的多個邏輯表示,或者多個物理資源的單一邏...
池化技術 執行緒池
執行緒池 任務 等待任務 全域性的任務佇列queue無任務且無銷毀,任務新增函式 等待add的signal 載入任務 add將新建任務指標task賦予queue 執行任務 執行緒池內任一rtn獲取當前佇列的頭 第乙個任務task 執行,並將此任務從佇列中剔除 退出執行緒池內某一rtn 根據實際的程式...