數據庫設計基礎知識 - 比較 PostgreSQL、Cassandra 和 MongoDB

大家好。 在五月假期的第二部分開始之前,我們與您分享我們翻譯的材料,以期在課程中推出新的課程 《關係型資料庫管理系統》.

數據庫設計基礎知識 - 比較 PostgreSQL、Cassandra 和 MongoDB

應用程式開發人員花費大量時間比較多個操作資料庫,以選擇最適合預期工作負載的資料庫。 需求可能包括簡化的資料建模、事務保證、讀/寫效能、水平擴展和容錯。 傳統上,選擇從資料庫類別(SQL 或 NoSQL)開始,因為每個類別都提出了一組明確的權衡。 低延遲和高吞吐量方面的高效能通常被視為不可權衡的要求,因此對於任何範例資料庫都是至關重要的。

本文的目的是幫助應用程式開發人員在應用程式資料建模的背景下在 SQL 和 NoSQL 之間做出正確的選擇。 我們將介紹一個 SQL 資料庫(即 PostgreSQL)和兩個 NoSQL 資料庫(Cassandra 和 MongoDB),以涵蓋資料庫設計的基礎知識,例如建立表、填入表、從表中讀取資料以及刪除表。 在下一篇文章中,我們一定會了解索引、事務、JOIN、TTL 指令和基於 JSON 的資料庫設計。

SQL 和 NoSQL 有什麼差別?

SQL 資料庫透過 ACID 事務保證以及在現有規範化關係型資料庫模型之上以意想不到的方式使用 JOIN 查詢資料的能力,提高了應用程式的靈活性。

鑑於其整體/單節點架構和使用主從複製模型來實現冗餘,傳統 SQL 資料庫缺乏兩個重要功能 - 線性寫入可擴展性(即跨多個節點的自動分區)和自動/零資料遺失。 這意味著接收的資料量不能超過單一節點的最大寫入吞吐量。 此外,容錯(在無共享架構中)必須考慮一些臨時資料遺失。 這裡您需要記住,最近的提交尚未反映在從屬副本中。 SQL資料庫中也很難實現非停機更新。

NoSQL 資料庫通常本質上是分散式的,即其中,資料被分為多個部分並分佈在多個節點上。 它們需要非規範化。 這意味著輸入的資料也必須複製多次才能回應您發送的特定請求。 總體目標是透過減少讀取期間可用的分片數量來獲得高效能。 這意味著 NoSQL 要求您對查詢建模,而 SQL 要求您對資料建模。

NoSQL 專注於在分散式叢集中實現高效能,這是許多資料庫設計權衡的基本原理,包括 ACID 事務遺失、JOIN 和一致的全域二級索引。

有一種觀點認為,雖然 NoSQL 資料庫提供線性寫入可擴展性和高容錯能力,但事務保證的喪失使它們不適合任務關鍵型資料。

下表顯示了 NoSQL 中的資料建模與 SQL 的不同之處。

數據庫設計基礎知識 - 比較 PostgreSQL、Cassandra 和 MongoDB

SQL 和 NoSQL:為什麼兩者都需要?

擁有大量用戶的現實世界應用程式(例如 Amazon.com、Netflix、Uber 和 Airbnb)的任務是執行複雜的多方面任務。 例如,像Amazon.com 這樣的電子商務應用程式需要儲存輕量級、高關鍵性的數據,例如用戶資訊、產品、訂單、發票,以及大量、不太敏感的數據,例如產品評論、支援訊息、用戶活動、用戶評論和建議。 當然,這些應用程式依賴至少一個 SQL 資料庫以及至少一個 NoSQL 資料庫。 在跨區域和全球系統中,NoSQL 資料庫可作為地理分散式緩存,用於儲存在特定區域運行的可信任來源 SQL 資料庫中的資料。

YugaByte DB如何結合SQL和NoSQL?

