自己在Excel整理了很多想寫的話題,但苦於最近比較忙(其實這是借口)。。。。
上篇文章《.Net中的並行編程-4.實現高性能異步隊列》介紹了異步隊列的實現,本篇文章介紹我實際工作者遇到了處理多線程問題及基於異步隊列底層數據結構的解決方案。
需求如下:1.提供數據服寫入服務供上層應用調用,數據寫入服務處理的吞吐量要達到60w/s每秒,也就是用戶每秒發送60w的數據然后通過數據寫入服務寫到數據庫中(數據庫為公司自主研發的實時數據庫)。
2.盡量簡化上層應用調用服務的復雜度。
一、分析性能瓶頸
1.實時數據庫要求傳入的數據需要帶有ID(類似於關系庫中的自增主鍵),但用戶只傳Name,需要根據Name去實時數據找到對應的數據ID,所以該地方頻繁的與數據庫交互是最大的瓶頸。
2.由於數據庫原始的.Net API是基於底層C++ API 進行的二次開發,需要.Net和C++編程模型之間的轉換(比如.Ne中存數數據用的是List,C++是數組,所以要把.Net中的List對象 轉換為c++中的數組對象),而這個轉換耗時相當大,所以數據轉換操作也是性能瓶頸之一。
二 、解決方案
1.對於性能瓶頸1可以使用本地緩存,本地緩存維護了name和數據庫ID直接的映射關系,所以查找id時,只要去本地緩存查找即可。由於該緩存讀遠遠大於寫,所以緩存部分使用了普通的Dictionary加上讀寫鎖,讀取數據時只要獲取讀鎖,跟新數據時加上寫鎖,所以設計出以下流程。
2.對於性能瓶頸2可以采用並行的方式解決,我們並行模型主要分為三種:數據並行,任務並行,和流水線並行,關於這三種模型的基本介紹可以參照《Intel Threading Building Blocks 編程指南》第二章 並行思維。有的以上基礎理論的支持,所以設計出以下模型。
上圖中,每個操作都是在單獨的線程中進行的,操作與操作之間采用隊列進行解耦。由於流水線模型是典型的空間換時間,每個操作之間累積了大量數據,所以一旦程序出現崩潰,那么導致隊列內的數據全部丟失,所以還要保證系統的高容錯,由於采用了上篇文章中的隊列而且在開始編碼的時候已經考慮到了系統異常處理的細節,所以異常處理和多線程編程的復雜度也大大降低,並且每個操作處理的線程數也可動態適配(其實最理想的情況是根據每個操作的處理能力以及CPU利用率等進行動態負載均衡,由於配置的線程數已盡滿足了需求所以動態負載均衡沒有進行開發)。
三、關於擴展性
如果未來數據量要增加,那么相應的處理能力也要增加。那么最簡單的方案就是動態增加每個處理操作的線程數,由於操作之間采用隊列進行通信,那必然會設計到鎖的問題,而且同時的進行多線程入隊和多線程出隊會造成頻繁的上下文切換並造成性能下降,所以為了避免這種多線程競爭了情況,可采用多流水線,每個流水線的流程都跟上圖一樣,對於流水線直之間的負載均衡可以采用加權平均輪訓,哈希求余等算法。
補充:
流水線模型的總體時間取決於最耗時的操作環節,所以再采用流水線模型時要盡量減少最耗時操作環節的時間,如在本案例中最耗時的環節是寫入數據庫部分,所以在進行編碼和設計時對該部分進行了重點優化。