返回文章列表

Apache Cassandra 核心數據操作與服務管理實踐

本文深入探討 Apache Cassandra 的核心數據操作與管理實踐。內容涵蓋數據操作語言(DML)的基礎,包括使用 INSERT 進行數據寫入與更新、透過 IF NOT EXISTS 實現條件插入,以及 SELECT 的多種查詢方式與 WHERE 子句的應用。同時,文章也闡述了基於主鍵的 DELETE

資料庫技術 分散式系統

Apache Cassandra 作為一款高度可擴展的分散式 NoSQL 資料庫,其數據操作模型與傳統關聯式資料庫有顯著差異。本文將從實踐角度出發,系統性地解析 Cassandra 查詢語言(CQL)中的核心數據操作指令。我們將探討 INSERT 操作的冪等性特質,它如何同時承擔新增與更新的雙重角色,並透過 IF NOT EXISTS 實現原子性的「檢查並寫入」。在查詢方面,將深入分析基於分區鍵的查詢效率,並說明 ALLOW FILTERING 在非優化查詢場景下的性能權衡。此外,文章還會剖析 DELETE 操作在 Cassandra 分散式架構下的墓碑(Tombstone)機制,最後將操作範疇擴展至服務層級,闡述如何利用容器化技術管理 Cassandra 實例的生命週期,並為構建初步的資料庫叢集提供基礎。

Apache Cassandra 數據操作實踐:插入、查詢與刪除

本節將深入探討 Apache Cassandra 的數據操作語言 (DML) 核心功能,包括如何向表中插入數據、如何執行各種查詢,以及如何從表中刪除數據。

插入數據 (INSERT)

INSERT INTO 語句用於向指定的 Table 中添加新的數據行。Cassandra 的 INSERT 操作也可以用於更新現有記錄,如果指定的 PRIMARY KEY 已經存在,則會用新值覆蓋舊值。

  1. 基本插入

    INSERT INTO catalog (catalog_id, journal, publisher, edition, title, author)
    VALUES ('catalog1', 'Oracle Magazine', 'Oracle Publishing', 'November-December 2013', 'Engineering as a Service', 'David A. Kelly');
    

    此命令向 catalog 表插入了一條記錄,並指定了所有列的值。

  2. 帶有 IF NOT EXISTS 的插入IF NOT EXISTS 子句可以防止在主鍵已存在時覆蓋現有記錄。如果記錄不存在,則插入;如果存在,則操作無效,不會返回錯誤。

    INSERT INTO catalog (catalog_id, journal, publisher, edition, title, author)
    VALUES ('catalog2', 'Oracle Magazine', 'Oracle Publishing', 'November-December 2013', 'Quintessential and Collaborative', 'Tom Haunert') IF NOT EXISTS;
    

    此命令嘗試插入 catalog2 記錄。如果 catalog_id'catalog2' 的記錄不存在,則會成功插入。

    執行插入操作後,Cassandra 會返回一個 WriteResult,其中包含 [applied] : True 表示操作成功應用,或者 [applied] : False 表示由於 IF NOT EXISTS 條件而未執行插入。

查詢數據 (SELECT)

SELECT 語句用於從 Table 中檢索數據。Cassandra 的查詢能力受到其分佈式架構的影響,通常建議基於分區鍵進行查詢以獲得最佳性能。

  1. 查詢所有列和所有行

    SELECT * FROM catalog;
    

    此查詢將返回 catalog 表中的所有記錄及其所有列的值。

  2. 查詢特定列: 您可以指定需要返回的列名,以減少數據傳輸量。

    SELECT title, author FROM catalog;
    
  3. 帶有 WHERE 子句的查詢WHERE 子句用於篩選查詢結果。通常,WHERE 子句必須包含對分區鍵的條件,以確保查詢能夠被有效地路由到包含這些數據的節點。

    SELECT * FROM catalog WHERE catalog_id = 'catalog1';
    

    此查詢僅返回 catalog_id'catalog1' 的記錄。

  4. ALLOW FILTERING 選項: 在某些情況下,您可能需要基於非分區鍵或非主鍵字段進行查詢。這通常需要額外添加 ALLOW FILTERING 選項。然而,使用 ALLOW FILTERING 可能會導致性能下降,因為它需要在集群中掃描更多數據。

    -- 範例:假設我們想基於 publisher 查詢,這可能需要 ALLOW FILTERING
    -- SELECT * FROM catalog WHERE publisher = 'Oracle Publishing' ALLOW FILTERING;
    

    注意:在生產環境中,應盡量避免過度依賴 ALLOW FILTERING,而是通過設計合適的表結構(例如,創建支持特定查詢的二級索引或使用不同的表結構)來優化查詢。

  5. ORDER BYLIMITORDER BY 子句用於對查詢結果進行排序,而 LIMIT 子句則用於限制返回的記錄數量。

    -- 範例:按標題降序排列,並限制返回 5 條記錄
    -- SELECT * FROM catalog ORDER BY title DESC LIMIT 5;
    

    注意ORDER BY 通常需要與聚簇鍵結合使用,並且對性能有較大影響。

