BerkeleyDB - WindyHana's Solanara

목차

개요

설치

db-*.gz 파일의 다운로드는 Oracle Berkeley DB Downloads에서 받을 수 있다. 로그인이 필요하지만 가입은 무료다. 설치에 대한 설명은 Chapter 7. Building Berkeley DB for UNIX/POSIX을 참고하자.
root@wl ~/src # tar xvfz db-6.2.23.tar.gz
root@wl ~/src # cd db-6.2.23
root@wl ~/src/db-6.2.23 # cd build_unix
root@wl ~/src/db-6.2.23/build_unix # ../dist/configure \
  --enable-dtrace \
  --enable-dbm \
  --enable-sql \
  --enable-sql_compat
root@wl ~/src/db-6.2.23/build_unix # make
root@wl ~/src/db-6.2.23/build_unix # make install
root@wl ~/src/db-6.2.23/build_unix # cd /usr/local
root@wl /usr/local # ln -s BerkeleyDB.6.2 db 1)
root@wl /usr/local # vi /etc/profile
...
LD_LIBRARY_PATH=/usr/local/db/lib:$LD_LIBRARY_PATH
1) 버클리DB가 버전마다 디렉토리가 다른 관계로, 솔라나라에서는 /usr/local/db를 사용해 버클리DB와 연동한다.

명령어

C 프로그램 예제

기본예제

Oracle Berkeley DB 12c Release 1문서의 Getting Started with Berkeley DB의 내용을 일부 발췌했으며 대부분의 코드는 매뉴얼에서 가져왔다. Berkeley DB Tutorial and Reference Guide, Version 4.1.24도 참고하면 좋겠다.
berkeleydb.c
다운로드 (6,202 바이트)
/*
	BerkeleyDB Sample Code
	WindyHana's Solanara: BerkeleyDB http://www.solanara.net/solanara/berkeleydb
	cc -L/usr/local/db/lib -ldb -I/usr/local/db/include -o berkeleydb berkeleydb.c
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "db.h"
#define  DBFILE "berkeleydb.db"
#define  ENV_DIRECTORY "./"

DB_ENV* dbenv;
DB* dbp;
//DB_MPOOL_STAT* dbmpoolstat;

int putData(char * key, char * val) {
    DBT dbkey, dbval;

    memset(&dbkey, 0, sizeof(dbkey));
    memset(&dbval, 0, sizeof(dbval));
    dbkey.data = key;
    dbkey.size = sizeof(key);
    dbval.data = val;
    dbval.size = sizeof(val);

	return dbp->put(dbp, NULL, &dbkey, &dbval, 0);
}

int genSampleData() {
    putData("apple", "red");
    putData("banana", "yellow");
    putData("grape", "purple");
}

void env_open(DB_ENV **dbenvp) {
	DB_ENV *dbenv;
	int flags;
	int ret;

	/* Create the environment handle. */
	if ((ret = db_env_create(&dbenv, 0)) != 0) {
		fprintf(stderr, "txnapp: db_env_create: %s\n", db_strerror(ret));
		exit (1);
	}

	/* Set up error handling. */
	dbenv->set_errpfx(dbenv, "데이터베이스 정보/오류");

	/* Do deadlock detection internally. */
	if ((ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT)) != 0) {
		dbenv->err(dbenv, ret, "set_lk_detect: DB_LOCK_DEFAULT");
		exit (1);
	}

	/* * Open a transactional environment: * create if it doesn't exist * free-threaded handle * run recovery * read/write owner only */
	
	flags = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN | DB_RECOVER | DB_THREAD;
	if ((ret = dbenv->open(dbenv, ENV_DIRECTORY, flags, S_IRUSR | S_IWUSR)) != 0) {
		dbenv->err(dbenv, ret, "dbenv->open: %s", ENV_DIRECTORY);
		exit (1);
	}

	*dbenvp = dbenv;
}

void * checkpoint_thread(void *arg) {
	DB_ENV *dbenv;
	int ret;
	pthread_t ptid;
	
	dbenv = arg;
	printf("체크포인트 쓰레드(%lu) ", (u_long)pthread_self());

	/* Checkpoint once a minute. */
	for (;; sleep(60)) if ((ret = dbenv->txn_checkpoint(dbenv, 0, 0, 0)) != 0) {
		dbenv->err(dbenv, ret, "checkpoint thread");
		exit (1);
	}
}

/* 필요 없는 로그 파일 삭제 */
void * logfile_thread(void *arg) {
	DB_ENV *dbenv;
	int ret;
	char **begin, **list;
	dbenv = arg;
	printf("로그 삭제 쓰레드(%lu) ", (u_long)pthread_self());

	/* Check once every 5 minutes. */
	for (;; sleep(300)) {
		/* Get the list of log files. */
		if ((ret = dbenv->log_archive(dbenv, &list, DB_ARCH_ABS)) != 0) {
			dbenv->err(dbenv, ret, "DB_ENV->log_archive");
			exit (1);
		}
		/* Remove the log files. */
		if (list != NULL) {
			for (begin = list; *list != NULL; ++list) {
				if ((ret = remove(*list)) != 0) {
					dbenv->err(dbenv, ret, "remove %s", *list);
					exit (1);
				}
			}
			free (begin);
		} 
	}
}

