Created
May 27, 2017 09:58
-
-
Save hirokuma/311087bbd5859fe28565058e9728512d to your computer and use it in GitHub Desktop.
lmdbのmtest.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* mtest.c - memory-mapped database tester/toy */ | |
/* | |
* Copyright 2011-2017 Howard Chu, Symas Corp. | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted only as authorized by the OpenLDAP | |
* Public License. | |
* | |
* A copy of this license is available in the file LICENSE in the | |
* top-level directory of the distribution or, alternatively, at | |
* <http://www.OpenLDAP.org/license.html>. | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <time.h> | |
#include "lmdb.h" | |
#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) | |
#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) | |
#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ | |
"%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) | |
int main(int argc, char * argv[]) | |
{ | |
int i = 0, j = 0, rc; | |
MDB_env *env; | |
MDB_dbi dbi; | |
MDB_val key, data; | |
MDB_txn *txn; | |
MDB_stat mst; | |
MDB_cursor *cursor, *cur2; | |
MDB_cursor_op op; | |
int count; | |
int *values; | |
char sval[32] = ""; | |
srand(time(NULL)); | |
/* | |
* ランダムで個数を決めて、その範囲をランダム値で埋める | |
*/ | |
count = (rand() % 384) + 64; | |
values = (int *)malloc(count * sizeof(int)); | |
for (i = 0; i < count; i++) { | |
values[i] = rand() % 1024; | |
} | |
/* | |
* lmdbは、mdb_env_create()から始まる。 | |
* 作ったenvは、mdb_env_open()でオープンしなくてはならない。 | |
* | |
* mdb_env_set_maxreaders()は、envにアクセスするスレッドなどの数を設定する。 | |
* http://www.lmdb.tech/doc/group__mdb.html#gae687966c24b790630be2a41573fe40e2 | |
* デフォルトは126とのこと。 | |
* | |
* mdb_env_set_mapsize()は、envが使用するmmapのサイズを設定する。 | |
* http://www.lmdb.tech/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5 | |
* mmap()とは書いてないけど、memory mapと書いてあるから、きっとそうなんだろう? | |
* 関数内でmmap()は直接呼んでいないが、munmap()は呼んでいるから間違いなかろう。 | |
* ということは、mmapが使える処理系じゃないと移植できないということか。 | |
* デフォルトは10485760らしいので、今回はデフォルト値にしたというところか。 | |
* | |
* マクロE()は、戻り値がMDB_SUCCESSでなかったらログ出してabort()するようになっている。 | |
*/ | |
E(mdb_env_create(&env)); | |
E(mdb_env_set_maxreaders(env, 1)); | |
E(mdb_env_set_mapsize(env, 10485760)); | |
/* | |
* mdb_env_open()は、envをオープンする。 | |
* envってなんじゃ?と思ってしまうが、thisとかselfとか、そんなものだろう。 | |
* http://www.lmdb.tech/doc/group__mdb.html#ga32a193c6bf4d7d5c5d579e71f22e9340 | |
* 今回はMDB_FIXEDMAPになっているが、ここはフラグ設定になっているようだ。 | |
* http://www.lmdb.tech/doc/group__mdb__env.html | |
* openの説明に書いてあるだけで11項目もあるが、これはマクロの数と一致している。 | |
* 奇妙なことに、MDB_FIXEDMAPはexperimental扱いになっている。 | |
* MDB_VL32マクロが定義されている場合、MDB_FIXEDMAPだとエラーになっている。 | |
* | |
* MDB_VL32はソース中に定義する箇所が出てこないので、意識して設定するものらしいが、情報がない。 | |
*/ | |
E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP /*|MDB_NOSYNC*/, 0664)); | |
/* | |
* トランザクションの開始。 | |
* http://www.lmdb.tech/doc/group__mdb.html#gad7ea55da06b77513609efebd44b26920 | |
* トランザクションの終わりは、mdb_txn_abort()での中断か、mdb_txn_commit()での確定。 | |
* | |
* そのトランザクションを使ってmdb_dbi_open()を呼ぶ。 | |
* http://www.lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a | |
* これが「データベースを開く」になる。 | |
* このサンプルだけ見ると、開いたデータベースとトランザクションはセットで使っている。 | |
* そして閉じるときだけは、トランザクションではなくenvを使っている。 | |
* もしかすると、トランザクションをabortした後だからかもしれないと思ったが、他のAPIはなさそうだ。 | |
*/ | |
E(mdb_txn_begin(env, NULL, 0, &txn)); | |
E(mdb_dbi_open(txn, NULL, 0, &dbi)); | |
/* | |
* keyはMDB_val型で、これがKVSの'K'に相当する構造体。 | |
* メンバはこの2つだけ。 | |
* | |
* svalはchar[32]なのに、なぜsizeof(int)なのだろうか? | |
* そのまま解釈すると、svalの先頭4byteだけkeyとして使う、ということになる。 | |
* しかし、次に出てくるfor文中のdataもMDB_val型で、こちらはKVSの'V'になる。 | |
* その'V'にもまたsvalが使われている。 | |
* | |
* いや、異常というわけではない。 | |
* 単にそういうデータとして保持したいだけかもしれないからだ。 | |
* とはいえ、サンプルプログラムとしては分かりづらいんじゃなかろうか? | |
* 保持するデータ量を削減したいなら、Keyの部分を外してもよいと思う。 | |
* | |
* ちなみにsvalの先頭4byteは、[%03x ]となっていて、values[]は16進数3桁以内になる。 | |
* ランダム値を使うため、チェックするためにそうしたのかもしれない。 | |
*/ | |
key.mv_size = sizeof(int); | |
key.mv_data = sval; | |
/* | |
* mdb_put()で値を格納(store)している。 | |
* http://www.lmdb.tech/doc/group__mdb.html#ga4fa8573d9236d54687c61827ebf8cac0 | |
* 重複許可していない(デフォルト)なら既存keyであれば置き換えるし、MDB_DUPSORTなら追加する。 | |
* MDB_DUPSORTは、mdb_env_open()で指定できるフラグの1つだ。 | |
* | |
* ただ、putでもフラグを指定してMDB_NOOVERWRITEなどと指定できる。 | |
* これは「keyが既に存在するなら上書きしない」だ。 | |
* 予想しているかもしれないが、MDB_APPENDのように追加指定もできてしまう。 | |
* 指定したくなかったり、変更したくないビットは0のままにしてけばよいとのこと。 | |
* | |
* マクロRES()は・・・戻り値が第1引数だったら真…なのか? | |
* jはkeyが重複した数をカウントしていることになる。 | |
* | |
* "1c0 448 foo bar"のような値がsval[]に入るはずである。 | |
*/ | |
printf("Adding %d values\n", count); | |
for (i = 0; i < count; i++) { | |
sprintf(sval, "%03x %d foo bar", values[i], values[i]); | |
/* Set <data> in each iteration, since MDB_NOOVERWRITE may modify it */ | |
data.mv_size = sizeof(sval); | |
data.mv_data = sval; | |
if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE))) { | |
j++; | |
data.mv_size = sizeof(sval); | |
data.mv_data = sval; | |
} | |
} | |
if (j) { | |
printf("%d duplicates skipped\n", j); | |
} | |
/* | |
* ここまでの結果をcommitする。 | |
* http://www.lmdb.tech/doc/group__mdb.html#ga846fbd6f46105617ac9f4d76476f6597 | |
* | |
* mdb_env_stat()は現在のenvの統計解析。 | |
* http://www.lmdb.tech/doc/group__mdb.html#gaf881dca452050efbd434cd16e4bae255 | |
* 今回は使っていないようだ。 | |
*/ | |
E(mdb_txn_commit(txn)); | |
E(mdb_env_stat(env, &mst)); | |
/* | |
* 新しくトランザクションを開始する。 | |
* 今回は、cursorを使うサンプルのようである。 | |
* http://www.lmdb.tech/doc/group__mdb.html#ga9ff5d7bd42557fd5ee235dc1d62613aa | |
* | |
* mdb_cursor_get()で、初回を含め次のcursorに進めている。 | |
* http://www.lmdb.tech/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0 | |
* | |
* 実行すると、こういう文字列が標準出力にたくさん出ている。 | |
* "key: 0x7f021c9b279c 003 , data: 0x7f021c9b27a0 003 3 foo bar" | |
* keyで昇順にソートされている? | |
*/ | |
E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); | |
E(mdb_cursor_open(txn, dbi, &cursor)); | |
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { | |
printf("key: %p %.*s, data: %p %.*s\n", | |
key.mv_data, (int) key.mv_size, (char *) key.mv_data, | |
data.mv_data, (int) data.mv_size, (char *) data.mv_data); | |
} | |
/* | |
* エラーが返ったら、おそらく最後まで終わったはずなので、チェックしてcurosrクローズ。 | |
* http://www.lmdb.tech/doc/group__mdb.html#gad685f5d73c052715c7bd859cc4c05188 | |
* トランザクションとしては書込んでいないためか、abortで終わらせているのだろう。 | |
*/ | |
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); | |
mdb_cursor_close(cursor); | |
mdb_txn_abort(txn); | |
/* | |
* ランダムで登録済みデータを削除していく。 | |
* n番目のkeyを指定して mdb_del()を呼んでいる。 | |
* http://www.lmdb.tech/doc/group__mdb.html#gab8182f9360ea69ac0afd4a4eaab1ddb0 | |
* | |
* keyの重複を認めているので、MDB_NOTFOUNDでチェックしている。 | |
* 削除前にmdb_txn_begin()して、削除したらmdb_txn_commit()、できなかったらmdb_txn_abort()。 | |
* 普通のDBと同じように、for全体をトランザクションとする方が速くなるのか? | |
*/ | |
j = 0; | |
key.mv_data = sval; | |
for (i = count - 1; i > -1; i -= (rand() % 5)) { | |
j++; | |
txn = NULL; | |
E(mdb_txn_begin(env, NULL, 0, &txn)); | |
sprintf(sval, "%03x ", values[i]); | |
if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, NULL))) { | |
j--; | |
mdb_txn_abort(txn); | |
} else { | |
E(mdb_txn_commit(txn)); | |
} | |
} | |
free(values); | |
printf("Deleted %d values\n", j); | |
/* | |
* 削除後のDBをMDB_NEXTで出力。 | |
*/ | |
E(mdb_env_stat(env, &mst)); | |
E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); | |
E(mdb_cursor_open(txn, dbi, &cursor)); | |
printf("Cursor next\n"); | |
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { | |
printf("key: %.*s, data: %.*s\n", | |
(int) key.mv_size, (char *) key.mv_data, | |
(int) data.mv_size, (char *) data.mv_data); | |
} | |
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); | |
/* | |
* mdb_cursor_get(MDB_NEXT)でエラーになると、最後の状態で止まっているらしい。 | |
* そのため、mdb_cursor_get(MDB_LAST)で最後に出力したものと同じデータがとれる。 | |
*/ | |
printf("Cursor last\n"); | |
E(mdb_cursor_get(cursor, &key, &data, MDB_LAST)); | |
printf("key: %.*s, data: %.*s\n", | |
(int) key.mv_size, (char *) key.mv_data, | |
(int) data.mv_size, (char *) data.mv_data); | |
/* | |
* 続けて、mdb_cursor_get(MDB_PREV)で逆順にたどっている。 | |
*/ | |
printf("Cursor prev\n"); | |
while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { | |
printf("key: %.*s, data: %.*s\n", | |
(int) key.mv_size, (char *) key.mv_data, | |
(int) data.mv_size, (char *) data.mv_data); | |
} | |
CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); | |
/* | |
* 逆順にたどっても、MDB_LASTは正順での最後だし、MDB_PREVはその1つ前。 | |
*/ | |
printf("Cursor last/prev\n"); | |
E(mdb_cursor_get(cursor, &key, &data, MDB_LAST)); | |
printf("key: %.*s, data: %.*s\n", | |
(int) key.mv_size, (char *) key.mv_data, | |
(int) data.mv_size, (char *) data.mv_data); | |
E(mdb_cursor_get(cursor, &key, &data, MDB_PREV)); | |
printf("key: %.*s, data: %.*s\n", | |
(int) key.mv_size, (char *) key.mv_data, | |
(int) data.mv_size, (char *) data.mv_data); | |
mdb_cursor_close(cursor); | |
mdb_txn_abort(txn); | |
/* | |
* cursorを使ってのデータ削除。先頭から50個分。 | |
* 理由は分からないが、最初の%pだけ桁数が多い。 | |
* mmap()したどこかか? | |
*/ | |
printf("Deleting with cursor\n"); | |
E(mdb_txn_begin(env, NULL, 0, &txn)); | |
E(mdb_cursor_open(txn, dbi, &cur2)); | |
for (i = 0; i < 50; i++) { | |
if (RES(MDB_NOTFOUND, mdb_cursor_get(cur2, &key, &data, MDB_NEXT))) { | |
break; | |
} | |
printf("key: %p %.*s, data: %p %.*s\n", | |
key.mv_data, (int) key.mv_size, (char *) key.mv_data, | |
data.mv_data, (int) data.mv_size, (char *) data.mv_data); | |
E(mdb_del(txn, dbi, &key, NULL)); | |
} | |
/* | |
* MDB_FIRSTで先頭を取り出し、それ以降はMDB_NEXTで正順に取得する。 | |
* 33回ループしているが、何の数だろうか? | |
* countを乱数で決めるときに+64しているので、その真ん中くらいということだろうか。 | |
*/ | |
printf("Restarting cursor in txn\n"); | |
for (op = MDB_FIRST, i = 0; i <= 32; op = MDB_NEXT, i++) { | |
if (RES(MDB_NOTFOUND, mdb_cursor_get(cur2, &key, &data, op))) { | |
break; | |
} | |
printf("key: %p %.*s, data: %p %.*s\n", | |
key.mv_data, (int) key.mv_size, (char *) key.mv_data, | |
data.mv_data, (int) data.mv_size, (char *) data.mv_data); | |
} | |
mdb_cursor_close(cur2); | |
E(mdb_txn_commit(txn)); | |
/* | |
* forの回し方は前回と同じ。 | |
* 違うのは、cursorを取り直したところか? | |
* 出力される%pは、mdb_put()した直後の%pと同じ値になっているようだ。 | |
* outside txnだから、commit/abortさせる前と後での違いを見せたかったということか。 | |
*/ | |
printf("Restarting cursor outside txn\n"); | |
E(mdb_txn_begin(env, NULL, 0, &txn)); | |
E(mdb_cursor_open(txn, dbi, &cursor)); | |
for (op = MDB_FIRST, i = 0; i <= 32; op = MDB_NEXT, i++) { | |
if (RES(MDB_NOTFOUND, mdb_cursor_get(cursor, &key, &data, op))) { | |
break; | |
} | |
printf("key: %p %.*s, data: %p %.*s\n", | |
key.mv_data, (int) key.mv_size, (char *) key.mv_data, | |
data.mv_data, (int) data.mv_size, (char *) data.mv_data); | |
} | |
mdb_cursor_close(cursor); | |
mdb_txn_abort(txn); | |
/* | |
* クローズ。 | |
*/ | |
mdb_dbi_close(env, dbi); | |
mdb_env_close(env); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment