閱讀541 返回首頁    go 阿裏雲 go 技術社區[雲棲]


MongoDB如何使用wiredTiger?

Mongodb 3.0支持用戶自定義存儲引擎,用戶可配置使用mmapv1或者wiredTiger存儲引擎,本文主要介紹Mongodb是如何使用wiredTiger數據庫作為底層的數據存儲層。目前還沒有讀過wiredTiger的源碼,本文的內容都是基於wiredTiger官方文檔,以及Mongodb對wiredTiger封裝代碼,有問題請指出。

wiredTiger引擎存儲布局

wiredTiger(簡稱WT)支持行存儲、列存儲以及LSM等3種存儲形式,Mongodb使用時,隻是將其作為普通的KV存儲引擎來使用,mongodb的每個集合對應一個WT的table,table裏包含多個Key-value pairs,以B樹形式存儲。

以下是一個典型的使用WT存儲引擎的數據目錄布局(配置了directoryPerDB選項,啟用了journal)

$tree
.
├── admin
│   ├── collection-11--5764503550749656746.wt
│   ├── collection-14--6907424972913303461.wt
│   ├── collection-16--6907424972913303461.wt
│   ├── collection-20--6907424972913303461.wt
│   ├── collection-8--6907424972913303461.wt
│   ├── collection-9--5764503550749656746.wt
│   ├── index-10--5764503550749656746.wt
│   ├── index-12--5764503550749656746.wt
│   ├── index-13--5764503550749656746.wt
│   ├── index-15--6907424972913303461.wt
│   ├── index-17--6907424972913303461.wt
│   └── index-9--6907424972913303461.wt
├── journal
│   ├── WiredTigerLog.0000000003
│   └── WiredTigerPreplog.0000000001
├── local
│   ├── collection-0--5764503550749656746.wt
│   ├── collection-2--5764503550749656746.wt
│   ├── collection-4--5764503550749656746.wt
│   ├── collection-6--5764503550749656746.wt
│   ├── collection-7--5764503550749656746.wt
│   ├── index-1--5764503550749656746.wt
│   ├── index-3--5764503550749656746.wt
│   ├── index-5--5764503550749656746.wt
│   └── index-8--5764503550749656746.wt
├── _mdb_catalog.wt
├── mongod.lock
├── products
│   ├── collection-6--6907424972913303461.wt
│   └── index-7--6907424972913303461.wt
├── sizeStorer.wt
├── storage.bson
├── WiredTiger
├── WiredTiger.basecfg
├── WiredTiger.lock
├── WiredTiger.turtle
└── WiredTiger.wt

WiredTiger*等文件存儲WT的一些配置信息。

local、journal、admin、products等每個目錄代表一個DB,DB裏包含集合數據及集合的索引數據,每個集合的數據對應一個WT的table(一個.wt後綴的文件),集合的每項索引也對應一個WT的table。

journal目錄下存儲WT的write ahead log,當服務crash時,可通過log來恢複數據。

_mdb_catalog.wt裏存儲了所有集合的元數據,包括集合對應的WT table名字,集合的創建選項,集合的索引信息等,WT存儲引擎初始化時,會從_mdb_catalog.wt裏讀取所有的集合信息,並加載元信息到內存。

集合名與WT table名的對應關係可以通過db.collection.stats()獲取

mongo-9552:PRIMARY> db.system.users.stats().wiredTiger.uri
statistics:table:admin/collection-10--1436312956560417970

也可以直接dump出_mdb_catalog.wt裏的內容查看,dump出的內容為BSON格式,閱讀起來不是很方便。

wt -C "extensions=[/usr/local/lib/libwiredtiger_snappy.so]"  -h . dump table:_mdb_catalog

sizeStorer.wt裏存儲所有集合的容量信息,如文檔數、文檔總大小等,當插入、刪除、更新文檔時,這些信息會先cache到內存,沒操作1000次會刷盤一次;mongod進程crash可能導致sizeStorer.wt裏的數據與實際信息不匹配,可通過validate()命令來重新掃描集合以訂正統計信息。

wiredTiger API

WT官方提供了C、java、python API,mongodb使用C API來訪問WT數據庫,主要包括3個核心的數據結構。

  1. WT_CONNECTION代表一個到WT數據庫的連接,通常每個進程隻用建立一個連接,WT_CONNECTION的所有方法都是線程安全的。
  2. WT_SESSION代表一個數據庫操作的上下文,每個線程需創建獨立的session。
  3. WT_CURSOR用於操作某個數據集(如某個table、file),可使用cursor來進行數據庫插入、查詢操作。

如下是使用wiredTiger C API的示例,展示了如何向WT數據庫裏插入數據,更多示例參考這裏

#include <wiredtiger.h>

char *home = "WT_HOME";

int main(void)
{
    WT_CONNECTION *conn;
    WT_CURSOR *cursor;
    WT_SESSION *session;
    const char *key, *value;
    int ret;

    /* Open a connection to the database */
    ret = wiredtiger_open(home, NULL, "create", &conn);

    /* Open a session in conn */
    ret = conn->open_session(conn, NULL, NULL, &session);

    /* Create table if not exist */
    ret = session->create(session,
        "table:access", "key_format=S,value_format=S");

    /* Open a cursor and insert key-value pair */
   ret = session->open_cursor(session,
        "table:access", NULL, NULL, &cursor);
    cursor->set_key(cursor, "key1");
    cursor->set_value(cursor, "value1");
    ret = cursor->insert(cursor);

    /* Close conn */
    ret = conn->close(conn, NULL);

   return ret;
}

