一个简单的Key-Value小数据库tmdb的原理和实现
来源:互联网 发布:ubuntu安装vim 编辑:程序博客网 时间:2024/05/17 02:42
1 基本特点
ey-Value 数据库是很早起比较典型的老式数据库,从Unix早期的dbm,后来的GNU版本的gdbm,还有ndbm,sdbm, cdb 以及功能强大的Berkeley DB (BDB)、还有这两年风头很劲的qdbm,都是典型代表。实际上来说,Key-Value 数据库不是严格意义上的数据库,只是一个简单快速的数据存储功能。
tmdb 也是差不多这么一个性质Key-Value小数据存储(DBM),设定存储数据目标量级是10W级,性能嘛也不是很好,算是一个小实验型产品。
说说它的基本特点:
- 存储数据量级为10W,超过后性能下降的厉害
- 因为存储特点决定,更适合存储只读数据,当然,它也是可以删除和修改数据的,只是比较浪费空间
- Key长度不能超过64个字节,数据长度不能超过65536个字节,适合存储一些小数据
- 使用的不是行级锁(Row-Level-Lock),而且是全局锁,所以并发读写情况下,性能不是很好
- 索引文件和数据文件分离,备份情况下要全部备份
- 接口API基本是按照传统的dbm的API来设定,整个库文件较小,可直接静态编译进程序
2 存储结构设计
索引使用的是静态索引,Hash表的长度不能动态扩容,缺省是 65535 Hash Bucket,如果冲突的情况使用开拉链法,那么如果冲突厉害,或者数据量大,自然大大增加了查找一条记录的时间,所以小数据量并且Key分布均匀下性能比较好(所有hash都是这样好不好 ^_^)。
上面特点说了,索引和数据文件是分离的,主要是为了动态扩容的时候不用做太多数据迁移和位置计算。
数据存储是单个文件,头部预留了256个字节的剩余,后面的都是用来存数据,所有数据都是 append 的方式,一个数据被删除,只在索引修改标志位,不会做实际数据迁移,也不会做空闲数据空间链表记录(偷懒啊),所以结构比较简单。
看一下索引文件的存储结构:
Index File Struct:
+-----------+----------------------+--------------+----------------+
| Header | Key ptr buckets | Key Record 1 |Key Record 2 .. |
+-----------+----------------------+--------------+----------------+
256Bytes 262144Bytes(256KB) 76Bytes 76Bytes
预留了 256字节的头部空间,用来后续扩展,然后是 256K 的用来存储hash桶到一条Key的指针位置(Key Record),设定的是 65536 * 4 = 256K,所以整个索引文件不能超过2G数据文件,否则单个4字节的指针空间存储不下 (^_^)。
Key Record 是存储一个Key信息的记录,一个Key信息的结构:
Index key record
+-------+--------------+----------+----------+
| Flag | Key | Data ptr | Next ptr |
+-------+--------------+----------+----------+
4Bytes 64Bytes 4Bytes 4Bytes
Flag 4个字节是标志是否删除,或者别的。Key 是定长的 64 字节,Data Ptr 是数据指针,指到数据文件中具体Value的记录位置,也是4个字节,所以决定了数据文件大小也不能超过2G (^_^),Next ptr 是存储同样一个Hash值下一条记录的Key记录指针 (多么简单的设计啊,就是一个内存Hash Table)。
无论Key多长,为了保证性能,都是定长方式存储,所以这个如果Key多的情况,浪费比较严重,而且实际使用中,如果数据值比较短,一般索引文件会比数据文件更大。(-_-!)
再看看数据文件的存储结构:
Data File structure:
+----------------+----------------+-----------------+
| Header | Data Record 1 |Data Record 2.. |
+----------------+----------------+-----------------+
256bytes dynamics length dynamics length
256个字节的预留头,然后是每个不定长的数据记录,逐个往后排列。再看看单个数据记录的结构:
Data record
+--------+-------+------------------+----------+
| Flag | len | Data | Next ptr |
+--------+-------+------------------+----------+
4Bytes 4Bytes dynamics length 4Bytes
4个字节的标志位(预留),4个字节存储实际数据长度,然后是下一条记录的数据指针。
整个存储结构还是比较简单明了的,因为使用了索引文件和数据文件的分开,所以很多方式实现就简单了,不过就是打开文件会多打开一个文件描述符。(^_^)
3 数据结构
struct tdb_index_header_t {int record_total;/* db all record total */char pre_alloc[252];/* previous alloc header storage space */};struct tdb_index_key_ptr {int keys[TDB_MAX_HASH_BUCKET];/* index key list (hash bucket) array */};/* Index one key structure *//*Index key record+-------+--------------+----------+----------+| Flag | Key | Data ptr | Next ptr |+-------+--------------+----------+----------+ 4Bytes 64Bytes 4Bytes 4Bytes*/struct tdb_key_record_t {int flag;/* a key flag, delete */char key[TDB_MAX_KEY_LEN];/* key data */int data_ptr;/* target data recored pointer */int next_ptr;/* next key pointer */};/* Data recored structure *//*Data record+--------+-------+------------------+----------+| Flag | len | Data | Next ptr |+--------+-------+------------------+----------+ 4Bytes 4Bytes dynamics length 4Bytes*/struct tdb_data_record_head_t {int flag;/* a data flag, delete */int len;/* record data length */};struct tdb_data_record_t {int flag;/* a data flag, delete */int len;/* record data length */char *data;/* record read data */int next;/* next recored pointer */};/* * Library's private representation of the database. */typedef unsigned longTDBHASH;/* hash values */typedef unsigned longCOUNT;/* unsigned counter */typedef short STATUS;typedef struct {FILE *idx_fp;/* file pointer for index file */FILE *dat_fp;/* file pointer for data file */int idx_fd;/* index file fd */int dat_fd;/* data file fd */char *idxbuf;/* malloc'ed buffer for index record */char *datbuf;/* malloc'ed buffer for data record*/char *name;/* name db was opened under */ char *idx_name;/* index file name */ char *dat_name;/* data file name*/TDBHASH nhash;/* hash table size */TDBHASH hash;/* current hash postion */off_t idxoff;/* offset in index file of index record *//* key is at (idxoff + PTR_SZ + IDXLEN_SZ) */size_t idxlen;/* length of index record *//* excludes IDXLEN_SZ bytes at front of record *//* includes newline at end of index record */off_t preidx;/* previous key postion */off_t key_ptr_pos;/* Key bucket item store Key pointer postion (address)*/off_t datoff;/* offset in data file of data record */size_t datlen;/* length of data record *//* includes newline at end */off_t next_off;/* Next record index offset *///off_t ptrval;/* contents of chain ptr in index record *///off_t ptroff;/* chain ptr offset pointing to this idx record *///off_t chainoff;/* offset of hash chain for this index record *///off_t hashoff;/* offset in index file of hash table *///COUNT cnt_delok; /* delete OK *///COUNT cnt_delerr; /* delete error *///COUNT cnt_fetchok; /* fetch OK *///COUNT cnt_fetcherr; /* fetch error *///COUNT cnt_nextrec; /* nextrec *///COUNT cnt_stor1; /* store: TDB_INSERT, no empty, appended *///COUNT cnt_stor2; /* store: TDB_INSERT, found empty, reused *///COUNT cnt_stor3; /* store: TDB_REPLACE, diff len, appended *///COUNT cnt_stor4; /* store: TDB_REPLACE, same len, overwrote *///COUNT cnt_storerr; /* store error */} TDB;
4 测试程序
#include <stdio.h> #include <string.h> #include <time.h> #include "tmdb.h" int main() { char *df = "db"; TDB *db = tdb_open(df, "c"); if ( !db ){ printf("tdb_open() %s fail./n", df); return -1; } printf("tdb_open() %s success./n", df); int s; char *ret; char *key = "test_key"; char *val = "test_value"; s = tdb_store(db, key, val, TDB_INSERT); if (TDB_SUCCESS == s){ printf("tdb_store() %s success./n", key); ret = tdb_fetch(db, key); if (NULL != ret){ printf("tdb_fetch() %s success, value: %s./n", key, ret); } } tdb_close(db); printf("Close db done/n"); return 0; }编译的时候记得加上库路径:(L 是库路径,l是库名, I是头文件路径)
$ gcc -o db_test db_test.c -L. -ltmdb -I.
$ ./db_test
tdb_open() db success.
tdb_store() test_key success.
tdb_fetch() test_key success, value: test_value.
Close db done
5 《unix环境高级编程》上的数据库存储结构
- 一个简单的Key-Value小数据库tmdb的原理和实现
- 【原创】Key-Value小数据库tmdb发布:原理和实现
- Key-Value小数据库tmdb发布:原理和实现
- Key-Value小数据库tmdb发布:原理和实现 url
- Key-Value小数据库tmdb发布:原理和实现 .
- 简单的key-value实现
- key-value数据库的一种实现
- key-value数据库的一种实现
- php实现简单key-value hash数据库
- 分布式key-value数据库LightCloud的设计原理
- HASHDB:一个简单的Key-value的存储系统原型
- 小数据量的Key-Value查找类的实现
- tstdb一个快速简单的key-value store
- HashMap和HashTable,HashMap中key和value的原理
- HashMap和HashTable,HashMap中key和value的原理
- HashMap和HashTable,HashMap中key和value的原理
- 遍历一个map,从中取得key 和value。不知道key的情况下取value。
- 关于使用key/value数据库redis和TTSERVER的心得体会
- 【GBT28181开发:SIP协议实践】之设备信息查询
- ON_MESSAGE / NOTIFY
- Java基础,自减自增"--","++"
- sys_reboot
- SurfaceView,SurfaceHolder,SurfaceHolder.CallBack
- 一个简单的Key-Value小数据库tmdb的原理和实现
- /etc/fstab 错误的修复方法
- IOS研究院之使用Xcode4制作静态库详解
- 网页中的表达式语法(.Net
- JAVA类,别名问题
- Mina框架总结
- 5种流行的Linux发行版:你更喜欢哪一个呢?
- Silverlight中的Action与Trigger
- 生活,温暖如花