/* 사용하는 로그 파일 보임 */
void log_archlist(DB_ENV *dbenv)
{
	int ret;
	char **begin, **list;

	/* Get the list of database files. */
	if ((ret = dbenv->log_archive(dbenv, &list, DB_ARCH_ABS | DB_ARCH_DATA)) != 0) {
		dbenv->err(dbenv, ret, "DB_ENV->log_archive: DB_ARCH_DATA");
		exit (1);
	}

	if (list != NULL) {
		for (begin = list; *list != NULL; ++list) printf("database file: %s\n", *list);
		free (begin);
	}

	/* Get the list of log files. */
	if ((ret = dbenv->log_archive(dbenv, &list, DB_ARCH_ABS | DB_ARCH_LOG)) != 0) {
		dbenv->err(dbenv, ret, "DB_ENV->log_archive: DB_ARCH_LOG");
		exit (1);
	}

	if (list != NULL) {
		for (begin = list; *list != NULL; ++list) printf("데이터베이스 로그파일: %s\n", *list);
		free (begin);
	}
}

int main() {
    DBT key, val;
    int ret;
    pthread_t ptid;

    printf("데이터베이스 환경 오픈\n");
	env_open(&dbenv);

    printf("데이터베이스 체크 포인트 쓰레드 시작\n");
	if ((ret = pthread_create(&ptid, NULL, checkpoint_thread, (void *)dbenv)) != 0) {
		fprintf(stderr, "checkpoint: failed spawning checkpoint thread: %s\n", db_strerror(ret));
		exit (1);
	}

    fflush(stdout); printf("데이터베이스 로그 삭제 쓰레드 시작\n");
	if ((ret = pthread_create(&ptid, NULL, logfile_thread, (void *)dbenv)) != 0) {
		fprintf(stderr, "txnapp: failed spawning log file removal thread: %s\n", db_strerror(ret));
		exit (1);
	}

    printf("데이터베이스 생성 (버전: %s)\n", DB_VERSION_STRING);
    if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
        fprintf(stderr, "db_create: %s\n", db_strerror(ret));
        exit(1);
    }

    printf("데이터 베이스 오픈(%s): ", DBFILE);
    if ((ret = dbp->open(dbp, NULL, DBFILE, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) {
        printf("Fail - "); fflush(stdout); dbp->err(dbp, ret, "db->open(%s)", DBFILE);
        goto done;
    }
    printf("성공\n");
    
    memset(&key, 0, sizeof(key));
    memset(&val, 0, sizeof(val));
    key.data = "key1";
    key.size = sizeof("key1");
    val.data = "val1";
    val.size = sizeof("val1");
    printf("데이터 넣기 (%s, %s): ", key.data, val.data);
    if ((ret = dbp->put(dbp, NULL, &key, &val, 0)) != 0) {
        printf("Fail - "); fflush(stdout); dbp->err(dbp, ret, "db->put");
        goto done;
    }
    printf("성공\n");
    
    memset(&val, 0, sizeof(val));
    printf("데이터 얻기 (%s): ", key.data);

    key.flags = DB_DBT_REALLOC;
    val.flags = DB_DBT_REALLOC;
	if ((ret = dbp->get(dbp, NULL, &key, &val, 0)) != 0) {
		printf("Fail - "); fflush(stdout); dbp->err(dbp, ret, "db->get");
		goto done;
	}
	printf("성공 - %s\n", val.data);
                 
	printf("데이터 삭제 (키: %s): ", key.data);
	if ((ret = dbp->del(dbp, NULL, &key, 0)) != 0) {
		printf("Fail - "); fflush(stdout); dbp->err(dbp, ret, "DB->del");
		goto done;
	}
	printf("성공\n");

	printf("삭제된 데이터 얻기(실패해야함): ", key.data);
	if ((ret = dbp->get(dbp, NULL, &key, &val, 0)) != 0) {
		printf("Fail - "); fflush(stdout); dbp->err(dbp, ret, "DB->get");
		goto done;
	}
	printf("성공\n"); // Cannot be reached.

done:
	genSampleData();
	printf("샘플 데이터 삽입됨.\n");
	log_archlist(dbenv);

    if (dbp != NULL) { ret = dbp->close(dbp, 0); }
    if (dbenv != NULL) { ret = dbenv->close(dbenv, 0); }
	printf("데이터 베이스 닫음.\n");
    exit(ret);
}
# ./berkeleydb
데이터베이스 환경 오픈
데이터베이스 체크 포인트 쓰레드 시작
데이터베이스 로그 삭제 쓰레드 시작
데이터베이스 생성 (버전: Berkeley DB 6.2.23: (March 28, 2016))
체크포인트 쓰레드(2) 로그 삭제 쓰레드(3) 데이터 베이스 오픈(berkeleydb.db): 성공
데이터 넣기 (key1, val1): 성공
데이터 얻기 (key1): 성공 - val1
데이터 삭제 (키: key1): 성공
삭제된 데이터 얻기(실패해야함): Fail - 데이터베이스 정보/오류: DB->get: BDB0073 DB_NOTFOUND: No matching key/data pair found
샘플 데이터 삽입됨.
데이터베이스 로그파일: /root/t/./log.0000000002
데이터베이스 로그파일: /root/t/./log.0000000003
데이터 베이스 닫음.

SQLite

SQLite 에 대한 대체 라이브러리(Drop-in Replacement)를 제공한다. (BerkeleyDB에서 SQL을 구현할때 SQLite 와 호환되도록 만든것으로 알고 있다) 이를 이용해 SQLite 로 작성된 프로그램은 소스 수정 없이 BerkeleyDB를 사용하도록 할 수 있다. 예제 소스코드는 윈디하나의 솔라나라: SQLite - C 예제를 수정없이 사용했다.
root@wl ~ # wget http://www.solanara.net/contents/includes/sqlite_init.sql
root@wl ~ # /usr/local/db/bin/sqlite3 userdb.sqlite ".read sqlite_init.sql" 1)
SQLite 3.8.10.2
0|0|0|SEARCH TABLE authinfo AS a USING COVERING INDEX sqlite_autoindex_authinfo_1 (userid=?)
0|1|1|SEARCH TABLE userinfo AS u USING INTEGER PRIMARY KEY (rowid=?)
root|루트|010-111-1111|1.0|2016-09-16 13:34:30
windy|윈디하나|010-000-0000|1.0|2016-09-16 13:34:30
2016-09-16 13:34:30|2

root@wl ~ # wget http://www.solanara.net/contents/includes/sqlite.c
root@wl ~ # cc -lsqlite3 -lmd5 -I/usr/local/db/include -L/usr/local/db/lib -o sqlite sqlite.c -lnsl -lsocket 2)
root@wl ~ # ./sqlite
SQLite 3.8.10.2
SQLite Lib 3.8.10.2 3)

