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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末峻堰,一起剝皮案震驚了整個濱河市边坤,隨后出現(xiàn)的幾起案子祠饺,更是在濱河造成了極大的恐慌弛姜,老刑警劉巖寻行,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件击你,死亡現(xiàn)場離奇詭異,居然都是意外死亡敏储,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進店門朋鞍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來已添,“玉大人,你說我怎么就攤上這事滥酥「瑁” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵坎吻,是天一觀的道長缆蝉。 經(jīng)常有香客問我,道長瘦真,這世上最難降的妖魔是什么刊头? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮诸尽,結(jié)果婚禮上原杂,老公的妹妹穿的比我還像新娘。我一直安慰自己您机,他們只是感情好穿肄,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布年局。 她就那樣靜靜地躺著,像睡著了一般咸产。 火紅的嫁衣襯著肌膚如雪矢否。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天脑溢,我揣著相機與錄音僵朗,去河邊找鬼。 笑死焚志,一個胖子當(dāng)著我的面吹牛衣迷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酱酬,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼壶谒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了膳沽?” 一聲冷哼從身側(cè)響起汗菜,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挑社,沒想到半個月后陨界,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡痛阻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年菌瘪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阱当。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡俏扩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出弊添,到底是詐尸還是另有隱情录淡,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布油坝,位于F島的核電站嫉戚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏澈圈。R本人自食惡果不足惜彬檀,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瞬女。 院中可真熱鬧凤覆,春花似錦、人聲如沸拆魏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拥峦,卻和暖如春贴膘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背略号。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工刑峡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人玄柠。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓突梦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親羽利。 傳聞我的和親對象是個殘疾皇子宫患,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容