YugaByte DB 基於日誌為導向的混合儲存引擎、自動分片、分片分散式共識複製和 ACID 分散式事務(受 Google Spanner 啟發)構建,是世界上第一個同時相容 NoSQL(Cassandra 和 Redis)和SQL( PostgreSQL)。 如下表所示,相容於 Cassandra 的 YugaByte DB API YCQL 在 NoSQL API 中新增了單鍵、多鍵 ACID 事務和全域二級索引的概念,從而開啟了事務型 NoSQL 資料庫時代。 此外,YCQL(與 PostgreSQL 相容的 YugaByte DB API)在 SQL API 中加入了線性寫入擴充和自動容錯的概念,將分散式 SQL 資料庫帶入世界。 由於 YugaByte DB 本質上是事務性的,因此 NoSQL API 現在可以在關鍵任務資料的上下文中使用。

數據庫設計基礎知識 - 比較 PostgreSQL、Cassandra 和 MongoDB

如同之前文章所說 “YSQL 簡介:適用於 YugaByte DB 的 PostgreSQL 相容分散式 SQL API”,YugaByte DB 中 SQL 或 NoSQL 的選擇完全取決於底層工作負載的特性:

  • 如果您的主要工作負載是多鍵 JOIN 操作,那麼在選擇 YSQL 時,請了解您的鍵可能分佈在多個節點上,從而導致比 NoSQL 更高的延遲和/或更低的吞吐量。
  • 否則,請選擇兩個 NoSQL API 之一,請記住,由於一次從一個節點提供查詢,您將獲得更好的效能。 YugaByte DB 可以用作需要同時管理多個工作負載的現實複雜應用程式的單一操作資料庫。

下一節中的資料建模實驗室是基於 PostgreSQL 和 Cassandra API 相容的 YugaByte DB 資料庫,而不是本機資料庫。 這種方法強調與同一資料庫叢集的兩個不同 API(在兩個不同連接埠上)進行互動的便捷性,而不是使用兩個不同資料庫的完全獨立的叢集。
在以下部分中,我們將了解資料建模實驗室,以說明所涉及資料庫的差異和一些共同點。

數據建模實驗室

資料庫安裝

鑑於對資料模型設計(而不是複雜的部署架構)的重視,我們將在本機電腦上的 Docker 容器中安裝資料庫,然後使用它們各自的命令列 shell 與它們進行互動。

PostgreSQL 和 Cassandra 相容於 YugaByte DB 資料庫

mkdir ~/yugabyte && cd ~/yugabyte
wget https://downloads.yugabyte.com/yb-docker-ctl && chmod +x yb-docker-ctl
docker pull yugabytedb/yugabyte
./yb-docker-ctl create --enable_postgres

MongoDB的

docker run --name my-mongo -d mongo:latest

命令列訪問

讓我們使用對應 API 的命令列 shell 連接到資料庫。

PostgreSQL的

PSQL 是一個用於與 PostgreSQL 互動的命令列 shell。 為了方便使用,YugaByte DB 在 bin 資料夾中附帶了 psql。

docker exec -it yb-postgres-n1 /home/yugabyte/postgres/bin/psql -p 5433 -U postgres

卡桑德拉

青青山 是一個命令列 shell,用於透過 CQL(Cassandra 查詢語言)與 Cassandra 及其相容資料庫進行互動。 為了方便使用,YugaByte DB 配備了 cqlsh 在目錄中 bin.
請注意,CQL 受到 SQL 的啟發,並且具有類似的表、行、列和索引概念。 然而,作為一種 NoSQL 語言,它增加了一定的限制,其中大部分我們也將在其他文章中介紹。

docker exec -it yb-tserver-n1 /home/yugabyte/bin/cqlsh

MongoDB的

蒙戈 是一個用於與 MongoDB 互動的命令列 shell。 它可以在 MongoDB 安裝的 bin 目錄中找到。

docker exec -it my-mongo bash 
cd bin
mongo

建立表

現在我們可以使用命令列與資料庫互動來執行各種操作。 讓我們先創建一個表格來儲存有關不同藝術家創作的歌曲的資訊。 這些歌曲可能是專輯的一部分。 歌曲的可選屬性還有發行年份、價格、流派和評級。 我們需要透過「標籤」欄位考慮將來可能需要的其他屬性。 它可以以鍵值對的形式儲存半結構化資料。

PostgreSQL的

