卡桑德拉。 只懂Oracle如何不死

嘿哈布爾。

我的名字是 Misha Butrimov,我想向您介紹一些有關 Cassandra 的資訊。 我的故事對於那些從未接觸過 NoSQL 資料庫的人來說將會很有用——它有很多你需要了解的實作特性和陷阱。 如果您除了 Oracle 或任何其他關係資料庫之外沒有見過任何東西,這些東西將救您的命。

卡桑德拉有什麼好處? 它是一個 NoSQL 資料庫,設計無單點故障,可擴充性良好。 如果您需要為某些資料庫新增幾個 TB,您只需將節點新增至環中即可。 將其擴展到另一個數據中心? 將節點新增到叢集。 增加已處理的 RPS? 將節點新增到叢集。 它也在相反的方向上起作用。

卡桑德拉。 只懂Oracle如何不死

她還有什麼擅長的? 這是關於處理大量請求。 但多少才算很多呢? 每秒 10、20、30、40 萬個請求並不算多。 每秒還有 100 萬個記錄請求。 有公司表示他們每秒鐘保留 2 萬個請求。 他們可能不得不相信這一點。

原則上,Cassandra 與關係數據有一個很大的區別——它與它們根本不相似。 記住這一點非常重要。

並非所有看起來相同的東西都一樣工作

有一次一位同事來找我問:「這是一個 CQL Cassandra 查詢語言,它有一個 select 語句,它有 where,它有 and。 我寫信但沒用。 為什麼?」。 將 Cassandra 像關係型資料庫一樣對待是暴力自殺的完美方式。 我不是宣傳它,它在俄羅斯是被禁止的。 你只會設計出錯誤的東西。

例如,一位客戶來找我們說:「讓我們為電視劇建立一個資料庫,或是為食譜目錄建立資料庫。 我們會在那裡提供菜餚或電視劇和演員的名單。” 我們高興地說:“走吧!” 只需發送兩個字節,幾個符號即可完成,一切都會非常快速可靠地工作。 一切都很好,直到顧客過來說家庭主婦也在解決相反的問題:她們有一份產品清單,她們想知道自己想做什麼菜。 你死了。

這是因為 Cassandra 是一個混合資料庫:它同時提供鍵值並將資料儲存在寬列中。 在 Java 或 Kotlin 中,可以這樣描述:

Map<RowKey, SortedMap<ColumnKey, ColumnValue>>

即,也包含排序映射的映射。 此映射的第一個鍵是行鍵或分區鍵 - 分區鍵。 第二個鍵是已排序映射的鍵,是聚類鍵。

為了說明資料庫的分佈,我們畫三個節點。 現在您需要了解如何將資料分解為節點。 因為如果我們把所有東西都塞進一個(順便說一句,可以有一千個、兩千個、五個——只要你喜歡),這並不是真正的分配。 因此,我們需要一個返回數字的數學函數。 只是一個數字,一個會落入某個範圍的長整型。 我們將有一個節點負責一個範圍,第二個節點負責第二個範圍,第 n 個節點負責第 n 個範圍。

卡桑德拉。 只懂Oracle如何不死

該數字是使用哈希函數獲取的,該函數應用於我們所說的分區鍵。 這是在主鍵指令中指定的列,而該列將成為映射的第一個也是最基本的鍵。 它決定哪個節點將接收哪些資料。 在 Cassandra 中建立表格的語法與 SQL 幾乎相同:

CREATE TABLE users (
	user_id uu id,
	name text,
	year int,
	salary float,
	PRIMARY KEY(user_id)

)

本例中的主鍵由一列組成,它也是分區鍵。

我們的用戶將如何表現? 有些將前往一個節點,有些將前往另一個節點,有些將前往第三個節點。 結果是一個普通的雜湊表,也稱為映射,在Python中也稱為字典,或者是一個簡單的鍵值結構,我們可以從中讀取所有值,透過鍵讀寫。

卡桑德拉。 只懂Oracle如何不死

選擇:何時允許過濾變成全面掃描,或不做什麼

讓我們寫一些選擇語句: select * from users where, userid = 。 事實證明,就像在 Oracle 中一樣:我們寫 select,指定條件,一切正常,使用者得到它。 但是,例如,如果您選擇具有特定出生年份的用戶,Cassandra 會抱怨它無法滿足該請求。 因為她根本不知道我們如何分發有關出生年份的數據 - 她只有一列指示為鍵。 然後她說:「好吧,我仍然可以滿足這個要求。 添加允許過濾。” 我們加入指令,一切正常。 就在這時,可怕的事情發生了。

當我們運行測試數據時,一切都很好。 當您在生產中執行查詢時,例如,我們有 4 萬筆記錄,那麼一切對我們來說都不是很好。 因為允許過濾是一個指令,允許Cassandra從所有節點、所有資料中心(如果這個叢集中有很多資料中心)收集該表中的所有數據,然後才對其進行過濾。 這是全掃描的類似物,幾乎沒有人對此感到滿意。

