天天看點

SSDB 資料庫SSDB資料庫

原文: SSDB 資料庫

SSDB資料庫

SSDB

是一套基于

LevelDB

存儲引擎的非關系型資料庫(NOSQL),可用于取代

Redis

,更适合海量資料的存儲。

另外,

rocksdb

是FB在LevelDB的二次開發版本,是以也存在使用RocksDB作為存儲引擎的SSDB版本,可以參考

這裡

編譯和安裝

wget --no-check-certificate https://github.com/ideawu/ssdb/archive/master.zip
unzip master
cd ssdb-master
make
# 将安裝在 /usr/local/ssdb 目錄下
sudo make install      

啟動服務

# 啟動主庫
./ssdb-server ssdb.conf

# 或者啟動為背景程序
./ssdb-server -d ssdb.conf

# 啟動 ssdb 指令行用戶端
./tools/ssdb-cli -p 8888

# 停止 ssdb-server
./ssdb-server ssdb.conf -s stop
# 對于舊版本
kill `cat ./var/ssdb.pid`      

配置檔案

ssdb.conf:

work_dir = ./var
pidfile = ./var/ssdb.pid

server:
    ip: 127.0.0.1
    port: 8888
    # bind to public ip
    #ip: 0.0.0.0
    # format: allow|deny: all|ip_prefix
    # multiple allows or denys is supported
    #deny: all
    #allow: 127.0.0.1
    #allow: 192.168
    # auth password must be at least 32 characters
    #auth: very-strong-password

replication:
    binlog: yes 
    # Limit sync speed to *MB/s, -1: no limit
    sync_speed: -1
    slaveof:
        # to identify a master even if it moved(ip, port changed)
        # if set to empty or not defined, ip:port will be used.
        #id: svc_2
        # sync|mirror, default is sync
        #type: sync
        #ip: 127.0.0.1
        #port: 8889

logger:
    level: debug
   # 支援的日志級别有:          debug, warn, error, fatal                .
    output: log.txt
    rotate:
        size: 1000000000

leveldb:
    # in MB
    cache_size: 500 
    # in KB
    block_size: 32
    # in MB
    write_buffer_size: 64
    # in MB
    compaction_speed: 1000
    # yes|no
    compression: yes       

一個 ssdb-server 執行個體占用的記憶體瞬時(有可能, 而且即使達到, 也隻是持續短時間)最高達到(MB):

cache_size + write_buffer_size * 66 + 32      

這是對于壓縮選項沒有開啟的情況, 如果 compression: yes, 計算公式是:

cache_size + 10 * write_buffer_size * 66 + 32      

你可以調整配置參數, 限制 ssdb-server 的記憶體占用.

SSDB指令

與Redis類似,SSDB也支援多種資料結構(KV list, hash, soreted set),下面列出了

常用指令

dbsize           # 傳回資料庫占用空間,以位元組為機關

flushdb           # 清空資料庫

info             # 傳回伺服器資訊

auth password      # 驗證通路密碼

KV結構

set key value 

setx key value ttl     # 設定key的同時設定ttl

setnx key value       # 若key已存在,則不設定

multi_set key1 value1 key2 value2 ...

exists key

get key

getset key value           # 更新key,并傳回舊value

multi_get key1 key2 ...

keys key_start key_end limit     # 傳回指定範圍内的key,左開右閉區間(SSDB的key有序存儲)

rkeys key_start key_end limit

scan key_start key_end limit

rscan key_start key_end limit

expire key ttl 

ttl key 

del key

multi_del key1 key2 ...

substr key start size         # 傳回子串

strlen key

incr key [num]

getbit key offset

setbit key offset val

bitcount key [start] [end]

countbit key start size

SSDB 資料庫SSDB資料庫

hashmap結構

hset name key value

multi_hset name key1 value1 key2 value2 ...

hget name key

multi_hget name key1 key2 ...

hgetall name

hkeys name key_start key_end

hscan key_start key_end limit

hrscan key_start key_end limit

hdel name key            # 删除一個字段

hclear name             # 删除所有字元

multi_hdel name key1 key2 ...

hexists name key

hsize name