CREATE TABLE Music (
    Artist VARCHAR(20) NOT NULL, 
    SongTitle VARCHAR(30) NOT NULL,
    AlbumTitle VARCHAR(25),
    Year INT,
    Price FLOAT,
    Genre VARCHAR(10),
    CriticRating FLOAT,
    Tags TEXT,
    PRIMARY KEY(Artist, SongTitle)
);	

卡桑德拉

在 Cassandra 中建立表與 PostgreSQL 非常相似。 主要區別之一是缺乏完整性約束(例如 NOT NULL),但這是應用程式的責任,而不是 NoSQL 資料庫的責任。 主鍵由一個分區鍵(下例中的 Artist 欄位)和一組叢集列(下例中的 SongTitle 欄位)組成。 分區鍵決定應將行放入哪個分區/分片,而叢集列指示應如何在目前分片內組織資料。

CREATE KEYSPACE myapp;
USE myapp;
CREATE TABLE Music (
    Artist TEXT, 
    SongTitle TEXT,
    AlbumTitle TEXT,
    Year INT,
    Price FLOAT,
    Genre TEXT,
    CriticRating FLOAT,
    Tags TEXT,
    PRIMARY KEY(Artist, SongTitle)
);

MongoDB的

MongoDB 將資料組織到資料庫(Database)(類似 Cassandra 中的 Keyspace),其中包含包含 Document(類似表中的行)的 Collection(類似表)。 在MongoDB中,基本上不需要定義初始模式。 團隊 “使用資料庫”如下所示,在第一次呼叫時實例化資料庫並更改新建立的資料庫的上下文。 甚至集合也不需要明確建立;只需將第一個文件新增至新集合中,它們就會自動建立。 請注意,MongoDB 預設使用測試資料庫,因此任何不指定特定資料庫的集合級操作都會預設在該資料庫上執行。

use myNewDatabase;

獲取有關表的信息
PostgreSQL的

d Music
Table "public.music"
    Column    |         Type          | Collation | Nullable | Default 
--------------+-----------------------+-----------+----------+--------
 artist       | character varying(20) |           | not null | 
 songtitle    | character varying(30) |           | not null | 
 albumtitle   | character varying(25) |           |          | 
 year         | integer               |           |          | 
 price        | double precision      |           |          | 
 genre        | character varying(10) |           |          | 
 criticrating | double precision      |           |          | 
 tags         | text                  |           |          | 
Indexes:
    "music_pkey" PRIMARY KEY, btree (artist, songtitle)

卡桑德拉

DESCRIBE TABLE MUSIC;
CREATE TABLE myapp.music (
    artist text,
    songtitle text,
    albumtitle text,
    year int,
    price float,
    genre text,
    tags text,
    PRIMARY KEY (artist, songtitle)
) WITH CLUSTERING ORDER BY (songtitle ASC)
    AND default_time_to_live = 0
    AND transactions = {'enabled': 'false'};

MongoDB的

use myNewDatabase;
show collections;

將資料輸入表中
PostgreSQL的

INSERT INTO Music 
    (Artist, SongTitle, AlbumTitle, 
    Year, Price, Genre, CriticRating, 
    Tags)
VALUES(
    'No One You Know', 'Call Me Today', 'Somewhat Famous',
    2015, 2.14, 'Country', 7.8,
    '{"Composers": ["Smith", "Jones", "Davis"],"LengthInSeconds": 214}'
);
INSERT INTO Music 
    (Artist, SongTitle, AlbumTitle, 
    Price, Genre, CriticRating)
VALUES(
    'No One You Know', 'My Dog Spot', 'Hey Now',
    1.98, 'Country', 8.4
);
INSERT INTO Music 
    (Artist, SongTitle, AlbumTitle, 
    Price, Genre)
VALUES(
    'The Acme Band', 'Look Out, World', 'The Buck Starts Here',
    0.99, 'Rock'
);
INSERT INTO Music 
    (Artist, SongTitle, AlbumTitle, 
    Price, Genre, 
    Tags)