刪除數據 (DELETE)

DELETE 語句用於從 Table 中移除數據。您可以選擇刪除整行記錄,或僅刪除特定列的值。

  1. 刪除整行記錄: 要刪除整行,您需要指定主鍵。

    DELETE FROM catalog WHERE catalog_id = 'catalog1';
    

    此命令會從 catalog 表中移除 catalog_id'catalog1' 的整條記錄。

  2. 刪除特定列的值: 您可以選擇性地刪除行中的某些列。

    DELETE journal, publisher FROM catalog WHERE catalog_id = 'catalog2';
    

    此命令會將 catalog_id'catalog2' 的記錄中的 journalpublisher 列的值設為 null(實際上是移除了這些列的值),但保留該行的主鍵和其他列。

    重要說明:當您刪除一個行的所有列時(包括主鍵列),Cassandra 實際上是將該行的所有數據標記為已刪除。在查詢時,如果主鍵列的值仍然存在,但其他列的值被刪除,則會顯示為 null。這與關係型數據庫的行為有所不同。

@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

object "使用者" as User {
  執行 CQL 指令
}

object "CQL Shell (cqlsh)" as CQLShell {
  CQL 指令介面
}

object "Cassandra Server" as CassandraServer {
  資料處理引擎
}

object "Table (catalog)" as CatalogTable {
  資料表儲存
}

User --> CQLShell : 輸入指令
CQLShell --> CassandraServer : 傳送請求
CassandraServer --> CatalogTable : 執行操作
CatalogTable --> CassandraServer : 返回結果
CassandraServer --> CQLShell : 回傳響應
CQLShell --> User : 顯示結果

note right of User : INSERT 操作\n- INSERT INTO catalog VALUES\n- IF NOT EXISTS 條件插入

note right of CQLShell : SELECT 操作\n- SELECT * FROM catalog\n- SELECT 特定欄位\n- WHERE 條件查詢

note right of CassandraServer : DELETE 操作\n- DELETE 整行記錄\n- DELETE 特定欄位\n- 標記為已刪除

note right of CatalogTable : 資料狀態\n- [applied]: True/False\n- 刪除後顯示 null\n- 主鍵永遠保留

end note

end note

end note

end note

@enduml

看圖說話:

此圖示詳盡地描繪了 Apache Cassandra 中數據的插入、查詢和刪除操作。在插入部分,它展示了如何使用 INSERT INTO 語句添加數據,特別強調了 IF NOT EXISTS 子句的使用,以防止意外覆蓋現有數據,並說明了操作結果的 [applied] 標記。查詢部分則演示了多種 SELECT 語句的用法,包括查詢所有列、特定列,以及基於主鍵進行精確查詢,同時也提到了 ALLOW FILTERING 的概念及其潛在影響。刪除部分是圖示的重點,它清晰地展示了如何通過 DELETE 語句來刪除整行記錄或僅刪除特定列的值,並特別指出,即使刪除了所有列,主鍵仍然會被保留,而其他列的值會顯示為 null,這與關係型數據庫的行為有所區別。總體而言,圖示直觀地呈現了 Cassandra 的 DML 操作邏輯。

Apache Cassandra 數據管理與服務生命週期