hincr name key [num]

hlist name_start name_end limit      # 列出指定範圍的所有hash表

hrlist name_start name_end limit

SSDB 資料庫SSDB資料庫

list結構

qpush_front name item1 item2 ...     # 往隊頭插入新元素

qpush_back name item1 item2 ...      # 往隊尾插入新元素

qpop_front name size             # 從隊頭彈出若幹個元素

qpop_back name size            # 從隊尾彈出若幹個元素

qtrim_front name size            # 從隊頭移除多個元素

qtrim_back name size           # 從隊尾移除多個元素

qfront name                # 傳回隊頭

qback name                # 傳回隊尾

qsize name                # 傳回隊長

qget name index              # 傳回指定位置元素

qset name index val

qrange name offset limit          # 傳回一個切片

qslice name begine end          # 傳回一個切片

qclear name

qlist name_start name_end limit

qrlist name_start name_end limit

SSDB 資料庫SSDB資料庫

sorted set結構

zset name key socre

zget name key

zdel name key

zexists name key

zsize name

zincr name key num

導出/導入

SSDB才有

LSM模型

,也就是說它的key是有序存儲的,是以,我們可以導出所有key的資料,也可以導出指定範圍内key的資料。

1、使用ssdb-cli 指令行用戶端

導出整個資料庫:

# backup current database into file backup.ssdb
ssdb 127.0.0.1:8888> export backup.ssdb      

按照 Key 區間導出資料庫(互動模式)

ssdb 127.0.0.1:8888> export -i backup.ssdb
input KV range[start, end]:
  start(inclusive, default none): a
    end(inclusive, default none): z
input HASH range:
  start(inclusive, default none):
    end(inclusive, default none):
input ZSET range:
  start(inclusive, default none):
    end(inclusive, default none):
input QUEUE range:
  start(inclusive, default none):
    end(inclusive, default none):      

指令 export -i backup.ssdb 将導出區間 [a, z] 内的 KV, 所有的 HASH, ZSET, QUEUE.

導入指令:

# import backup.ssdb into current database
ssdb 127.0.0.1:8888> import backup.ssdb      

import 指令會把資料庫中的相同 key 給替換。

2、SSDB 另一個專門用于導出工具是 ssdb-dump,用法如下:

./tools/ssdb-dump ip port output_folder      

目錄 output_folder 必須不存在, 因為 ssdb-dump 會建立這個目錄. 導出之後, 這個目錄裡将有兩個子目錄, data 目錄裡包含着資料, 還有一個空的 meta 目錄.

如果想從導出的目錄恢複資料,可以将 output_folder 目錄拷貝到你的伺服器上面,然後修改你的 ssdb.conf 配置檔案, 将 work_dir 指向 output_folder 目錄, 然後重新開機 ssdb-server。

限制

最大 Key 長度 200 位元組

最大 Value 長度 31MB

最大請求或響應長度 31MB

單個 HASH 中的元素數量 9,223,372,036,854,775,807

單個 ZSET 中的元素數量 9,223,372,036,854,775,807

單個 QUEUE 中的元素數量 9,223,372,036,854,775,807

指令最多參數個數 所有參數加起來體積不超過 31MB 大小

Replication

Redis的主從複制在主庫挂了的時候就無法再寫入資料了,而SSDB不但支援主-從結構,還支援多主結構。

主-從配置

#server1:

replication:
    slaveof:      

#server2:

replication:
    slaveof:
        id: svc_1
        # sync|mirror, default is sync
        type: sync
        ip: 127.0.0.1
        port: 8888      

主-主配置

replication:
    slaveof:
        id: svc_2
        # sync|mirror, default is sync
        type: mirror
        ip: 127.0.0.1
        port: 8889      
replication:
    slaveof:
        id: svc_1
        # sync|mirror, default is sync
        type: mirror
        ip: 127.0.0.1
        port: 8888      

多主配置

在一組一共包含 n 個執行個體的 SSDB 執行個體群中, 每一個執行個體必須 slaveof 其餘的 n-1 個執行個體.

