之前比較關注如何使用Cassandra,但是真正想大規模使用前提還是需要搞清楚備份機制,確保數據安全。
本文主要內容來自文檔 "Cassandra2.2"的翻譯。最后部分為真實操作案例。
這里假設你已經了解了Cassandra的壓縮、墓碑、數據一致性。
原始文檔鏈接:http://docs.datastax.com/en/cassandra/2.2/cassandra/operations/opsBackupRestore.html
備份和數據恢復
關於鏡像
Cassandra 通過直接保存所有在data目錄中的磁盤數據文件(SSTable file)的鏡像來備份數據。當系統還在線的時候,你可以保存所有的keyspace數據或者單個keyspace數據,或者某一張表的數據。
使用並行的ssh工具,比如pssh,你可以給整個集群做鏡像。這提供一種最終一致性備份。雖然沒有一個節點可以在鏡像制作過程中保證他和備份節點數據的一致性,Cassandra內置一致性機制會使用一個恢復鏡像恢復一致性。
當整個系統范圍內的鏡像都已經完成,你可以開啟每個節點的增量備份,它將備份那些最后一次鏡像后有改變的數據:每次SSTable 刷新,一個硬鏈接被復制到 data目錄的/backups 子目錄中(provided JNA is enabled)
如果允許JNA,鏡像將只是建立一個硬鏈接。否則io將由於文件被從一個地方拷貝到另一處而增長,將明顯降低效率。
如何得到一份鏡像
通過 nodetool snapshot指令來獲取每個節點的鏡像。如果要獲取全局的鏡像,使用並行ssh工具運行這個指令,比如pssh。
鏡像首先將所有內存數據刷新到磁盤中,然后給keyspace中的每個SSTable創建硬鏈接。你需要在每個節點上有足夠的空間來容納生成的鏡像文件。單一鏡像需要很小的空間。然而鏡像會導致你的磁盤使用量隨時間快速增長,因為鏡像需要留着那些被刪除的過期數據。當鏡像制作完成。如果你需要的話,你可以將備份文件移動到其他位置,或者就放在那。
注意:Cassandra只能在table schema存在的情況下恢復數據,所以建議你同樣備份一下schema(也就是system.schema_*系列的表).
操作過程
運行nodetool snapshot指令,需要指定hostname,JMX端口,keyspace名稱。比如:
$ nodetool -h localhost -p 7199 snapshot mykeyspace
結果
snapshot被創建在(原文)目錄中,每個s鏡像都包含多個包含鏡像當時數據的.db文件。
鏡像的路徑可能是:
使用安裝包:/var/lib/cassandra/data/mykeyspace/users-081a1500136111e482d09318a3b15cc2/snapshots/1406227071618/mykeyspace-users-ka-1-Data.db
使用tar包: install_location/data/data/mykeyspace/users-081a1500136111e482d09318a3b15cc2/snapshots/1406227071618/mykeyspace-users-ka-1-Data.db
增量備份
當增量備份被開啟(默認關閉),Cassandra 將硬鏈接每個刷新過的(flushed)的在keyspace數據目錄中的SStable到備份目錄中。這允許保存備份的偏移量而不是整個鏡像。同時,增量備份將和鏡像一起提供可靠的最新的備份機制。
和鏡像一樣,Cassandra 並不會自動清理增量備份文件。DataStax建議配置一個程序在新的鏡像生成后,清理增量備份文件。
操作過程
編輯cassandra.yaml 配置文件,將incremental_backups 設置為true,你需要在每個節點都進行此操作
從鏡像中恢復
從鏡像中還原keyspace需要所有的table的鏡像文件,如果使用增量備份,任何在鏡像后創建的增量備份文件都是必須的。
正常來說,在從鏡像還原之前,你需要清空(truncate)table. 如果備份發生在刪除之前,而你在刪除后還原數據沒有先清空,你無法得到原始的數據(行)。在壓縮后,墓碑存檔在和原始數據不同的SSTable中,所以當還原包含着原始數據的SSTable但是沒有移除墓碑,數據依舊表現出被刪除狀態(雖然它還在那)。
Cassandra 僅能再table schema存在的情況下備份鏡像。如果你沒有備份schema,你可以做下面的任何一種工作。
方法1:
- 還原鏡像,按照下面的方法
- 重新創建schema
方法2 :
- 重建schema
- 還原鏡像
- 執行nodetool refresh
你有很多方法可以還原數據:
- 使用sstableloader 工具
- 拷貝鏡像的SSTable文件到/data/keyspace/table_name-uuid目錄中,然后用JConsole 在各個column family 中調用JMX方法loadNewSSTables()在column family MBean,你可以使用nodetool refresh 代替調用loadNewSSTables()
data目錄位置依賴於你的安裝或者配置方式,默認是下面兩種。
- Package installations: /var/lib/cassandra/data
- Tarball installations: install_location/data/data
- 使用Node Restart Method 下面將會介紹。
如果還原一個節點,你需要首先關閉節點。如果還原整個集群,你需要關閉所有節點,還原鏡像數據,然后再次啟動所有節點。
注意:還原鏡像數據將導致正在還原的節點,CPU和IO臨時增加。
譯者注:這邊可能文檔沒有更新完整,前面有提到需要truncate table,在關閉節點前先執行一次。
- 關閉節點
- 清理所有commitlog中的文件
這是為了防止commitlog重放導致數據更新,這可能導致還原到某個固定時間點的目標失敗。
- 清理有*.db文件在data_directory/keyspace_name/keyspace_name-keyspace_name目錄中,但是不要刪除鏡像和備份子目錄。
- 確定最近的鏡像數據目錄
- 將數據拷貝內容到這個目錄
data_directory/keyspace_name/talbe_name-UUID
- 如果你采用了增量備份,復制這里的所有數據
data_driectory/keyspace_name/table_name-UUID/backups
- 粘貼到
data_directory/keyspace_name/table_name-UUID中
- 重啟節點
這會導致暫時性的io增長和占有大量CPU資源。
- 執行nodetool repair
實際操作記錄 (Node Restart Method)
集群簡介
4個節點,分別為
192.168.1.2
192.168.1.3
192.168.1.4
192.168.1.5
之后就不寫完整ip 用節點2 節點3 節點4 節點5
Cassandra 版本2.1.7
創建備份測試用的數據
登錄負載最低的節點2,創建一個keyspace
/* 創建tshop的keyspace */
CREATE KEYSPACE tshop WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '2'} ;
use tshop;
/* 創建一張user表 */
CREATE TABLE user (
uid int PRIMARY KEY,
group_id int,
nick varchar
) WITH compaction = { 'class': 'org.apache.cassandra.db.compaction.LeveledCompactionStrategy'};
/* 寫入一些數據 */
insert into user (uid,group_id,nick) values(1,1,'kevin');
insert into user (uid,group_id,nick) values(2,1,'luxi');
insert into user (uid,group_id,nick) values(3,1,'happy');
insert into user (uid,group_id,nick) values(4,1,'豆沙柚子');
insert into user (uid,group_id,nick) values(5,1,'五仁月餅');
備份所有節點數據:
$ bin/nodetool -h 192.168.1.2 snapshot tshop
$ bin/nodetool -h 192.168.1.3 snapshot tshop
$ bin/nodetool -h 192.168.1.4 snapshot tshop
$ bin/nodetool -h 192.168.1.5 snapshot tshop
備份完成后會顯示一個時間郵戳,這就是備份目錄的名字,也方便還原時判定備份的時間。
/* 刪除數據,假設是一次意外事故 */
delete from user where uid in (1,2,3,4,5);
關閉來自前端的請求,因為數據恢復的過程中,集群不得不關閉。
清空user表,前面有提到原因。
>truncate user;
關閉集群的所有節點
清理commitlog 避免有未提交數據的破壞
把數據目錄下的*.db文件刪除
把備份目錄中的*.db文件放到數據目錄中
重啟所有節點
在每個節點上執行nodetool repair
$ bin/nodetool -h 192.168.1.2 repair tshop
$ bin/nodetool -h 192.168.1.3 repair tshop
$ bin/nodetool -h 192.168.1.4 repair tshop
$ bin/nodetool -h 192.168.1.5 repair tshop
結果
之前丟失的user數據全部還原成功
實際操作案例(增量備份)
增量備份的數據體積更小,但是備份時間似乎不好控制,先試一下。
首先基於上面一個案例,開啟增量備份。
如果有安裝OpsCenter 就直接改,否則需要到每個節點上去開啟這個配置。
incremental_backups: true
繼續往數據庫寫入數據
insert into user (uid,group_id,nick) values(6,1,'昵稱1');
insert into user (uid,group_id,nick) values(7,1,'昵稱2');
insert into user (uid,group_id,nick) values(8,1,'昵稱3');
insert into user (uid,group_id,nick) values(9,1,'昵稱4');
insert into user (uid,group_id,nick) values(10,1,'昵稱5');
insert into user (uid,group_id,nick) values(11,1,'昵稱6');
insert into user (uid,group_id,nick) values(12,1,'昵稱7');
/* 如果有新數據寫入,執行flush操作,可以讓增量備份作一次備份,但是這並不是總是有效果,有可能當時沒有寫入數據,不過也意味着距離上次備份不需要再備份新的數據了 */
$ bin/nodetool -h 192.168.1.2 flush tshop
$ bin/nodetool -h 192.168.1.3 flush tshop
$ bin/nodetool -h 192.168.1.4 flush tshop
$ bin/nodetool -h 192.168.1.5 flush tshop
這時候,目錄中多了一個backup的目錄。
/* 再一次 刪除數據,假設是一次意外事故 */
delete from user where uid in (3,4,5,6,7);
我觀察了一下backup目錄文件,會有多組文件,所謂多組就是這些文件名可能非常相似,只有中間一個數字差別.
$ ls backup
-rw-rw-r-- 1 cassandra cassandra 43 Sep 3 18:39 tshop-user-ka-2-CompressionInfo.db
-rw-rw-r-- 1 cassandra cassandra 138 Sep 3 18:39 tshop-user-ka-2-Data.db
-rw-rw-r-- 1 cassandra cassandra 9 Sep 3 18:39 tshop-user-ka-2-Digest.sha1
-rw-rw-r-- 1 cassandra cassandra 16 Sep 3 18:39 tshop-user-ka-2-Filter.db
-rw-rw-r-- 1 cassandra cassandra 54 Sep 3 18:39 tshop-user-ka-2-Index.db
-rw-rw-r-- 1 cassandra cassandra 4442 Sep 3 18:39 tshop-user-ka-2-Statistics.db
-rw-rw-r-- 1 cassandra cassandra 80 Sep 3 18:39 tshop-user-ka-2-Summary.db
-rw-rw-r-- 1 cassandra cassandra 91 Sep 3 18:39 tshop-user-ka-2-TOC.txt
-rw-rw-r-- 1 cassandra cassandra 43 Sep 3 18:44 tshop-user-ka-4-CompressionInfo.db
-rw-rw-r-- 1 cassandra cassandra 138 Sep 3 18:44 tshop-user-ka-4-Data.db
-rw-rw-r-- 1 cassandra cassandra 9 Sep 3 18:44 tshop-user-ka-4-Digest.sha1
-rw-rw-r-- 1 cassandra cassandra 16 Sep 3 18:44 tshop-user-ka-4-Filter.db
-rw-rw-r-- 1 cassandra cassandra 54 Sep 3 18:44 tshop-user-ka-4-Index.db
-rw-rw-r-- 1 cassandra cassandra 4442 Sep 3 18:44 tshop-user-ka-4-Statistics.db
-rw-rw-r-- 1 cassandra cassandra 80 Sep 3 18:44 tshop-user-ka-4-Summary.db
-rw-rw-r-- 1 cassandra cassandra 91 Sep 3 18:44 tshop-user-ka-4-TOC.txt
顯然有 “2” 組 和 “4” 組,時間一前一后,如果想恢復到18:39分,那么,就別吧“4”組的數據放到數據目錄中。這個數字的變化我暫時沒有找到一個解釋,是不是隨着flush操作次數增加而增大?(測試結果是這樣的)但是還原操作后,這個數字似乎會回到一個比較小的值。所以用數字判斷數據前后應該不靠譜,文件生成時間會靠譜一些,但是反復還原操作后,備份也會混亂。
周期性鏡像備份還是不可替代,增量備份可以作為一種粒度更小的時間控制的補充。例如每天一個鏡像,最后一個鏡像后,開始保留增量。保留最新增量的方法就是在鏡像制作完成后,刪除所有當前增量備份,這樣就是最新了,但是這兩個操作間的數據又怎么辦?
其他操作和之前鏡像還原完全相同。
結果
數據全部還原