上述示例包含如下步驟

  1. wiredtiger_open()建立連接
  2. conn->open_session建立session
  3. session->create()創建access表,並指定key、value格式
  4. session->open_cursor創建cursor,並插入key-value
  5. 訪問結束後conn->close()關閉連接

wiredTiger in Mongodb

Mongodb使用wiredTiger作為存儲引擎時,直接使用其C API來存儲、查詢數據。

wiredtiger_open

Mongodb在WiredTigerKVEngine構造的時候wiredtiger_open建立連接,在其析構時關閉連接,其指定的配置參數為:

配置項 含義說明
create 如果數據庫不存在則先創建
cache_size=xx cache大小,使用Mongod cacheSizeGB配置項的值
session_max=20000 最大session數量
eviction=(threads_max=4) 淘汰線程最大數量,用於將page從cache逐出
statistics=(fast) 統計數據采用fast模式
statistics_log=(wait=xx) 統計數據采集周期,使用mongod statisticsLogDelaySecs配置項的值
file_manager=(close_idle_time=100000) 空閑文件描述符回收時間
checkpoint=(wait=xx,log_size:2G) 開啟周期性checkpoint,采用Mongod syncPeriodSecs配置項的值
log=(enabled=true,archive=true... 啟用write ahead log,達到2G時觸發checkpoint

重點介紹下checkpoint和log2個配置項,其決定了數據持久化的安全級別;wiredTiger支持2種數據持久化級別,分別是Checkpoint durability 和 Commit-level durability

Checkpoint durability

wiredTiger支持對當前的數據集進行checkpoint,checkpoint代表當前數據集的一個快照(或鏡像),wiredTiger可配置周期性的進行checkpoint(或當log size達到一定閾值是做checkpoint)。

比如WT配置了周期性checkpoint(沒開啟log),每5分鍾做一次checkpoint,在T1時刻做了一次Checkpoint得到數據集C1,則在接下來的5分鍾內,如果服務crash,則WT隻能將數據恢複到T1時刻。

Commit-level durability

wiredTiger通過write ahead log來支持commit-level durability。

開啟write ahead log後,對WT數據庫的更新都會先寫log,log的刷盤策略(通過trasaction_sync配置項 或者 begion_transaction參數指定)決定了持久化的級別。

mongodb的使用的持久化級別配置為

  1. checkpoint=(wait=60,log_size=2G)
  2. log=(enabled=true,archive=true,path=journal,compressor=snappy)
  3. begin_transcation("sync=true")

具體策略為

  1. 每60s做一次checkpoint
  2. 開啟write ahead log,當log size達到2GB時做checkpoint;並自動刪除不需要的log文件。
  3. 每次commit_transaction時,調用fsync持久化已經commit的log。

基於上述配置,mongodb可以保證服務crash時,所有已經commit的操作都能通過log恢複。

open_session

mongodb使用session pool來管理WT的session,isolation=snapshot指定隔離級別為snapshot。

 conn->open_session(conn, NULL, "isolation=snapshot", &_session);

create table

創建數據集合的參數如下

配置項 含義說明
create 如果集合不存在則先創建
memory_page_max=10m page內存最大值
split_pct=90 page split百分比
checksum=on 開啟校驗
key_format=q,value_format=u key為int64_t類型(RecordId),value為WT_ITEM

數據集合的key為int64_t類型的RecordId,RerordId在集合內部唯一,value為二進製的BSON格式。

創建索引集合的參數如下

配置項 含義說明
create 如果集合不存在則先創建
type=file,internal_page_max=16k,leaf_page_max=16k 配置樹節點大小
checksum=on 開啟校驗
key_format=u,value_format=u key-value均為WT_ITEM格式

索引集合的key、value均為二進製數據。

table創建好之後,就可以往table

比如,往某個集合插入一組元素

db.coll.insert({_id: "apple", count: 100});
db.coll.insert({_id: "peach", count: 200});
db.coll.insert({_id: "grape", count: 300});

對應一個coll的數據集合,其對應的WT數據類似於

key value
1 {_id: "apple", count: 100}
2 {_id: "peach", count: 200}
3 {_id; "grape", count: 300}

以及基於id的索引集合,其對應的WT數據類似於

key value
"apple" 1
"peach" 2
"grape" 3

接下來如果在count上建索引,索引會存儲在新的WT table裏,數據類似於

db.coll.ensureIndex({count: -1})
key value
300 3
200 2
100 1

總結

Mongodb使用wiredTiger存儲引擎時,其將wiredTiger作為一個KV數據庫來使用,mongodb的集合和索引都對應一個wiredTiger的table。並依賴於wiredTiger提供的checkpoint + write ahead log機製提供高數據可靠性。

最後更新:2017-04-01 13:37:07

  上一篇:go MongoDB複製集原理
  下一篇:go 如何站在雙11的肩膀上 詳解阿裏雲企業級互聯網架構