本節將聚焦於 Apache Cassandra 的數據管理進階操作,包括清空和刪除表、刪除 Keyspace,以及如何管理 Cassandra 服務的啟動與停止,並探討運行多個 Cassandra 實例的場景。

表與 Keyspace 的刪除操作

在完成數據操作後,我們需要了解如何清理不再需要的數據結構。

  1. 截斷 Table (TRUNCATE)TRUNCATE 命令用於快速刪除一個 Table 中的所有數據,但保留 Table 的結構本身。這是一種高效的清空數據方式。

    TRUNCATE catalog;
    

    執行此命令後,再次查詢 catalog 表將不會返回任何數據。

  2. 刪除 Table (DROP TABLE)DROP TABLE 命令用於永久刪除一個 Table 及其所有數據。

    DROP TABLE IF EXISTS catalog;
    
    • IF EXISTS 子句是一個重要的安全機制。如果指定的 Table 不存在,使用 IF EXISTS 不會產生錯誤,而是靜默地完成操作。若省略 IF EXISTS,在 Table 不存在時會報錯。
  3. 刪除 Keyspace (DROP KEYSPACE)DROP KEYSPACE 命令用於永久刪除一個 Keyspace 及其包含的所有 Table 和數據。

    DROP KEYSPACE IF EXISTS CatalogKeyspace;
    

    同樣,IF EXISTS 子句可以防止在 Keyspace 不存在時產生錯誤。

退出 CQL Shell 與停止 Cassandra 服務

完成與 Cassandra 的交互後,需要正確地退出 Shell 並停止服務。

  1. 退出 cqlsh: 在 cqlsh 提示符下,輸入 exit 命令即可結束當前的 CQL Shell 會話。

  2. 停止 Cassandra 服務: 如果 Cassandra 是通過 Docker 容器運行的,停止服務意味著停止其 Docker 容器。

    sudo docker stop cassandradb
    

    停止容器後,再次運行 sudo docker ps 將不再列出 cassandradb 容器,表明服務已成功停止。

運行多個 Apache Cassandra 實例

在某些場景下,可能需要在同一台主機上運行多個 Cassandra 實例,例如進行測試或模擬集群環境。

  1. 命名衝突: Docker 要求每個容器必須有唯一的名稱。如果您嘗試使用已存在(即使是已停止)的容器名稱來創建新容器,Docker 會報錯。

    # 嘗試重用已存在的 cassandradb 名稱會失敗
    # sudo docker run ... --name cassandradb ...
    

    要解決此問題,需要先使用 docker rm <container_name_or_id> 命令刪除舊容器,然後才能使用相同的名稱創建新容器。

  2. 啟動多個容器: 為每個 Cassandra 實例指定不同的容器名稱。

    # 啟動第一個實例
    sudo docker run -t -i -v /cassandra/data1:/var/lib/cassandra/data --name cassandradb1 -d -p 7000:7000 -p 9042:9042 cassandra:latest
    
    # 啟動第二個實例
    sudo docker run -t -i -v /cassandra/data2:/var/lib/cassandra/data --name cassandradb2 -d -p 7001:7000 -p 9043:9042 cassandra:latest
    
    • 為每個容器分配獨立的數據卷掛載點(如 /cassandra/data1, /cassandra/data2)。
    • 為每個容器指定唯一的名稱(如 cassandradb1, cassandradb2)。
    • 為每個容器映射不同的主機端口(如 9042, 9043),以避免端口衝突。節點間通信端口(7000)也需要注意映射。
  3. 構建集群: 若要將這些獨立的實例組合成一個 Cassandra 集群,需要在啟動容器時設置 CASSANDRA_SEEDS 環境變量,指向集群中其他節點的 IP 地址。這使得新節點能夠發現並加入現有集群。

    # 範例:啟動第三個節點並指定前兩個節點為種子節點
    sudo docker run -t -i -v /cassandra/data3:/var/lib/cassandra/data --name cassandradb3 -d \
      -p 7002:7000 -p 9044:9042 \
      -e CASSANDRA_SEEDS="<ip_of_node1>,<ip_of_node2>" \
      cassandra:latest
    

    其中 <ip_of_node1><ip_of_node2> 是運行 cassandradb1cassandradb2 的主機的 IP 地址。

@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

object "使用者" as User {
  執行操作指令
}

