[sqlite] the speed of embedded database engines, sqlite3 vs berkeley db, I'm confused

our project is using sqlite3,but found that the speed is too slow.
I just tested the BerkeleyDB,and found that the speed is very fast. But I
knew the sqlite3 is fast enough. And so I'm confused.
I may be using sqlite3 in wrong way?
anyway,next is my test code. I'm glad to receive your message.
______________________________________________________________________________
// http://www.ibm.com/developerworks/cn/linux/l-embdb/

////////////////////////////// head ///////////////////////////////
#include <stdio.h>
#include <stdlib.h> // for system
#include <string.h> // for memset strcpy
#include <time.h> // for time

#include <sqlite3.h> // for Sqlite3
#include <db.h> // for Berkeley DB



//////////////////////////// macro and struct /////////////////////////////
#define DB_FILE_SQLITE "test_sqlite_0.1.db"
#define DB_FILE_BDB "test_bdb_0.1.db"

struct customer
{
int c_id;
char name[10];
char address[20];
int age;
};


////////////////////////////// global variable ///////////////////////////////

sqlite3 *db = NULL;

int ret = -1; // 各函数返回值


////////////////////////////// func proto ///////////////////////////////

void way01(); // 打开、关闭的影响


///////// sqlite3 //////////

int sqlite_createtb(sqlite3 *db);
int sqlite_insertdb(sqlite3 *db);

int getdata_sqlite ( sqlite3 *db,struct customer *tb1 );


/////// berkeley db ////////

int bdb_createdb(); // 包含了插入数据

void print_error(int r);
void init_dbt( DBT *key,DBT *data );


/////////////////////////////// code ///////////////////////////////

int main ( void )
{
int c = 0;

system ( "rm -rf test_0.1.db" );
ret = sqlite3_open ( DB_FILE_SQLITE,&db );
ret = sqlite_createtb(db);
ret = sqlite_insertdb(db);
sqlite3_close (db);

printf ( "Sqlite3 / Berkeley DB,创建数据库 + 插入数据 ... 完成/n" );

printf ( "//////////////////////////////////////////////////n" );
printf ( "1 : 查询性能比较 - Berkeley DB 与 Sqlite3 ////n" );

while ( (c=getchar()) != 'q' )
{
switch (c)
{
case '1':
way01();
break;
default:
break;
}
}

system ( "rm -rf test_sqlite_0.1.db" );
system ( "rm -rf test_bdb_0.1.db" );

return 0;
}

///////////////////////////////////////////////////////////////
// 查询性能比较 - Berkeley DB 与 Sqlite3
void way01()
{
time_t tick1,tick2;

int i = 0;
int num = 1000*100;

struct customer tb_data;

///////////////////////////////////////////////////////////
time ( &tick1 );
for ( i=0; i<num; i++ )
{
ret = sqlite3_open ( DB_FILE_SQLITE,&db );
ret = getdata_sqlite ( db,&tb_data );
sqlite3_close (db);
}
time ( &tick2 );
printf("Sqlite3 : 打开、关闭并操作数据库文件 %d 次,时间为: %4ld s/n",num,tick2 -
tick1 );

///////////////////////////////////////////////////////////
bdb_createdb();
}

///////////////////////////////////////////////////////////////
void *callback(void *para,int col,char **value,char **colname )
{
// int i;
// for(i=0; i<col; i++){
// printf("%s,",(value[i] ? value[i] : "NULL") );
// }
// printf("col = %d/n",col);
return (void *) 0;
}
int getdata_sqlite ( sqlite3 *db,struct customer *tb1 )
{
char *sql = "SELECT * FROM table1 WHERE id=500;";
ret = sqlite3_exec ( db,sql,(void *)callback,NULL,NULL );
return 0;
}

