原文地址:http://www.datastax.com/dev/blog/basic-rules-of-cassandra-data-modelingsql
選擇一個正確的數據模型是Cassandra使用中最難的部分(譯者也這麼認爲)。若是你有關係型數據庫開發經驗,你會以爲CQL看起來都很類似(和MySQL等),可是你使用它的方式會很是的不一樣。這篇文章的目的就是解釋當你在設計一個Cassandra數據庫的時候須要牢記在內心的一些基本規則。若是你遵照這些規則,你會獲得很好的拿來就能用的性能提高。更好的是,你的性能將會隨着你的集羣的節點增長而線性增加。數據庫
來自有關係型數據庫背景的開發常常會嘗試把他們在關係型數據庫的設計規則經驗放到Cassandra來用。爲了不浪費時間在那些不適用Cassandra的規則上,我須要指出一下非目標:網絡
在Cassandra中寫入不是免費的,可是很是廉價。Cassandra在寫入吞吐量方面作了高度的優化,幾乎全部的寫入性能都是平等的(計數器、輕量事務和在list中插入數據除外)。若是你額外的寫入可讓你改進讀的性能,這通常是一種好的設計。讀取是一種更昂貴而且更困難的東西。數據結構
反範式設計和冗餘數據是Cassandra的設計核心。不要擔憂這個問題。相比CPU,內存,磁盤IO,網絡IO,磁盤的儲存空間是很是廉價的,而Cassandra正是遵循這個思路來設計的。爲了有更好的讀取性能,你每每須要重複數據。負載均衡
另外Cassandra不提供JOIN ,你不會想在分佈式系統中用這些特性。數據庫設計
你的數據模型中有2個很是高優先級的目標分佈式
還有一些小規則你須要記住,可是這兩個最重要。因此多數狀況下,我會把這2個規則做爲關注重點。固然你還有一些花哨的技巧可使用,可是你最好知道如何評測它的效果。工具
你但願集羣中的全部的節點都有大體差很少數量的數據。Cassandra作這個很容易,但不是全自動的。行基於分區主鍵被分佈到集羣的各個位置。因此你須要選擇一個好的主鍵,我將簡單解釋一下。post
分區是一組數據共享一個分區key。當你執行一個讀取的查詢,你但願讀取儘可能少的分區來獲取你須要的數據。性能
爲什麼如此重要?由於每一個分區可能在不一樣的節點上。代理將會構建許多命令去不一樣的節點去執行以知足你的請求。這增長了大量的前置操做而且致使各類延遲的狀況。並且,即便在單個節點的狀況下,因爲數據的存儲方式致使讀取多個分區的數據也比單個分區要慢的多。
你須要儘可能減小讀取的時候須要的分區數量,爲何不把數據放在一個大分區?你須要幹掉規則1,它說要讓數據分佈在整個集羣。
事實是,這兩個規則確實常常衝突,因此你須要平衡他們。
最小話查詢分區的辦法就是讓你的數據結構適應你的查詢語句。不要依據關係來設計(譯註:沒錯,你的數據會更加抽象化,且很差理解)。基於查詢設計。下面是如何作:
儘可能嘗試去肯定你有哪些語句須要執行。這裏可能包含大量的注意事項你可能一開始沒有考慮到,你須要考慮:
任何一個查詢請求的改變都會影響到最優數據模型的設計。
事實上這意味着你可能須要爲每一個查詢創建一張表(譯註:也就是說你一開始會有不少表,他們數據會很重複)。若是你須要應付多個查詢,那麼你須要更多的表。
換句話說,每一個表須要高度匹配查詢語句須要的答案。若是你須要不一樣的答案,你須要不一樣的表。這是你如何優化讀操做。
記住,重複數據沒有問題。你的許多表可能重複了不少數據。
爲了展現如何進行一個好的設計,我將帶你經過設計解決一個簡單的問題。
核心需求,咱們有許多用戶,我想找到他們。
決定要執行哪些查詢。咱們但願能夠經過用戶名,或者用戶的email來查詢他們。任何一個查詢,咱們都須要獲得他們的全部信息。
嘗試建立一個表來知足查詢需求,而且只須要用到1個分區。由於咱們須要獲得用戶的全部信息,這須要2張表。
CREATE TABLE users_by_username ( username text PRIMARY KEY, email text, age int ) CREATE TABLE users_by_email ( email text PRIMARY KEY, username text, age int )
如今咱們來確認一下是否符合規則:
數據分佈均勻?每一個用戶使用它們本身的分區,因此是的。
最小化分區讀取?每一個用戶咱們只須要讀取一個分區,因此是的。
如今咱們來嘗試對「非 - 目標」進行優化,而後又有了下面的設計方式
CREATE TABLE users ( id uuid PRIMARY KEY, username text, email text, age int ) CREATE TABLE users_by_username ( username text PRIMARY KEY, id uuid ) CREATE TABLE users_by_email ( email text PRIMARY KEY, id uuid )
這個數據模型一樣將數據分佈到全部的節點,可是有個問題,咱們須要讀取2個分區。一個是users_by_username/users_by_email 而後是 users表。因此讀取的成本大致上是以前的2倍。
核心需求:用戶被分到不一樣的組中,咱們但願讀取分組的全部用戶。
決定要執行哪些查詢。咱們但願獲取確切分組的用戶的全部信息,不關心排序。
嘗試建立一個表來知足查詢需求,而且只須要用到1個分區。咱們如何讓分組分不到不一樣的分區中?咱們能夠設計這樣的分區主鍵:
CREATE TABLE groups ( groupname text, username text, email text, age int, PRIMARY KEY (groupname, username) )
注意到主鍵中有2個部分,groupname,這個是分區主鍵,username,被稱做clustering key (集羣主鍵)。這會使得每一個groupname在一個分區中。在一個特定的groupname中,行是按照username排序的。讀取分組數據很是簡單:
SELECT * FROM groups WHERE groupname = ?
這知足了最小化查詢分區的要求,由於咱們只須要讀取1個分區。可是它並無在數據均勻分佈上作的很好。若是咱們考慮有成千上萬的小分組,每一個分組有幾百人,咱們將獲得一個至關於均勻分佈的模型。可是若是有一個分組有1百萬用戶,全部的負擔都會被一個節點承擔。
若是咱們但願負載均衡,有一些策略咱們能夠採用。最基本的是添加另外一個字段到主鍵中作成一個複合分區主鍵,下面是例子:
CREATE TABLE groups ( groupname text, username text, email text, age int, hash_prefix int, PRIMARY KEY ((groupname, hash_prefix), username) )
新的HASH字段hash_prefix,保存了用戶名的hash的前綴。好比第一個字節對4取模。和groupname一塊兒,這兩個字段組合成了一個複合分區主鍵。和單個分區不一樣,如今它分佈到4個分區中了。咱們的數據更加均勻,可是咱們須要讀取4次的分區。這是一個規則衝突的例子。你須要在它們間找到一個合適的平衡。
若是你有不少的讀操做,並且分組不會太大,那麼將取模的值從4改成2是不錯的選擇。若是你有較少的讀取,可是單個分組會增加到很大,將4改成10會更好。
還有一些其餘方法能夠分割分區,我將會在下面的例子中介紹。
在我繼續以前,讓我總結一下這個數據模型:咱們屢次重複了用戶信息,每一個group一次,你可能但願像這樣重建模型來減小重複的數量:
CREATE TABLE users ( id uuid PRIMARY KEY, username text, email text, age int ) CREATE TABLE groups ( groupname text, user_id uuid, PRIMARY KEY (groupname, user_id) )
顯然,這最小化了重複。可是咱們須要讀取多少分區呢?若是分組是1000個用戶,咱們須要讀取1001個分區。這和從一個分區讀取數據相比是100倍的差距。若是要求讀優先,那這正不是一個好的設計。另外一方面,若是讀不頻繁,可是修改(update)是頻繁的,這個模型仍是有道理的。當你在設計數據庫的時候,必定要肯定你的讀/寫頻率。
例3:用戶加入分組的時間
假設咱們繼續以前分組的例子,可是新增一個需求,讀取分組中新增的前X個用戶。
咱們能夠用和以前有點類似的表:
CREATE TABLE group_join_dates ( groupname text, joined timeuuid, username text, email text, age int, PRIMARY KEY (groupname, joined) )
這裏咱們使用timeuuid(和時間戳很像,可是不會衝突)做爲clustering column 。在一個分組中,行將按照用戶加入的時間順序排序。這容許咱們獲取最新的用戶信息,像下面這樣。
SELECT * FROM group_join_dates WHERE groupname = ? ORDER BY joined DESC LIMIT ?
這是很是高效的,咱們在同一個分區中順序查詢數據。爲了不老是要用到order by joined desc,這會讓查詢效率下降,咱們能夠修改clustering order:
CREATE TABLE group_join_dates ( groupname text, joined timeuuid, username text, email text, age int, PRIMARY KEY (groupname, joined) ) WITH CLUSTERING ORDER BY (joined DESC)
如今咱們能夠執行更高效的查詢了
SELECT * FROM group_join_dates WHERE groupname = ? LIMIT ?
咱們以前的例子中,咱們有個問題就是若是有一個分組太大,如何把數據均勻的分配到全部節點中。在那個例子裏咱們隨機的分割了數據到同的分區(取模)。可是這個例子不一樣,咱們能夠利用咱們對查詢模板的認識來分區:用時間分區。
好比咱們用date來分區(date應該相似於:2016-05-19,因此天天有個分區)
CREATE TABLE group_join_dates ( groupname text, joined timeuuid, join_date text, username text, email text, age int, PRIMARY KEY ((groupname, join_date), joined) ) WITH CLUSTERING ORDER BY (joined DESC)
咱們再次使用了複合分區主鍵,可是此次咱們用了加入時間。天天都有一個新的分區。當查詢最近的X個用戶時,會先找今天的分區,而後昨天,前天,直到咱們有X個用戶。咱們可能會在獲得limit個用戶以前,查找多個分區。
爲了減小分區的查詢,咱們須要爲分區指定一個時間範圍,這樣你就只須要查詢1-2個分區。好比咱們平均天天有新3個用戶大體上,而後咱們按4天一分區,這樣,你就能夠在1-2個分區查完10個最新用戶。
這裏提到的最基本的數據模型規則涵蓋了現有的全部版本的Cassandra,而且在未來的版本中應該也是同樣的。一些其餘的小的數據模型問題,好比如何處理墓碑(刪除的數據),同樣是須要考慮的,可是這些可能在未來的Cassandra版本中可能會改變。
除了這裏提到的基本策略,一些Cassandra華麗的功能,像集合,用戶自定義數據結構,靜態字段,同樣能夠在讀的時候減小分區的使用。在設計的時候不要忘了考慮這些選擇。
但願我在大家處理不一樣的數據庫設計的時候已經給予了一些有用的工具。若是你想了解更多,我建議閱讀:Datastax’s free, self-paced online data modeling course (DS220) (譯者:這是一個英文視頻教程)
https://academy.datastax.com/courses/ds220-data-modeling?dxt=blogposting 一帆風順!