Cassandra primary key, clustering key and Secondary Index

最近在Cassandra的使用過程中, 發(fā)現(xiàn)Cassandra的查詢操作異常緩慢(花費了700~900ms), 經(jīng)過排查后發(fā)現(xiàn)是使用了Secondary Index的原因.本文整理了primary key 和 Secondary Index在Cassandra中的存儲方式, 也解釋了為什么使用Secondary Index查詢會非常緩慢.

Primary Key

Cassandra的Primary Key由Partition Key和Clustering Key兩部分組成. 先看只有Partition Key的情況.
例如, 如下建表語句:

CREATE TABLE sample (
  id text,
  name text,
  PRIMARY KEY (id)
);

該表的primary key就是其partition key. 在Cassandra中, 每一個partition key對應(yīng)著一行, 所有的column都存儲在該行中.如下圖所示:

        +-------------+----------+-----------+------------+
        |  partition  |  column  |  column   |   column   |
        |    key 1    |    1     |     2     |      3     |
        +-------------+----------+-----------+------------+
        |  partition  |  column  |  column   |   column   |
        |    key 2    |    1     |     2     |      3     |
        +-------------+----------+-----------+------------+

例如, 在sample中插入若干記錄, 查詢該表:

SELECT * FROM sample;

得到如下結(jié)果:

 id  | name
-----+-------
 id3 | name3
 id1 | name1
 id2 | name2

然后, 再用Cassandra-Cli去查詢, 得到如下結(jié)果:

    RowKey: id3
    => (name=, value=, timestamp=1442491586006000)
    => (name=name, value=6e616d6533, timestamp=1442491586006000)
    -------------------
    RowKey: id1
    => (name=, value=, timestamp=1442491594600000)
    => (name=name, value=6e616d6531, timestamp=1442491594600000)
    -------------------
    RowKey: id2
    => (name=, value=, timestamp=1442491578108000)
    => (name=name, value=6e616d6532, timestamp=1442491578108000)

    3 Rows Returned.
    

可以看到, 每一個partition key對應(yīng)著一個RowKey, 所有的column都存放在這一行上.

Clustering Key

Cassandra中所有的數(shù)據(jù)都只能根據(jù)Primary Key中的字段來排序, 因此, 如果想根據(jù)某個column來排序, 必須將改column加到Primary key中, 如 primary key (id, c1, c2 ,c3), 其中id時partition key, c1, c2 ,c3是Clustering Key.(如果想用id和c1作為partition key, 只需添加括號: primary key ((id, c1), c2 ,c3)).

創(chuàng)建表:

CREATE TABLE sample3 (
  id text,
  gmt_create bigint,
  name text,
  score text,
  PRIMARY KEY ((id), gmt_create)
);

在該表中插入若干紀(jì)錄后, 使用select *查詢該表, 得到如下紀(jì)錄:

id  | gmt_create | name  | score
-----+------------+-------+--------
 id3 |       1925 | name3 | score3
 id3 |       1926 | name4 | score4
 id1 |       1923 | name1 | score1
 id2 |       1924 | name2 | score2

可以看到cql表中有四條記錄, 其中id=id3有兩條記錄, 那么是否意味著cassandra中有四行來保存這些記錄呢?用Cssandra-Cli來查詢:

    RowKey: id3
    => (name=1925:, value=, timestamp=1442494070376000)
    => (name=1925:name, value=6e616d6533, timestamp=1442494070376000)
    => (name=1925:score, value=73636f726533, timestamp=1442494070376000)
    => (name=1926:, value=, timestamp=1442494335821000)
    => (name=1926:name, value=6e616d6534, timestamp=1442494335821000)
    => (name=1926:score, value=73636f726534, timestamp=1442494335821000)
    -------------------
    RowKey: id1
    => (name=1923:, value=, timestamp=1442494039343000)
    => (name=1923:name, value=6e616d6531, timestamp=1442494039343000)
    => (name=1923:score, value=73636f726531, timestamp=1442494039343000)
    -------------------
    RowKey: id2
    => (name=1924:, value=, timestamp=1442494054817000)
    => (name=1924:name, value=6e616d6532, timestamp=1442494054817000)
    => (name=1924:score, value=73636f726532, timestamp=1442494054817000)

    3 Rows Returned.
    

