非常好的一篇博客,详见:https://blog.csdn.net/lijinqi1987/article/details/51852721
首先,建立数据库
rc = sqlite3_exec(db, "create table if not exists testinfo (id integer primary key, age integer, height text, weight text)", NULL, NULL, &zErr);
插入篇:
1、sqlite3_exec()
通常,我们使用sqlite3_exec()函数来处理数据的插入操作,该函数直接调用sql语句对数据进行插入,所以使用起来很方便,插入100w条数据
for( i = 0; i < 1000000; i++)
{
snprintf(sqlcmd, sizeof(sqlcmd), "insert into testinfo values(%d, %d, '%d', '%s')", i, i*2, i*10, buf);
sqlite3_exec(db, sqlcmd, NULL, NULL, &zErr);
}
该函数每调用一次,都会隐式地开启一次事务,对于大批量的操作,如果不加修饰地直接多次调用该函数,会导致插入效率极低
执行5次平均耗时:1721.272秒,极其慢
2、显式调用事务
利用事务的互斥性,如果在批量的插入操作前显式地开启一次事务,在插入操作结束后,提交事务,那么所有的操作将只执行一次事务,大大地提高IO效率
sqlite3_exec(db, "BEGIN;", 0, 0, NULL);
for( i = 0; i < 1000000; i++)
{
snprintf(sqlcmd, sizeof(sqlcmd), "insert into testinfo values(%d, %d, '%d', '%s')", i, i*2, i*10, buf);
sqlite3_exec(db, sqlcmd, NULL, NULL, &zErr);
}
sqlite3_exec(db, "COMMIT;", 0, 0, NULL);
执行5次平均耗时:15.559秒 有了很大改善
3、执行准备
sqlite3_exec()函数直接调用sql语句字符串,每执行一次该函数,都要进行一此“词法分析”和“语法分析”
为此sqlite引入了“执行准备”这一功能,即事先把sql语句编译成系统能够理解的语言,然后一步一步执行,这样大大地提高了效率,同样是插入100w条数据:
sqlite3_exec(db, "BEGIN;", 0, 0, 0);
const char* sql = "insert into testinfo values(?,?,?,?)";
sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, 0);
for(i = 0; i < 1000000; i++)
{
sprintf(tmpstr, "%d", i*10);
sqlite3_reset(stmt);
sqlite3_bind_int(stmt, 1, i);
sqlite3_bind_int(stmt, 2, i*2);
sqlite3_bind_text(stmt, 3, tmpstr, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 4, buf, -1, SQLITE_STATIC);
sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
sqlite3_exec(db, "COMMIT;", 0, 0, 0);
执行5次平均耗时5.298秒
4、关闭写同步
如果有定期备份的机制,而且少量数据丢失可接受,可将同步方式设置为OFF,默认为FULL。
sqlite3_exec(db, "PRAGMA synchronous = OFF; ", 0,0,0);
full写入速度最慢,但保证数据是安全的,不受断电、系统崩溃等影响,而off可以加速数据库的一些操作,但如果系统崩溃或断电,则数据库可能会损毁,所以不是很推荐这种做法
执行5次平均耗时5.468秒(不是很理想)
5、使用WAL模式
WAL:Write Ahead Logging,他是数据库中用于实现原子事务的一种机制,从3.7.0版本后引入
WAL模式主要有两个优点:
1、读写可以完全并发进行,不会互相阻塞(但是写之间仍然不能并发)
2、WAL在大多情况下,拥有更好的性能(因为无需每次写入时都要写两个文件)
Rollback journal机制原理:在修改数据库文件中的数据前,先将修改所在分页中的数据备份在另一个地方,然后再将修改写入到数据中;如果事务失败,则将备份数据拷贝回来,撤销修改;如果事务成功,则删除备份,提交修改。
WAL机制原理:修改并不直接写入到数据库文件中,而是写入到另外一个称为WAL的文件中,如果事务失败,wal中的文件会被忽略,撤销修改;如果事务成功,它将在随后的某个时间被写回到数据库文件中,提交修改。
性能差异主要源于每次事务提交,wal只需要将更新的日志写入磁盘,而delete模式首先要将原始数据拷贝到日志文件中,并进行fsync,然后将修改页写入磁盘,同时也需要fsync,确保数据落盘,并且还要清除日志文件。因此写事务在WAL模式下,只需要一次fsync,并且是顺序写,而在delete模式下需要至少两次fsync(日志,数据),并且更新的数据离散分布在多个page中,因此可能需要多个fsync。
WAL使用共享内存技术,因此所有读写进程必须在同一个机器上
开启WAL模式的方法:
sqlite3_exec(db, "PRAGMA journal_mode=WAL; ", 0,0,0);
在前面的基础上,使用WAL模式后执行5次操作平均耗时4.324秒
6、内存数据库
另外,如果数据无需长时间保存,可以使用sqlite的内存数据库替代文件数据库
开启sqlite内存数据库的方式:
sqlite3* db = NULL;
rc = sqlite3_open(":memory", &db);
执行5次平均耗时:4.052秒
但是内存数据库存在如下缺点:
1.断电或者程序崩溃后数据库就会消失
2.在内存中的数据库不能被别的进程访问
3.不支持像在硬盘上的读写互斥处理,需要自己加锁
查询篇:
1、sqlite3_get_table()
通常使用sqlite3_get_table()函数来执行查询
for( i = 0; i < 300000; i++)
{
snprintf(sqlcmd, sizeof(sqlcmd), "select * from testinfo where id = %d", i);
sqlite3_get_table(db, sqlcmd, &pRecord, &rows, &cols, &zErr);
}
执行5次平均耗时229.438秒
2、显式开启事务
和sqlite3_exec()一样,大批量的调用该函数会导致效率极其低下,所以还是使用事务的方式来提高效率
sqlite3_exec(db, "BEGIN", 0, 0, NULL);
for( i = 0; i < 300000; i++)
{
snprintf(sqlcmd, sizeof(sqlcmd), "select * from testinfo where id = %d", i);
sqlite3_get_table(db, sqlcmd, &pRecord, &rows, &cols, &zErr);
}
sqlite3_exec(db, "COMMIT", 0, 0, NULL);
执行5次平均耗时23.177秒
3、使用执行准备:
sqlite3_exec(db, "BEGIN", 0, 0, NULL);
char *sql = "select * from testinfo where id = ?";
sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, 0);
for(i = 0; i < 1000000; i++)
{
sqlite3_reset(stmt);
sqlite3_bind_int(stmt, 1, i);
rc = sqlite3_step(stmt);
while(rc == SQLITE_ROW)
{
n1 = sqlite3_column_int(stmt, 0);
n2 = sqlite3_column_int(stmt, 1);
ch1 = sqlite3_column_text(stmt, 2);
ch2 = sqlite3_column_text(stmt, 3);
rc = rc = sqlite3_step(stmt);
}
}
sqlite3_finalize(stmt);
sqlite3_exec(db, "COMMIT", 0, 0, NULL);
执行5次平均耗时3.544秒
4、查询内存数据库
执行5次平均耗时3.235秒
附上完整代码:
#include
#include
#include
#include
#include"sqlite3.h"
int main(int argc, char* argv[])
{
int rc = 0;
int i = 0;
int j = 0;
int rows, cols;
int n1, n2;
sqlite3* db = NULL;
sqlite3* dbMem = NULL;
char* zErr = NULL;
char **pRecord = NULL;
sqlite3_stmt *stmt = NULL;
char *buf = "CJcEEAAYASCgExEFAaATEqATEy";
struct timeval tmv1;
struct timeval tmv2;
float tmcost;
char tmpstr[32] = {0};
char sqlcmd[2048] = {0};
const char* ch1;
const char* ch2;
rc = sqlite3_open("kaf.db", &db);
if (rc)
{
fprintf(stderr, "Can't open database:%s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
rc = sqlite3_exec(db, "create table if not exists testinfo (id integer primary key, age integer, height text, weight text)", NULL, NULL, &zErr);
if (SQLITE_OK != rc) {
fprintf(stderr, "create sql failed:%s\n", zErr);
sqlite3_close(db);
exit(1);
}
//sqlite3_exec()逐条插入
if (atoi(argv[1]) == 1)
{
gettimeofday(&tmv1, NULL);
for( i = 0; i < 1000000; i++)
{
snprintf(sqlcmd, sizeof(sqlcmd), "insert into testinfo values(%d, %d, '%d', '%s')", i, i*2, i*10, buf);
sqlite3_exec(db, sqlcmd, NULL, NULL, &zErr);
}
gettimeofday(&tmv2, NULL);
tmcost = (float)(tmv2.tv_sec*1000*1000+tmv2.tv_usec - tmv1.tv_sec*1000*1000+tmv1.tv_usec)/1000000;
printf("the 1 operation costs %f\n", tmcost);
}
//开启事务
else if(atoi(argv[1]) == 2)
{
gettimeofday(&tmv1, NULL);
sqlite3_exec(db, "BEGIN;", 0, 0, NULL);
for( i = 0; i < 1000000; i++)
{
snprintf(sqlcmd, sizeof(sqlcmd), "insert into testinfo values(%d, %d, '%d', '%s')", i, i*2, i*10, buf);
sqlite3_exec(db, sqlcmd, NULL, NULL, &zErr);
}
sqlite3_exec(db, "COMMIT;", 0, 0, NULL);
gettimeofday(&tmv2, NULL);
tmcost = (float)(tmv2.tv_sec*1000*1000+tmv2.tv_usec - tmv1.tv_sec*1000*1000+tmv1.tv_usec)/1000000;
printf("the 2 operation costs %f\n", tmcost);
}
//执行准备
else if(atoi(argv[1]) == 3)
{
gettimeofday(&tmv1, NULL);
sqlite3_exec(db, "BEGIN;", 0, 0, 0);
const char* sql = "insert into testinfo values(?,?,?,?)";
sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, 0);
for(i = 0; i < 1000000; i++)
{
sprintf(tmpstr, "%d", i*10);
sqlite3_reset(stmt);
sqlite3_bind_int(stmt, 1, i);
sqlite3_bind_int(stmt, 2, i*2);
sqlite3_bind_text(stmt, 3, tmpstr, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 4, buf, -1, SQLITE_STATIC);
sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
sqlite3_exec(db, "COMMIT;", 0, 0, 0);
gettimeofday(&tmv2, NULL);
tmcost = (float)(tmv2.tv_sec*1000*1000+tmv2.tv_usec - tmv1.tv_sec*1000*1000+tmv1.tv_usec)/1000000;
printf("the 4 operation costs %f\n", tmcost);
}
//关闭写同步
else if(atoi(argv[1]) == 4)
{
sqlite3_exec(db, "PRAGMA synchronous = OFF; ", 0,0,0);
gettimeofday(&tmv1, NULL);
sqlite3_exec(db, "BEGIN;", 0, 0, 0);
const char* sql = "insert into testinfo values(?,?,?,?)";
sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, 0);
for(i = 0; i < 1000000; i++)
{
sprintf(tmpstr, "%d", i*10);
sqlite3_reset(stmt);
sqlite3_bind_int(stmt, 1, i);
sqlite3_bind_int(stmt, 2, i*2);
sqlite3_bind_text(stmt, 3, tmpstr, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 4, buf, -1, SQLITE_STATIC);
sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
sqlite3_exec(db, "COMMIT;", 0, 0, 0);
gettimeofday(&tmv2, NULL);
tmcost = (float)(tmv2.tv_sec*1000*1000+tmv2.tv_usec - tmv1.tv_sec*1000*1000+tmv1.tv_usec)/1000000;
printf("the 5 operation costs %f\n", tmcost);
}
//使用WAL模式
else if(atoi(argv[1]) == 5)
{
sqlite3_exec(db, "PRAGMA journal_mode=WAL; ", 0,0,0);
gettimeofday(&tmv1, NULL);
sqlite3_exec(db, "BEGIN;", 0, 0, 0);
const char* sql = "insert into testinfo values(?,?,?,?)";
sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, 0);
for(i = 0; i < 1000000; i++)
{
sprintf(tmpstr, "%d", i*10);
sqlite3_reset(stmt);
sqlite3_bind_int(stmt, 1, i);
sqlite3_bind_int(stmt, 2, i*2);
sqlite3_bind_text(stmt, 3, tmpstr, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 4, buf, -1, SQLITE_STATIC);
sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
sqlite3_exec(db, "COMMIT;", 0, 0, 0);
gettimeofday(&tmv2, NULL);
tmcost = (float)(tmv2.tv_sec*1000*1000+tmv2.tv_usec - tmv1.tv_sec*1000*1000+tmv1.tv_usec)/1000000;
printf("the 5 operation costs %f\n", tmcost);
}
//内存数据库
else if(atoi(argv[1]) == 6)
{
rc = sqlite3_open(":memory:", &dbMem);
rc = sqlite3_exec(dbMem, "create table if not exists testinfo (id integer primary key, age integer, height text, weight text)", NULL, NULL, &zErr);
sqlite3_exec(dbMem, "PRAGMA synchronous = OFF; ", 0,0,0);
gettimeofday(&tmv1, NULL);
sqlite3_exec(dbMem, "BEGIN;", 0, 0, 0);
const char* sql = "insert into testinfo values(?,?,?,?)";
sqlite3_prepare_v2(dbMem, sql, strlen(sql), &stmt, 0);
for(i = 0; i < 1000000; i++)
{
sprintf(tmpstr, "%d", i*10);
sqlite3_reset(stmt);
sqlite3_bind_int(stmt, 1, i);
sqlite3_bind_int(stmt, 2, i*2);
sqlite3_bind_text(stmt, 3, tmpstr, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 4, buf, -1, SQLITE_STATIC);
sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
sqlite3_exec(dbMem, "COMMIT;", 0, 0, 0);
gettimeofday(&tmv2, NULL);
tmcost = (float)(tmv2.tv_sec*1000*1000+tmv2.tv_usec - tmv1.tv_sec*1000*1000+tmv1.tv_usec)/1000000;
printf("the 6 operation costs %f\n", tmcost);
}
//sqlite3_get_table()逐条查询
else if(atoi(argv[1]) == 7)
{
gettimeofday(&tmv1, NULL);
for( i = 0; i < 1000000; i++)
{
snprintf(sqlcmd, sizeof(sqlcmd), "select * from testinfo where id = %d", i);
sqlite3_get_table(db, sqlcmd, &pRecord, &rows, &cols, &zErr);
}
gettimeofday(&tmv2, NULL);
tmcost = (float)(tmv2.tv_sec*1000*1000+tmv2.tv_usec - tmv1.tv_sec*1000*1000+tmv1.tv_usec)/1000000;
printf("the 7 operation costs %f\n", tmcost);
}
//开启事务
else if(atoi(argv[1]) == 8)
{
gettimeofday(&tmv1, NULL);
sqlite3_exec(db, "BEGIN", 0, 0, NULL);
for( i = 0; i < 1000000; i++)
{
snprintf(sqlcmd, sizeof(sqlcmd), "select * from testinfo where id = %d", i);
sqlite3_get_table(db, sqlcmd, &pRecord, &rows, &cols, &zErr);
}
sqlite3_exec(db, "COMMIT", 0, 0, NULL);
gettimeofday(&tmv2, NULL);
tmcost = (float)(tmv2.tv_sec*1000*1000+tmv2.tv_usec - tmv1.tv_sec*1000*1000+tmv1.tv_usec)/1000000;
printf("the 8 operation costs %f\n", tmcost);
}
//执行准备
else if(atoi(argv[1]) == 9)
{
char *sql = "select * from testinfo where id = ?";
gettimeofday(&tmv1, NULL);
sqlite3_exec(db, "BEGIN", 0, 0, NULL);
sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, 0);
for(i = 0; i < 1000000; i++)
{
sqlite3_reset(stmt);
sqlite3_bind_int(stmt, 1, i);
rc = sqlite3_step(stmt);
while(rc == SQLITE_ROW)
{
n1 = sqlite3_column_int(stmt, 0);
n2 = sqlite3_column_int(stmt, 1);
ch1 = sqlite3_column_text(stmt, 2);
ch2 = sqlite3_column_text(stmt, 3);
rc = rc = sqlite3_step(stmt);
}
}
sqlite3_finalize(stmt);
sqlite3_exec(db, "COMMIT", 0, 0, NULL);
gettimeofday(&tmv2, NULL);
tmcost = (float)(tmv2.tv_sec*1000*1000+tmv2.tv_usec - tmv1.tv_sec*1000*1000+tmv1.tv_usec)/1000000;
printf("the 9 operation costs %f\n", tmcost);
}
//内存数据库
else if(atoi(argv[1]) == 10)
{
rc = sqlite3_open(":memory:", &dbMem);
rc = sqlite3_exec(dbMem, "create table if not exists testinfo (id integer primary key, age integer, height text, weight text)", NULL, NULL, &zErr);
sqlite3_exec(dbMem, "PRAGMA synchronous = OFF; ", 0,0,0);
gettimeofday(&tmv1, NULL);
sqlite3_exec(dbMem, "BEGIN;", 0, 0, 0);
const char* sql = "insert into testinfo values(?,?,?,?)";
sqlite3_prepare_v2(dbMem, sql, strlen(sql), &stmt, 0);
for(i = 0; i < 1000000; i++)
{
sprintf(tmpstr, "%d", i*10);
sqlite3_reset(stmt);
sqlite3_bind_int(stmt, 1, i);
sqlite3_bind_int(stmt, 2, i*2);
sqlite3_bind_text(stmt, 3, tmpstr, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 4, buf, -1, SQLITE_STATIC);
sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
sqlite3_exec(dbMem, "COMMIT;", 0, 0, 0);
gettimeofday(&tmv2, NULL);
tmcost = (float)(tmv2.tv_sec*1000*1000+tmv2.tv_usec - tmv1.tv_sec*1000*1000+tmv1.tv_usec)/1000000;
printf("the 6 operation costs %f\n", tmcost);
char *sql2 = "select * from testinfo where id = ?";
gettimeofday(&tmv1, NULL);
sqlite3_exec(dbMem, "BEGIN", 0, 0, NULL);
sqlite3_prepare_v2(dbMem, sql2, strlen(sql2), &stmt, 0);
for(i = 0; i < 1000000; i++)
{
sqlite3_reset(stmt);
sqlite3_bind_int(stmt, 1, i);
rc = sqlite3_step(stmt);
while(rc == SQLITE_ROW)
{
n1 = sqlite3_column_int(stmt, 0);
n2 = sqlite3_column_int(stmt, 1);
ch1 = sqlite3_column_text(stmt, 2);
ch2 = sqlite3_column_text(stmt, 3);
rc = rc = sqlite3_step(stmt);
}
}
sqlite3_finalize(stmt);
sqlite3_exec(dbMem, "COMMIT", 0, 0, NULL);
gettimeofday(&tmv2, NULL);
tmcost = (float)(tmv2.tv_sec*1000*1000+tmv2.tv_usec - tmv1.tv_sec*1000*1000+tmv1.tv_usec)/1000000;
printf("the 9 operation costs %f\n", tmcost);
}
sqlite3_close(db);
return 1;
}
编译方法:
将sqlite3.c sqlite3.h放在同级目录
gcc test.c sqlite3.c -o test -lpthread -ldl