官方文档针对不同类型的存储一股脑地介绍了一大堆选项,至于哪个选项适用于哪种类型的存储并不清楚(可能是我没看清楚),下面只说一下影响memory db的参数选项:
参考源码: tcadb.c :
bool tcadbopen(TCADB *adb, const char *name){
…
} else if (!tcstricmp(path, "*" )){
adb->mdb = bnum > 0 ? tcmdbnew2(bnum) : tcmdbnew();
adb->capnum = capnum;
adb->capsiz = capsiz;
adb->capcnt = 0;
adb->omode = ADBOMDB;
} else if (!tcstricmp(path, "+" )){
…
}
}
/* Open an abstract database. */ bool tcadbopen ( TCADB * adb , const char *name ) { . . . } else if ( ! tcstricmp ( path , " *" ) ) { adb -> mdb = bnum > 0 ? tcmdbnew2 ( bnum ) : tcmdbnew ( ) ; adb -> capnum = capnum ; adb -> capsiz = capsiz ; adb -> capcnt = 0 ; adb -> omode = ADBOMDB ; } else if ( ! tcstricmp ( path , " +" ) ) { . . . } }
说明: 有三个选项影响全内存型的hash存储:bnum : 总的bucket的数量; 注意map有8个(固定的);8个map中共有bnum个bucketcapnum : 最大允许的记录数; capsize : 最大允许的key-value使用的存储的大小;
下面解释一下capnum与capsize的使用逻辑: 先看一下源代码吧: tcadb.c :
bool tcadbput(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
bool err = false ;
char numbuf[TCNUMBUFSIZ];
ADBSKEL *skel;
switch (adb->omode){
case ADBOMDB:
if (adb->capnum > 0 || adb->capsiz > 0){
tcmdbput3(adb->mdb, kbuf, ksiz, vbuf, vsiz);
adb->capcnt++;
if ((adb->capcnt & 0xff) == 0){
if (adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100)
tcmdbcutfront(adb->mdb, 0x100);
if (adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz)
tcmdbcutfront(adb->mdb, 0x200);
}
} else {
tcmdbput(adb->mdb, kbuf, ksiz, vbuf, vsiz);
}
break ;
case ADBONDB:
…
}
}
/* Store a record into an abstract database object. */ bool tcadbput ( TCADB * adb , const void *kbuf , int ksiz , const void *vbuf , int vsiz ) { assert ( adb & & kbuf & & ksiz > = 0 & & vbuf & & vsiz > = 0 ) ; bool err = false ; char numbuf [ TCNUMBUFSIZ ] ; ADBSKEL * skel ; switch ( adb -> omode ) { case ADBOMDB : if ( adb -> capnum > 0 | | adb -> capsiz > 0 ) { tcmdbput3 ( adb -> mdb , kbuf , ksiz , vbuf , vsiz ) ; adb -> capcnt ++; if ( ( adb -> capcnt & 0xff ) == 0 ) { // 256的倍数时才检查,否则会比较影响性能 if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100) tcmdbcutfront(adb->mdb, 0x100); if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200); } } else { tcmdbput(adb->mdb, kbuf, ksiz, vbuf, vsiz); } break; case ADBONDB: ... } }
说明: 1. 如果设置了capnum或者设置了capsiz, 则使用tcmdbput3()来写入,然后判断capnum或capsiz是否超过了设置; 2. 并不是记录数超过设置立即删除,也不是超过多少删除多少; 而是记录数为0x100(即:256)的倍数时才检查,如果超过则删除,而且是删除256条记录; 3. 如果内存使用(这里说内存使用不确切,而应是key-value字节数)超过了capsiz限制,则直接删除256条记录; 4. 这里面的tcmdbput3() 和 tcmdbput() 的差别在于: 如果插入的记录是存在的,对于tcmput3()来讲,将把该条记录移动到链表的尾部,视为热数据,删除总是从链表头开始的。但是作者没有在get的时候也做这样的调整,不知道是疏忽还是另有考虑。
或许你测试发现删除的并非恰好256条记录,原因是这样的,先看源码: tcutil.c
void tcmdbcutfront(TCMDB *mdb, int num){
assert(mdb && num >= 0);
num = num / TCMDBMNUM + 1;
for ( int i = 0; i < TCMDBMNUM; i++){
if (pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){
tcmapcutfront(mdb->maps, num);
pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i);
}
}
}
void tcmdbcutfront ( TCMDB * mdb , int num ) { assert ( mdb & & num > = 0 ) ; num = num / TCMDBMNUM + 1 ; for ( int i = 0 ; i < TCMDBMNUM ; i ++) { if ( pthread_rwlock_wrlock ( ( pthread_rwlock_t * ) mdb -> mmtxs + i ) == 0 ) { tcmapcutfront ( mdb -> maps < i > , num ) ; pthread_rwlock_unlock ( ( pthread_rwlock_t * ) mdb -> mmtxs + i ) ; } } }
说明: 因为存储是分了TCMDBMNUM(8)个map来存储的,不能只删除一个map中的num个,这样太不公平了。所以平均了一下,但是可能平均的不能分完,所以每个就多删除1个; 即使这样,其实还是有问题的,因为有的map重可能不够num条记录,或者根本就没有记录;
关于从map中删除记录tcmapcutfront的说明: tcutil.c
void tcmapcutfront(TCMAP *map, int num){
assert(map && num >= 0);
tcmapiterinit(map);
while (num– > 0){
int ksiz;
const char *kbuf = tcmapiternext(map, &ksiz);
if (!kbuf) break ;
tcmapout(map, kbuf, ksiz);
}
}
/* Remove front records of a map object. */ void tcmapcutfront ( TCMAP * map , int num ) { assert ( map & & num > = 0 ) ; tcmapiterinit ( map ) ; while ( num -- > 0 ) { int ksiz ; const char *kbuf = tcmapiternext ( map , & ksiz ) ; if ( ! kbuf ) break ; tcmapout ( map , kbuf , ksiz ) ; } }
说明: 因为每个map下面的记录通过pre、next指针形成一个链表结构; 删除记录的时候也就从链表的头部开始删除了; 而且这种删除也是不会考虑到数据的冷热的,删除完全是一种先进先出的策略; 对于memcache来讲,内存不够用时,也是从一头儿开始删除,但是因为memcache在读取的时候会将数据调整到链表的另一头,所以memcache删除的总是最冷的数据。
在tc的memory db的实现中,获取数据时,完全可以把获取的数据调整到链表的尾部(在非全内存的btree的map中是这么实现的,见tcutil.c:tcmapget3()),这样剔除的时候就不至于把热数据给删除掉了
————————- 说句废话: xmsiz选项对mdb是没有关系的。