可以看到, cassandra中只用3行來保存數(shù)據(jù), 其中id=id3到兩條記錄被保存在同一行上. 事實上, 當(dāng)存在clustering key時, 當(dāng)partition key相同而clustering key不同時, 其紀(jì)錄是保存在同一行上的, 而每一個column的name責(zé)由clustering的值 + ":" + 原來的column name構(gòu)成(如sample3中, column score的name = 1923:score), sample3在cassandra中的存儲結(jié)構(gòu)如下圖所示:

        +-------------+----------------+-------------------+----------------------+
        |             |  name = 1925:  |  name=1925:name   |    name=1925:score   |
        |             |  value =       |  value=6e616d6533 |  value=73636f726533  |
        |             |  timestamp     |     timestamp     |       timestamp      |
        |    id 3     +----------------+-------------------+----------------------+
        |             |  name = 1926:  |  name=1926:name   |    name=1926:score   |
        |             |  value=        |  value=6e616d6534 |  value=73636f726534  |
        |             |  timestamp     |     timestamp     |       timestamp      |
        +-------------+----------------+-------------------+----------------------+
        |             |  name = 1923:  |  name=1923:name   |    name=1923:score   |
        |    id 1     |  value =       |  value=6e616d6531 |  value=73636f726531  |
        |             |  timestamp     |     timestamp     |       timestamp      |
        +-------------+----------------+-------------------+----------------------+
        |             |  name = 1924:  |  name=1924:name   |    name=1924:score   |
        |    id 2     |  value =       |  value=6e616d6532 |  value=73636f726532  |
        |             |  timestamp     |     timestamp     |       timestamp      |
        +-------------+----------------+-------------------+----------------------+

Secondary Index

從cassandra 0.7開始支持Secondary Index. 例如, 在sample3上創(chuàng)建Secondary Index:

create index idx_score on sample3(score);

而Secondary Index的原理類似于創(chuàng)建一張新的表, 該表的primary key就是索引的列.因此, 若原sample表中有數(shù)據(jù):

{
    "id1": {
        "name" : "jim"
        "score": "60"
    },
    "id2": {
        "name" : "kate"
        "score": "60"
    }
}

索引表的內(nèi)部結(jié)構(gòu)類似于如下:

{
    "60": {
        "id1": data
        "id2": data
    }
}

與原表的不同之處在于: 原表的數(shù)據(jù)按照partition key分發(fā)到不同的節(jié)點進(jìn)行存儲, 當(dāng)cassandra進(jìn)行讀取時, 任意一個節(jié)點返回數(shù)據(jù)即可(取決于讀取時的一致性要求), 而secondary index 表則會存儲自己節(jié)點上的數(shù)據(jù), 因此, 按照secondary index進(jìn)行讀取時, 需要等待每一個節(jié)點讀取完畢, 這個過程相比于primary key讀取的過程會比較緩慢.(當(dāng)并發(fā)非常高時, 就會造成大量的random I/O, 導(dǎo)致查詢非常緩慢.)

因此, 當(dāng)secondary index的column是類似于'國家'這樣有許多重復(fù)紀(jì)錄的字段時, 讀取時相當(dāng)高效的(因為n個節(jié)點都進(jìn)行一個磁盤搜素, 時間復(fù)雜度O(n), 然而查詢到的紀(jì)錄數(shù)有可能是x條, 大多數(shù)情況下x>n, 平均下來時非常高效的), 但是當(dāng)secondary index的column是類似于email這樣幾乎每個人唯一的字段時, 是非常低效的, 因為按照email進(jìn)行查詢是, 每一次查詢幾乎只能命中一條紀(jì)錄, 而secondary index搜索的時間復(fù)雜度則是O(n)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容