SQLITE_LIMIT_LENGTH = 1000000000
SQLITE_LIMIT_COLUMN = 2000
SQLITE_LIMIT_EXPR_DEPTH = 1000
SQLITE_LIMIT_COMPOUND_SELECT = 500
SQLITE_LIMIT_VDBE_OP = 25000
SQLITE_LIMIT_FUNCTION_ARG = 127
SQLITE_LIMIT_ATTACHED = 10
SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 50000
SQLITE_LIMIT_VARIABLE_NUMBER = 999
SQLITE_LIMIT_TRIGGER_DEPTH = 1000

SQL: select md5('한글') "한글(UTF-8)MD5해시"
        한글(UTF-8)MD5해시
1       52b8c54ab4ea672ee6cdfdfef0a31db4
SQL: select first(username) fun from userinfo
        fun
1       루트
SQL: select * from userlist
        userid  username        tel1    grade   rdate
1       root    루트    010-111-1111    1.0     2016-09-16 13:34:30
2       windy   윈디하나        010-000-0000    1.0     2016-09-16 13:34:30
1) BerkeleyDB에 설치된 SQLite 라이브러리가 아닌 시스템에 설치된 SQLite 라이브러리가 로드된 경우 SQLite header and source version mismatch와 같은 메시지가 나온다
# /usr/local/db/bin/sqlite3
SQLite header and source version mismatch
2015-05-20 18:17:19 2ef4f3a5b1d1d0c4338f8243d40a2452cc1f7fe4
2014-02-11 14:52:19 ea3317a4803d71d88183b29f1d3086f46d68a00e
이런 경우 LD_LIBRARY_PATH환경변수 값을 시스템에 설치되어있는 SQLite 라이브러리 보다 BerkeleyDB에 설치된 SQLite 라이브러리 경로를 우선하게 조절하면 해결할 수 있다.
2) SQLite 는 소켓 라이브러리를 필요로 하지 않지만, BerlekenyDB 는 기본적으로 소켓 라이브러리를 필요로 한다. 소스는 수정 안해도 되지만, 빌드시 소켓 라이브러리 부분은 추가해야 한다.
3) SQLite 헤더는 3.8.3.1 이지만 SQLite Lib 는 시스템에 설치된 라이브러리 버전이 표시될 수 있다. 따라서 헤더 버전과 라이브러리 버전이 다를 수 있다. 윈디하나의 솔라나라: SQLite - 설치대로 설치하고 실행해보면 달리 나올 것이다.
RSS ATOM XHTML 1.0 CSS3