VALUES(
    'The Acme Band', 'Still In Love', 'The Buck Starts Here',
    2.47, 'Rock', 
    '{"radioStationsPlaying": ["KHCR", "KBQX", "WTNR", "WJJH"], "tourDates": { "Seattle": "20150625", "Cleveland": "20150630"}, "rotation": Heavy}'
);

卡桑德拉

整體表現 INSERT Cassandra 中的看起來與 PostgreSQL 中的非常相似。 然而,語義上存在很大差異。 在卡桑德拉 INSERT 實際上是一個操作 UPSERT,如果該行已存在,則將最後一個值新增至該行。

資料輸入類似PostgreSQL INSERT 以上

.

MongoDB的

儘管 MongoDB 與 Cassandra 一樣是 NoSQL 資料庫,但其插入操作與 Cassandra 的語意行為沒有任何共同之處。 在 MongoDB 中 插() 沒有機會 UPSERT,這使得它類似於 PostgreSQL。 新增預設數據,無需 _idspecified 將導致新文件新增至集合。

db.music.insert( {
artist: "No One You Know",
songTitle: "Call Me Today",
albumTitle: "Somewhat Famous",
year: 2015,
price: 2.14,
genre: "Country",
tags: {
Composers: ["Smith", "Jones", "Davis"],
LengthInSeconds: 214
}
}
);
db.music.insert( {
artist: "No One You Know",
songTitle: "My Dog Spot",
albumTitle: "Hey Now",
price: 1.98,
genre: "Country",
criticRating: 8.4
}
);
db.music.insert( {
artist: "The Acme Band",
songTitle: "Look Out, World",
albumTitle:"The Buck Starts Here",
price: 0.99,
genre: "Rock"
}
);
db.music.insert( {
artist: "The Acme Band",
songTitle: "Still In Love",
albumTitle:"The Buck Starts Here",
price: 2.47,
genre: "Rock",
tags: {
radioStationsPlaying:["KHCR", "KBQX", "WTNR", "WJJH"],
tourDates: {
Seattle: "20150625",
Cleveland: "20150630"
},
rotation: "Heavy"
}
}
);

表查詢

也許 SQL 和 NoSQL 在查詢建構上最顯著的差異是所使用的語言 FROM и WHERE。 SQL允許after表達式 FROM 選擇多個表,並使用表達式 WHERE 可以是任何複雜性(包括操作 JOIN 表之間)。 然而,NoSQL 往往對以下方面施加了嚴格的限制: FROM,並且僅使用一個指定的表,並且在 WHERE,必須始終指定主鍵。 這與我們之前討論過的 NoSQL 效能提升有關。 這種願望導致任何跨表和跨鍵交互的一切可能的減少。 當回應請求時,它可能會在節點間通訊中引入很大的延遲,因此通常最好避免。 例如,Cassandra 要求查詢僅限於某些運算子(僅 =, IN, <, >, =>, <=) 在分割區鍵上,除非請求二級索引(此處僅允許使用 = 運算子)。

PostgreSQL的

以下是 SQL 資料庫可以輕鬆執行的三個查詢範例。

  • 顯示藝術家的所有歌曲;
  • 顯示與標題第一部分相符的藝術家的所有歌曲;
  • 顯示某個藝術家的所有標題中包含特定單字且價格低於 1.00 的歌曲。
SELECT * FROM Music
WHERE Artist='No One You Know';
SELECT * FROM Music
WHERE Artist='No One You Know' AND SongTitle LIKE 'Call%';
SELECT * FROM Music
WHERE Artist='No One You Know' AND SongTitle LIKE '%Today%'
AND Price > 1.00;

卡桑德拉

在上面列出的 PostgreSQL 查詢中,只有第一個查詢可以在 Cassandra 中保持不變,因為運算符 LIKE 不能應用於聚類列,例如 SongTitle。 在這種情況下,僅允許操作員 = и IN.

SELECT * FROM Music
WHERE Artist='No One You Know';
SELECT * FROM Music
WHERE Artist='No One You Know' AND SongTitle IN ('Call Me Today', 'My Dog Spot')
AND Price > 1.00;

MongoDB的

如前面的範例所示,在 MongoDB 中建立查詢的主要方法是 db.collection.find() 函數。 此方法明確包含集合的名稱(music 在下面的範例中),因此禁止查詢多個集合。

