當數據量巨大時,使用大批量隨機鍵值集獲取對應記錄集合,不僅僅考驗數據庫軟件本身,更在于程序員對數據的理解!如何在硬件資源有限的情況下將性能發揮到極致?
本次測試主要針對集算器組表索引實現的批量鍵值取數性能,并與 Oracle 進行同規模運算對比。
按以上數據結構,造出 6 億條記錄的行存組表文件和對應的索引文件:
A1:包含 26 個英文字母和 10 個阿拉伯數字的字符串。
A2、A3:建立結構為 (id,data) 的組表文件,@r 選項表示使用行式存儲方式。
A4:循環 6000 次,循環體B4、B5,每次生成 10 萬條對應結構的記錄,并追加到組表文件。
執行后,生成組表文件:id_600m.ctx
A2:根據組表文件的 id 列,建立組表索引。
執行后,生成組表的索引文件:id_600m.ctx__id_idx
A2:循環一萬次,每次獲取對應組表文件 id 列中的隨機一個,并排序。(可能會有少量重復值,但對測試影響不大)
A4:在組表的 icursor()這個函數中,使用索引 id_idx,以條件 A2.contain(id) 來過濾組表。集算器會自動識別出 A2.contain(id) 這個條件可以使用索引,并會自動將 A2 的內容排序后從前向后查找。
原本希望多次執行后,求得一個平均值作為測試結果。但是發現每執行完畢一次該測試代碼,都會比上一次執行快一些,這里列出從第一次執行該代碼后的 5 次測試查詢耗時:
手動一次次點擊設計器中的執行按鈕,并記錄下查詢耗時,太費勁了。為了找出規律,將代碼改為以下形式:
B7:將循環體中 icursor() 函數每一次查詢的耗時,在 A1 中追加記錄下來。
執行過程中,觀察 A1 中新追加的查詢耗時與上一次的比較,發現經過大約 350 次循環后接近極限值 25 秒。再后續近千次循環中,查詢耗時也都是如此,基本穩定。
難道是集算器對數據進行了緩存?抱著懷疑的態度,重啟了集算器設計器,再次執行了查詢代碼。發現重啟后第一次的查詢耗時也是 25 秒。這樣看來提速的原因和集算器本身并沒有什么直接的關系了。
另一方面,可以想到基于目前測試的數據量,能夠在短時間內完成查詢,部分數據可能已經裝載至內存,那么很可能是 linux 操作系統的文件緩存造成了這個現象。重啟服務器后,再通過集算器設計器來執行查詢,發現耗時又開始從 80 秒左右慢慢減少了。
進一步的測試中,使用了 linux 的 free 命令查看系統內存使用情況。發現每完成一次組表的查詢,其中的 cached 一項就會變大。而隨著 cached 慢慢的變大,查詢的耗時又逐步減少。
在網絡上查詢了一些資料,了解到 Linux 會存在緩存內存,通常叫做 Cache Memory。就是之前使用 free 命令看到其中的 cached 一項,執行 free -h:
當我們讀寫文件的時候,Linux 內核為了提高讀寫效率與速度,會將文件在內存中進行緩存,這部分內存就是 Cache Memory(緩存內存)。即使我們的程序運行結束后,Cache Memory 也不會自動釋放。這就會導致我們在 Linux 系統中程序頻繁讀寫文件后,我們會發現可用物理內存會很少。其實這個緩存內存在我們需要使用內存的時候會自動釋放,所以我們不必擔心沒有內存可用。并且手動去釋放 Cache Memory 也是有辦法的,但此處不再詳細探討。
這個函數涉及數據量有 111G,比機器的物理內存 64G 更大,顯然不可能把所有數據都緩存到內存中,那么到底緩存了哪些數據后就能穩定地提高查詢性能呢?是不是可以事先就把需要這些數據先緩存起來以獲得高性能?請教了高手后,發現果然還有選項可以來預先緩存索引的索引。在使用 icursor()函數查詢之前,對組表索引使用了 T.index@2(idx) 使用了 T.index@3(idx)。代碼如下:
集算器的索引有個分級緩存,@3 的意思是將索引的第三級緩存先加載進內存。經過 index@3 預處理,第一遍查詢時間也能達到上面查詢數百次后才能達到的極限值。
測試環境、數據結構和規模與上文一致,測試對象如下:
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 進行查詢的集算器腳本為:
由于 oracle 的 in 個數有限制,腳本中進行分批執行后合并。
使用 10 線程連接 Oracle 進行查詢的集算器腳本為:
使用單線程對行存組表進行查詢的集算器腳本為:
使用 10 線程對行存組表進行查詢的集算器腳本為:
從 6 億條數據總量中取 1 萬條批量隨機鍵值,在都建立索引的測試結果:
集算器列存采用了數據分塊并壓縮的算法,這樣對于遍歷運算來講,訪問數據量會變小,也就會具有更好的性能。但對于基于索引隨機取數的場景,由于要有額外的解壓過程,而且每次取數都會針對整個分塊,運算復雜度會高很多。因此,從原理上分析,這時候的性能應當會比行存要差。
上述代碼中把生成組表的 create() 函數不用 @r 選項,即可生成列存文件。重復上面的運算,單線程情況下 6 億行中取 1 萬行耗時為 129120 毫秒,比行存方式慢了 6 倍多。不過平均到一行也只有 13 毫秒,對于大多數單條取數的場景仍然有足夠的實用性。
同一份數據不能在遍歷運算和隨機取數這兩方面都達到最優性能,在實際應用中就要根據需求做一下取舍了,一定要追求各種運算的極限性能時,可能就要把數據冗余多份了。
集算器確實也提供了冗余索引機制,可以用于提高列存數據的隨機訪問性能,代碼如下:
在對組表建立索引時,當 index 函數有數據列名參數,如本例 A2 中的 data,就會在建索引時把數據列 data 復制進索引。當有多個數據列時,可以寫為:index(id_idx;id;data1,data2,…)
因為在索引中做了冗余,索引文件也自然會較大,本文中測試的列存組表和索引冗余后的文件大小為:
當數據復制進索引后,實際上讀取時不再訪問原數據文件了。
從 6 億條數據總量中取 1 萬條批量隨機鍵值,完整的測試結果對比:
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。