Couchbase服務器可以單獨運行,也可以作為集群運行。在Couchbase集群里,運行一個或多個Couchbase實例。集群里所有節點是相等的,提供相同的功能和信息,沒有層次結構或者拓撲的概念,也沒有主節點、從節點之分。整個集群共享每個獨立節點的信息,每個節點負責對數據的一部分進行響應。
集群是水平擴展的。要增加集群的容量,你只需加多一個節點。節點間沒有父子關系或者層次結構。這意味着Couchbase在存儲容量和性能方面,都可以做到線性擴容。
集群管理
集群里的每個節點包含了集群管理器組件。集群管理器負責下述行為:
• 集群管理
• 節點管理
• 節點監控
• 可管理的REST API
• 統計報表
• 實時日志
• Multitenancy
• 訪問安全
Buckets
Couchbase使用命名buckets提供數據管理服務,buckets是獨立的虛擬數據容器。一個bucket就是Couchbase服務器集群里的一邏輯組物理資源,它可以被集群里的多個客戶端應用使用。buckets提供安全的機制來組織、管理、分析數據存儲資源。
Couchbase提供兩種核心類型的buckets,如下描述。Couchbase根據bucket類型來提供運行時的統計報告。
• Couchbase類型:提供高可用和動態重配置的分布式數據存儲,提供持久化存儲和復制服務。這種bucket也100%兼容Memcached協議。
• Memcached類型:提供直接尋址的、分布式的、內存型的文本緩存。這種bucket被設計來作為關系型數據庫的補充 — 緩存經常查詢的數據,從而減少對數據庫的查詢量,提高性能。
不同的bucket類型提供不同的核心功能。Couchbase類型的bucket提供一種高可用、動態重配置、分布式的數據存儲,在集群的節點發生故障時,它允許集群自我修復,並繼續提供服務。
Couchbase bucket的特有功能
• 持久性:數據單元異步從內存寫往磁盤,防范服務重啟或較小的故障發生時數據丟失。持久性屬性是在bucket級設置的。
• 復制:對couchbase類型的bucket,可以配置數據復制的份數。集群里的每個節點既保存活躍的數據,又保存數據副本。假如某個節點掛了,數據副本可以提升為活躍的容器,從而繼續提供高可用服務。
• 重新組織:集群里的數據可以重新組織和分布,從而動態增加或刪除bucket和服務器。
• bucket容積改變:couchbase類型的bucket可以動態調整容積,在應用需要時它們的大小可以被改變。
buckets可以用來隔離單個應用程序提供多租戶,或隔離數據類型,以提高性能和可視性。couchbase服務器允許你配置不同的端口來訪問不同的buckets,每個bucket都可以設置密碼驗證。
Smart client客戶端通過使用couchbase的管理REST API,自動發現集群結構的改變。這點保證了客戶端應用可以無間斷的從正確節點上訪問所需數據。
couchbase服務器允許你在生產環境里混合使用不同類型的buckets。內存和磁盤配額是基於bucket配置的,所以資源使用可以跨集群管理。配額可以在運行時修改,使得管理員能隨時重新分配資源。
vBuckets
一個vBucket定義為couchbase集群里key空間的一個子集的擁有者。通過使用vBuckets,信息在集群里分發更有效。vBucket系統被用於分布式數據,以及支持多節點間的數據復制。
客戶端在訪問bucket里的數據時,是與存儲了該數據的vBucket所在的集群節點進行通信。這種直接訪問方式允許客戶端與數據節點直接通信,而無需使用代理或重定向架構。其結果是從邏輯分區數據里抽象了物理拓撲,保證了couchbase的彈性服務。
這種架構也不同於memcached所用的方法,memcached使用客戶端key哈希,從預定義的列表里選取服務器。這要求維護一份服務器的活躍列表,並指定哈希算法例如Ketama,以在拓撲里維護數據一致性。vBucket架構也比傳統的RDBMS系統使用的數據分區更靈活。
vBuckets並非面向用戶的組件,但它們是couchbase服務器里非常重要的組件,是至關重要的可用性和彈性服務的支承。
每個文檔ID屬於一個vBucket。有一個映射函數用來計算給定的文檔屬於哪個vBucket。在couchbase服務器里,該映射函數是個哈希函數,它取文檔ID作為輸入,輸出vBucket標識符。一旦定位了vBucket標識符,會繼續從一個表里查找該vBucket位於哪個服務器上。這個表包含每行一個vBucket,vBucket與它的宿主主機成對出現。位於該表里的服務器通常服務了多個vBuckets。
內存數據
Couchbase架構包含了一個內置的cache層。這種機制允許非常快速的響應時間,因為數據是直接寫往內存的,並且讀的時候,也是從內存返回數據給客戶端。
這種設計的效果,提供了一個內置的cache層作為系統操作的中央部分延伸。客戶端接口與內存數據打交道,它將信息寫往Couchbase的內存;返回的數據也是從內存里獲取,或者先從磁盤加載到內存,再返回給客戶端。
這種處理方式保證了最佳性能。為了提高性能,你應該給每個節點分配最大數量的可用內存。內存跨集群匯總起來以供使用。
這點與其他數據庫系統的設計不同,其他數據庫的處理方式是,信息寫往數據庫,然后要么有一個獨立的cache層,要么依賴於操作系統的cache機制,把經常使用的信息放在內存以供訪問。
Ejection
Ejection是和Couchbase buckets一起使用的機制,它的作用是從內存里刪除數據,給活躍的、更頻繁使用的數據讓出空間,它是cache系統的核心部分。Ejection自動執行,它聯合磁盤持久存儲系統,保證內存里的數據已經持久寫往磁盤,從而安全的刪除。
該系統確保內存存儲的數據在刪除前,已經寫往磁盤,在下次客戶端需要時,它又可以從磁盤加載到內存。Ejection的核心作用是讓系統能夠保持經常使用的數據駐留在內存,並且在客戶端需要從磁盤加載數據時,它能重新在內存里分配空間。
對於Couchbase buckets,數據永不刪除,除非客戶端明確的刪除文檔,或者文檔已到達過期時間。而ejection機制在從內存里刪除數據時,會保存數據的副本到磁盤上。
Expiration
每個存在Couchbase里的文檔有一個可選的過期時間(expiration)。默認是沒有過期時間(例如,數據永久存儲)。過期時間可用來設置數據的生命周期,系統自動從數據庫里刪除過期的數據。
在數據存儲時,由用戶指定文檔的過期時間。在數據更新時,過期時間可以同步被更新,還可以通過couchbase協議手工更新。過期時間可以是相對時間(例如60秒),也可以是絕對時間(例如2012年12月31日中午12點)。
使用過期時間的典型場景是Web session。假如用戶停止了活動,你希望session里的數據自動刪除。通過設置expiration,session數據會過期並且自動刪除,從而釋放內存和磁盤給其他數據使用。
Eviction
Eviction是針對memcached buckets從內存里完全刪除數據的過程。memcached使用一種LRU(最少近期使用)算法來從系統里完全刪除不再使用的數據。
在memcached bucket里,LRU數據會完全刪除以釋放空間,因為memcached buckets沒有持久化存儲。
磁盤存儲
為了提高性能,Couchbase傾向於在內存里存儲數據和提供服務。然而,很難保證有足夠的資源可以做到這點。比較常見的做法是把經常使用的工作數據存放在內存,並且快速響應給客戶端。
除了盡可能多的存放數據在內存外,couchbase也保存數據到磁盤。磁盤持久性允許更容易的備份/恢復操作,也允許數據量增大到超過內置cache層的容量。
Couchbase自動在內存和磁盤間轉移數據(在后台異步執行),保持經常使用的數據在內存,不經常使用的數據在磁盤。couchbase經常監控客戶端訪問的信息,讓活躍的數據保留在cache層內。
將數據從cache里刪除,騰出空間給更活躍信息使用的過程叫做ejection(前面的章節已描述)。通過couchbase集群里每個bucket的預先設定的閾值來決定何時執行ejection.
使用磁盤存儲引發的問題是,客戶端在請求文檔ID時,必須知道信息是否存在。couchbase使用元數據結構來解決這個問題。元數據里存儲了數據庫里每個文檔的信息,並且元數據位於內存里。這意味着假如文檔ID無效,服務器可以立刻返回”document ID not found”消息。當然,如果文檔有效,那么要么從內存里立刻返回,要么先從磁盤讀取到內存再返回(從磁盤讀會產生延時,或者導致超時)。
轉移數據到磁盤的過程是異步的。在couchbase提供服務的同時,數據在后台異步轉移到磁盤。如果並發寫往數據庫的量很大,客戶端可能收到服務器內存臨時不夠的通知,直到更多數據轉移到磁盤,內存有剩余為止。
類似的,假如couchbase需要從磁盤加載數據回內存,這個過程也是在后台發生的,后台進程從隊列里讀取請求,然后從磁盤讀取數據裝載回內存。客戶端一直等待,直到數據加載到內存,然后返回給客戶端。
這種異步機制以及使用隊列的方式,使得讀寫處理非常快,從而消除了典型的負載和性能尖峰,這通常是造成RDBMS性能不穩定的原因。
熱啟動
當couchbase重啟,或者執行備份恢復的啟動時,它進入熱啟動的狀態。熱啟動從磁盤加載數據到內存,從而讓數據對客戶端可用。
在服務請求之前,熱啟動必須完成。根據數據庫容量和配置的不同,以及存儲數據數量的不同,熱啟動可能要花費一定的時間來完成。
Rebalancing
數據在couchbase里的存儲方式是通過vBucket結構提供的分布式機制來實現的。假如你想擴展或收縮couchbase集群,這時存儲在vBuckets里的信息需要在集群節點間重新分布,並且對應的vBucket映射表也需要更新來適應新的結構。這個過程叫做rebalancing.
在集群的存儲結構改變時,rebalancing必須手工執行。rebalance進程重新對存儲信息的vBuckets進行分配,在集群節點間物理的轉移數據,以匹配新的結構。
Rebalancing過程可以在集群運行並正常服務請求時執行。數據在后台進行轉移,客戶端的讀和寫仍然針對當前存在的結構,直到轉移完成,系統會更新vBucket映射表,並將結果通知smart clients和Moxi proxy(它們是couchbase的客戶端)。
其結果是整個集群的分布式數據重新分配,數據在整個數據庫里均衡分布,並兼顧了支持系統運轉的數據和數據副本的數量。
副本和復制
除了集群里的分布式數據外,couchbase還可以在集群里創建數據副本。這些副本也與vBucket結構協調工作,各個vBucket的數據副本在整個集群里分布。分布式副本跟核心數據的處理方式一樣,而副本的存在可以防止集群里的單點故障。
集群里的副本復制是完全點對點的,數據在節點間直接交換,沒有拓撲、層次或主從關系。客戶端將數據寫往一個節點時,數據被存在vBucket里,同時使用TAB系統分發到一個或多個副本vBucket.
在集群里的一個節點發生故障時,副本vBucket被激活,用來代替故障節點的vBuckets進行工作。這個過程秒完成,因為副本是在原始數據創建的同時就創建了,不會臨時執行拷貝;副本vBucket已經持有數據在那里,坐等被激活。副本vBucket激活后,會更新系統的映射表,以便客戶端直接與新的vBucket結構通信。
副本的配置是基於每個bucket的。根據數據安全層次的不同,你可以對不同的bucket配置不同數量的副本。請注意,只有在集群里的機器數量足夠時,副本才可能被激活。例如,你配置了一個bucket保持3個副本,只有在集群里有4個節點時,副本才會激活。
一個bucket的副本數量,在bucket創建后不允許再修改。
Failover
數據的副本在整個集群里分布。對於couchbase類型的bucket你可以配置副本的數量,就是說在一個couchbase集群里對每份數據保存多少數量的副本。
在服務器發生故障時(不管是臨時故障還是管理維護),可以使用稱為failover的技術把故障節點標記為不可用,從而激活該服務器對應的副本vBuckets。
failover進程聯系每個保存了副本的服務器,更新內部映射表,將客戶端的請求映射到新的可用節點上。
可以手工執行failover,也可以使用內置的自動failover機制,在集群里的節點不可用超過一定時間后,failover自動打開。
TAP
TAP是couchbase集群的內部協議,在多個方面用來進行內部數據交換。TAP提供了系統內執行了變更的數據的數據流。
TAP被用於數據復制,在不同的vBuckets之間拷貝數據副本。它也用於rebalance過程,在vBuckets之間轉移數據從而使數據在整個系統里重新分布。
客戶端接口
有許多couchbase的客戶端可用,它們歸為2類,一類是smart clients,另一類是memcached兼容客戶端。smart clients完全與集群進行通信,根據內置的集群配置和基於vBuckets的分布式信息,數據自動寫往集群里的正確節點。smart clients與集群保持通信,確保在故障轉移或者rebalancing時,客戶端更新自己的配置,將數據寫入到正確的節點。
如果使用非智能的memcached兼容的客戶端,就必須使用一個位於客戶端的Moxi組件。Moxi作為一個代理服務器存在,位於客戶端連接和couchbase集群之間。除了讓傳統的memcached客戶端可以寫往couchbase集群,Moxi還提供了集群級的分布和接口。使用Moxi還讓你在不改變任何已存在的memcached應用的前提下,獲取couchbase的特有功能所帶來的優勢。
在couchbase服務器里,存儲和獲取信息的方式根據實際情況而不同。所有方法可以歸類為CRUD這4類基本操作:Create(創建),Retrieve(獲取),Update(更新),Delete(刪除)。
創建
使用couchbase的客戶端接口,根據文檔ID將文檔信息存儲到數據庫里。批量操作也可行,並且比多個單次操作更有效。
對於基本的存儲、獲取信息的操作,couchbase兼容memcached客戶端協議。對於更高級的操作,你需要使用couchbase客戶端庫。
存儲的值可以是任何二進制值,包括結構化和非結構化的串,序列化對象,或者原生的二進制數據例如圖片或音頻。
獲取
為了獲取數據,你必須先知道文檔ID。也可以執行批量操作,同時獲取多個文檔,這比單次操作更有效。
更新
包括更新整個文檔的操作,也包括追加數據到已存在記錄的操作,或者遞增和遞減整數值。
刪除
有個單一的刪除操作,用來從數據庫里刪除整個文檔。
各語言的庫
couchbase官方支持下列語言和環境的smart clients庫:
• Java (http://www.couchbase.com/develop/java/current)
• .NET (http://www.couchbase.com/develop/net/current)
• PHP (http://www.couchbase.com/develop/php/current)
• Ruby (http://www.couchbase.com/develop/ruby/current)
• C [libcouchbase] (http://www.couchbase.com/develop/c/next)
在筆者寫此書時,也有一個實驗性的Python庫可用(http://www.couchbase.com/develop/python/current)。Mark Nunberg還寫了個Perl客戶端Couchbase::Client,它基於C的libcouchbase庫。你可以在CPAN上獲取到這個庫。
Proxy (Moxi)
Couchbase的Moxi組件提供了一個代理服務,允許傳統的memcached客戶端在不修改應用的前提下,使用couchbase服務。該代理服務提供了在客戶端和服務器之間的連接池,在couchbase集群的內部拓撲變更時,它及時通知客戶端,從而保證了信息在集群里分布正確。
假如你使用了smart clients客戶端庫,就不必使用Moxi。
Moxi可以部署在服務端,也可以在客戶端。產品環境里在服務端部署Moxi可能會帶來問題,建議只部署在客戶端。
管理工具
Couchbase被設計為盡可能易用,不要求管理員太多的關注,除了監控健康狀態和容量。系統提供了三種途徑來管理和監控couchbase服務器和集群。
• Web管理控制台
Couchbase包含一個內置的web管理控制台,提供了完整的接口功能,包括配置、管理、監控你的服務器。
• 命令行接口
Couchbase提供了一套命令行工具,用於控制和訪問couchbase服務器和集群。可以結合命令行和你自己的腳本和管理過程,來提供附加的功能,比如自動故障轉移、備份等。
• 管理REST API
Web控制台和命令行工具都利用了內置的REST API,API提供了完整的管理功能。所有的管理功能都通過REST API提供,並且它扮演了服務器的認證接口的角色。
因為REST API提供了完善的功能,你可以在自己的管理腳本或程序里使用它,來實現不同的操作。
統計和監控
為了了解couchbase集群正在做什么以及如何執行,系統提供了完整的統計和監控信息。統計信息在所有的管理接口都可以看到。統計系統非常完整,你可以監控和定位到每一個細節。監控系統健康的核心統計報表通過web控制台提供,該報表使用內置的實時圖形,允許你實時監控系統的健康和性能狀況。
Hello Couchbase
Couchbase存儲信息時,信息的值為文檔,鍵是文檔ID。這使得開發和部署應用非常簡單。在存儲信息時,提供文檔內容和對應的文檔ID。在獲取信息時,提供文檔ID就可以獲取到對應的值。
只要你知道文檔ID,就總可以獲取到信息的內容。數據簡單的按字節順序存放。這意味着你既可以存放裸信息(例如字串或整數)、復雜的數據結構(例如JSON),也可以存放序列化對象。序列化會轉換特定語言的原生對象為合適的字節串,今后從服務器里獲取時,它們又可以還原為對象。
基本的存取過程非常簡單。下述示例里我使用了Ruby,不過其他語言的客戶端都以相同方式工作,因為它們都使用了相同的核心協議。
安裝了ruby客戶端庫后,就可以編寫一段簡單的程序來存放信息到couchbase,然后再獲取信息。如下是示例的hello-wrold.rb程序:
require 'rubygems'
require 'couchbase'
client = Couchbase.new "http://127.0.0.1:8091/pools/default"
client.quiet = false
begin
spoon = client.get "spoon"
puts spoon
rescue Couchbase::Error::NotFound => e
puts "There is no spoon."
client.set "spoon", "Hello World!", :ttl => 10
end
• 頭兩行加載必要的庫
• 下一行打開到couchbase集群的連接。此處定義里,URL必須指向集群里的至少一個節點,這里是本機地址。default表示bucket名字,你可以使用其他bucket,假如已經配置了。
• 后面的行執行獲取和存儲操作。假如初次獲取操作(針對spoon這個ID)失敗,我們就將數據寫入到DB里。只要文檔ID存在,腳本就打印出對應的存放值。
你可以從命令行運行和測試該腳本。第一次運行時,應該輸出這個錯誤串:
shell> ruby hello-world.rb
There is no spoon.
指定的文檔並沒有存在於數據庫,但隨后就加進去了。第二次運行時,就可以打印文檔的值:
shell> ruby hello-world.rb
Hello World!
此外,字串文檔在存儲時,賦予了一個過期時間10秒。這意味着在存放信息后,等待超過10秒信息就被刪除了。假如首次運行腳本后,超過10秒再第二次運行該腳本,會輸出如下錯誤串:
shell> ruby hello-world.rb
There is no spoon.
盡管這是一個非常簡單的示例程序,它描述了使用基本的get/set操作,在couchbase里存取信息的原理。
客戶端與集群的交互
在開發應用時,最常見的問題是,客戶端和客戶端庫如何與集群通信,如何適應運行中集群的拓撲結構改變。通常而言,在客戶端與數據庫交互中,couchbase扮演一個黑盒子。假如你使用了smart client,集群的拓撲、節點結構,以及對應信息的變更,完全由vBucket映射表和客戶端庫聯合起來自動處理。
客戶端庫負責客戶端與集群中各個節點的直接通信。你用來初次建立連接的那個節點,不會扮演代理或網關的角色。smart client(或Moxi)會加載vBucket映射表,從映射表里學習到把不同信息存儲到集群里的哪個節點。客戶端直接與正確的節點通信,中間沒有代理或網關。
在拓撲結構改變時(例如,rebalance或者故障轉移),客戶端庫自動處理任何臨時的錯誤。總之而言,你不必關心任何集群的配置與拓撲相關信息。
關於客戶端與集群的通信機制,請見之前的文檔,Couchbase的Smartclient有何作用
過期時間
過期時間(time to live [TTL])的用途是,在存儲信息時設置一個超時值,它讓文檔自動過期刪除。除了delete()函數外,文檔的過期值是從數據庫里刪除信息的唯一方法。一旦過期時間到了,數據就會刪除。
過期時間設置為一個數字,它代表秒數。如果這個數字代表的秒數,小余30天(30*24*60*60秒),這個值就是相對值。例如,3600秒表示文檔在一個小時后過期。如果秒數大於30天,過期值就是絕對值,表示從epoch時間以來的絕對秒數。
過期時間可以用在不同的應用場景,但最普通的場景是使用它存儲session數據。例如你可以用它存儲session並設置過期時間2小時,用戶如果超過2小時沒訪問網站,session自動刪除。
如果用戶還在訪問數據,可以使用touch()和getAndTouch()函數來更新過期時間,不必另外執行數據更新操作來更新過期值。
除了過期時間,所有文檔在存儲時也帶了一系列標簽(flags)。並非所有的客戶端庫都支持標簽,但如果支持,你可以用標簽來增加文檔描述信息,例如文檔類型。
【注】這次測試中,遇到了一些問題,因此本文用E文編寫,隨后發到Couchbase社區咨詢一下。
We have five nodes as the couchbase cluster, the servers have enough memory and disk, with ubuntu 12.04 OS. Couchbase server version: 2.0.1 community edition (build-170). I use the default bucket for test, it has been assigned 5GB memory totally.
The test Ruby script is as below:
require 'couchbase'
require 'securerandom'
client = Couchbase.connect("http://couch.example.com:8091/pools/default/buckets/default"
100000.times do |s|
value_10k = SecureRandom.hex(5120)
key_uuid = SecureRandom.uuid
begin
client.set key_uuid, value_10k, :ttl => 3600
rescue Couchbase::Error::Base => e
puts e
end
end
More »
文檔數據
文檔數據是純字節序列,服務器不會試圖去解析或理解存儲的文檔格式。這意味着你可以存儲從數字到圖片的任何東西。這種開放的存儲結構,也意味着不必去聲明或定義要存儲信息的結構,你可以充分靈活的自己定義所需要的結構。
存儲簡單的信息,例如數字或字串,只需簡單的將數據寫進文檔值。存儲復雜的信息結構,你可能需要序列化對象,或者更通用的JSON結構。
序列化
序列化將特定語言的復雜的內部結構,例如hash或對象,轉換為字節序列,從而可以存儲在couchbase里。序列化的結構還能被還原成原來的數據結構,從而被特定的語言直接使用。
所有的couchbase客戶端庫在存取文檔時,都自動支持序列化和反序列化結構或對象。
JSON
序列化信息的問題是,它是語言約定的。假如你在Java里存儲一個對象或數據結構到couchbase,它被序列化為一個只有java語言庫才能識別的串。假如要跨語言進行信息存儲,你需要使用更通用的格式,比如JSON。
JSON之所以流行,一是因為它很簡潔(它看起來像許多腳本語言的內置hash結構),二是它可以被Javascript直接使用,這樣在web基礎的應用里,不必對它做特別處理。
JSON的格式有良好的描述,詳見http://json.org. 在couchebase里使用JSON的最好方法是,每條記錄存儲一個JSON哈希結構。例如,可以定義一條啤酒記錄如下:
{
"id": "beer_Hoptimus_Prime",
"type": "beer",
"abv": 10.0,
"brewery": "Legacy Brewing Co.",
"category": "North American Ale",
"name": "Hoptimus Prime",
"style": "Imperial or Double India Pale Ale",
}
許多語言支持類似的hash、hashmap或關聯數組結構,有相應的庫可以將hash結構轉換為JSON格式,並還原它們。
請注意:couchbase 2.0在使用JSON存儲信息時,允許你使用查詢和索引的高級功能。
在couchbase里存儲數據
couchbase是一個嚴格的文檔型數據庫。這就意味着,信息根據文檔ID存儲在數據庫里。沒有必要設置數據格式、創建表結構,甚至不需要告訴couchbase關於要存儲的信息。你要做的所有工作就是根據指定文檔ID存儲文檔數據。
因為文檔的結構原因,在開發應用時有一些不同的考慮點。讓我們了解下文檔ID和文檔值的基本因素。
文檔ID
文檔ID(或key)非常重要,它用來索引存儲的數據。key在一個bucket里必須是唯一的。
key用來標識所存儲的信息,可以是任意字串,通常最大長度128位。couchbase沒有機制為你自動創建文檔ID。假如使用UUID,就必須在你自己的程序里使用對應的UUID庫。
通常實踐是,使用前綴、類型、分隔符來區分存儲在每個bucket里的不同信息。例如,可以使用beer_9834759這個ID來存儲關於啤酒的信息。這里的beer前綴標識記錄類型,下划線作為分隔符,后面的數字作為唯一的啤酒ID。
couchbase 1.8沒有獲取文檔ID列表的功能,也不能遍歷一個bucket里的所有文檔。除非指定文檔ID,你不能查詢信息。然而,這一點會在couchbase 2.0里予以改進和支持。
針對上述問題的一個解決方案是在應用里創建信息鏈。例如,當一個新的啤酒記錄追加到數據庫里時,你可以更新一個beer_list的文檔,它包含了所有的啤酒記錄ID。因為更新是原子性的,所以可以維護這么一份最新的信息列表。實踐做法可以參考這篇博客:https://blog.couchbase.com/maintaining-set-memcached
應用程序可以通過使用和讀取一個固定的記錄來引導自身的數據查詢,這個記錄要么是本地的配置,要么是在數據庫里的配置記錄。
CAS機制
除了核心函數外,還有一個特殊函數叫做CAS (compare and swap). CAS提供了一個校驗合,讓多個客戶端在同時更新文檔時避免產生沖突。
例如,考慮如下場景:
1. 客戶端A獲取到文檔Martin的值
2. 客戶端B也獲取到文檔Martin的值
3. 客戶端A修改文檔,並更新到數據庫
4. 客戶端B也修改文檔,並更新到數據庫
在上述場景里,客戶端B的修改會覆蓋掉A修改的值。
為了解決這種情況,可以使用cas()函數。它要求提供從數據庫里返回的唯一的CAS值。CAS值在文檔更新時,每次都會改變,即使文檔更新后的內容不變。將更新發送到服務器時,假如客戶端提供的CAS值與服務器里當前存儲的CAS不匹配,更新就會失敗。
使用CAS后的應用場景如下:
1. 客戶端A獲取到文檔Martin的值,以及對應的CAS值
2. 客戶端B也獲取到文檔Martin的值,以及對應的CAS值
3. 客戶端A修改了文檔,並且提交到數據庫,同時提交CAS,本次更新成功,數據庫也同步更新CAS
4. 客戶端B也修改了文檔,並且嘗試使用CAS進行數據庫更新,本次更新失敗,因為客戶端的CAS與服務器存儲的CAS現在不同了
因此,CAS提供了一種檢查機制,保證你當前更新的文檔自上次獲取以來,沒有發生變更過。
在編程代碼里,CAS是一個類似於update()的函數。取決於環境的不同,你可能先要使用gets()函數來獲取到文檔信息和CAS值。
例如,在java里先用gets()獲取文檔信息和CAS值,接着用cas()方法來更新文檔:
1
2
3 CASValue customer = client.gets("customer";
CASResponse casr = client.cas("customer", customer.getCas(), "new string value";
CAS的局限性是在客戶端庫這一級並沒有強制執行它。假如你想對所有的更新操作使用CAS,就必須明確的使用它來代替標准的文檔更新函數。
在google groups里建立了一個Couchbase中文討論組:
Couchbase是開源分布式、面向文檔的NoSQL數據庫。它是基於集群設計的,通過它的web管理系統,很容易配置一套高性能的集群。擴容方便,性能隨着容量增加而線性增加。包括AOL、Linkedin、Zynga等公司在使用它。風河博客(www.nsbeta.info)翻譯了一系列關於Couchbase的文檔。
對Couchbase有興趣童鞋可使用Gmail加入討論。論壇地址:
https://groups.google.com/d/forum/couchbase-china
couchbase支持的core協議和操作方法如下表所示。
不管客戶端庫如何,這些函數在各種語言里工作方式都差不多,可能在語言自己的實現規范上有所不同。例如,可以在ruby里增加一個值:
1 couchbase.incr("counter", 5)
在.NET里,函數調用是:
1 client.Increment("counter", 100, 1);
上述第二個參數是假如指定文檔ID不存在時的默認值。
基本操作
couchbase基於文檔ID,執行非常簡單的文檔存取模型。在存儲信息時,不需要定義表或結構,不需要寫復雜的查詢去獲取信息。
couchbase里的所有操作遵循下列規則:
• 所有操作是原子性的
這意味着服務器里沒有鎖機制,不可能存在來自多個客戶端的並發命令破壞了數據。然而,這也意味着如果多個客戶端對同一文檔ID執行set操作,只有最后一個操作有效。為了管理這種並發和競爭條件,可以使用CAS操作。這要求提供一個附加的校驗值,在校驗值不合的情況下,文檔不會被更新。
• 所有數據操作都要求key
所有對數據的操作,都要求提供一個key。不能執行全局操作,或者針對多個key的操作(multiple-get除外)。
• 沒有內部鎖
在存儲或更新數據時,系統並沒有一個內部鎖。操作要么完全成功,要么因為某種理由失敗(例如,臨時內存不足)。
不同的客戶端語言執行core協議,從而與couchbase服務器通信:
• 所有客戶端執行core協議
對不同的語言和環境,盡管在結構和函數名字上有些不同,但它們都執行同樣的核心操作協議。例如,所有實現里都有set()協議調用,盡管有些客戶端把它叫做”store”.
• 函數調用結構差異
因為不同的語言和環境的差異,對於core協議的函數調用結構也許不同。例如在java里,可變參數方法不可用,因而有多個同一函數的變體。在其他語言里,例如perl、python、ruby,hash是核心變量類型,經常被用來存儲和返回信息。
• 不同的語言提供額外的功能
某些客戶端實現提供額外的函數調用和結構,這些是原生core協議所沒有的。例如在java里,所有操作既可以是同步也可以是異步的,允許你在get或set操作時,繼續處理其他信息。
• 並非所有實現支持標簽
標簽在服務器里和數據一起存儲,並非被所有語言的客戶端支持。
配置選項
為了得到最佳的couchbase服務器和客戶端環境,你應該使用couchbase客戶端的一種。這些smart clients結合了核心接口協議(用來存取數據)和管理協議。后者允許客戶端直接與couchbase集群通信,理解vBucket映射表,以便信息能直接發送到集群里的單個節點。在故障轉移或rebalance時,同樣的機制允許vBucket映射表的變更快速生效。
couchbase直接支持六種客戶端庫:
• Java
• .NET
• PHP
• Ruby
• C (libcouchbase)
• Python
上述每種都叫做smart client,提供了系統關鍵功能和集群管理配置的最佳組合。可以從這里了解更多信息:http://www.couchbase.com/communities/all-client-libraries
假如你想使用memcached兼容的庫,或者你的應用已經使用了這種協議,那么建議用Moxi服務,它在兼容memcached的同時,又利用了couchbase集群架構的優勢。
Moxi代理服務在memcached協議和couchbase集群之間扮演接口角色。couchbase在協議級100%與memcached兼容。你應當在每個客戶端安裝Moxi,配置Moxi連接到couchbase集群,然后本地程序使用localhost作為主機名連接到Moxi服務。更多信息請參考couchbase官方文檔:http://www.couchbase.com/documentation
盡管couchbase兼容memcached協議,但是某些高級couchbase協議是memcached不支持的,這些優勢就會利用不上。