C++网络编程(四)轻量级数据库sqlite3
前言
这里貌似与网络编程的关联度已经不大,属于可选的分支发展方向,鉴于经常遇见需要数据库的情况,这里对轻量级的数据库sqlite3在C++中的相关调用做简单整理。
sqlite3获取
Windows
首先下载sqlite3的c语言源码
https://www.sqlite.org/2022/sqlite-amalgamation-3400000.zip
windows下的dll文件:
https://www.sqlite.org/2022/sqlite-dll-win64-x64-3400000.zip
MinGW编译器:
将sqlite3.h头文件复制到~\MinGW\x86_64-w64-mingw32\include\
目录下。
将sqlite3.dll库文件复制到~\MinGW\lib\
目录下。
编译时加上-lsqlite3
参数即可。
类似于openGL等库的调用
Linux
wget https://www.sqlite.org/2022/sqlite-autoconf-3400100.tar.gz
tar -zvxf sqlite-autoconf-3400100.tar.gz
cd sqlite-autoconf-3400100
./configure
make
sudo make install
流程如上,获取安装包后解压,进入到解压后的文件开始配置。
make编译过程稍稍较慢,需要耐心等待。
安装成功后可以直接使用sqlite3.h头文件。但是程序的编译过程也要加上-lsqlite3链接参数
C++提供的API
/*创建或者打开数据库
filename: 要打开的数据库文件,不存在则创建
ppdb: 保存打开数据库实例的指针*/
int sqlite3_open(const char *filename, sqlite3 **ppdb);
/*关闭数据库
pdb: 数据库指针*/
int sqlite3_close(sqlite3 *pdb);
int sqlite3_exec(//执行sql命令
sqlite3 *db,
const char *sql,
int(*callback)(void *arg,int num,char **columnvalue,char **columnname),
void *data,
char **errmsg);
/*参数
db: 数据库句柄
sql: sql语句
callback: 回调函数,在执行查询操作的时候调用,一行数据调用一次
data: 传递给回调函数第一个参数的指针,可以为NULL
errmsg: 错误信息
回调函数参数:
arg: sqlite3_exec中的data参数
num: 数据的列数
columnvalue: 数据的内容,字符串形式
columnname: 列名
*/
以上函数执行成功,返回SQLITE_OK(即 0),执行出错,返回错误信息;
SQL语句此处不做整理了
运行优化
性能优化一般是在插入数据的时候,如果是要插入大量数据,而只调用sqlite3_exec函数,效率极其低下。采用-O3编译优化。
显式开启事务
在默认情况下,每一次调用sqlite3_exec函数都要隐式的开启和关闭一次事务,这是极其耗费时间的,一种优化方法是将所有的sql语句放到一个事务中去执行,即显示开启事务,在开始前执行sql命令begin
,结束后执行sql命令commit
。
//默认
string sql1("insert into student values(");
string sql2, sql3(",'aa',20);"), sql4;
for (int i = 0; i < 10000; i++)
{
sql2 = to_string(i);
sql4 = sql1 + sql2 + sql3;
res = sqlite3_exec(db, sql4.c_str(), 0, 0, &zErrMsg);
if (res != SQLITE_OK)
{
cout << zErrMsg << endl;
break;
}
}
//显式开启事务
string sql1("insert into student values(");
string sql2, sql3(",'aa',20);"), sql4;
sqlite3_exec(db, "begin;", 0, 0, 0); //显示开启事务
for (int i = 0; i < 10000; i++)
{
sql2 = to_string(i);
sql4 = sql1 + sql2 + sql3;
res = sqlite3_exec(db, sql4.c_str(), 0, 0, &zErrMsg);
if (res != SQLITE_OK)
{
cout << zErrMsg << endl;
break;
}
}
sqlite3_exec(db, "commit;", 0, 0, 0); //提交事务
事实上,后者比前者速度提高了约1000倍。
执行准备
在插入大量数据时,sql语句存在很多重复操作,这样的语句每一次都会耗费时间用于解析sql语句。我们可以预先将sql语句编译好再执行,每次只改变要插入的数据,这样可以省去较多的解析时间,提高效率。
- 调用sqlite3_prepare_v2函数对sql语句进行预编译
int sqlite3_prepare_v2(
sqlite3 *db,
const char *zsql,
int nByte,
sqlite3_stmt **ppstmt,
const char **pzTail);
/*
db: 数据库句柄
zsql: SQL语句
nByte: SQL语句的最大长度,字节单位,如果为-1则读取到第一个零终止符,如果为正数则读取字节数,如果为0则不生成语句
ppstmt: 输出编译好的语句句柄
pzTail: 输出指向SQL语句未使用部分的指针
*/
- 调用sqlite3_bind函数来绑定数据。
int sqlite3_bind_blob(sqlite3_stmt *,int ,const void *,int ,void(*)(void *));//blob二进制大对象
int sqlite3_bind_double(sqlite3_stmt *,int,double);//double浮点数据
int sqlite3_bind_int(sqlite3_stmt *,int,int);//int整数
int sqlite3_bind_null(sqlite3_stmt *,int);//空值
int sqlite3_bind_text(sqlite3_stmt *,int,const char *,int ,void(*)(void *));//text字符串
/*
应用时,第一个参数用于指明SQL语句
第二个参数用于指明SQL语句中第几个参数,从1开始
第三个参数是值
text第四个参数是字符串长度,第五个参数是字符串析构函数,可为NULL
*/
- 调用sqlite3_step来完成一行数据插入
sqlite3_step(sqlite3_stmt *);
在调用sqlite3_step之后记得调用sqlite3_reset用于清除sqlite3_stmt中绑定的数据,因为当下一次调用绑定的时候无法覆盖之前的内容。
sqlite3_reset一般用于绑定数据之前。
//以?代替具体数值
char sql[] = "insert into student values(?,?,?);";
sqlite3_stmt *stmt;
sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, 0);
sqlite3_exec(db, "begin;", 0, 0, 0);
for (int i = 0; i < 1000000; i++)
{
sqlite3_reset(stmt);
sqlite3_bind_int(stmt, 1, i);
sqlite3_bind_text(stmt, 2, "aa", 2, NULL);
sqlite3_bind_int(stmt, 3, 20);
sqlite3_step(stmt);
}
sqlite3_exec(db, "commit;", 0, 0, 0);