プログラミング

2011年04月02日

BDBで測定やらなんやらやっていたら
DBの情報を保存するファイルサイズが
ものすごいサイズになっていました。

そんなわけで今回は回数ごとにファイルサイズを出してみました。

ちなみにRECNOにしたデータベースファイルでは
データ用のバッファがまるまる保存されるのか
少ないエントリでもえらい容量を食ってました\(^o^)/
プログラムも作ってないので
今回はhashとbtreeでサイズの比較をしています。

1回のDBファイルサイズ
% ls -s *.db
16 -rwx------ 1 nanodayo staff 8.0K 2 17 13:52 btree.db*
32 -rwx------ 1 nanodayo staff 16K 2 17 13:51 hash.db*


hashのほうが容量必要なみたいですねー。
ある程度余分な領域があるのでしょうか。

エントリ数は1でも2でもサイズが同じだったので
エントリ数が一定以上になった場合に改めて確保・・・という形ではないかと思われます。

10,000回のDBファイルサイズ
992 -rwx------ 1 nanodayo staff 496K 2 14 21:48 btree.db*
720 -rwx------ 1 nanodayo staff 360K 2 14 21:47 hash.db*


100,000回のDBファイルサイズ
9928 -rwx------ 1 nanodayo staff 4.8M 2 14 23:14 btree.db*
6104 -rwx------ 1 nanodayo staff 3.0M 2 14 23:04 hash.db*


1,000,000回のDBファイルサイズ
112456 -rwx------ 1 nanodayo staff 55M 2 15 03:14 btree.db*
84280 -rwx------ 1 nanodayo staff 41M 2 15 01:34 hash.db*


こっちではbtreeの方が容量を多く使っていますね。
領域を確保する際のしきい値はbtreeの方が低いのでしょうか。

しきい値の違いを求めるには、エントリ数を増やしながら
ファイルサイズを見ていかないといけなさそうです。
(今回はやっていませんorz)

エントリ数を10倍にしてってますが
容量もだいたい10倍になっていますね。

10,000,000回のDBファイルサイズ
1142768 -rwx------ 1 nanodayo staff 558M 2 17 10:46 btree.db*
134418952 -rwx------ 1 nanodayo staff 64G 2 16 18:03 hash.db*


ただここでは逆転。
容量の差も大きいです。

しきい値の計算方法自体に違いがあるということでしょうか。

nanodayo at 09:19コメント(0)トラックバック(0) 

2010年12月10日

DBって何?


データベースです。

プログラムで使うデータを保存するためのものですね。
データベースと言われて、真っ先に出てくるのはSQL。
主流なのはPostgreSQLとMySQLで、高機能なのがPostgresSQLで、シンプルな代わりに軽いのがMySQLというような住み分けになっています。

最近ではNoSQLなんて動きもあって、そもそもSQL自体が大げさなのでもっとシンプルなKVSとかが注目を集めています。

なんて話をしておきながら、今回の記事はSQLもKVSも扱いません。BDBです。
部類的にはKVSだったりするんでしょうか。まぁいい。

歴史的経緯


正式名称はBerkeley Database。
カルフォルニア大学のバークレー校で開発されたものらしいですが、開発者がSleepycat Softwareという会社を設立してそちらで開発。
その後はOracleに買収されて今に至る と紆余曲折しているようです。
オープンソースのもあるので利用はできるようです。

使い方


プログラムからは関数を呼び出して使います。
当然DBの操作を行うコードを書かねばなりません。

C言語での使い方の流れはこんな感じ。
  1. ヘッダファイルをinclude
  2. dbopenでデータベースファイルを開く
  3. put/getでデータベース内のデータを扱う
  4. syncで操作した内容をファイルに反映させる
  5. dbcloseでファイルを閉じる


ヘッダファイル
#include <sys/types.h>
#include <db.h>
#include <fcntl.h>
#include <limits.h>


dbopen
DB * dbopen(const char *file, int flags, int mode, DBTYPE type, const void *openinfo);

fileはファイルの名前を渡します。
flagsとmodeについてはopen()で指定するものと同様です。

