Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1791929
  • 博文数量: 335
  • 博客积分: 4690
  • 博客等级: 上校
  • 技术积分: 4341
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-08 21:38
个人简介

无聊之人--除了技术,还是技术,你懂得

文章分类

全部博文(335)

文章存档

2016年(29)

2015年(18)

2014年(7)

2013年(86)

2012年(90)

2011年(105)

分类: C/C++

2012-12-26 18:48:12

打算利用自己的业余时间,将APUE上的DB动手进行实际操作演练一遍,以加深自己的对DB的理解。
将采用分而治之的思想,首先讲解源文件,makefile,然后从最重要的函数开始讲起,最后给出gdb调试步骤。
数据库的函数库使用两个文件来存储信息:
索引文件
数据文件
索引文件包含键值(通过hash)以及指向该键值所对应数据的指针,本文中采用取模的方法,
此处的指针同C中的指针不是一个概念,但是功能是类似的,不要混淆)
数据文件 主要包含数据记录(同我们常见DB中的record相对应)
索引文件使用固定大小的hash表来存储键值(大小为137),这也就意味着即使存储的数据时均匀分布,在插入137条记录(最理想的情况下),当继续插入138条记录,将会产生哈希冲突,解决方法是使用链表法解决解决散列冲突。
需要说明的索引文件与数据都是采用ascii形式进行存储,这样才不同的平台就可以实现共享。
由于本文采用C实现,而C中没有字符串变量,因此对字符的处理仍然是采用C的惯例,以'\n'表示。
索引文件的构成如下:
FREE POINTER|HASH POINTER HP1| HASH POINTER HP2|......|HASH POINTER HP137\n
紧跟着hash指针的INDEX RECORD
INDEX RECORD的构成如下:
OFFSET_OF_NEXT_INDEX_RECORD|
INDEX_RECORD_LENGTH|KEY|SEPERATOR|
OFFSET_OF_DATA_RECORD|SEPERATOR
|LENGTH_OF_DATA_RECORD\n
INDEX RECORD 的构成相对来说比较简单,第一个字段表示指向下一个 INDEX RECORD OFFSET,
INDEX_RECORD_LENGTH 该条 INDEX_RECORD的长度,这两个字段为定长字段
剩下三个字段KEY 键值,OFFSET_DATA_RECORD(IN DATA FIEL) 数据记录在数据文件中的偏移量,LENGTH_OF_DATA_RECORD,该键值所对应的数据记录的长度,使用separator分隔符来分隔,具体实现的时候,使用STRCHR来定位字符,从而计算出每一个字段的长度。
如下图所示:
数据文件相对来说比较简单,都是使用\n来分隔的字符串,每一个数据记录对应不同的偏移量。
下面是具体的实现部分。
文件source的整体布局如下:
ubuntu@ubuntu-virtual-machine:~/Desktop/mydb/diydb$ ls
apue.c  apue.o  db.h  error.c  main    makefile
apue.h  db.c    db.o  error.o  main.c  
使用过的makefile如下
ubuntu@ubuntu-virtual-machine:~/Desktop/mydb/diydb$ cat makefile
main:apue.o error.o db.o
    gcc -Wall -g -o main apue.o error.o db.o main.c
    rm db4.*
apue.o:apue.c
    gcc -c apue.c
error.o:error.c
    gcc -c error.c
db.o:db.c
    gcc -c db.c
为了便于理解,我将从DB.C函数开始讲解。该函数包含了我们DB的最主要的实现。
首先说点题外话,由于C的函数必须是先定义后使用,因此函数定义的顺序一定早于
函数调用的顺序,以防止不必要的错误。
每一个DB在创建的时候,都包含两个文件,一个是数据文件,一个是索引文件。索引文件使用hash值
来进行数据的访问。因此首先讲解hash函数的实现。

  1. static DBHASH
  2. _db_hash(DB *db,const char *key ) {
  3.     DBHASH hval = 0;
  4.     char c;
  5.     int i;
  6.     for ( i = 1;(c = *key++) != 0;i++)
  7.         hval +=c*i; /* ascii char times its 1-based index */
  8.     return(hval % db->nhash);
  9. }