///////////////////////////////////////////////////////////////
int sqlite_createtb( sqlite3 *db )
{
char *sql1 = "CREATE TABLE table1 (id INTEGER,name VARCHAR(10),address
VARCHAR(20),age INTEGER)";
ret = sqlite3_exec ( db,sql1,NULL );
return 0;
}
int sqlite_insertdb(sqlite3 *db)
{
time_t tick1,tick2;
int i = 0;
int num = 1000;

char *qf = "INSERT INTO table1 VALUES (%d,%Q,%d)";
char *sql = NULL;

time ( &tick1 );
sqlite3_exec ( db,"BEGIN",NULL );
for (i=0;i<num;i++)
{
sql = sqlite3_mprintf ( qf,i,"javer","chengdu",32*i );
ret = sqlite3_exec(db,NULL);
sqlite3_free (sql);
}
sqlite3_exec(db,"COMMIT",NULL);
time ( &tick2 );

return 0;
}



///////////////////////////////////////////////////////////////
int bdb_createdb()
{
time_t tick1,tick2;
int i = 0;
int num = 1000;
int key_cust_c_id = 500;

DB *dbp;
DBT key,data;
struct customer cust;


ret = db_create(&dbp,0);
ret = dbp->open(dbp,DB_FILE_BDB,DB_BTREE,DB_CREATE,
0664);

time ( &tick1 );
for ( i=0; i<num; i++ )
{
cust.c_id = i;
strncpy(cust.name,9);
strncpy(cust.address,19);
cust.age = 32*i;

init_dbt( &key,&data );

key.size = sizeof(int);
key.data = &(cust.c_id);

data.size = sizeof(struct customer);
data.data = &cust;

ret = dbp->put(dbp,&key,&data,DB_NOOVERWRITE);
print_error(ret);
}
time ( &tick2 );
//printf ( "Berkeley DB 打开+插入+关闭,耗时: %ld s/n",tick2-tick1 );


printf("Berkeley DB : 插入完成/n");
time ( &tick1 );
for ( i=0; i<num*10000; i++ )
{
init_dbt( &key,&data );

key.size = sizeof(int);
key.data = &key_cust_c_id;

memset(&cust,sizeof(struct customer));

data.data = &cust;
data.ulen = sizeof(struct customer);
data.flags = DB_DBT_USERMEM;

ret = dbp->get(dbp,0);
print_error(ret);

// printf("c_id = %d,name = %s,address = %s,age = %d./n",
// cust.c_id,cust.name,cust.address,cust.age);
}
time ( &tick2 );
printf("Berkeley DB : 打开、关闭并操作数据库文件 %d 次,tick2 -
tick1 );

if(dbp != NULL)
dbp->close(dbp,0);

return 0;
}
// 数据结构DBT在使用前,应首先初始化,否则编译可通过但运行时报参数错误
void init_dbt( DBT *key,DBT *data )
{
memset(key,sizeof(DBT));
memset(data,sizeof(DBT));
}
// DB的函数执行完成后,返回0代表成功,否则失败
void print_error(int r)
{
if(r != 0)
printf("ERROR: %s/n",db_strerror(r));
}

sqlite has edge over BDB:

1) code readability is not as clean and clear as sqlite
2) forums not sure if they are responsive as sqlite
3) sqlite is stable and does not crash on-restarts,bdb make uses of mmap and its behaviour is undefined under diskful condition. get sigbus sometimes,sometimes throws error.
4) sqlite supports vfs,hence u can write your own layer to meet performance
5) too many parameters to configure in bdb. sqlite is zero config system.
6) easy to extend for later inclusions of alter table,bdb is key-value system
7) footprint is very small of sqlite compared with bdb.
8) sqlite does not support row or table level concurrency but bdb supports it well.
9) DB file is os independednt.
My opinion is sqlite is painfree system.
regards
ragha


Instead of using sqlite3_mprintf() and sqlite3_exec() for your insert
statements,try using prepared statements.

Regards,
Eugene Wee


in your test code I see that you open the sqlite DB
each time you perform the single query.
I guess it would be fair to open the sqlite DB only once
outside your test loop.
2nd: You query against ID without an index,try either
define an index on ID or change your table definition
into "ID INTEGER PRIMARY KEY" which will have an index
on ID automatically,I think..


Sqlte provides COMMIT and ROLLBACK unlike Berkeley. If you can get by
without the advanced features of Sqlite,then use Berkely and take
advantage of its simplicity and faster execution.