replication:
    slaveof:
        id: svc_1
        # sync|mirror, default is sync
        type: mirror
        ip: 127.0.0.1
        port: 8888
    slaveof:
        id: svc_2
        # sync|mirror, default is sync
        type: mirror
        ip: 127.0.0.1
        port: 8889
    # ... more slaveof      

監控

info指令可以傳回SSDB服務狀态:

ssdb 127.0.0.1:8899> info
binlogs
    capacity : 10000000
    min_seq  : 1
    max_seq  : 74
replication
    client 127.0.0.1:55479
        type     : sync
        status   : SYNC
        last_seq : 73
replication
    slaveof 127.0.0.1:8888
        id         : svc_2
        type       : sync
        status     : SYNC
        last_seq   : 73
        copy_count : 0
        sync_count : 44      

binlogs,目前執行個體的寫操作狀态:

  • capacity: binlog 隊列的最大長度
  • min_seq: 目前隊列中的最小 binlog 序号
  • max_seq: 目前隊列中的最大 binlog 序号

replication,可以有多條 replication 記錄. 每一條表示一個連接配接進來的 slave(client), 或者一個目前伺服器所連接配接的 master(slaveof).

  • slaveof|client ip:port, 遠端 master/slave 的 ip:port
  • type: 類型, sync|mirror
  • status: 目前同步狀态, DISCONNECTED|INIT|OUT_OF_SYNC|COPY|SYNC,見下面的解釋
  • last_seq: 上一條發送或者收到的 binlog 的序号
  • slaveof.id: master 的 id(這是從 slave's 角度來看的, 你永遠不需要在 master 上配置它自己的 id)
  • slaveof.copy_count: 在全量同步時, 已經複制的 key 的數量
  • slaveof.sync_count: 發送或者收到的 binlog 的數量.

關于 status:

  1. DISCONNECTED: 與 master 斷開了連接配接, 一般是網絡中斷
  2. INIT: 初始化狀态
  3. OUT_OF_SYNC: 由于短時間内在 master 有大量寫操作, 導緻 binlog 隊列淘汰, slave 丢失同步點, 隻好重新複制全部的資料
  4. COPY: 正在複制基準資料的過程中, 新的寫操作可能無法及時地同步
  5. SYNC: 同步狀态是健康的

判斷同步狀态

binlogs.max_seq 是指目前執行個體上的最新一次的寫(寫/更新/删除)操作的序号;

replication.client.last_seq 是指已發送給 slave 的最新一條 binlog 的序号;

是以, 如果你想判斷主從同步是否已經同步到位(實時更新), 那麼就判斷 binlogs.max_seq 和 replication.client.last_seq 是否相等。

SSDB協定

SSDB協定與Redis的文本協定也類似:

SSDB資料包的結構:

Packet := Block+ '\n'
Block  := Size '\n' Data '\n'
Size   := literal_integer
Data   := size_bytes_of_data      

請求:

Request := Cmd Blocks*
Cmd     := Block      

請求指令包括: get, set, del, ...

響應:

Response := Status Block*
Status   := Block      

響應狀态碼包括: ok, not_found, error, fail, client_error

示例:

用 telnet 或者 nc 指令連接配接到 SSDB 伺服器, 然後輸入下面的代碼(用最後一行空行結束):

3
get
3
key

      

你将看到類似這樣的響應:

2
ok
3
val

      

SSDB 協定解析器的C實作:

#include <stdlib.h>
#include <string.h>

int len = buffer->size();
char *ptr = buffer->data();
while(len > 0){
    char *data = (char *)memchr(ptr, '\n', len);
    if(data == NULL){
        break;
    }
    data += 1;
    int num = data - ptr;
    if(num == 1 || (num == 2 && ptr[0] == '\r')){
        // Packet received.
        return OK;
    }
    // Size received
    int size = (int)strtol(ptr, NULL, 10);

    len -= num + size;
    ptr += num + size;
    if(len >= 1 && ptr[0] = '\n'){
        len -= 1;
        ptr += 1;
    }else if(len >= 2 && ptr[0] == '\r' && ptr[1] == '\n'){
        len -= 2;
        ptr += 2;
    }else{
        break;
    }
    // Data received
}