db.music.find( {
  artist: "No One You Know"
 } 
);
db.music.find( {
  artist: "No One You Know",
  songTitle: /Call/
 } 
);

讀取表的所有行

讀取所有行只是我們之前看到的查詢模式的一個特例。

PostgreSQL的

SELECT * 
FROM Music;

卡桑德拉

與上面的 PostgreSQL 範例類似。

MongoDB的

db.music.find( {} );

編輯表中的數據

PostgreSQL的

PostgreSQL 提供說明 UPDATE 更改數據。 她沒有機會 UPSERT,因此如果該行不再存在於資料庫中,則該語句將失敗。

UPDATE Music
SET Genre = 'Disco'
WHERE Artist = 'The Acme Band' AND SongTitle = 'Still In Love';

卡桑德拉

卡桑德拉有 UPDATE 類似於 PostgreSQL。 UPDATE 具有相同的語義 UPSERT, 相似的 INSERT.

與上面的 PostgreSQL 範例類似。

MongoDB的
手術 update() MongoDB 中可以完全更新現有文件或僅更新某些欄位。 預設情況下,它僅更新一個禁用語義的文檔 UPSERT。 更新多個文件和類似行為 UPSERT 可以透過為操作設定附加標誌來套用。 例如,在下面的範例中,特定藝術家的流派根據他的歌曲進行更新。

db.music.update(
  {"artist": "The Acme Band"},
  { 
    $set: {
      "genre": "Disco"
    }
  },
  {"multi": true, "upsert": true}
);

從表中刪除數據

PostgreSQL的

DELETE FROM Music
WHERE Artist = 'The Acme Band' AND SongTitle = 'Look Out, World';

卡桑德拉

與上面的 PostgreSQL 範例類似。

MongoDB的

MongoDB 有兩種類型的刪除文件的操作 - 刪除一個() /刪除很多() и 去掉()。 兩種類型都會刪除文檔,但會傳回不同的結果。

db.music.deleteMany( {
        artist: "The Acme Band"
    }
);

刪除表

PostgreSQL的

DROP TABLE Music;

卡桑德拉

與上面的 PostgreSQL 範例類似。

MongoDB的

db.music.drop();

結論

關於在 SQL 和 NoSQL 之間進行選擇的爭論已經持續了 10 多年。 這場爭論有兩個主要面向:資料庫引擎架構(整體式事務性 SQL 與分散式非事務性 NoSQL)和資料庫設計方法(使用 SQL 建模資料與使用 NoSQL 建模查詢)。

有了像 YugaByte DB 這樣的分散式事務資料庫,關於資料庫架構的爭論就可以輕鬆平息。 隨著資料量變得大於可以寫入單一節點的資料量,支援具有自動分片/重新平衡的線性寫入可擴展性的完全分散式架構變得必要。

此外,正如其中一篇文章所述 Google雲端與非事務性、最終一致的架構相比,事務性的、強一致的架構現在更多地用於提供更好的開發敏捷性。

回到資料庫設計討論,可以公平地說,這兩種設計方法(SQL 和 NoSQL)對於任何複雜的現實應用程式都是必要的。 SQL「資料建模」方法使開發人員能夠更輕鬆地滿足不斷變化的業務需求,而NoSQL「查詢建模」方法允許相同的開發人員以低延遲和高吞吐量操作大量資料。 正是出於這個原因,YugaByte DB 在一個公共核心中提供 SQL 和 NoSQL API,而不是推廣其中一種方法。 此外,透過提供與 PostgreSQL 和 Cassandra 等流行資料庫語言的兼容性,YugaByte DB 確保開發人員無需學習另一種語言即可使用分散式、高度一致的資料庫引擎。

在本文中,我們研究了 PostgreSQL、Cassandra 和 MongoDB 之間的資料庫設計基礎知識有何不同。 在以後的文章中,我們將深入探討高階設計概念,例如索引、事務、JOIN、TTL 指令和 JSON 文件。

我們祝您週末愉快,並邀請您參加 免費網絡研討會,將於 14 月 XNUMX 日舉行。

來源: www.habr.com

添加評論