object "CQL Shell" as CQLShell {
  CQL 指令介面
}

object "Docker CLI" as DockerCLI {
  Docker 指令介面
}

object "Docker Daemon" as DockerDaemon {
  容器管理服務
}

object "Host Machine" as HostMachine {
  主機系統
}

object "Cassandra Container" as CassandraContainer {
  cassandradb 容器
}

object "Cassandra Server" as CassandraServer {
  Cassandra 服務進程
}

object "Cassandra Table" as CatalogTable {
  catalog 資料表
}

object "Cassandra Keyspace" as CatalogKeyspace {
  CatalogKeyspace 鍵空間
}

User --> CQLShell : CQL 指令
User --> DockerCLI : Docker 指令
DockerCLI --> DockerDaemon : 容器管理請求
CQLShell --> CassandraServer : 資料庫操作
CassandraServer --> CatalogTable : 表格操作
CassandraServer --> CatalogKeyspace : 鍵空間操作
DockerDaemon --> CassandraContainer : 容器控制
CassandraContainer --> CassandraServer : 包含

note left of CatalogTable : 資料清理操作\n- TRUNCATE catalog\n- DROP TABLE catalog\n- DROP KEYSPACE

note right of DockerDaemon : 容器管理\n- docker stop\n- docker ps\n- docker run

note bottom of CassandraContainer : 多實例部署\n- cassandradb1\n- cassandradb2\n- cassandradb3\n配置 CASSANDRA_SEEDS

end note

end note

end note

@enduml

看圖說話:

此圖示全面涵蓋了 Apache Cassandra 的數據管理後續操作和服務生命週期管理。在數據清理部分,它展示了如何使用 TRUNCATE 命令快速清空 Table 中的所有數據,以及如何通過 DROP TABLEDROP KEYSPACE 命令來永久刪除 Table 和 Keyspace,並強調了 IF EXISTS 子句在避免錯誤方面的作用。服務管理部分則演示了如何通過 docker stop 命令停止 Cassandra 容器,並通過 docker ps 確認服務已停止。最為關鍵的是,圖示詳細闡述了運行多個 Cassandra 實例的場景:首先說明了 Docker 容器名稱的唯一性要求,接著展示了如何通過為每個容器分配不同的名稱、數據卷和端口來同時運行多個實例,最後點出了如何通過設置 CASSANDRA_SEEDS 環境變量來將這些獨立的實例組合成一個 Cassandra 集群,這是構建分佈式數據庫的基礎。

核心操作回顧
  1. Keyspace 與 Table 管理

    • 我們學習了如何使用 CQL (Cassandra Query Language) 來創建、修改、使用和刪除 Keyspace,這是數據的頂層組織單元。
    • 進一步,我們掌握了 Table(或稱 Column Family)的創建,包括定義列、設置主鍵和配置 Compaction 策略。
    • 數據的插入 (INSERT)、查詢 (SELECT)、更新(通過覆蓋插入)、刪除 (DELETE),以及清空表 (TRUNCATE) 和刪除表 (DROP TABLE) 的操作均已詳細闡述。
  2. 服務生命週期管理

    • 通過 Docker 命令,我們學會了如何啟動 (docker run) 和停止 (docker stop) Cassandra 服務容器。
    • docker ps 命令用於監控容器的運行狀態。
  3. 多實例部署與集群構建

    • 本節重點探討了如何在同一主機上運行多個 Cassandra 實例,包括如何處理容器命名衝突,以及如何通過映射不同端口和數據卷來實現。
    • 更重要的是,我們演示了如何通過配置 CASSANDRA_SEEDS 環境變量,將多個獨立的 Cassandra 實例連接起來,形成一個基本的 Cassandra 集群。這為構建高可用、可擴展的數據解決方案奠定了基礎。

前瞻性觀點

通過本章的實踐,讀者應能理解 Cassandra 的基本架構和操作模式。在實際應用中,除了掌握這些基礎操作外,還需要進一步關注:

  • 數據模型設計:根據查詢模式設計合適的表結構,優化查詢性能。
  • 副本策略與一致性級別:根據業務需求選擇合適的副本策略(SimpleStrategy vs NetworkTopologyStrategy)和一致性級別,以平衡數據的可用性、一致性和容錯能力。
  • 性能調優:理解 Compaction 策略、JVM 參數配置、硬件選擇等對 Cassandra 性能的影響。
  • 監控與故障排除:建立有效的監控機制,並掌握常見故障的診斷和解決方法。

