MergeTree 允許您依據(jù)主鍵和日期創(chuàng)建索引,并進(jìn)行實時的數(shù)據(jù)更新操作。MergeTree 是 ClickHouse 里最為先進(jìn)的表引擎。請注意不要將 MergeTree 跟 Merge引擎混淆。
MergeTree 引擎在創(chuàng)建時接收以下4個參數(shù),
- 日期字段的名稱 (索引字段)
- 采樣表達(dá)式 (可選的)
- 含有主鍵相關(guān)字段的元組
- 稀疏索引的粒度(見下文)。
不使用采樣表達(dá)式的例子:
MergeTree(EventDate, (CounterID, EventDate), 8192)*
使用采樣表達(dá)式的例子:
MergeTree(EventDate, intHash32(UserID), (CounterID, EventDate, intHash32(UserID)), 8192)*
以 MergeTree 作為引擎的數(shù)據(jù)表必須含有一個獨立的 Date 字段。比如說, EventDate 字段。這個日期字段必須是 Date 類型的(非 DateTime 類型)。
主鍵可以是任意表達(dá)式構(gòu)成的元組(通常是列名稱的元組),或者是單獨一個字段。
抽樣表達(dá)式(可選的)可以是任意表達(dá)式。這個表達(dá)式必須在主鍵中。上面的例子使用了 CounterID 的哈希 intHash32 作為采樣表達(dá)式,旨在近乎隨機(jī)地在 CounterID 和 EventDate 內(nèi)打亂數(shù)據(jù)條目。換而言之,當(dāng)我們在查詢中使用 SAMPLE 子句時,我們就可以得到一個近乎隨機(jī)分布的用戶列表。
數(shù)據(jù)表將數(shù)據(jù)分割為小的索引塊作為單位進(jìn)行處理。 每個索引塊之間依照主鍵排序。每個索引塊記錄了指定的開始日期和結(jié)束日期。在您插入數(shù)據(jù)時,MergeTree 就會對數(shù)據(jù)進(jìn)行排序處理,以保證存儲在索引塊內(nèi)的數(shù)據(jù)有序。 索引塊之間的合并過程會在系統(tǒng)后臺定期自動執(zhí)行。MergeTree 引擎會選擇幾個相鄰的索引塊進(jìn)行合并(通常是較小的索引塊), 然后對二者合并、排序。
具體而言, 向 MergeTree 表中插入數(shù)據(jù)時,引擎會首先對新數(shù)據(jù)執(zhí)行遞增排序而保存索引塊;其后,數(shù)據(jù)索引塊之間又會進(jìn)一步合并,以減少總體索引塊數(shù)量。 因此,合并過程本身并無過多排序工作。
向 MergeTree 插入數(shù)據(jù)時,不同月份的數(shù)據(jù)會被自動分散在不同索引塊中。不同月份的索引塊不會被合并。這是為了便于本地化數(shù)據(jù)修改(以及備份)。
索引塊合并時設(shè)有體積上限,以避免索引塊合并產(chǎn)生龐大的新索引塊。
除了保存索引塊中的數(shù)據(jù)外, 引擎會額外保存一個索引文件,以儲存每 index_granularity 行的主鍵值和對應(yīng)位置,這就構(gòu)成了對有序數(shù)據(jù)的稀疏的索引。
對列而言,MergeTree 在每index_granularity行的位置也寫入了標(biāo)記,從而確定數(shù)據(jù)所在的范圍,以便查找。
當(dāng)使用 SELECT 讀取表內(nèi)數(shù)據(jù)時, MergeTree 會判斷是否能夠使用索引。以下兩種情況里,索引將被使用:
- 當(dāng) WHERE 語句或 PREWHERE 語句用于判斷相等或不等判關(guān)系時 (作為子句);
- 或當(dāng) IN 語句的對象為主鍵或者 Date 或者 它們之間的邏輯關(guān)系。
因此, MergeTree 能夠快速查詢一個或多個主鍵范圍的值。在下面的示例中,MergeTree 能夠快速的查詢一個明確的 CounterID ,指定范圍的日期區(qū)間里的一個明確的 CounterID ,各種 CounterID 的集合等。
下面例子中的查詢會使用基于日期和主鍵的索引。
SELECT count() FROM table WHERE EventDate = toDate(now()) AND CounterID = 34;
SELECT count() FROM table WHERE EventDate = toDate(now()) AND (CounterID = 34 OR CounterID = 42);
SELECT count() FROM table WHERE ((EventDate >= toDate('2014-01-01') AND EventDate <= toDate('2014-01-31')) OR EventDate = toDate('2014-05-01')) AND CounterID IN (101500, 731962, 160656) AND (CounterID = 101500 OR EventDate != toDate('2014-05-01'));
索引也可以被用在更加復(fù)雜的查詢之中;讀取表的過程是按部就班進(jìn)行的,所以使用索引絕不會比全表搜索耗時。
示例貼:
可以看到,下面的例子中, MergeTree 無法使用索引。
SELECT count() FROM table WHERE CounterID = 34 OR URL LIKE '%upyachka%'
若要知曉 MergeTree 能否在查詢中使用索引, 請配置系統(tǒng)參數(shù) force_index_by_date、 force_primary_key。
全局的索引之中僅僅保存了單個數(shù)據(jù)索引塊的日期范圍。然而,一個數(shù)據(jù)索引塊可能包含很多日期的數(shù)據(jù)(甚乎整月),MergeTree 在數(shù)據(jù)索引塊內(nèi)部依照主鍵排序,然而用于分組的日期并不一定在數(shù)據(jù)表的首列。因此,在查詢語句中,如果只有日期范圍而沒有限定主鍵范圍,這將可能導(dǎo)致不必要的數(shù)據(jù)讀取。
對于并發(fā)查詢, MergeTree 使用了多版本管理 : 當(dāng)我們試圖同時讀取、寫入數(shù)據(jù)時,查詢操作將會在已經(jīng)插入完畢的索引塊中進(jìn)行,而排除沒有寫入完畢的索引塊,正在被寫入的塊因而不會受到干擾,這個過程沒有使用任何鎖機(jī)制,同時插入操作不會阻塞讀取操作。
對 MergeTree 進(jìn)行讀取的操作會在引擎內(nèi)部自動的被并行執(zhí)行。
MergeTree 支持 OPTIMIZE 語句,它會調(diào)用額外的合并步驟。
它可以管理一張很大的數(shù)據(jù)表,我們也可以小批量、連續(xù)地向其添加數(shù)據(jù),這正是 MergeTree 設(shè)計之初衷。
它支持?jǐn)?shù)據(jù)備份功能,具體見 數(shù)據(jù)副本一章。