Cassandra 文件

版本

您正在檢視預發行版本的說明文件。

SASI 索引

SASIIndex,簡稱 SASI,是 Cassandra 的 Index 介面的實作,可用作現有實作的替代方案。SASI 的索引和查詢透過特別針對 Cassandra 的需求進行調整,進而改善現有實作。在以前需要進行篩選的查詢中,SASI 具有優異的效能。為了達成此效能,SASI 旨在比現有實作消耗更少的資源,包括記憶體、磁碟和 CPU 使用率。此外,SASI 支援字串的前置詞和包含查詢(類似於 SQL 的 LIKE = "foo*"LIKE = "foo"')。

以下內容將說明如何開始使用 SASI,示範如何使用範例,並提供一些實作的詳細資料。

使用 SASI

以下範例將逐步說明如何建立表格和其欄位的索引,以及如何對插入的資料執行查詢。

以下範例假設已建立 demo keyspace 並已使用中。

cqlsh> CREATE KEYSPACE demo WITH replication = {
   ... 'class': 'SimpleStrategy',
   ... 'replication_factor': '1'
   ... };
cqlsh> USE demo;

所有範例都針對 sasi 表格執行

cqlsh:demo> CREATE TABLE sasi (id uuid, first_name text, last_name text,
        ... age int, height int, created_at bigint, primary key (id));

建立索引

若要建立 SASI 索引,請使用 CQL CREATE CUSTOM INDEX 陳述式

cqlsh:demo> CREATE CUSTOM INDEX ON sasi (first_name) USING 'org.apache.cassandra.index.sasi.SASIIndex'
        ... WITH OPTIONS = {
        ... 'analyzer_class':
        ...   'org.apache.cassandra.index.sasi.analyzer.NonTokenizingAnalyzer',
        ... 'case_sensitive': 'false'
        ... };

cqlsh:demo> CREATE CUSTOM INDEX ON sasi (last_name) USING 'org.apache.cassandra.index.sasi.SASIIndex'
        ... WITH OPTIONS = {'mode': 'CONTAINS'};

cqlsh:demo> CREATE CUSTOM INDEX ON sasi (age) USING 'org.apache.cassandra.index.sasi.SASIIndex';

cqlsh:demo> CREATE CUSTOM INDEX ON sasi (created_at) USING 'org.apache.cassandra.index.sasi.SASIIndex'
        ...  WITH OPTIONS = {'mode': 'SPARSE'};

已建立的索引具有一些選項,可自訂其行為,並可能影響效能。first_name 上的索引不區分大小寫。分析器會在後續範例中進一步說明。NonTokenizingAnalyzer 對於文字不會執行任何分析。每個索引都有一個模式:PREFIXCONTAINSSPARSE,第一個為預設值。last_name 索引是使用 CONTAINS 模式建立的,它會比對字尾上的字詞,而不是只比對字首。以下提供此類範例,您可以在 OnDiskIndex 區段中找到更多詳細資料。created_at 欄位是使用 SPARSE 模式建立的,目的是改善查詢大型、密集數字範圍(例如每毫秒插入資料的時間戳記)的效能。您也可以在 OnDiskIndex 區段中找到 SPARSE 實作的詳細資料。age 索引是使用預設 PREFIX 模式建立的,而且由於欄位是數字,因此未指定大小寫敏感度或文字分析選項。

在插入下列資料並執行 nodetool flush 之後,可以在 Cassandra 的記錄中看到 SASI 執行索引快取至磁碟的動作,儘管不需要直接呼叫快取(請參閱 IndexMemtable 以取得更多詳細資料)。

cqlsh:demo> INSERT INTO sasi (id, first_name, last_name, age, height, created_at)
        ... VALUES (556ebd54-cbe5-4b75-9aae-bf2a31a24500, 'Pavel', 'Yaskevich', 27, 181, 1442959315018);

cqlsh:demo> INSERT INTO sasi (id, first_name, last_name, age, height, created_at)
        ... VALUES (5770382a-c56f-4f3f-b755-450e24d55217, 'Jordan', 'West', 26, 173, 1442959315019);

cqlsh:demo> INSERT INTO sasi (id, first_name, last_name, age, height, created_at)
        ... VALUES (96053844-45c3-4f15-b1b7-b02c441d3ee1, 'Mikhail', 'Stepura', 36, 173, 1442959315020);

cqlsh:demo> INSERT INTO sasi (id, first_name, last_name, age, height, created_at)
        ... VALUES (f5dfcabe-de96-4148-9b80-a1c41ed276b4, 'Michael', 'Kjellman', 26, 180, 1442959315021);

cqlsh:demo> INSERT INTO sasi (id, first_name, last_name, age, height, created_at)
        ... VALUES (2970da43-e070-41a8-8bcb-35df7a0e608a, 'Johnny', 'Zhang', 32, 175, 1442959315022);

cqlsh:demo> INSERT INTO sasi (id, first_name, last_name, age, height, created_at)
        ... VALUES (6b757016-631d-4fdb-ac62-40b127ccfbc7, 'Jason', 'Brown', 40, 182, 1442959315023);

cqlsh:demo> INSERT INTO sasi (id, first_name, last_name, age, height, created_at)
        ... VALUES (8f909e8a-008e-49dd-8d43-1b0df348ed44, 'Vijay', 'Parthasarathy', 34, 183, 1442959315024);

cqlsh:demo> SELECT first_name, last_name, age, height, created_at FROM sasi;

 first_name | last_name     | age | height | created_at
------------+---------------+-----+--------+---------------
    Michael |      Kjellman |  26 |    180 | 1442959315021
    Mikhail |       Stepura |  36 |    173 | 1442959315020
      Jason |         Brown |  40 |    182 | 1442959315023
      Pavel |     Yaskevich |  27 |    181 | 1442959315018
      Vijay | Parthasarathy |  34 |    183 | 1442959315024
     Jordan |          West |  26 |    173 | 1442959315019
     Johnny |         Zhang |  32 |    175 | 1442959315022

(7 rows)

相等性與字首查詢

SASI 支援 CQL 已支援的所有查詢,包括用於 PREFIX、CONTAINS 和 SUFFIX 搜尋的 LIKE 陳述式。

cqlsh:demo> SELECT first_name, last_name, age, height, created_at FROM sasi
        ... WHERE first_name = 'Pavel';

  first_name | last_name | age | height | created_at
-------------+-----------+-----+--------+---------------
       Pavel | Yaskevich |  27 |    181 | 1442959315018

(1 rows)
cqlsh:demo> SELECT first_name, last_name, age, height, created_at FROM sasi
       ... WHERE first_name = 'pavel';

  first_name | last_name | age | height | created_at
-------------+-----------+-----+--------+---------------
       Pavel | Yaskevich |  27 |    181 | 1442959315018

(1 rows)
cqlsh:demo> SELECT first_name, last_name, age, height, created_at FROM sasi
        ... WHERE first_name LIKE 'M%';

 first_name | last_name | age | height | created_at
------------+-----------+-----+--------+---------------
    Michael |  Kjellman |  26 |    180 | 1442959315021
    Mikhail |   Stepura |  36 |    173 | 1442959315020

(2 rows)

當然,由於在建立索引時提供的選項,first_name 欄位的查詢大小寫無關緊要。

cqlsh:demo> SELECT first_name, last_name, age, height, created_at FROM sasi
        ... WHERE first_name LIKE 'm%';

 first_name | last_name | age | height | created_at
------------+-----------+-----+--------+---------------
    Michael |  Kjellman |  26 |    180 | 1442959315021
    Mikhail |   Stepura |  36 |    173 | 1442959315020

(2 rows)

複合查詢

SASI 支援具有多個謂詞的查詢,但由於預設索引實作的特性,CQL 要求使用者指定 ALLOW FILTERING,以選擇加入此類查詢的潛在效能陷阱。使用 SASI 時,儘管仍需要包含 ALLOW FILTERING,但由於過濾未執行,因此效能陷阱不存在,以減少對語法的修改。SASI 如何從多個謂詞加入資料的詳細資料,請參閱下方的 實作詳細資料 區段。

cqlsh:demo> SELECT first_name, last_name, age, height, created_at FROM sasi
        ... WHERE first_name LIKE 'M%' and age < 30 ALLOW FILTERING;

 first_name | last_name | age | height | created_at
------------+-----------+-----+--------+---------------
    Michael |  Kjellman |  26 |    180 | 1442959315021

(1 rows)

字尾查詢

下一個範例示範 last_name 欄位上的 CONTAINS 模式。透過使用此模式,謂詞可以搜尋任何包含搜尋字串作為子字串的字串。在此情況下,字串包含 a'' 或 an''。

cqlsh:demo> SELECT * FROM sasi WHERE last_name LIKE '%a%';

 id                                   | age | created_at    | first_name | height | last_name
--------------------------------------+-----+---------------+------------+--------+---------------
 f5dfcabe-de96-4148-9b80-a1c41ed276b4 |  26 | 1442959315021 |    Michael |    180 |      Kjellman
 96053844-45c3-4f15-b1b7-b02c441d3ee1 |  36 | 1442959315020 |    Mikhail |    173 |       Stepura
 556ebd54-cbe5-4b75-9aae-bf2a31a24500 |  27 | 1442959315018 |      Pavel |    181 |     Yaskevich
 8f909e8a-008e-49dd-8d43-1b0df348ed44 |  34 | 1442959315024 |      Vijay |    183 | Parthasarathy
 2970da43-e070-41a8-8bcb-35df7a0e608a |  32 | 1442959315022 |     Johnny |    175 |         Zhang

(5 rows)

cqlsh:demo> SELECT * FROM sasi WHERE last_name LIKE '%an%';

 id                                   | age | created_at    | first_name | height | last_name
--------------------------------------+-----+---------------+------------+--------+-----------
 f5dfcabe-de96-4148-9b80-a1c41ed276b4 |  26 | 1442959315021 |    Michael |    180 |  Kjellman
 2970da43-e070-41a8-8bcb-35df7a0e608a |  32 | 1442959315022 |     Johnny |    175 |     Zhang

(2 rows)

非索引欄位上的表達式

SASI 也支援在非索引欄位上進行篩選,例如 height。表達式只能使用 AND 來縮小現有查詢的範圍。

cqlsh:demo> SELECT * FROM sasi WHERE last_name LIKE '%a%' AND height >= 175 ALLOW FILTERING;

 id                                   | age | created_at    | first_name | height | last_name
--------------------------------------+-----+---------------+------------+--------+---------------
 f5dfcabe-de96-4148-9b80-a1c41ed276b4 |  26 | 1442959315021 |    Michael |    180 |      Kjellman
 556ebd54-cbe5-4b75-9aae-bf2a31a24500 |  27 | 1442959315018 |      Pavel |    181 |     Yaskevich
 8f909e8a-008e-49dd-8d43-1b0df348ed44 |  34 | 1442959315024 |      Vijay |    183 | Parthasarathy
 2970da43-e070-41a8-8bcb-35df7a0e608a |  32 | 1442959315022 |     Johnny |    175 |         Zhang

(4 rows)

基於分隔符號的標記化分析

提供的簡單文字分析是基於分隔符號的標記化。這提供了索引集合的替代方案,因為分隔符號分隔的文字可以在沒有 CONTAINS 模式的負擔下進行索引,也不需要使用 PREFIXSUFFIX 查詢。

cqlsh:demo> ALTER TABLE sasi ADD aliases text;
cqlsh:demo> CREATE CUSTOM INDEX on sasi (aliases) USING 'org.apache.cassandra.index.sasi.SASIIndex'
        ... WITH OPTIONS = {
        ... 'analyzer_class': 'org.apache.cassandra.index.sasi.analyzer.DelimiterAnalyzer',
        ... 'delimiter': ',',
        ... 'mode': 'prefix',
        ... 'analyzed': 'true'};
cqlsh:demo> UPDATE sasi SET aliases = 'Mike,Mick,Mikey,Mickey' WHERE id = f5dfcabe-de96-4148-9b80-a1c41ed276b4;
cqlsh:demo> SELECT * FROM sasi WHERE aliases LIKE 'Mikey' ALLOW FILTERING;

 id                                   | age | aliases                | created_at    | first_name | height | last_name
--------------------------------------+-----+------------------------+---------------+------------+--------+-----------
 f5dfcabe-de96-4148-9b80-a1c41ed276b4 |  26 | Mike,Mick,Mikey,Mickey | 1442959315021 |    Michael |    180 |  Kjellman

文字分析(標記化和詞幹提取)

最後,為了展示文字分析,需要在表格上新增一個欄位。其定義、索引和更新列的陳述式如下所示。

cqlsh:demo> ALTER TABLE sasi ADD bio text;
cqlsh:demo> CREATE CUSTOM INDEX ON sasi (bio) USING 'org.apache.cassandra.index.sasi.SASIIndex'
        ... WITH OPTIONS = {
        ... 'analyzer_class': 'org.apache.cassandra.index.sasi.analyzer.StandardAnalyzer',
        ... 'tokenization_enable_stemming': 'true',
        ... 'analyzed': 'true',
        ... 'tokenization_normalize_lowercase': 'true',
        ... 'tokenization_locale': 'en'
        ... };
cqlsh:demo> UPDATE sasi SET bio = 'Software Engineer, who likes distributed systems, doesnt like to argue.' WHERE id = 5770382a-c56f-4f3f-b755-450e24d55217;
cqlsh:demo> UPDATE sasi SET bio = 'Software Engineer, works on the freight distribution at nights and likes arguing' WHERE id = 556ebd54-cbe5-4b75-9aae-bf2a31a24500;
cqlsh:demo> SELECT * FROM sasi;

 id                                   | age | bio                                                                              | created_at    | first_name | height | last_name
--------------------------------------+-----+----------------------------------------------------------------------------------+---------------+------------+--------+---------------
 f5dfcabe-de96-4148-9b80-a1c41ed276b4 |  26 |                                                                             null | 1442959315021 |    Michael |    180 |      Kjellman
 96053844-45c3-4f15-b1b7-b02c441d3ee1 |  36 |                                                                             null | 1442959315020 |    Mikhail |    173 |       Stepura
 6b757016-631d-4fdb-ac62-40b127ccfbc7 |  40 |                                                                             null | 1442959315023 |      Jason |    182 |         Brown
 556ebd54-cbe5-4b75-9aae-bf2a31a24500 |  27 | Software Engineer, works on the freight distribution at nights and likes arguing | 1442959315018 |      Pavel |    181 |     Yaskevich
 8f909e8a-008e-49dd-8d43-1b0df348ed44 |  34 |                                                                             null | 1442959315024 |      Vijay |    183 | Parthasarathy
 5770382a-c56f-4f3f-b755-450e24d55217 |  26 |          Software Engineer, who likes distributed systems, doesnt like to argue. | 1442959315019 |     Jordan |    173 |          West
 2970da43-e070-41a8-8bcb-35df7a0e608a |  32 |                                                                             null | 1442959315022 |     Johnny |    175 |         Zhang

(7 rows)

索引詞和查詢搜尋字串會針對 bio 欄位進行詞幹提取,因為它已設定為使用 StandardAnalyzer,而 analyzed 已設定為 truetokenization_normalize_lowercase 類似於 case_sensitive 屬性,但適用於 StandardAnalyzer。這些查詢展示了 StandardAnalyzer 套用的詞幹提取。

cqlsh:demo> SELECT * FROM sasi WHERE bio LIKE 'distributing';

 id                                   | age | bio                                                                              | created_at    | first_name | height | last_name
--------------------------------------+-----+----------------------------------------------------------------------------------+---------------+------------+--------+-----------
 556ebd54-cbe5-4b75-9aae-bf2a31a24500 |  27 | Software Engineer, works on the freight distribution at nights and likes arguing | 1442959315018 |      Pavel |    181 | Yaskevich
 5770382a-c56f-4f3f-b755-450e24d55217 |  26 |          Software Engineer, who likes distributed systems, doesnt like to argue. | 1442959315019 |     Jordan |    173 |      West

(2 rows)

cqlsh:demo> SELECT * FROM sasi WHERE bio LIKE 'they argued';

 id                                   | age | bio                                                                              | created_at    | first_name | height | last_name
--------------------------------------+-----+----------------------------------------------------------------------------------+---------------+------------+--------+-----------
 556ebd54-cbe5-4b75-9aae-bf2a31a24500 |  27 | Software Engineer, works on the freight distribution at nights and likes arguing | 1442959315018 |      Pavel |    181 | Yaskevich
 5770382a-c56f-4f3f-b755-450e24d55217 |  26 |          Software Engineer, who likes distributed systems, doesnt like to argue. | 1442959315019 |     Jordan |    173 |      West

(2 rows)

cqlsh:demo> SELECT * FROM sasi WHERE bio LIKE 'working at the company';

 id                                   | age | bio                                                                              | created_at    | first_name | height | last_name
--------------------------------------+-----+----------------------------------------------------------------------------------+---------------+------------+--------+-----------
 556ebd54-cbe5-4b75-9aae-bf2a31a24500 |  27 | Software Engineer, works on the freight distribution at nights and likes arguing | 1442959315018 |      Pavel |    181 | Yaskevich

(1 rows)

cqlsh:demo> SELECT * FROM sasi WHERE bio LIKE 'soft eng';

 id                                   | age | bio                                                                              | created_at    | first_name | height | last_name
--------------------------------------+-----+----------------------------------------------------------------------------------+---------------+------------+--------+-----------
 556ebd54-cbe5-4b75-9aae-bf2a31a24500 |  27 | Software Engineer, works on the freight distribution at nights and likes arguing | 1442959315018 |      Pavel |    181 | Yaskevich
 5770382a-c56f-4f3f-b755-450e24d55217 |  26 |          Software Engineer, who likes distributed systems, doesnt like to argue. | 1442959315019 |     Jordan |    173 |      West

(2 rows)

實作細節

雖然 SASI 在表面上只是 Index 介面的實作,但其核心有幾個資料結構和演算法用於滿足它。這些在這裡說明。此外,說明了 Cassandra 內部支援 SASI 整合的變更。

Index 介面將實作者的責任分為兩部分:索引和查詢。此外,Cassandra 使得可以將這些責任分為記憶體和磁碟元件。SASI 利用 Cassandra 的一次寫入、不可變、已排序資料模型,在將記憶表沖洗到磁碟時建立索引,這就是「SSTable 附加次要索引」名稱的由來。

SASI 索引資料結構在撰寫 SSTable 時在記憶體中建立,並在 SSTable 撰寫完成前沖洗到磁碟。每個索引檔案的撰寫只需要對磁碟進行順序寫入。在某些情況下,會執行部分沖洗,然後再縫合在一起,以減少記憶體使用量。這些資料結構針對此使用案例進行最佳化。

在查詢時間利用 Cassandra 的已排序資料模型,縮小候選索引的搜尋範圍,將所做的工作量減到最低。然後使用一種有效率的方法進行搜尋,根據需要從磁碟串流資料。

索引

每個 SSTable,SASI 會為每個索引欄位寫入一個索引檔案。這些檔案的資料會使用 OnDiskIndexBuilder 在記憶體中建立。寫入磁碟後,資料會使用 OnDiskIndex 類別讀取。這些類別由表示索引詞彙的位元組組成,分別針對有效率的寫入或搜尋進行組織。它們所包含的鍵和值代表 SSTable 中的代碼和位置,而且會儲存在 TokenTreeBuilder(用於寫入)和 TokenTree(用於查詢)中,每個索引詞彙一個。這些索引檔案會在寫入磁碟後進行記憶體對應,以加快存取速度。SASI 會使用 IndexMemtable 類別索引記憶表中的資料。

OnDiskIndex(Builder)

每個 OnDiskIndex 都是修改過 後綴陣列資料結構的實例。 OnDiskIndex 由排序詞彙和指向詞彙相關資料的指標的頁面大小區塊組成,以及資料本身,也儲存在一個或多個頁面大小區塊中。 OnDiskIndex 的結構為陣列樹,其中每個層級描述下一個層級的詞彙,最後一個層級為詞彙本身。PointerLevel 和其 PointerBlock 包含詞彙和指向以這些詞彙結尾的其他區塊的指標。DataLevel(最後一個層級)及其 DataBlock 包含詞彙和指向資料本身的指標,資料本身包含在 TokenTree 中。

寫入 OnDiskIndex 的詞彙會根據其 模式而有所不同:PREFIXCONTAINSSPARSE。在 PREFIXSPARSE 的情況下,詞彙的精確值會在每個 OnDiskIndex 中寫入一次。例如,當使用包含詞彙 JasonJordanPavelPREFIX 索引時,這三個詞彙都會包含在索引中。CONTAINS 索引會為每個詞彙的每個後綴遞迴寫入其他詞彙。繼續使用前面的範例,儲存先前詞彙的 CONTAINS 索引也會儲存 asonordanavelsonrdanvel 等。這允許針對字串的後綴進行查詢。SPARSE 模式與 PREFIX 的不同之處在於,對於每 64 個詞彙區塊,會建立一個 TokenTree,將每個詞彙的 TokenTree 合併成一個。這份資料拷貝用於有效率地反覆處理大範圍的資料,例如時間戳記。索引 模式 可在建立索引時針對每個欄位進行設定。

TokenTree(Builder)

TokenTree 是廣為人知的 B+-tree 實作,已針對其使用案例進行修改以進行最佳化。特別是,它已針對將令牌(長整數)與 SSTable 中的一組位置(也是長整數)關聯進行最佳化。允許一組長整數值能容納令牌中的雜湊衝突可能性,但資料結構已針對此類衝突不太可能發生的情況進行最佳化。

為了針對其一次寫入環境進行最佳化,TokenTreeBuilder 會在建置樹時完全載入其內部節點,並使用已針對大量載入資料結構進行最佳化的廣為人知的演算法。

TokenTree 提供方法來反覆運算與特定詞彙相符的令牌和檔案位置,並在反覆運算中向前跳過,這是查詢時間大量使用的操作。

IndexMemtable

IndexMemtable 處理索引化記憶體中儲存在記憶表中的資料。IndexMemtable 會依序管理每欄的 TrieMemIndexSkipListMemIndex。所使用的索引類型選擇取決於資料。TrieMemIndex 用於文字類型。AsciiTypeUTF8Type 預設為文字類型,但任何欄位都可以在建立索引時使用 is_literal 選項設定為文字類型。對於非文字類型,會使用 SkipListMemIndexTrieMemIndex 是一種實作,可以有效支援針對類似字元的資料進行前置查詢。相反地,SkipListMemIndex 更適合其他 Cassandra 資料類型,例如數字。

TrieMemIndex 建置時會使用 com.goooglecode.concurrenttrees 套件中的 ConcurrentRadixTreeConcurrentSuffixTree。這兩個選項的選擇是根據索引模式,分別是 PREFIX 或其他模式,以及 CONTAINS 模式。

SkipListMemIndex 建置在 java.util.concurrent.ConcurrentSkipListSet 之上。

查詢

負責將內部的 IndexExpression 表示轉換成 SASI 的 OperationExpression 樹狀結構,最佳化樹狀結構以減少執行的工作量,並驅動查詢本身,QueryPlan 是 SASI 查詢實作的執行核心。為了有效執行聯集和交集運算,SASI 提供了幾個類似於 Cassandra 的 MergeIterator 的反覆運算器,但針對 SASI 的使用量身打造,同時包含更多功能。RangeUnionIterator,如同其名稱所示,會對符合查詢的代幣/金鑰集合執行集合聯集,僅從每個集合讀取滿足查詢所需資料。RangeIntersectionIterator 類似於其對應項目,會對其資料執行集合交集。

查詢計畫

每個搜尋查詢實例化的 QueryPlan 是 SASI 查詢實作的核心。其工作可分為兩個階段:分析和執行。

在分析階段,QueryPlan 會從 Cassandra 的 IndexExpression 內部表示轉換,該表示也已修改為支援編碼包含 OR 和使用括號對表示式進行分組的查詢(有關更多詳細資訊,請參閱下方的 Cassandra 內部變更 區段)。此程序會產生 Operation 樹狀結構,而 Operation 樹狀結構又可能包含 Expression,所有這些都提供查詢的替代、更有效率的表示。

執行期間,QueryPlan 使用從 Operation 樹狀結構建立的 DecoratedKey 產生反覆運算。這些金鑰會從磁碟讀取,並使用 Operation 樹狀結構再次進行最終檢查,以確保滿足查詢。在找到所需數量相符資料或沒有更多相符資料時,會透過現有的內部元件將結果組傳回給協調器。

每個表格/資料行家族會維護查詢數目(總計/失敗/逾時)及其延遲時間。

SASI 也支援在 SSTable 中同時反覆運算相同索引的詞彙。並行因素由 cassandra.search_concurrency_factor 系統屬性控制。預設為 1

QueryController

每個 QueryPlan 都會參考一個 QueryController,用於整個執行階段。 QueryController 有兩個責任:管理並確保適當清除資源(索引),以及嚴格執行使用者透過範圍區段逾時設定的每個查詢時間限制。所有索引都是透過 QueryController 存取,以便稍後可以安全地釋放。 QueryControllercheckpoint 函式會在執行路徑的特定位置呼叫,以確保執行時間限制。

QueryPlan 最佳化

在分析階段,QueryPlan 會對查詢執行多項潛在最佳化。這些最佳化的目標是減少執行階段執行的作業量。

執行的最簡單最佳化是將多個透過邏輯交集(AND)連接的表達式壓縮成單一 Operation,其中包含三個或更多 Expression。例如,查詢 WHERE age < 100 AND fname = 'p*' AND first_name != 'pa*' AND age > 21 在未修改的情況下,會有以下樹狀結構

                      ┌───────┐
             ┌────────│  AND  │──────┐
             │        └───────┘      │
             ▼                       ▼
          ┌───────┐             ┌──────────┐
    ┌─────│  AND  │─────┐       │age < 100 │
    │     └───────┘     │       └──────────┘
    ▼                   ▼
┌──────────┐          ┌───────┐
│ fname=p* │        ┌─│  AND  │───┐
└──────────┘        │ └───────┘   │
                    ▼             ▼
                ┌──────────┐  ┌──────────┐
                │fname!=pa*│  │ age > 21 │
                └──────────┘  └──────────┘

QueryPlan 會移除多餘的右分支,其根節點為最後一個 AND,葉節點為 fname != pa*age > 21。這些 Expression 會壓縮到父層 AND 中,由於 AND 具有結合性和交換性,因此這項操作是安全的。產生的樹狀結構如下所示

                              ┌───────┐
                     ┌────────│  AND  │──────┐
                     │        └───────┘      │
                     ▼                       ▼
                  ┌───────┐             ┌──────────┐
      ┌───────────│  AND  │────────┐    │age < 100 │
      │           └───────┘        │    └──────────┘
      ▼               │            ▼
┌──────────┐          │      ┌──────────┐
│ fname=p* │          ▼      │ age > 21 │
└──────────┘    ┌──────────┐ └──────────┘
                │fname!=pa*│
                └──────────┘

在使用 != 排除結果集中的結果時,QueryPlan 會決定最佳的處理方法。例如,對於範圍查詢,最佳化的方法可能是將範圍分成多個部分,並在排除部分留一個洞。然而,對於像這樣字串查詢,在掃描索引時,最佳化的方法是簡單地記錄要略過或排除的資料。在這個最佳化之後,樹狀結構如下

                               ┌───────┐
                      ┌────────│  AND  │──────┐
                      │        └───────┘      │
                      ▼                       ▼
                   ┌───────┐             ┌──────────┐
           ┌───────│  AND  │────────┐    │age < 100 │
           │       └───────┘        │    └──────────┘
           ▼                        ▼
    ┌──────────────────┐         ┌──────────┐
    │     fname=p*     │         │ age > 21 │
    │ exclusions=[pa*] │         └──────────┘
    └──────────────────┘

針對此查詢套用的最後一種類型最佳化,當然是在不修改查詢意義的情況下,合併樹狀結構各個分支的範圍表達式。在這個案例中,由於查詢包含所有 AND,因此可以壓縮 age 表達式。隨著這個最佳化,也可以再次套用最初壓縮不必要的 AND,以產生這個最終樹狀結構,用來執行查詢

                        ┌───────┐
                 ┌──────│  AND  │───────┐
                 │      └───────┘       │
                 ▼                      ▼
       ┌──────────────────┐    ┌────────────────┐
       │     fname=p*     │    │ 21 < age < 100 │
       │ exclusions=[pa*] │    └────────────────┘
       └──────────────────┘
運算式和表達式

正如討論的,QueryPlan 會最佳化一個樹狀結構,其內部節點由 Operation 表示,而葉節點由 Expression 表示。更具體地說,Operation 類別可以有 0 個、1 個或 2 個 Operation 作為子節點,以及無限個表達式。用來執行查詢的迭代器(在 `Range(Union|Intersection)Iterator'' 區段中討論)會實作必要的邏輯,以透明地合併結果,而不管 `Operation 的子節點為何。

除了參與 QueryPlan 執行的最佳化之外,Operation 也負責取得查詢傳回的列,並執行最終驗證,以確認它確實相符。這個 satisfiesBy 運算會從 Operation 樹狀結構的根節點,針對特定查詢遞迴執行。這些檢查會直接在特定列的資料上執行。如需有關 satisfiesBy 運作方式的更多詳細資料,請參閱 程式碼中的文件

Range(Union|Intersection)Iterator

抽象的 RangeIterator 類別在執行路徑中的各種層級中提供一個統一的介面,以執行 SASI 執行的兩個主要操作:集合交集和聯集。這些操作會以反覆運算或「串流」的方式執行,以避免不必要的元素讀取。在交集和聯集的情況中,演算法會利用預先使用相同排序順序(例如詞彙或標記順序)排序的資料。

RangeUnionIterator 執行 排序合併聯結 演算法的「合併聯結」部分,並具有外聯結或聯集的特性。它實作了多項最佳化,以提升其在大量反覆運算器(要聯集的集合)上的效能。特別是,反覆運算器會利用資料很可能具有許多重疊範圍的子群組,以及所有範圍不太可能彼此重疊的情況。如需更多詳細資訊,請參閱 javadoc

RangeIntersectionIterator 本身不是 RangeIterator 的子類別。它是多個類別的容器,其中之一為 AbstractIntersectionIterator,是 RangeIterator 的子類別。SASI 支援兩種執行交集操作的方法,以及根據資料的某些屬性在兩者之間進行適應性選擇的能力。

BounceIntersectionIteratorBOUNCE 策略的運作方式類似於 RangeUnionIterator,因為它會執行「合併聯結」,但其性質類似於內聯結,其中相同的值會由資料特定的合併函數合併(例如,將兩個標記合併到清單中,以便稍後在 SSTable 中查詢)。如需更多關於其實作的詳細資訊,請參閱 javadoc

LookupIntersectionIteratorLOOKUP 策略執行不同的操作,更類似於在關聯資料結構中查詢,或在資料庫術語中稱為「雜湊查詢」。同樣地,可以在 javadoc 中找到關於實作的詳細資訊。

在兩個反覆運算器之間的選擇,或 ADAPTIVE 策略,是基於最小範圍和最大範圍的交集資料集大小的比率。如果最小範圍中的元素數量除以元素數量是最大範圍小於或等於 0.01,則 ADAPTIVE 策略會選擇 LookupIntersectionIterator,否則會選擇 BounceIntersectionIterator

SASIIndex 類別

上述元件是由 SASIIndex 類別黏合在一起,該類別實作 Index,並為包含 SASI 索引的每個表格實例化。它透過 sasi.conf.DataTrackersasi.conf.view.View 元件管理表格的所有索引,透過其 PerSSTableIndexWriter 控制 SSTable 的所有索引寫入,並使用 Searcher 初始化搜尋。這些類別將先前提到的索引元件與 Cassandra 的 SSTable 生命週期黏合在一起,確保索引不僅在 Memtable 的快取時寫入,而且在 SSTable 壓縮時也會寫入。對於查詢,Searcher 幾乎沒有作用,但會轉交給 QueryPlan 並更新 SASI 顯示的例如延遲指標。

Cassandra 內部變更

為了支援上述變更,並將它們整合到 Cassandra 中,對 Cassandra 本身進行了一些次要的內部變更。以下說明這些變更。

SSTable 寫入生命週期通知

SSTableFlushObserver 是類似觀察者模式的介面,其子類別可以註冊以接收 SSTable 寫入生命週期中的事件通知。子類別可以在快取開始和結束時接收通知,以及在即將寫入每一個下一列和每一個下一行時接收通知。上面討論的 SASI 的 PerSSTableIndexWriter 是目前唯一的子類別。

限制和注意事項

以下項目可以在未來的更新中處理,但目前無法在這個儲存庫中取得,或尚未實作。

  • 叢集必須設定為使用會產生 LongToken 的分割器,例如 Murmur3Partitioner。其他現有的分割器不會產生 LongToken,例如 ByteOrderedPartitionerRandomPartitioner,無法與 SASI 搭配使用。

  • 此版本已移除不等於和 OR 支援,同時對 Cassandra 本身進行變更以支援這些功能。