本章的內容為後續更複雜的 Cassandra 集群管理和應用開發打下了堅實的基礎。

@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

object "讀者" as Reader {
  學習者
}

object "Docker CLI" as DockerCLI {
  Docker 指令介面
}

object "Cassandra Container" as CassandraContainer {
  Cassandra 容器
}

object "Cassandra Server" as CassandraServer {
  Cassandra 服務
}

object "CQL Shell" as CQLShell {
  cqlsh 介面
}

object "Cassandra Cluster" as CassandraCluster {
  Cassandra 集群
}

object "下一章節" as NextChapter {
  後續學習內容
}

Reader --> DockerCLI : docker pull
DockerCLI --> CassandraContainer : 下載鏡像

Reader --> DockerCLI : docker run
CassandraContainer --> CassandraServer : 啟動服務

Reader --> CQLShell : 連接容器
CQLShell --> CassandraServer : CQL 指令
CassandraServer --> Reader : 操作結果

Reader --> DockerCLI : 多容器部署
CassandraContainer --> CassandraCluster : 形成集群

Reader --> DockerCLI : docker stop
DockerCLI --> CassandraContainer : 停止服務

Reader --> NextChapter : 進入下一主題

note left of Reader : 學習流程\n1. 環境設置\n2. 單實例操作\n3. 集群部署\n4. 服務管理

note right of CassandraServer : 操作內容\n- Keyspace 管理\n- Table CRUD\n- Data CRUD

note bottom of CassandraCluster : 集群配置\n- CASSANDRA_SEEDS\n- 節點發現\n- 集群形成

end note

end note

end note

@enduml

看圖說話:

此圖示對本章關於 Apache Cassandra 的學習內容進行了結構化的總結。它首先展示了讀者如何通過 Docker CLI 獲取 Cassandra 鏡像,為後續操作奠定基礎。接著,圖示清晰地描繪了單一 Cassandra 實例的部署過程,包括啟動容器、啟動服務,以及通過 CQL Shell 執行各種數據操作(Keyspace 和 Table 的增刪改查,以及數據的增刪改查)。隨後,圖示重點突出了多實例部署和集群構建的關鍵步驟,展示了如何通過 Docker 啟動多個容器,並通過配置 CASSANDRA_SEEDS 來實現節點間的發現與集群的形成。最後,圖示總結了服務管理(停止容器)和整個學習過程的回顧,並預示了讀者將進入下一個主題。整個流程圖直觀地呈現了從基礎到進階的學習路徑,強調了 Docker 在 Cassandra 部署中的核心作用。

縱觀從基礎數據操作到服務生命週期管理的完整實踐,我們不僅掌握了 Apache Cassandra 的核心指令,更重要的是,洞悉了其作為頂尖分散式資料庫的設計哲學與工程價值。這趟學習旅程,如同高階管理者從掌握單點業務到佈局整體生態的過程。

真正的挑戰並非 INSERTSELECT 的語法,而在於從關聯式資料庫「先有結構再查詢」的慣性,轉變為 Cassandra「為查詢而設計模型」的核心思維。諸如 ALLOW FILTERING 的性能陷阱,正是此思維轉變過程中的關鍵試煉。將單一容器操作擴展至多節點集群的部署實踐,更是從理論認知邁向系統級掌控能力的必經之路,它迫使我們直面端口管理、數據同步與節點發現等真實世界的分散式系統議題。

可以預見,未來對 Cassandra 的掌握將不再僅限於操作層面的熟練度,而是更側重於數據模型設計、副本策略與一致性級別權衡的架構決策能力。這代表了從「數據操作者」到「系統架構師」的價值躍遷。

綜合評估後,玄貓認為,精通 Cassandra 的路徑需要語法掌握與架構思維的雙軌並行。對於追求技術卓越的工程師而言,唯有突破傳統資料庫的思維框架,才能真正釋放 Cassandra 在高可用與大規模擴展上的完整潛力。