Note that for best performance an Sqlite application should group
database inserts,deletes and updates into transactions.


On Fri,Apr 24,2009 at 5:27 AM,John Stanton <jo...@viacognis.com> wrote:
> Sqlte provides COMMIT and ROLLBACK unlike Berkeley. If you can get by
> without the advanced features of Sqlite,then use Berkely and take
> advantage of its simplicity and faster execution.

BDB does support transactions...

http://www.oracle.com/technology/documentation/berkeley-db/xml/gsg_xml_txn/cxx/usingtxns.html

In fact,BDB acts as the (a) transactional layer in MySQL (the
alternative is InnoDB).

Of course,other advantages of SQLite still apply. A key-value
metaphor can only be pushed so far.


Guess what make BDB run faster - no transactions. If you are not using
transactions BDB mght be for you. Personally as a long time user of BDB
in various ways I have a very poor opinion of it. It has a very
different application to Sqlite.

thanks

I'm not sure of the real reason.
It's true that the speed of inserting with transaction is very fast. But my
project is using SQLite mainly in selecting something. I don't know how to
use transaction in the situation. May some friends give me some codes on
that?

My application needs some funcs that serve as the database API,so I must
encapsulate SQLite func in my func. It includes the basic routine like this:
1.) sqlite3_open();
2.) sqlite3_get_table(),sqlite3_exec()+callback,
sqlite3_prepare_v2()+sqlite3_step()+sqlite3_column*+sqlite3_finalize()
3.) sqlite3_close();

I found that every routine running in the object board (Atmel 9260 (200MHz)
+ 128M RAM + ...) spend 20 millisecond. It's too slow...

And I have tried some PRAGMA settings for example: cache_size/page_size,but
the effect is very few.


As someone else mentioned,you probably don't want to open/close the
database every time you do a single SELECT. In the example you're
doing 100,000 iterations of open/exec/close,which will give pretty
terrible performance.

Instead,try creating an initialization call,which opens the
database,prepare()s your SELECT stmt (with a '?' placeholder for the
ID in this example),and returns a handle to the caller. The caller
can then loop 100,000 times calling getdata() with this handle.
getdata() can just do sqlite3_bind_XXX() followed by sqlite3_step()
(and sqlite3_reset()),which will be _much_ faster than
sqlite3_exec().

--
Matthew L. Creech


On Tue,Apr 28,2009 at 3:25 AM,liubin liu <7101...@sina.com> wrote:
>
> Thanks!
>
> It sounds pretty good. But I don't understand it exactly. Could you write
> down the sample codes?
>

Please see the documentation here:

http://sqlite.org/c3ref/funclist.html

You'll have something along the lines of (just a sketch,obviously):

typedef struct {
sqlite3 *db;
sqlite3_stmt *stmt;
} my_handle_t;

my_handle_t my_init() {
sqlite3_open();
sqlite3_prepare();
return handle;
}

void my_exec(my_handle_t handle,int id) {
sqlite3_bind_int();
/* Put in a loop or whatever: */
sqlite3_step();
/* After you've gotten all the result rows: */
sqlite3_reset();
}


Your caller would then call my_init() once to get a handle,then call
my_exec() a bunch of times using that handle. The way your example is
doing it,_every_ time through the loop it does an exec(),which
re-compiles the same SQL code (which is not a fast operation).

There are plenty of other examples floating around on this mailing
list,I'm sure - just do some digging.

--
Matthew L. Creech

If he wants go faster he can use BEGIN TRANSACTION (EXCLUSIVE or other) before the 100,000 iterations. In this way sqlite doesn't lock the database file 100.000 times,but only 1 I don't know if he can use this trick,but maybe he can group 10,50 or 100 select into a TRANSACTION. Giacomo Mussati

相关文章

SQLite架构简单,又有Json计算能力,有时会承担Json文件/RES...
使用Python操作内置数据库SQLite以及MySQL数据库。
破解微信数据库密码,用python导出微信聊天记录
(Unity)SQLite 是一个软件库,实现了自给自足的、无服务器...
安卓开发,利用SQLite实现登陆注册功能