批量隨機鍵值查詢測試 集算器
【摘要】
當資料量巨大時,使用大批量隨機鍵值集獲取對應記錄集合,不僅僅考驗資料庫軟體本身,更在於程式設計師對資料的理解!如何在硬體資源有限的情況下將效能發揮到極致?點選:批量隨機鍵值查詢測試,來乾學院一探究竟!
複製摘要
本次測試主要針對集算器組表索引實現的批量鍵值取數效能,並與 Oracle 進行同規模運算對比。
一、測試環境
處理器 | Intel(R) Xeon(R) CPU E5-2670 @ 2.60GHz兩顆 |
記憶體 | 64G |
硬碟 | SAS 1TB |
作業系統 | centos6.8(64 位) |
二、資料描述
2.1資料結構
欄位 | 型別 | 備註 |
id | long | 1000000000001開始自增 |
data | string | 隨機字串(長度為 180 位元組) |
2.2資料規模
按以上資料結構,造出 6 億條記錄的行存組表文件和對應的索引檔案:
型別 | 檔名 | 大小 |
組表 | id_600m.ctx | 111G |
索引 | id_600m.ctx__id_idx | 14G |
三、測試過程
3.1生成測試檔案
3.1.1 建組表
A | B | |
1 | 1234567890qwertyuiopasdfghjklzxcvbnm | |
2 | =file("id_600m.ctx") | |
3 | ||
4 | for 6000 | =to((A4-1)*100000+1,A4*100000).new(~+1000000000000:id,rands(A1,180):data) |
5 | =A3.append(B4.cursor()) |
A1:包含 26 個英文字母和 10 個阿拉伯數字的字串。
A2、A3:建立結構為 (id,data) 的組表文件,@r 選項表示使用行式儲存方式。
A4:迴圈 6000 次,迴圈體B4、B5,每次生成 10 萬條對應結構的記錄,並追加到組表文件。
執行後,生成組表文件:id_600m.ctx
3.1.2 建索引
A | |
1 | =file("id_600m.ctx ") |
2 | =A1.create().index(id_idx;id) |
A2:根據組表文件的 id 列,建立組表索引。
執行後,生成組表的索引檔案:id_600m.ctx__id_idx
3.2查詢測試
A | |
1 | =file("id_600m.ctx").create() |
2 | =10000.(1000000000000+(rand(600000000)+1)).sort() |
3 | =now() |
4 | =A1.icursor(A2.contain(id),id_idx).fetch() |
5 | [email protected](A3,now()) |
A2:迴圈一萬次,每次獲取對應組表文件 id 列中的隨機一個,並排序。(可能會有少量重複值,但對測試影響不大)
A4:在組表的 icursor()這個函式中,使用索引 id_idx,以條件 A2.contain(id) 來過濾組表。集算器會自動識別出 A2.contain(id) 這個條件可以使用索引,並會自動將 A2 的內容排序後從前向後查詢。
3.3奇怪的現象
原本希望多次執行後,求得一個平均值作為測試結果。但是發現每執行完畢一次該測試程式碼,都會比上一次執行快一些,這裡列出從第一次執行該程式碼後的 5 次測試查詢耗時:
次數 | 查詢耗時(毫秒) |
1 | 79778 |
2 | 78391 |
3 | 78186 |
4 | 76192 |
5 | 74244 |
手動一次次點選設計器中的執行按鈕,並記錄下查詢耗時,太費勁了。為了找出規律,將程式碼改為以下形式:
A | B | |
1 | ||
2 | for | =file("id_600m.ctx").create() |
3 | =10000.(1000000000000+(rand(600000000)+1)).sort() | |
4 | =now() | |
5 | =B2.icursor(B3.contain(id),id_idx).fetch() | |
6 | [email protected](B4,now()) | |
7 | >A1=A1|B6 |
B7:將迴圈體中 icursor() 函式每一次查詢的耗時,在 A1 中追加記錄下來。
執行過程中,觀察 A1 中新追加的查詢耗時與上一次的比較,發現經過大約 350 次迴圈後接近極限值 25 秒。再後續近千次迴圈中,查詢耗時也都是如此,基本穩定。
難道是集算器對資料進行了快取?抱著懷疑的態度,重啟了集算器設計器,再次執行了查詢程式碼。發現重啟後第一次的查詢耗時也是 25 秒。這樣看來提速的原因和集算器本身並沒有什麼直接的關係了。
另一方面,可以想到基於目前測試的資料量,能夠在短時間內完成查詢,部分資料可能已經裝載至記憶體,那麼很可能是 linux 作業系統的檔案快取造成了這個現象。重啟伺服器後,再通過集算器設計器來執行查詢,發現耗時又開始從 80 秒左右慢慢減少了。
進一步的測試中,使用了 linux 的 free 命令檢視系統記憶體使用情況。發現每完成一次組表的查詢,其中的 cached 一項就會變大。而隨著 cached 慢慢的變大,查詢的耗時又逐步減少。
[email protected]的使用
在網路上查詢了一些資料,瞭解到 Linux 會存在快取記憶體,通常叫做 Cache Memory。就是之前使用 free 命令看到其中的 cached 一項,執行 free -h:
total | used | free | shared | buffers | cached | |
Mem: | 62G | 62G | 519M | 9.1M | 10M | 45G |
當我們讀寫檔案的時候,Linux 核心為了提高讀寫效率與速度,會將檔案在記憶體中進行快取,這部分記憶體就是 Cache Memory(快取記憶體)。即使我們的程式執行結束後,Cache Memory 也不會自動釋放。這就會導致我們在 Linux 系統中程式頻繁讀寫檔案後,我們會發現可用實體記憶體會很少。其實這個快取記憶體在我們需要使用記憶體的時候會自動釋放,所以我們不必擔心沒有記憶體可用。並且手動去釋放 Cache Memory 也是有辦法的,但此處不再詳細探討。
這個函式涉及資料量有 111G,比機器的實體記憶體 64G 更大,顯然不可能把所有資料都快取到記憶體中,那麼到底快取了哪些資料後就能穩定地提高查詢效能呢?是不是可以事先就把需要這些資料先快取起來以獲得高效能?請教了高手後,發現果然還有選項可以來預先快取索引的索引。在使用 icursor()函式查詢之前,對組表索引使用了 [email protected](idx) 使用了 [email protected](idx)。程式碼如下:
A | |
1 | =file("id_600m.ctx").create() |
2 | =now() |
3 | [email protected](id_idx) |
4 | [email protected](A2,now()) |
5 | =10000.(1000000000000+(rand(600000000)+1)).sort() |
6 | =now() |
7 | =A1.icursor(A5.contain(id),id_idx).fetch() |
8 | [email protected](A6,now()) |
集算器的索引有個分級快取,@3 的意思是將索引的第三級快取先載入進記憶體。經過 [email protected] 預處理,第一遍查詢時間也能達到上面查詢數百次後才能達到的極限值。
四、與 Oracle 對比
測試環境、資料結構和規模與上文一致,測試物件如下:
產品 | 版本 |
Oracle資料庫 | Oracle Database 12c Release 1(12.1.0.2.0) |
集算器 | 集算器 V2018 |
Oracle建表語句為:
create table ctx_600m (id number(13),data varchar2(200));
資料由集算器生成同結構的文字檔案後,使用 Oracle 的 SqlLoader 匯入表中。
Oracle建索引語句為:
create unique index idx_id_600m on ctx_600m(id);
使用 Oracle 進行批量隨機取數測試時,我們使用這樣的 SQL:
select * from ctx_600m where id in (…)
使用單執行緒連線 Oracle 進行查詢的集算器指令碼為:
A | |
1 | =10000.(1000000000000+rand(600000000)+1).sort() |
2 | =A1.group((#-1)\1000) |
3 | =connect("oracle12c") |
4 | =now() |
5 | =A2.(A3.query("select * from ctx_600m where id in (?)",~)).conj() |
6 | [email protected](A4,now()) |
7 | >A3.close() |
由於 oracle 的 in 個數有限制,指令碼中進行分批執行後合併。
使用 10 執行緒連線 Oracle 進行查詢的集算器指令碼為:
A | B | |
1 | =10000.(1000000000000+rand(600000000)+1).sort() | |
2 | =A1.group((#-1)\1000) | |
3 | =now() | |
4 | fork A2 | =connect("oracle12c") |
5 | =B4.query("select * from ctx_600m where id in (?)",A4) | |
6 | >B4.close() | |
7 | =A4.conj() | |
8 | [email protected](A3,now()) |
使用單執行緒對行存組表進行查詢的集算器指令碼為:
A | |
1 | =file("id_600m.ctx").create() |
2 | =now() |
3 | [email protected](id_idx) |
4 | [email protected](A2,now()) |
5 | =10000.(1000000000000+(rand(600000000)+1)).sort() |
6 | =now() |
7 | =A1.icursor(A5.contain(id),id_idx).fetch() |
8 | [email protected](A6,now()) |
使用 10 執行緒對行存組表進行查詢的集算器指令碼為:
A | B | |
1 | =file("id_600m.ctx").create() | |
2 | =now() | |
3 | [email protected](id_idx) | |
4 | [email protected](A2,now()) | |
5 | =10000.(1000000000000+(rand(600000000)+1)).sort() | |
6 | =A5.group((#-1)\1000) | |
7 | =now() | |
8 | fork A6 | =A1.icursor(A8.contain(id),id_idx) |
9 | =B8.fetch() | |
10 | return B9 | |
11 | =A8.conj() | |
12 | [email protected](A7,now()) |
從 6 億條資料總量中取 1 萬條批量隨機鍵值,在都建立索引的測試結果:
耗時(毫秒) | |||
單執行緒 | 多執行緒(10 執行緒) | ||
Oracle | 集算器組表 | Oracle | 集算器組表 |
117322 | 20745 | 39549 | 10975 |
五、列存索引測試
集算器列存採用了資料分塊並壓縮的演算法,這樣對於遍歷運算來講,訪問資料量會變小,也就會具有更好的效能。但對於基於索引隨機取數的場景,由於要有額外的解壓過程,而且每次取數都會針對整個分塊,運算複雜度會高很多。因此,從原理上分析,這時候的效能應當會比行存要差。
上述程式碼中把生成組表的 create() 函式不用 @r 選項,即可生成列存檔案。重複上面的運算,單執行緒情況下 6 億行中取 1 萬行耗時為 129120 毫秒,比行存方式慢了 6 倍多。不過平均到一行也只有 13 毫秒,對於大多數單條取數的場景仍然有足夠的實用性。
同一份資料不能在遍歷運算和隨機取數這兩方面都達到最優效能,在實際應用中就要根據需求做一下取捨了,一定要追求各種運算的極限效能時,可能就要把資料冗餘多份了。
六、索引冗餘機制
集算器確實也提供了冗餘索引機制,可以用於提高列存資料的隨機訪問效能,程式碼如下:
A | |
1 | =file("id_600m.ctx") |
2 | =A1.create().index(id_data_idx;id;data) |
在對組表建立索引時,當 index 函式有資料列名引數,如本例 A2 中的 data,就會在建索引時把資料列 data 複製進索引。當有多個數據列時,可以寫為:index(id_idx;id;data1,data2,…)
因為在索引中做了冗餘,索引檔案也自然會較大,本文中測試的列存組表和索引冗餘後的檔案大小為:
型別 | 檔名 | 大小 |
組表 | id_600m.ctx | 105G |
索引 | id_600m.ctx__id_data_idx | 112G |
當資料複製進索引後,實際上讀取時不再訪問原資料檔案了。
從 6 億條資料總量中取 1 萬條批量隨機鍵值,完整的測試結果對比:
耗時(毫秒) | |||||
單執行緒 | 多執行緒(10 執行緒) | ||||
Oracle | 行存索引 | 冗餘索引 | Oracle | 行存索引 | 冗餘索引 |
117322 | 20745 | 19873 | 39549 | 10975 | 9561 |
作者:tossman
連結:http://c.raqsoft.com.cn/article/1543371487193
來源:乾學院
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。