bdb,gdbm,tc,sqlite3 性能测试
见图,都是默认编译选项,内存占用情况:bdb 最少, 800 KB 至 1MB 多点,
gdbm 稍微多点,1MB 多,sqlite3 3MB 多点,tc 最大,30MB~63MB,感觉是
整个弄到内存里操作。
操作:写 50000 条 200 - 4096 字节长度记录,然后逐条读出;
20 个进程并发遍历读取这 50000 条记录。
bdb 4.6.19
gdbm 1.8.3
tokyo cabinet 1.3.2
sqlite 3.6.1
加锁粒度和缓存大小对性能影响很大:加锁粒度大会使写速度加快,因为不用频繁的对文件逐段加锁解锁,但会导致并发读取速度减慢,加锁有三个粒度:整个文件加锁,对页加锁(一次锁 4096 字节的文件内容),对记录加锁。缓存大自然对读写都非常有利,但很可能浪费内存。
bdb 耗内存小,加锁粒度应该较小,因此并发读取时性能很不错。
gdbm 大概加锁粒度大点,导致写速度很快,并发读取却比较差。
tc 用 mmap 占用了大量内存,很少写文件,写速度极快,但并发读取性能比较差,可能是加锁粒度较大的缘故。
sqlite3 在插入式用自动事务和手动事务性能差别巨大,自动事务时性能惨不忍睹。
gdbm 和 tc 在查询数据时,返回的 key 和 value 都需要调用方释放,而 bdb 和 sqlite3 则是自行管理,
让人惊奇的是 gdbm 的性能没有受到 malloc/free 影响,可以印证 glibc 的 doug lea malloc 实现非常高效。
在 tc 的文档中有如下一段:
Exclusion control between processes is performed when connecting to a database by file locking. While a writer is connected to a database,neither readers nor writers can be connected. While a reader is connected to a database,other readers can be connect,but writers can not.
这里说法并不准确,tc 在函数退出时会释放锁,因此可以多个读写线程同时打开同一个数据库,但 tc 对整个数据库用 pthread rwlock,会导致读写线程并发能力很差。 多进程之间用的 fcntl 对整个文件加锁,读写进程并发应该会很差。
gdbm 居然也是用 flock or fcntl 对整个文件加锁:
#define UNLOCK_FILE(dbf) flock (dbf->desc,LOCK_UN)
#define READLOCK_FILE(dbf) lock_val = flock (dbf->desc,LOCK_SH + LOCK_NB)
#define WRITELOCK_FILE(dbf) lock_val = flock (dbf->desc,LOCK_EX + LOCK_NB)
而且是打开文件时即获取锁,导致不能有两个进程同时以写方式打开,而且在读进程和写进程之间没有互斥。
奇怪的是多个读进程之间没有互斥,怎么并发读取性能那么差。