加速JDBC的快捷方法
可以想到的辦法是利用多 CPU 手段采用并行方案來提速,但 Java 的并行程序非常難寫,要考慮資源共享沖突等麻煩事務。
下面介紹使用集算器的并行技術(shù)來提升數(shù)據(jù)庫 JDBC 取數(shù)性能,可以避免 JAVA 硬編碼的復雜性,還能夠方便實現(xiàn)多線程結(jié)果集的合并。適用于:
集算器并行配置
通過集算器進行并行取數(shù)前需要配置集算器的并行屬性。IDE 中通過菜單“工具 - 選項”設置 IDE 支持的最大并行數(shù)量,一般建議最大并行數(shù)不要超過 CPU 核數(shù)。
集算器服務端則需要修改 raqsoftConfig.xml 配置:
單表并行取數(shù)
有時我們查詢的某個表數(shù)據(jù)量較大、時間較長,這時就可以通過集算器針對單表并行取數(shù)提升性能。這里所謂的單表是指通過條件并行讀取一份(單表)數(shù)據(jù)。
全內(nèi)存
假設內(nèi)存可以容納全部要讀取的數(shù)據(jù),并行取數(shù)后再進行下一步運算(全內(nèi)存的計算速度最快)。
舉例
訂單(Orders)有訂單 ID,訂購日期,訂單金額等字段,其中訂單 ID 是遞增的整數(shù)邏輯主鍵。
【計算目標】 并行讀取某時間段內(nèi)訂單數(shù)
面向單表(單條 SQL)并行取數(shù)需要通過參數(shù)將源數(shù)據(jù)拆分多段,建立多個數(shù)據(jù)庫連接并行查詢。往往需要將數(shù)據(jù)盡可能平均拆分以避免查詢時間不均導致任務等待,同時分段參數(shù)盡可能建立在索引字段上以保證分段效率。
集算器實現(xiàn)
集算器參數(shù)
根據(jù)查詢時間段建立腳本參數(shù),查詢起止日期
集算器實現(xiàn)
分段策略(一)基于索引字段分段
基于單表(單 SQL)并行取數(shù)前需要進行數(shù)據(jù)分段,盡量保證每個分段的數(shù)據(jù)平均。而分段參數(shù)盡量基于建立索引的字段(如訂單編號)。之所以要使用索引字段來分段,是因為使用索引并不會真地遍歷整個表,而是直接定位,當數(shù)據(jù)量較大時優(yōu)勢明顯。
集算器腳本
編寫并行取數(shù)腳本,這里按照建立索引的訂單編號進行分段:
| 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 | / 并行數(shù) | |
| 4 | =p.(b+(e-b)*~\p) | / 分段參數(shù)終值 | |
| 5 | =b | A4.to(,p-1).(~+1) | / 分段參數(shù)初值 |
| 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() | / 合并查詢結(jié)果 |
腳本解析:
1、A2 根據(jù)查詢起止日期獲得最大訂單編號和最小訂單編號,用于后面分段
2、B2-C2 將最小訂單號和最大訂單號分別賦值給變量 b 和 e
3、A3 設置并行數(shù),使用并行取數(shù)前應檢查集算器的并行數(shù)配置以及授權(quán)中對并行數(shù)量的許可
4、A4-A5 根據(jù)起止訂單編號和并行數(shù)計算每個并行任務的起止分段參數(shù)(序列)
5、通過 fork 啟動多個(4 個)線程,參數(shù)為分段起止參數(shù)序列,這里可以看到 fork 啟動的線程數(shù)與參數(shù)序列成員數(shù)相同。在集算器中,經(jīng)常將序表、序列作為參數(shù)值參與運算,非常方便
6、B7 為每個線程(子任務)建立數(shù)據(jù)庫連接,需要注意連接必須在 fork 子句中建立,以便為多線程分別使用,若共用一個連接無法起到加速取數(shù)的效果,數(shù)據(jù)庫會自動把同一連接上的多個請求改為串行執(zhí)行。因此只有當數(shù)據(jù)庫負擔不重,有足夠多連接可用時才可以使用并行取數(shù)提升性能
7、B8 分別查詢每個分段數(shù)據(jù),查詢結(jié)果返回到 A6 格。這里 fork 子句直接返回查詢結(jié)果(子句最后一行),如果想返回其中某個或某幾個計算值可以顯示使用 return 關(guān)鍵字返回子線程計算結(jié)果
8、返回結(jié)果的 A6 格結(jié)果,4 個線程返回 4 個結(jié)果集
9、A9 合并所有子線程查詢結(jié)果,以便進行下一步計算
基于索引字段進行數(shù)據(jù)分段,并且數(shù)據(jù)分段比較平均時,使用多線程并行查詢數(shù)據(jù)庫幾乎可以獲得線程數(shù)倍(線性)的性能提升。
分段策略(二)基于非索引字段分段
如果數(shù)據(jù)庫負擔不重時,也可以基于非索引字段進行分段(如日期),相對 JDBC 取數(shù)時間,多次遍歷庫表時間也并不是很大,而這樣做的好處是不需要事先查詢數(shù)據(jù)庫以確定起止段界(如最大最小訂單編號)。
集算器腳本
| 1 | =connect(“db”) | |||
| 2 | =n=interval(begin,end) | |||
| 3 | =p=4 | / 并行數(shù) | ||
| 4 | =p.(n*~\p).(elapse(begin,~)) | / 分段參數(shù)終值 | ||
| 5 | =begin | A4.to(,p-1).(after(~,-1)) | / 分段參數(shù)初值 | |
| 6 | fork A5,A4 | |||
| 7 | =connect(“db”) | |||
| 8 | =B7.query@x(“select * from 訂單 where 訂購日期 >=? and 訂購日期 <=?”,A6(1),A6(2)) | |||
| 9 | =A6.conj() | / 合并查詢結(jié)果 |
腳本解析:
與上述使用索引字段訂單 ID 分段不同,這里使用非索引字段訂購日期進行分段。設起止日期為:2012-01-01 和 2015-12-31。
1、A2 計算起止日期查詢參數(shù)間隔天數(shù),用于分段;interval 函數(shù)還可以計算年、季度、月、時、分、秒等間隔,用于日期時間處理很方便
2、A3-A5 根據(jù)并行數(shù)和日期間隔計算分段起止參數(shù)序列
3、A6 根據(jù)參數(shù)序列啟動多線程,B8 完成查詢并將結(jié)果返回到 A6 格,A9 合并查詢結(jié)果
分段策略(三)并行線程數(shù)多于 CPU 核數(shù)
前面我們提到:建議并行任務數(shù)不要超過 CPU 核數(shù),因為更多的任務數(shù)并不會增加并行度,而且還可以避免 CPU 進行線程切換帶來的額外時間開銷。但有時也可以將任務數(shù)設置到遠大于 CPU 核數(shù),可以設置為 CPU 核數(shù)的倍數(shù)個,這樣多 CPU 負載也可以達到動態(tài)平衡,而且某些計算還可以簡化分段。如上述例子中,若只查詢某一年數(shù)據(jù)就可以把線程數(shù)設置為 12(月),從而簡化分段。
集算器提供了多線程任務動態(tài)平衡機制,當任務數(shù)大于并行數(shù)配置時,集算器會自動為計算結(jié)束的線程分配下一個任務,這時可以保證某個線程會多跑幾個小任務,另一個線程只跑少量大任務,達到總體平衡,而不必拘泥于必須把數(shù)據(jù)量平均分配。
集算器腳本
| 1 | fork to(1,12) | ||
| 2 | =connect(“demo”) | ||
| 3 | =B2.query@x(“select * from 訂單 wheremonth( 訂購日期)=? and 訂購日期 >=? and 訂購日期 <=?”,A1,begin,end) | ||
| 4 | =A1.conj() | / 合并查詢結(jié)果 |
腳本解析:
1、A1 根據(jù) 1 到 12 的序列啟動 12 個線程
2、B3 每個線程查詢一個月的數(shù)據(jù)并返回結(jié)果到 A1
關(guān)于 fork 語句
在集算器中,通過 fork 語句可以啟動多個線程實施并行計算,而且集算器還提供了多種 merge 函數(shù)可以很方便合并并行結(jié)果,十分方便。
外存
有時某一條語句(一個表)的數(shù)據(jù)量較大,分段后并行子任務仍然無法全部加載到內(nèi)存中,這時需要使用集算器提供的外存計算機制,基于游標查詢數(shù)據(jù)。
舉例
沿用上面的例子,假設分段后的數(shù)據(jù)量很大需要使用游標分批讀取處理數(shù)據(jù)。
集算器實現(xiàn)
| 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 | / 并行數(shù) | |
| 4 | =p.(b+(e-b)*~\p) | / 分段參數(shù)終值 | |
| 5 | =b|A4.to(,p-1).(~+1) | / 分段參數(shù)初值 | |
| 6 | fork A5,A4 | ||
| 7 | =connect(“db”) | ||
| 8 | =B7.cursor@x(“select * from 訂單 where 訂單 ID>=? and 訂單 ID<=? and 訂購日期 >=? and 訂購日期 <=?”,A6(1),A6(2),begin,end) | ||
| 9 | =A6.mcursor() | / 合并查詢結(jié)果 | |
| 10 | =file(“D:\\ 訂單.txt”).export@t(A9) | / 基于游標寫入文件 |
腳本解析:
1、B8 建立數(shù)據(jù)庫游標,查詢并不真正取數(shù),多個游標返回到 A6 格
2、A9 合并多路游標,接下來就可以當做一個游標繼續(xù)使用
3、A10 基于游標,將查詢數(shù)據(jù)分批寫入文件中。因為各個線程的運行速度無法保證規(guī)律性,所以基于多線程導出數(shù)據(jù)時次序不可控,對數(shù)據(jù)順序有要求時不能使用這個方法。
基于外存游標并行查詢與全內(nèi)存方式非常類似,當內(nèi)存資源較緊張時可以通過外存計算的方式減少內(nèi)存占用。
多表并行取數(shù)
除了通過條件針對單條 SQL(單表)進行并行取數(shù)外,在一些多 SQL 查詢場景(如報表多數(shù)據(jù)集)下仍然可以通過并行同時執(zhí)行多條語句進行取數(shù)。
舉例
有多個查詢 SQL 基于多個表查詢數(shù)據(jù),需要提升查詢性能。
【計算目標】 并行讀取 5 個表數(shù)據(jù),并完成關(guān)聯(lián)
這里我們使用 5 條非常簡單(基于單表)的查詢 SQL,實際業(yè)務中多條SQL 可以任意復雜。
集算器實現(xiàn)
| 1 | =connect(“db”) | ||
| 2 | =”select * from 訂單 where 訂購日期 >=date(‘”/begin/”‘) and 訂購日期 <=date(‘”/end/”‘)” | ||
| 3 | select 訂單 ID, 產(chǎn)品 ID, 單價, 數(shù)量 from 訂單明細 | ||
| 4 | select 客戶 ID, 公司名稱 from 客戶 | ||
| 5 | select 雇員 ID, 姓名 from 雇員 | ||
| 6 | select 產(chǎn)品 ID, 產(chǎn)品名稱 from 產(chǎn)品 | ||
| 7 | fork [A2:A6] | ||
| 8 | =connect(“db”) | ||
| 9 | =B8.query@x(A7) | ||
| 10 | = 訂單 =A7(1) | = 明細 =A7(2) | |
| 11 | = 客戶 =A7(3) | = 雇員 =A7(4) | = 產(chǎn)品 =A7(5) |
| 12 | > 訂單.switch(客戶 ID, 客戶: 客戶 ID; 雇員 ID, 雇員: 雇員 ID) | ||
| 13 | = 明細.switch(訂單 ID, 訂單: 訂單 ID; 產(chǎn)品 ID, 產(chǎn)品: 產(chǎn)品 ID) | ||
| 14 | =A13.new(訂單 ID. 客戶 ID. 公司名稱: 客戶名稱, 訂單 ID. 訂單 ID: 訂單編號, 訂單 ID. 雇員 ID. 姓名: 銷售, 產(chǎn)品 ID. 產(chǎn)品名稱: 產(chǎn)品, 單價: 價格, 數(shù)量) |
腳本解析:
1、A2-A6 為查詢用 SQL 語句串
2、A7 根據(jù)多條 SQL 組成序列啟動多線程(5 個)
3、B9 每個線程執(zhí)行 SQL 查詢數(shù)據(jù)將結(jié)果返回到 A7 格(5 個結(jié)果集組成的序列)
4、A10-C11 通過序號分別獲取 5 個結(jié)果集
5、為了保證完整性,A12-A14 對 5 個結(jié)果集進行關(guān)聯(lián)并通過外鍵屬性化的方式創(chuàng)建結(jié)果序表
以上是集算器并行取數(shù)的部分示例,事實上集算器還可以做更復雜的并行計算和結(jié)果歸并,詳見乾學院。集算器多線程并行的意義在于使用簡單、成本低,相對 JAVA 復雜的多線程編程集算器可以簡單到幾行腳本,相對數(shù)據(jù)庫集群方案集算器的成本更加可控,而且即使部署數(shù)據(jù)庫集群仍然可以使用集算器加速集群單個數(shù)據(jù)庫節(jié)點的取數(shù)速度。
轉(zhuǎn)載于:https://juejin.im/post/5ba48d035188255c89012c96
總結(jié)
以上是生活随笔為你收集整理的加速JDBC的快捷方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电子商务(电销)平台中用户模块(User
- 下一篇: MySQL数据库Keepalived双主