DBTYPEは、DB内部でのデータ構造です。
DB_BTREE、DB_HASH、DB_RECONOのいずれかを指定します。
openinfoは、データを処理するためのアクセスメソッドを指定するみたいですが、NULLにすればデフォルトのものが適用されるそうです。

DB構造体
typedef struct {
 DBTYPE type;
 int (*close)(const DB *db);
 int (*del)(const DB *db, const DBT *key, u_int flags);
 int (*fd)(const DB *db);
 int (*get)(const DB *db, DBT *key, DBT *data, u_int flags);
 int (*put)(const DB *db, DBT *key, const DBT *data, u_int flags);
 int (*sync)(const DB *db, u_int flags);
 int (*seq)(const DB *db, DBT *key, DBT *data, u_int flags);
} DB;


dbopen()の返り値がこれなので
db = dbopen(なんたらかんたら)みたいに実行してから
db->get(なんたらかんたら)のようにして、各種操作を行います。

DBT構造体
typedef struct {
 void *data;
 size_t size;
} DBT;


実際に扱うデータは、このDBT構造体で扱います。
dataへのポインタと、データサイズだけというとってもシンプルな構造。
key用とdata用に2つ作る必要があります。

サンプルコード

//dbtest.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <db.h>
#include <fcntl.h>
#include <limits.h>

int main(int argc, char** argv)
{
  const char* file;
  int flags;
  int mode;
  DBTYPE type;
  const void* openinfo = NULL;
  DB* db;
  DBT* key;
  DBT* value;
  DBT* get;
  int opt;
  char keybuf[512];
  char valuebuf[2048];
  char getbuf[2048];

  key = malloc(sizeof(DBT));
  value = malloc(sizeof(DBT));
  get = malloc(sizeof(DBT));
  key->data = keybuf;
  value->data = valuebuf;
  get->data = getbuf;

  while((opt = getopt(argc, argv, "k:v:")) != -1) {
    switch(opt) {
      case 'k':
        strcpy(key->data, optarg);
        printf("key is %s\n", (char *)key->data);
        break;
      case 'v':
        strcpy(value->data, optarg);
        printf("value is %s\n", (char *)value->data);
        break;
    }
  }

  key->size = strlen(key->data);
  value->size = (strlen(value->data)+1);
  printf("value size is %d\n", (int)value->size);

  file = "hash.db";
  flags = O_CREAT;
  flags |= O_RDWR;
  mode = S_IRWXU;
  type = DB_HASH;

  db = dbopen(file, flags, mode, type, openinfo);
  db->put(db, key, value, 0);
  db->sync(db, 0);
  db->get(db, key, get, 0);
  printf("got value is %s\n", (char *)get->data);
  printf("got value size is %d\n", (int)get->size);
  db->close(db);
  return 0;
}


引数で指定したkey/valueをdbに書きこんで、そのまま読み出すプログラム。
以下のように実行します。


# ./dbtest -k key -v value
key is key
value is value
value size is 6
got value is value
got value size is 6


value->size = (strlen(value->data)+1);で、+1しているのがポイントです。
strlenだと、ヌル文字をカウントしないので、+1しないと以下のような結果になってしまいます。

# ./dbtest -k key -v value
key is key
value is value
value size is 5
got value is valuekey
got value's size is 5

valueの後にkeyというデータもくっついています。
DB_TYPEにDB_HASHを使った場合は起きますが、DB_BTREEでは起きませんでした。
おそらくデータを保存する際の書式で、DB_BTREEではヌル文字で区切っているけど、DB_HASHではデータにヌル文字が含まれている前提なのではないでしょうか。
なのでgetした際に、ヌル文字が登場するまで読んでいて他のデータが混ざっているのかなぁと。

postmapで確認
同じプログラムからkey/valueを取得しても、ちゃんと動いているか分かりにくいので、postmapでも確認してみましょう。
postmapとはpostfixに付属のツールで、DBファイルのデータを扱えます。
※DBの確認方法としてはあんまり一般的ではないと思われます。

# postmap -q key hash
value


余談
postmapを最初に見つけたときに
postfixをposixに空目していて、何の疑いもなく
DBの確認に使っていましたとさ\(^o^)/

nanodayo at 00:51コメント(14)トラックバック(0)