您好,登錄后才能下訂單哦!
JAVA 應用必須通過 JDBC 從數據庫中取數,有時候我們會發現,數據庫的負擔一點也不重而且 SQL 很簡單,但取數的速度仍然很慢。仔細測試會發現,性能瓶頸主要在 JDBC 上,比如 MySQL 的 JDBC 性能就非常差,Oracle 也不好。但是,JDBC 是數據庫廠商提供的包,我們在外部沒辦法提高性能。
可以想到的辦法是利用多 CPU 手段采用并行方案來提速,但 Java 的并行程序非常難寫,要考慮資源共享沖突等麻煩事務。
下面介紹使用集算器的并行技術來提升數據庫 JDBC 取數性能,可以避免 JAVA 硬編碼的復雜性,還能夠方便實現多線程結果集的合并。適用于:
源數據規模較大的查詢報表
多數據集報表
ETL 數據抽取
通過集算器進行并行取數前需要配置集算器的并行屬性。IDE 中通過菜單“工具 - 選項”設置 IDE 支持的最大并行數量,一般建議最大并行數不要超過 CPU 核數。
集算器服務端則需要修改 raqsoftConfig.xml 配置:
有時我們查詢的某個表數據量較大、時間較長,這時就可以通過集算器針對單表并行取數提升性能。這里所謂的單表是指通過條件并行讀取一份(單表)數據。
假設內存可以容納全部要讀取的數據,并行取數后再進行下一步運算(全內存的計算速度最快)。
訂單(Orders)有訂單 ID,訂購日期,訂單金額等字段,其中訂單 ID 是遞增的整數邏輯主鍵。
【計算目標】 并行讀取某時間段內訂單數
面向單表(單條 SQL)并行取數需要通過參數將源數據拆分多段,建立多個數據庫連接并行查詢。往往需要將數據盡可能平均拆分以避免查詢時間不均導致任務等待,同時分段參數盡可能建立在索引字段上以保證分段效率。
集算器參數
根據查詢時間段建立腳本參數,查詢起止日期
集算器實現
分段策略(一)基于索引字段分段
基于單表(單 SQL)并行取數前需要進行數據分段,盡量保證每個分段的數據平均。而分段參數盡量基于建立索引的字段(如訂單編號)。之所以要使用索引字段來分段,是因為使用索引并不會真地遍歷整個表,而是直接定位,當數據量較大時優勢明顯。
集算器腳本
編寫并行取數腳本,這里按照建立索引的訂單編號進行分段:
A | B | C | |
---|---|---|---|
1 | =connect(“db”) | ||
2 | =A1.query(“select min( 訂單 ID) 最小 ID,max(訂單 ID) 最大 ID from 訂單 where 訂購日期 >=? and 訂購日期 <=?”,begin,end) | =b=A2. 最小 ID | =e=A2. 最大 ID |
3 | =p=4 | / 并行數 | |
4 | =p.(b+(e-b)*~\p) | / 分段參數終值 | |
5 | =b | A4.to(,p-1).(~+1) | / 分段參數初值 |
6 | fork A5,A4 | ||
7 | =connect(“db”) | ||
8 | =B7.query@x(“select * from 訂單 where 訂單 ID>=? and 訂單 ID<=? and 訂購日期 >=? and 訂購日期 <=?”,A6(1),A6(2),begin,end) | ||
9 | =A6.conj() | / 合并查詢結果 |
腳本解析:
1、A2 根據查詢起止日期獲得最大訂單編號和最小訂單編號,用于后面分段
2、B2-C2 將最小訂單號和最大訂單號分別賦值給變量 b 和 e
3、A3 設置并行數,使用并行取數前應檢查集算器的并行數配置以及授權中對并行數量的許可
4、A4-A5 根據起止訂單編號和并行數計算每個并行任務的起止分段參數(序列)
5、通過 fork 啟動多個(4 個)線程,參數為分段起止參數序列,這里可以看到 fork 啟動的線程數與參數序列成員數相同。在集算器中,經常將序表、序列作為參數值參與運算,非常方便
6、B7 為每個線程(子任務)建立數據庫連接,需要注意連接必須在 fork 子句中建立,以便為多線程分別使用,若共用一個連接無法起到加速取數的效果,數據庫會自動把同一連接上的多個請求改為串行執行。因此只有當數據庫負擔不重,有足夠多連接可用時才可以使用并行取數提升性能
7、B8 分別查詢每個分段數據,查詢結果返回到 A6 格。這里 fork 子句直接返回查詢結果(子句最后一行),如果想返回其中某個或某幾個計算值可以顯示使用 return 關鍵字返回子線程計算結果
8、返回結果的 A6 格結果,4 個線程返回 4 個結果集
2、A9 合并多路游標,接下來就可以當做一個游標繼續使用
3、A10 基于游標,將查詢數據分批寫入文件中。因為各個線程的運行速度無法保證規律性,所以基于多線程導出數據時次序不可控,對數據順序有要求時不能使用這個方法。
基于外存游標并行查詢與全內存方式非常類似,當內存資源較緊張時可以通過外存計算的方式減少內存占用。
除了通過條件針對單條 SQL(單表)進行并行取數外,在一些多 SQL 查詢場景(如報表多數據集)下仍然可以通過并行同時執行多條語句進行取數。
有多個查詢 SQL 基于多個表查詢數據,需要提升查詢性能。
【計算目標】 并行讀取 5 個表數據,并完成關聯
這里我們使用 5 條非常簡單(基于單表)的查詢 SQL,實際業務中多條SQL 可以任意復雜。
A | B | C | |
---|---|---|---|
1 | =connect(“db”) | ||
2 | =”select * from 訂單 where 訂購日期 >=date(‘”/begin/”‘) and 訂購日期 <=date(‘”/end/”‘)” | ||
3 | select 訂單 ID, 產品 ID, 單價, 數量 from 訂單明細 | ||
4 | select 客戶 ID, 公司名稱 from 客戶 | ||
5 | select 雇員 ID, 姓名 from 雇員 | ||
6 | select 產品 ID, 產品名稱 from 產品 | ||
7 | fork [A2:A6] | ||
8 | =connect(“db”) | ||
9 | =B8.query@x(A7) | ||
10 | = 訂單 =A7(1) | = 明細 =A7(2) | |
11 | = 客戶 =A7(3) | = 雇員 =A7(4) | = 產品 =A7(5) |
12 | > 訂單.switch(客戶 ID, 客戶: 客戶 ID; 雇員 ID, 雇員: 雇員 ID) | ||
13 | = 明細.switch(訂單 ID, 訂單: 訂單 ID; 產品 ID, 產品: 產品 ID) | ||
14 | =A13.new(訂單 ID. 客戶 ID. 公司名稱: 客戶名稱, 訂單 ID. 訂單 ID: 訂單編號, 訂單 ID. 雇員 ID. 姓名: 銷售, 產品 ID. 產品名稱: 產品, 單價: 價格, 數量) |
腳本解析:
1、A2-A6 為查詢用 SQL 語句串
2、A7 根據多條 SQL 組成序列啟動多線程(5 個)
3、B9 每個線程執行 SQL 查詢數據將結果返回到 A7 格(5 個結果集組成的序列)
4、A10-C11 通過序號分別獲取 5 個結果集
5、為了保證完整性,A12-A14 對 5 個結果集進行關聯并通過外鍵屬性化的方式創建結果序表
以上是集算器并行取數的部分示例,事實上集算器還可以做更復雜的并行計算和結果歸并。集算器多線程并行的意義在于使用簡單、成本低,相對 JAVA 復雜的多線程編程集算器可以簡單到幾行腳本,相對數據庫集群方案集算器的成本更加可控,而且即使部署數據庫集群仍然可以使用集算器加速集群單個數據庫節點的取數速度。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。