1、使用事务
默认情况下,SQLite 每次插入都会启动一个事务并立即提交,这样会导致性能低下。在一个事务中批量插入数据可以显著提高性能。
#include <stdio.h>
#include <sqlite3.h>
int main() {
sqlite3 *db;
char *errMsg = NULL;
int rc;
// 打开数据库
rc = sqlite3_open("example.db", &db);
if (rc) {
fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db));
return 1;
}
printf("数据库打开成功。\n");
// 创建表
const char *createTableSQL =
"CREATE TABLE IF NOT EXISTS test_table ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name TEXT NOT NULL);";
rc = sqlite3_exec(db, createTableSQL, NULL, NULL, &errMsg);
if (rc != SQLITE_OK) {
fprintf(stderr, "创建表失败: %s\n", errMsg);
sqlite3_free(errMsg);
sqlite3_close(db);
return 1;
}
printf("表创建成功。\n");
// 开始事务
rc = sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, &errMsg);
if (rc != SQLITE_OK) {
fprintf(stderr, "无法开始事务: %s\n", errMsg);
sqlite3_free(errMsg);
sqlite3_close(db);
return 1;
}
printf("事务开始。\n");
// 插入多条记录
const char *insertSQL = "INSERT INTO test_table (name) VALUES (?);";
sqlite3_stmt *stmt;
rc = sqlite3_prepare_v2(db, insertSQL, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
fprintf(stderr, "SQL 预编译失败: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
for (int i = 0; i < 10; i++) {
char name[20];
sprintf(name, "Name%d", i + 1);
sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC);
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
fprintf(stderr, "插入数据失败: %s\n", sqlite3_errmsg(db));
sqlite3_finalize(stmt);
sqlite3_close(db);
return 1;
}
sqlite3_reset(stmt); // 重置语句以便重新绑定数据
}
sqlite3_finalize(stmt); // 释放语句资源
// 提交事务
rc = sqlite3_exec(db, "COMMIT;", NULL, NULL, &errMsg);
if (rc != SQLITE_OK) {
fprintf(stderr, "提交事务失败: %s\n", errMsg);
sqlite3_free(errMsg);
sqlite3_close(db);
return 1;
}
printf("事务提交成功。\n");
// 关闭数据库
sqlite3_close(db);
printf("数据库关闭成功。\n");
return 0;
}
2、使用预编译语句
sqlite3_prepare_v2()
和 sqlite3_bind_*()
可避免反复解析 SQL,提高性能。
#include <stdio.h>
#include <sqlite3.h>
int main() {
sqlite3 *db;
int rc;
// 打开数据库
rc = sqlite3_open("example.db", &db);
if (rc) {
fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db));
return 1;
}
printf("数据库打开成功。\n");
// 创建表
const char *createTableSQL =
"CREATE TABLE IF NOT EXISTS my_table ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"col1 INTEGER, "
"col2 TEXT);";
rc = sqlite3_exec(db, createTableSQL, NULL, NULL, NULL);
if (rc != SQLITE_OK) {
fprintf(stderr, "创建表失败: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
printf("表创建成功。\n");
// SQL 预编译
const char *sql = "INSERT INTO my_table (col1, col2) VALUES (?, ?);";
sqlite3_stmt *stmt;
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
fprintf(stderr, "SQL 预编译失败: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
printf("SQL 预编译成功。\n");
// 插入数据
for (int i = 0; i < 1000; i++) {
sqlite3_bind_int(stmt, 1, i); // 绑定整数值到第一个占位符
sqlite3_bind_text(stmt, 2, "value", -1, SQLITE_STATIC); // 绑定文本到第二个占位符
rc = sqlite3_step(stmt); // 执行插入
if (rc != SQLITE_DONE) {
fprintf(stderr, "插入数据失败: %s\n", sqlite3_errmsg(db));
sqlite3_finalize(stmt);
sqlite3_close(db);
return 1;
}
sqlite3_reset(stmt); // 重置语句以便重用
}
sqlite3_finalize(stmt); // 释放语句资源
printf("批量插入完成。\n");
// 关闭数据库
sqlite3_close(db);
printf("数据库关闭成功。\n");
return 0;
}
3、关闭自动提交
SQLite 默认会为每个语句自动启动和提交一个事务,这种行为被称为“自动提交模式”。使用 BEGIN TRANSACTION;
明确开启一个事务后,SQLite 会关闭自动提交,直到显式调用 COMMIT;
或 ROLLBACK;
。可以减少磁盘 I/O,提高插入性能。如果某些操作失败,可以使用 ROLLBACK;
回滚,避免部分数据插入的情况。
#include <stdio.h>
#include <sqlite3.h>
int main() {
sqlite3 *db;
char *err_msg = NULL;
// 打开数据库
if (sqlite3_open("test.db", &db) != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
return 1;
}
// 关闭自动提交(通过显式管理事务)
if (sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, &err_msg) != SQLITE_OK) {
fprintf(stderr, "Failed to begin transaction: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
// 插入数据
for (int i = 0; i < 10; i++) {
char sql[256];
snprintf(sql, sizeof(sql), "INSERT INTO test_table (id, name) VALUES (%d, 'Name%d');", i, i);
if (sqlite3_exec(db, sql, NULL, NULL, &err_msg) != SQLITE_OK) {
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
// 如果发生错误,可以回滚事务
sqlite3_exec(db, "ROLLBACK;", NULL, NULL, NULL);
sqlite3_close(db);
return 1;
}
}
// 提交事务
if (sqlite3_exec(db, "COMMIT;", NULL, NULL, &err_msg) != SQLITE_OK) {
fprintf(stderr, "Failed to commit transaction: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
printf("Data inserted successfully.\n");
// 关闭数据库
sqlite3_close(db);
return 0;
}
4、使用内存模式
使用 PRAGMA journal_mode = MEMORY
将日志存储在内存中,而不是文件中。对于临时表,甚至可以使用内存数据库:sqlite3_open(":memory:", &db);
#include <stdio.h>
#include <sqlite3.h>
int main() {
sqlite3 *db;
char *errMsg = NULL;
// 打开数据库
if (sqlite3_open("test.db", &db) != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
return 1;
}
// 设置 journal_mode 为 MEMORY 模式
if (sqlite3_exec(db, "PRAGMA journal_mode = MEMORY;", NULL, NULL, &errMsg) != SQLITE_OK) {
fprintf(stderr, "Failed to set journal_mode: %s\n", errMsg);
sqlite3_free(errMsg);
sqlite3_close(db);
return 1;
} else {
printf("Journal mode set to MEMORY successfully.\n");
}
// 创建一个表(如果尚未存在)
const char *create_table_sql =
"CREATE TABLE IF NOT EXISTS test_table (id INTEGER PRIMARY KEY, name TEXT);";
if (sqlite3_exec(db, create_table_sql, NULL, NULL, &errMsg) != SQLITE_OK) {
fprintf(stderr, "Failed to create table: %s\n", errMsg);
sqlite3_free(errMsg);
sqlite3_close(db);
return 1;
}
// 插入数据
const char *insert_sql = "INSERT INTO test_table (id, name) VALUES (1, 'Alice');";
if (sqlite3_exec(db, insert_sql, NULL, NULL, &errMsg) != SQLITE_OK) {
fprintf(stderr, "Failed to insert data: %s\n", errMsg);
sqlite3_free(errMsg);
sqlite3_close(db);
return 1;
} else {
printf("Data inserted successfully.\n");
}
// 关闭数据库
sqlite3_close(db);
return 0;
}
5、批量插入
将多个插入合并为一个 INSERT
语句。
#include <stdio.h>
#include <sqlite3.h>
int main() {
sqlite3 *db;
char *errMsg = NULL;
// 打开数据库
if (sqlite3_open("test.db", &db) != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
return 1;
}
// 创建测试表(如果不存在)
const char *create_table_sql =
"CREATE TABLE IF NOT EXISTS my_table ("
"col1 INTEGER, "
"col2 TEXT);";
if (sqlite3_exec(db, create_table_sql, NULL, NULL, &errMsg) != SQLITE_OK) {
fprintf(stderr, "Failed to create table: %s\n", errMsg);
sqlite3_free(errMsg);
sqlite3_close(db);
return 1;
}
// 合并多个插入语句为一个 INSERT
const char *sql =
"INSERT INTO my_table (col1, col2) VALUES "
"(1, 'val1'), "
"(2, 'val2'), "
"(3, 'val3');";
// 执行插入语句
if (sqlite3_exec(db, sql, NULL, NULL, &errMsg) != SQLITE_OK) {
fprintf(stderr, "Failed to insert data: %s\n", errMsg);
sqlite3_free(errMsg);
sqlite3_close(db);
return 1;
}
printf("Data inserted successfully.\n");
// 关闭数据库
sqlite3_close(db);
return 0;
}
6、优化索引
插入数据前删除索引,插入后再创建索引,这样可以避免在插入过程中频繁更新索引。使用 PRAGMA temp_store = MEMORY;
将临时数据存储到内存中。
#include <stdio.h>
#include <sqlite3.h>
void check_sqlite_error(int rc, sqlite3 *db) {
if (rc != SQLITE_OK) {
printf("SQLite error: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
}
int main() {
sqlite3 *db;
char *err_msg = NULL;
int rc;
// 打开数据库
rc = sqlite3_open("test.db", &db);
check_sqlite_error(rc, db);
// 创建表
const char *create_table_sql =
"CREATE TABLE IF NOT EXISTS data ("
"id INTEGER PRIMARY KEY, "
"name TEXT, "
"value INTEGER);";
rc = sqlite3_exec(db, create_table_sql, NULL, NULL, &err_msg);
check_sqlite_error(rc, db);
// 创建索引
const char *create_index_sql =
"CREATE INDEX IF NOT EXISTS idx_value ON data(value);";
rc = sqlite3_exec(db, create_index_sql, NULL, NULL, &err_msg);
check_sqlite_error(rc, db);
// 删除索引以加快插入速度
const char *drop_index_sql = "DROP INDEX IF EXISTS idx_value;";
rc = sqlite3_exec(db, drop_index_sql, NULL, NULL, &err_msg);
check_sqlite_error(rc, db);
// 开始事务
rc = sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, &err_msg);
check_sqlite_error(rc, db);
// 插入大量数据
sqlite3_stmt *stmt;
const char *insert_sql = "INSERT INTO data (name, value) VALUES (?, ?);";
rc = sqlite3_prepare_v2(db, insert_sql, -1, &stmt, NULL);
check_sqlite_error(rc, db);
for (int i = 0; i < 100000; i++) {
sqlite3_bind_text(stmt, 1, "test", -1, SQLITE_STATIC); // 绑定 name 字段
sqlite3_bind_int(stmt, 2, i); // 绑定 value 字段
sqlite3_step(stmt); // 执行插入
sqlite3_reset(stmt); // 重置语句以便重复使用
}
sqlite3_finalize(stmt); // 释放预编译语句资源
// 提交事务
rc = sqlite3_exec(db, "COMMIT;", NULL, NULL, &err_msg);
check_sqlite_error(rc, db);
// 重新创建索引
rc = sqlite3_exec(db, create_index_sql, NULL, NULL, &err_msg);
check_sqlite_error(rc, db);
// 关闭数据库
sqlite3_close(db);
printf("Data inserted and index optimized successfully.\n");
return 0;
}
7、减少写操作
如果写操作频繁且不需要立即持久化,可以使用 PRAGMA cache_size
来增加缓存。
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
void check_sqlite_error(int rc, sqlite3 *db) {
if (rc != SQLITE_OK) {
printf("SQLite error: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
exit(1);
}
}
int main() {
sqlite3 *db;
char *errMsg = NULL;
int rc;
// 打开数据库
rc = sqlite3_open("test.db", &db);
check_sqlite_error(rc, db);
// 设置缓存大小(单位:页面数,每页默认 4 KB)
// 10000 页面等于约 40 MB
rc = sqlite3_exec(db, "PRAGMA cache_size = 10000;", NULL, NULL, &errMsg);
if (rc != SQLITE_OK) {
printf("Error setting cache size: %s\n", errMsg);
sqlite3_free(errMsg);
} else {
printf("Cache size set to 10000 pages.\n");
}
// 创建表
const char *create_table_sql =
"CREATE TABLE IF NOT EXISTS data ("
"id INTEGER PRIMARY KEY, "
"value TEXT);";
rc = sqlite3_exec(db, create_table_sql, NULL, NULL, &errMsg);
check_sqlite_error(rc, db);
// 开始事务
rc = sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, &errMsg);
check_sqlite_error(rc, db);
// 插入大量数据
sqlite3_stmt *stmt;
const char *insert_sql = "INSERT INTO data (value) VALUES (?);";
rc = sqlite3_prepare_v2(db, insert_sql, -1, &stmt, NULL);
check_sqlite_error(rc, db);
for (int i = 0; i < 100000; i++) {
char value[32];
snprintf(value, sizeof(value), "Value %d", i);
sqlite3_bind_text(stmt, 1, value, -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_reset(stmt);
}
sqlite3_finalize(stmt);
// 提交事务
rc = sqlite3_exec(db, "COMMIT;", NULL, NULL, &errMsg);
check_sqlite_error(rc, db);
printf("Inserted 100000 records.\n");
// 关闭数据库
sqlite3_close(db);
return 0;
}