如果我們只需要透過 ID 來取得用戶,我們就可以接受。 但有時我們需要編寫其他查詢並對選擇施加其他限制。 因此,我們記住:這都是一個有分區鍵的映射,但裡面是一個排序的映射。

她還有一把鑰匙,我們稱之為聚類鑰匙。 該鍵又由我們選擇的列組成,借助這些列,Cassandra 了解其資料的物理排序方式以及如何位於每個節點上。 也就是說,對於某些分區鍵,聚類鍵將準確地告訴您如何將資料推送到這棵樹中,以及它將在其中佔據什麼位置。

這實際上是一棵樹,在那裡簡單地調用了一個比較器,我們以物件的形式向其傳遞一組特定的列,並且它也被指定為列的列表。

CREATE TABLE users_by_year_salary_id (
	user_id uuid,
	name text,
	year int,
	salary float,
	PRIMARY KEY((year), salary, user_id)

請注意主鍵指令;它的第一個參數(在我們的例子中為年份)始終是分區鍵。 它可以由一列或多列組成,這並不重要。 如果有多個列,則需要再次刪除括號中的列,以便語言預處理器知道這是主鍵,而其後面的所有其他列都是聚類鍵。 在這種情況下,它們將按照它們出現的順序在比較器中傳輸。 也就是說,第一列比較重要,第二列比較不重要,依此類推。 例如,我們如何編寫資料類別的 equals 字段:我們列出字段,並為它們編寫哪些字段較大,哪些字段較小。 在 Cassandra 中,相對而言,這些是資料類別的字段,將應用為其編寫的 equals。

我們設定排序並施加限制

您需要記住,排序順序(降序、升序等)是在建立鍵時同時設定的,並且以後無法變更。 它從物理上決定了資料的排序方式和儲存方式。 如果您需要變更聚類鍵或排序順序,則必須建立新表並將資料傳輸到其中。 這不適用於現有的。

卡桑德拉。 只懂Oracle如何不死

我們在表中填滿了用戶,發現他們落入一個環中,首先按出生年份,然後在每個節點上按工資和用戶 ID。 現在我們可以透過施加限制來選擇。

我們的工作又出現了 where, and,我們有了用戶,一切又恢復正常了。 但是,如果我們嘗試僅使用Clustering key 的一部分,並且是不太重要的key,那麼Cassandra 會立即抱怨它無法在映射中找到該物件(該物件具有null 比較器的這些欄位)和該物件所在的位置。那是剛剛設定好的,-他躺著的地方。 我將不得不再次從該節點提取所有數據並對其進行過濾。 這是節點內全掃描的模擬,這很糟糕。

在任何不清楚的情況下,建立一個新表

如果我們希望能夠透過ID、年齡、薪資來定位用戶,該怎麼做? 沒有什麼。 只需使用兩個表即可。 如果您需要以三種不同的方式接觸用戶,就會有三個表。 我們節省螺絲空間的日子已經一去不復返了。 這是最便宜的資源。 它的成本遠低於回應時間,這可能對用戶不利。 對於用戶來說,一秒鐘內收到東西比十分鐘內收到東西要愉快得多。

我們用不必要的空間和非規範化資料來換取良好擴展和可靠運作的能力。 畢竟,事實上,一個由三個數據中心組成的集群,每個數據中心有五個節點,具有可接受的數據保存水平(當沒有丟失任何數據時),能夠在一個數據中心死亡後完全倖存下來。 其餘兩個節點各有兩個節點。 只有在此之後,問題才開始出現。 這是一個非常好的冗餘,值得幾個額外的 SSD 驅動器和處理器。 因此,為了使用 Cassandra,它絕不是 SQL,其中沒有關係、外鍵,您需要了解簡單的規則。

我們根據您的要求設計一切。 最主要的不是數據,而是應用程式如何使用它。 如果它需要以不同的方式接收不同的數據或以不同的方式接收相同的數據,我們必須以方便應用程式的方式來放置。 否則,我們將在全掃描中失敗,Cassandra 不會為我們帶來任何優勢。

資料非規範化是常態。 我們忘記了範式,我們不再擁有關聯式資料庫。 如果我們把某樣東西放下 100 次,它就會躺下 100 次。 還是比停下來便宜。

我們選擇用於分割區的鍵,以便它們正常分佈。 我們不希望密鑰的雜湊落在一個狹窄的範圍內。 也就是說,上面例子中的出生年份就是一個壞例子。 更準確地說,如果我們的用戶按出生年份常態分佈,那就太好了;如果我們談論的是五年級學生,那就不好了——那裡的劃分不會很好。

排序在聚類鍵建立階段選擇一次。 如果需要更改,我們將不得不使用不同的鍵更新表。

最重要的是:如果我們需要以 100 種不同的方式檢索相同的數據,那麼我們將擁有 100 個不同的表格。

來源: www.habr.com

添加評論