標籤:
Apache Cassandra 是一套開源分布式 Key-Value 儲存系統。它最初由 Facebook 開發,用於儲存特別大的資料。 Cassandra 不是一個資料庫,它是一個混合型的非關係的資料庫,類似於 Google 的 BigTable。本文主要從以下五個方面來介紹 Cassandra:Cassandra 的資料模型、安裝和配製 Cassandra、常用程式設計語言使用 Cassandra 來儲存資料、Cassandra 叢集搭建。
在 IBM Bluemix 雲平台上開發並部署您的下一個應用。
開始您的試用
Cassandra 的資料存放區結構
Cassandra 的資料模型是基於列族(Column Family)的四維或五維模型。它借鑒了 Amazon 的 Dynamo 和 Google‘s BigTable 的資料結構和功能特點,採用 Memtable 和 SSTable 的方式進行儲存。在 Cassandra 寫入資料之前,需要先記錄日誌 ( CommitLog ),然後資料開始寫入到 Column Family 對應的 Memtable 中,Memtable 是一種按照 key 排序資料的記憶體結構,在滿足一定條件時,再把 Memtable 的資料批量的重新整理到磁碟上,儲存為 SSTable 。
圖 1. Cassandra 的資料模型圖表:
- Cassandra 的資料模型的基本概念:
- 1. Cluster : Cassandra 的節點執行個體,它可以包含多個 Keyspace
2. Keyspace : 用於存放 ColumnFamily 的容器,相當於關聯式資料庫中的 Schema 或 database3. ColumnFamily : 用於存放 Column 的容器,類似關聯式資料庫中的 table 的概念 4. SuperColumn :它是一個特列殊的 Column, 它的 Value 值可以包函多個 Column5. Columns:Cassandra 的最基本單位。由 name , value , timestamp 組成
下面是關於資料模型執行個體分析 :
圖 2. 資料模型執行個體分析
回頁首
Cassandra 節點的安裝和配置擷取 Cassandra
# wget http://labs.renren.com/apache-mirror/cassandra/0.6.0/apache- cassandra-0.6.0-rc1-bin.tar.gz # tar -zxvf apache-cassandra-0.6.0-rc1-bin.tar.gz # mv apache-cassandra-0.6.0-rc1 cassandra # ls Cassandra
Cassandra 的目錄說明
| bin |
存放與 Cassandra 操作的相關指令碼 |
| conf |
存放設定檔的目錄 |
| interface |
Cassandra 的 Thrift 介面定義檔案,可以用於產生各種程式設計語言的介面代碼 |
| Javadoc |
原始碼的 javadoc |
| lib |
Cassandra 運行時所需的 jar 包 |
配製 Cassandra 節點的資料存放區目錄和日誌目錄
修改配製檔案 storage-conf.xml:
預設的內容
<CommitLogDirectory>/var/lib/cassandra/commitlog</CommitLogDirectory> <DataFileDirectories> <DataFileDirectory>/var/lib/cassandra/data</DataFileDirectory> </DataFileDirectories>
配置後的內容
<CommitLogDirectory>/data3/db/lib/cassandra/commitlog</CommitLogDirectory> <DataFileDirectories> <DataFileDirectory>/data3/db/lib/cassandra/data</DataFileDirectory> </DataFileDirectories>
修改日誌配製檔案 log4j.properties:
log4j.properties 配置
# 日誌路徑 #log4j.appender.R.File=/var/log/cassandra/system.log # 配置後的日誌路徑 : log4j.appender.R.File=/data3/db/log/cassandra/system.log
建立檔案存放資料和日誌的目錄
# mkdir – p /data3/db/lib/cassandra # mkdir – p /data3/db/log/Cassandra
配製完成後,啟動 Cassandra
# bin/Cassandra
顯示資訊
INFO 09:29:12,888 Starting up server gossip INFO 09:29:12,992 Binding thrift service to localhost/127.0.0.1:9160
看到這兩行啟動回顯資訊時,說明 Cassandra 已啟動成功。
串連到 Cassandra 並添加、擷取資料
Cassandra 的 bin 目錄已內建了命令列串連工具 cassandra-cli,可使用它串連到 Cassandra,並添加、讀取資料。
串連到 Cassandra,並添加、讀取資料
# bin/cassandra-cli --host localhost --port 9160 Connected to: "Test Cluster" on localhost/9160 Welcome to cassandra CLI. Type ‘help‘ or ‘?‘ for help. Type ‘quit‘ or ‘exit‘ to quit. cassandra> cassandra> set Keyspace1.Standard2[‘studentA‘][‘age‘] = ‘18‘ Value inserted cassandra> get Keyspace1.Standard2[‘studentA‘] => (column=age, value=18, timestamp=1272357045192000) Returned 1 results
停止 Cassandra 服務查出 Cassandra 的 pid:16328
# ps -ef | grep cassandra # kill 16328
Cassandra 配製檔案 storage-conf.xml 相關配製介紹清單 1. storage-conf.xml 節點配製說明清單
<!-- 叢集時顯示的節點名稱 --> <ClusterName>Test Cluster</ClusterName> <!-- 節點啟動時,是否自動加入到叢集中,預設為 false --> <AutoBootstrap>false</AutoBootstrap> <!-- 叢集的節點配製 --> <Seeds> <Seed>127.0.0.1</Seed> </Seeds> <!-- 節點之間通迅的監聽地址 --> <ListenAddress>localhost</ListenAddress> <!-- 基於 Thrift 的 cassandra 用戶端監聽地址,叢集時設為:0.0.0.0 表示偵聽所有用戶端 , 預設為:localhost --> <ThriftAddress>localhost</ThriftAddress> <!-- 用戶端串連的連接埠 --> <ThriftPort>9160</ThriftPort> <!-- FlushDataBufferSizeInMB 將 memtables 上的資料寫入在 Disk 上,超過設定好的限制大小時 ( 預設 32M),則將資料寫入磁碟, FlushIndexBufferSizeInMB 超過設定的時間長度(預設 8 分鐘)後,將 memtables 由的資料寫入磁碟中 --> <FlushDataBufferSizeInMB>32</FlushDataBufferSizeInMB> <FlushIndexBufferSizeInMB>8</FlushIndexBufferSizeInMB> <!-- 節點之間的日誌記錄同步模式。 預設:periodic, 對應配製 CommitLogSyncPeriodInMS 啟動 batch 時,則對應的配製 CommitLogSyncBatchWindowInMS --> <CommitLogSync>periodic</CommitLogSync> <!-- 預設為每 10 秒同步一次日誌記錄 --> <CommitLogSyncPeriodInMS>10000</CommitLogSyncPeriodInMS> <!-- <CommitLogSyncBatchWindowInMS>1</CommitLogSyncBatchWindowInMS> -->
回頁首
常用程式設計語言使用 Cassandra 來儲存資料
在使用 Cassandra 時,通常情況下都需要使用第三方外掛程式 Thrift 來產生與 Cassandra 相關的庫檔案 , 您可以在 http://incubator.apache.org/thrift 下載此外掛程式,並學習它的使用方法。以下是分別在 Java、PHP、Python、C#、Ruby 五種常用程式設計語言中使用 Cassandra:
Java 程式使用 Cassandra
把 libthrift-r917130.jar,apache-cassandra-0.6.0-rc1.jar 加入到 Eclipse 的編譯路徑中。
建立資料庫連接:使用 libthrift-r917130.jar 的 TTransport 的 open 方法建立起與 Cassandra 服務端 (IP:192.168.10.2 連接埠:9160) 的串連。
資料庫操作:使用 Cassandra.Client 建立一個用戶端執行個體。調用 Client 執行個體的 insert 方法寫入資料,通過 get 方法擷取資料。
關閉資料庫連接:使用 TTransport 的 close 方法斷開與 Cassandra 服務端的串連。
清單 2. Java 串連 Cassandra,寫入並讀取資料。
package com.test.cassandra;| import java.io.UnsupportedEncodingException; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.TException; import org.apache.cassandra.thrift.Cassandra; import org.apache.cassandra.thrift.Column; import org.apache.cassandra.thrift.ColumnOrSuperColumn; import org.apache.cassandra.thrift.ColumnPath; import org.apache.cassandra.thrift.ConsistencyLevel; import org.apache.cassandra.thrift.InvalidRequestException; import org.apache.cassandra.thrift.NotFoundException; import org.apache.cassandra.thrift.TimedOutException; import org.apache.cassandra.thrift.UnavailableException; /* * 使 Java 用戶端串連 Cassandra 並進行讀寫操作 * @author jimmy * @date 2010-04-10 */ public class JCassandraClient{ public static void main(String[] args) throws InvalidRequestException, NotFoundException, UnavailableException, TimedOutException, TException, UnsupportedEncodingException { // 建立資料庫連接 TTransport tr = new TSocket("192.168.10.2", 9160); TProtocol proto = new TBinaryProtocol(tr); Cassandra.Client client = new Cassandra.Client(proto); tr.open(); String keyspace = "Keyspace1"; String cf = "Standard2"; String key = "studentA"; // 插入資料 long timestamp = System.currentTimeMillis(); ColumnPath path = new ColumnPath(cf); path.setColumn("age".getBytes("UTF-8")); client.insert(keyspace,key,path,"18".getBytes("UTF-8"), timestamp,ConsistencyLevel.ONE); path.setColumn("height".getBytes("UTF-8")); client.insert(keyspace,key,path,"172cm".getBytes("UTF-8"), timestamp,ConsistencyLevel.ONE); // 讀取資料 path.setColumn("height".getBytes("UTF-8")); ColumnOrSuperColumn cc = client.get(keyspace, key, path, ConsistencyLevel.ONE); Column c = cc.getColumn(); String v = new String(c.value, "UTF-8"); // 關閉資料庫連接 tr.close(); } }PHP 程式使用 Cassandra
在 PHP 代碼中使用 Cassandra,需要藉助 Thrift 來產生需要的 PHP 檔案,通過使用 thrift --gen php interface/cassandra.thrift 產生所需要的 PHP 檔案,產生的 PHP 檔案中提供了與 Cassandra 建立串連、讀寫資料時所需要的函數。
清單 3. PHP 串連 Cassandra,寫入並讀取資料。
<?php $GLOBALS[‘THRIFT_ROOT‘] = ‘/usr/share/php/Thrift‘; require_once $GLOBALS[‘THRIFT_ROOT‘].‘/packages/cassandra/Cassandra.php‘; require_once $GLOBALS[‘THRIFT_ROOT‘].‘/packages/cassandra/cassandra_types.php‘; require_once $GLOBALS[‘THRIFT_ROOT‘].‘/transport/TSocket.php‘; require_once $GLOBALS[‘THRIFT_ROOT‘].‘/protocol/TBinaryProtocol.php‘; require_once $GLOBALS[‘THRIFT_ROOT‘].‘/transport/TFramedTransport.php‘; require_once $GLOBALS[‘THRIFT_ROOT‘].‘/transport/TBufferedTransport.php‘; try { // 建立 Cassandra 串連 $socket = new TSocket(‘192.168.10.2‘, 9160); $transport = new TBufferedTransport($socket, 1024, 1024); $protocol = new TBinaryProtocolAccelerated($transport); $client = new CassandraClient($protocol); $transport->open(); $keyspace = ‘Keyspace1‘; $keyUser = "studentA"; $columnPath = new cassandra_ColumnPath(); $columnPath->column_family = ‘Standard1‘; $columnPath->super_column = null; $columnPath->column = ‘age‘; $consistency_level = cassandra_ConsistencyLevel::ZERO; $timestamp = time(); $value = "18"; // 寫入資料 $client->insert($keyspace, $keyUser, $columnPath, $value, $timestamp, $consistency_level); $columnParent = new cassandra_ColumnParent(); $columnParent->column_family = "Standard1"; $columnParent->super_column = NULL; $sliceRange = new cassandra_SliceRange(); $sliceRange->start = ""; $sliceRange->finish = ""; $predicate = new cassandra_SlicePredicate(); list() = $predicate->column_names; $predicate->slice_range = $sliceRange; $consistency_level = cassandra_ConsistencyLevel::ONE; $keyUser = studentA; // 查詢資料 $result = $client->get_slice($keyspace, $keyUser, $columnParent, $predicate, $consistency_level); // 關閉串連 $transport->close(); } catch (TException $tx) { }?>Python 程式使用 Cassandra
在 Python 中使用 Cassandra 需要 Thrift 來產生第三方 Python 庫,產生方式: thrift --gen py interface/cassandra.thrift, 然後在 Python 代碼中引入所需的 Python 庫,產生的 Python 庫提供了與 Cassandra 建立串連、讀寫資料時所需要的方法。
清單 4. Python 串連 Cassandra,寫入並讀取資料。
from thrift import Thrift from thrift.transport import TTransport from thrift.transport import TSocket from thrift.protocol.TBinaryProtocol import TBinaryProtocolAccelerated from cassandra import Cassandra from cassandra.ttypes import * import time import pprint def main(): socket = TSocket.TSocket("192.168.10.2", 9160) transport = TTransport.TBufferedTransport(socket) protocol = TBinaryProtocol.TBinaryProtocolAccelerated(transport) client = Cassandra.Client(protocol) pp = pprint.PrettyPrinter(indent=2) keyspace = "Keyspace1" column_path = ColumnPath(column_family="Standard1", column="age") key = "studentA" value = "18 " timestamp = time.time() try: # 開啟資料庫連接 transport.open() # 寫入資料 client.insert(keyspace,key,column_path, value,timestamp,ConsistencyLevel.ZERO) # 查詢資料 column_parent = ColumnParent(column_family="Standard1") slice_range = SliceRange(start="", finish="") predicate = SlicePredicate(slice_range=slice_range) result = client.get_slice(keyspace,key,column_parent, predicate,ConsistencyLevel.ONE) pp.pprint(result) except Thrift.TException, tx: print ‘Thrift: %s‘ % tx.message finally: # 關閉串連 transport.close() if __name__ == ‘__main__‘: main()C# 使用 Cassandra
在 C# 中使用 Cassandra 需要 Thrift.exe 來產生動態連結程式庫,使用 ./thrift.exe --gen csharp interface/cassandra.thrift 產生所需要的 DLL 檔案,產生的 DLL 提供了與 Cassandra 建立串連,讀寫資料等所需的類和方法,在編程環境中引入產生的 DLL,即可使用。
清單 5. C# 串連 Cassandra,寫入並讀取資料。
namespace CshareCassandra{ using System; using System.Collections.Generic; using System.Diagnostics; using Apache.Cassandra; using Thrift.Protocol; using Thrift.Transport; class CassandraClient{ static void Main(string[] args){ // 建立資料庫連接 TTransport transport = new TSocket("192.168.10.2", 9160); TProtocol protocol = new TBinaryProtocol(transport); Cassandra.Client client = new Cassandra.Client(protocol); transport.Open(); System.Text.Encoding utf8Encoding = System.Text.Encoding.UTF8; long timeStamp = DateTime.Now.Millisecond; ColumnPath nameColumnPath = new ColumnPath(){ Column_family = "Standard1", Column = utf8Encoding.GetBytes("age")}; // 寫入資料 client.insert("Keyspace1","studentA",nameColumnPath, utf8Encoding.GetBytes("18"),timeStamp, ConsistencyLevel.ONE); // 讀取資料 ColumnOrSuperColumn returnedColumn = client.get("Keyspace1", "studentA", nameColumnPath, ConsistencyLevel.ONE); Console.WriteLine("Keyspace1/Standard1: age: {0}, value: {1}", utf8Encoding.GetString(returnedColumn.Column.Name), utf8Encoding.GetString(returnedColumn.Column.Value)); // 關閉串連 transport.Close(); } }}Ruby 使用 Cassandra
在 Ruby 中使用 Cassandra 需要先安裝 gem,安裝命令:gem install cassandra
安裝完成後,開啟 Ruby 的 irb 開始使用 Cassandra。
清單 6. Ruby 串連 Cassandra,寫入並讀取資料
> require ‘rubygems‘ > require ‘cassandra‘ # 建立資料庫連接 > cdb = Cassandra.new(‘Keyspace1‘,"192.168.10.1:9160", :retries => 3) # 寫入資料 > cdb.insert(:Standard1, ‘studentA‘, {‘age‘ => ‘18‘}) # 讀取資料 > cdb.get(:Standard1, :studentA) # 關閉串連 > cdb.disconnect
回頁首
搭建 Cassandra 叢集環境
Cassandra 的叢集是沒有中心節點的,各個節點的地位完全相同,節點之間是通過 gossip 的協議來維護叢集的狀態。
以下是兩台安裝了 Linux 系統的伺服器,且初步設定了 Cassandra 環境和啟用了連接埠 7000,9160:
| 伺服器名 |
連接埠 |
IP 位址 |
| ServiceA |
7000,9160 |
192.168.10.3 |
| ServiceB |
7000,9160 |
192.168.10.2 |
配製伺服器 ServiceA、ServiceB 的 storage-conf.xml 檔案ServiceA 的配置
<Seeds> <Seed>192.168.10.3</Seed> </Seeds> <ListenAddress>192.168.10.2</ListenAddress> <ThriftAddress>0.0.0.0</ThriftAddress>
ServiceB 的配置
<Seeds> <Seed>192.168.10.3</Seed> <Seed>192.168.10.2</Seed> </Seeds> <ListenAddress>192.168.10.2</ListenAddress> <ThriftAddress>0.0.0.0</ThriftAddress>
配製完成後,分別啟動 ServiceA 和 ServiceB 上的 Cassandra 服務。
查看 ServiceA 和 ServiceB 是否叢集成功,可使用 Cassandra 內建的用戶端命令
bin/nodetool --host 192.168.10.2 ring
叢集成功則會返回以下類似資訊:
Address Status Load Range Ring 106218876142754404016344802054916108445 192.168.10.2 Up 2.55 KB 31730917190839729088079827277059909532 |<--| 192.168.10.3 Up 3.26 KB 106218876142754404016344802054916108445 |-->|
使用 Cassandra 命令列工具進行叢集測試
從 ServiceB 串連到 ServiceA,可使用命令:
cassandra-cli -host 192.168.10.3 -port 9160
叢集測試一
寫入叢集資料 ServiceA 串連到 ServiceA: # set Keyspace1.Standard2[‘studentAA‘][‘A2A‘] = ‘a2a‘ ServiceB 串連到 ServiceA: # set Keyspace1.Standard2[‘studentBA‘][‘B2A‘] = ‘b2a‘ ServiceA 串連到 ServiceB: # set Keyspace1.Standard2[‘studentAB‘][‘A2B‘] = ‘a2b‘
擷取叢集資料:
ServiceA 串連到 ServiceA : # get Keyspace1.Standard2[‘studentAA‘], get Keyspace1.Standard2[‘studentBA‘], get Keyspace1.Standard2[‘studentAB‘] ServiceB 串連到 ServiceA : # get Keyspace1.Standard2[‘studentAA‘], get Keyspace1.Standard2[‘studentBA‘], get Keyspace1.Standard2[‘studentAB‘] ServiceA 串連到 ServiceB : # get Keyspace1.Standard2[‘studentAA‘], get Keyspace1.Standard2[‘studentBA‘], get Keyspace1.Standard2[‘studentAB‘]
清單 8. 叢集測試清單二
ServiceA 停止 Cassandra 服務,ServiceA 串連到 ServiceB 並寫入資料
# set Keyspace1.Standard2[‘studentAR‘][‘A2R‘] = ‘a2R‘
啟動 ServiceA,並連結到 ServiceA 本身,讀取剛才在 ServiceB 寫入的資料
# bin/cassandra-cli -host 192.168.10.3 -port 9160 # get Keyspace1.Standard2[‘studentAR‘]
回頁首
總結
以上我們介紹了 Cassandra 的資料模型、節點安裝和配置、常用程式設計語言中使用 Cassandra 以及 Cassandra 的叢集和測試。Cassandra 是一個高效能的 P2P 去中心化的非關係型資料庫,可以分布式進行讀寫操作。在系統運行時可以隨意的添加或刪降欄位,是 SNS 應用的理想資料庫。
分布式 Key-Value 儲存系統:Cassandra 入門