该函数首先取每一个字符的ascii值乘以自己在字符中位置,然后将所有的值进行加和,最后采用最常用的hash计算方法获得哈希值。
其中db->nhash为DBhash表的大小,为137。在本例中为常量,采用宏定义来实现,便于扩展。
写到这里,为了后面叙述方便,我们首先给出头文件。

  1. #ifndef _APUE_DB_H
  2. #define _APUE_DB_H

  3. typedef void * DBHANDLE;
  4. DBHANDLE db_open(const char * ,int,...);
  5. void db_close(DBHANDLE);
  6. char * db_fetch(DBHANDLE,const char *);
  7. int db_store(DBHANDLE,const char *,const char *,int );
  8. int db_delete(DBHANDLE,const char *);
  9. void db_rewind(DBHANDLE);
  10. char * db_nextrec(DBHANDLE,char *);

  11. /*
  12.  * Flags for db_store()
  13.  */
  14. #define DB_INSERT 1 /* insert new record only */
  15. #define DB_REPLACE 2 /* replace existing record*/
  16. #define DB_STORE 3 /* replace or insert */

  17. /*
  18.  * implementation limits
  19.  */

  20. #define IDXLEN_MIN 6 /* KEY SEP START,SEP,length \n*/
  21. #define IDXLEN_MAX 1024 /* ARBITRARY */
  22. #define DATLEN_MIN 2 /* data byte ,newline */
  23. #define DATLEN_MAX 1024/* ARBITRARY */
  24. #endif /* _APUE_DB_H */
发现现在描述到这里,讲解有点纠结,感觉思路不是太好,或许我应该从DB的架构上把思路理清楚,那样再讲函数的实现会好一点。为此,首先给出DB的结构。

  1. typedef unsigned long count;  /* unsigned counter */
  2. typedef struct{
  3.     int idxfd ; /* fd for index file */
  4.     int datfd ; /* fd for data file */
  5.     char * idxbuf; /* malloc'ed buffer for index record */
  6.     char * datbuf; /* malloc'ed buffer for data record */
  7.     char * name ; /* name db was opened under */
  8.     off_t idxoff; /* offset in inde file of index record */
  9.      /* key is at (idxoff+PTR_SZ+IDXLEN_SZ) */
  10.     off_t idxlen; /* length of the index file record */
  11.     size_t datlen ; /* length of data record */
  12.      /* includes newline at end */
  13.     off_t datoff ; /* offset in the data file of data record*/
  14.     off_t ptrval ; /* contents of the chain ptr in index record */
  15.     off_t ptroff ; /* chain ptr offset pointing to this index record */
  16.     off_t chainoff; /* offset of hash chain of this index record */
  17.     off_t hashoff ; /*offset in the index file of hash table */
  18.     DBHASH nhash ; /* current hash table size ;*/
  19.     COUNT cnt_delok; /* delete ok */
  20.     COUNT cnt_delerr; /* delter error */
  21.     COUNT cnt_fetchok; /* fetch ok */
  22.     COUNT cnt_fetcherr; /* fetch error */
  23.     COUNT cnt_nextrec ; /* nextrec */
  24.     COUNT cnt_stor1; /* store: DB_INSERT,no empty,append */
  25.     COUNT cnt_stor2; /* store: DB_INSERT,found empty,reused */
  26.     COUNT cnt_stor3; /* store: DB_REPLACE,difflen,append */
  27.     COUNT cnt_stor4; /* store: DB_REPLACE,samelen,override */
  28.     COUNT cnt_storerr; /* store: DB_REPLACE,samelen,override */
  29. }DB;
从该结构的定义,不难看出,该结构体定义了两个文件描述符,一个用来指向DB 索引文件,一个指向DB的数据文件。然后定义了两个buffer,用来临时存储DATA RECORD,INDEX RECORD。
name 定义了该数据的名字,这个应该是最好理解的。
当一个新的DB创建以后,该DB所对应的图示(现在的理解,不一定全对)

待续---------------------


阅读(2170) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~