当你不使用 BEGIN TRANSACTION;而直接执行一条 INSERT 语句时,SQLite 会默认将这条语句包装在一个短暂的、隐式的事务中。这个过程可以看作:
-- 你只写了这一句
INSERT INTO table_name ...;
-- SQLite 在背后为你做了这些
BEGIN TRANSACTION;
INSERT INTO table_name ...;
COMMIT;
根据SQLite的特性,使用事务的版本应该比不使用事务的版本快几十到几百倍。这是因为:
-
隐式事务(单条语句,自动提交):每条INSERT都要打开/关闭日志文件、获取/释放锁、同步磁盘
-
显式事务:所有INSERT共享一次日志文件操作、一次锁获取、一次磁盘同步
实验如下,插入664579条数据,分别记录不带BEGIN TRANSACTION和带的情况下的耗时。
#include <iostream>
#include <vector>
#include <chrono>
#include <sqlite3.h>
#include <cmath>
class PrimeSieve {
private:
std::vector<bool> is_prime;
int limit;
public:
PrimeSieve(int n) : limit(n) {
is_prime.resize(n + 1, true);
is_prime[0] = is_prime[1] = false;
}
std::vector<int> find_primes() {
int sqrt_n = static_cast<int>(std::sqrt(limit));
for (int i = 2; i <= sqrt_n; ++i) {
if (is_prime[i]) {
for (int j = i * i; j <= limit; j += i) {
is_prime[j] = false;
}
}
}
std::vector<int> primes;
for (int i = 2; i <= limit; ++i) {
if (is_prime[i]) {
primes.push_back(i);
}
}
return primes;
}
};
class DatabaseManager {
private:
sqlite3* db;
public:
DatabaseManager() : db(nullptr) {}
bool open_database(const std::string& db_name) {
int rc = sqlite3_open(db_name.c_str(), &db);
if (rc) {
std::cerr << "无法打开数据库: " << sqlite3_errmsg(db) << std::endl;
return false;
}
return true;
}
void close_database() {
if (db) {
sqlite3_close(db);
db = nullptr;
}
}
bool create_table() {
const char* sql = "CREATE TABLE IF NOT EXISTS primes ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"prime_number INTEGER UNIQUE);";
char* err_msg = nullptr;
int rc = sqlite3_exec(db, sql, nullptr, nullptr, &err_msg);
if (rc != SQLITE_OK) {
std::cerr << "创建表失败: " << err_msg << std::endl;
sqlite3_free(err_msg);
return false;
}
// 清空表
sql = "DELETE FROM primes;";
rc = sqlite3_exec(db, sql, nullptr, nullptr, &err_msg);
if (rc != SQLITE_OK) {
std::cerr << "清空表失败: " << err_msg << std::endl;
sqlite3_free(err_msg);
return false;
}
return true;
}
// 不使用事务插入
bool insert_primes_without_transaction(const std::vector<int>& primes) {
auto start_time = std::chrono::high_resolution_clock::now();
char* err_msg = nullptr;
int rc = SQLITE_OK;
int success_count = 0;
// 准备插入语句
sqlite3_stmt* stmt;
const char* sql = "INSERT INTO primes (prime_number) VALUES (?);";
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
if (rc != SQLITE_OK) {
std::cerr << "准备语句失败: " << sqlite3_errmsg(db) << std::endl;
return false;
}
for (size_t i = 0; i < primes.size(); ++i) {
sqlite3_bind_int(stmt, 1, primes[i]);
rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE) {
success_count++;
} else {
// 忽略重复键错误等
}
sqlite3_reset(stmt);
}
sqlite3_finalize(stmt);
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
std::cout << "无事务插入: " << success_count << " 个质数,耗时: "
<< duration.count() << " 毫秒" << std::endl;
return true;
}
// 使用事务插入
bool insert_primes_with_transaction(const std::vector<int>& primes) {
auto start_time = std::chrono::high_resolution_clock::now();
char* err_msg = nullptr;
int rc = SQLITE_OK;
int success_count = 0;
// 开始事务
rc = sqlite3_exec(db, "BEGIN TRANSACTION;", nullptr, nullptr, &err_msg);
if (rc != SQLITE_OK) {
std::cerr << "开始事务失败: " << err_msg << std::endl;
sqlite3_free(err_msg);
return false;
}
// 准备插入语句
sqlite3_stmt* stmt;
const char* sql = "INSERT INTO primes (prime_number) VALUES (?);";
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr);
if (rc != SQLITE_OK) {
std::cerr << "准备语句失败: " << sqlite3_errmsg(db) << std::endl;
sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
return false;
}
for (size_t i = 0; i < primes.size(); ++i) {
sqlite3_bind_int(stmt, 1, primes[i]);
rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE) {
success_count++;
}
sqlite3_reset(stmt);
}
sqlite3_finalize(stmt);
// 提交事务
rc = sqlite3_exec(db, "COMMIT;", nullptr, nullptr, &err_msg);
if (rc != SQLITE_OK) {
std::cerr << "提交事务失败: " << err_msg << std::endl;
sqlite3_free(err_msg);
sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
return false;
}
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
std::cout << "有事务插入: " << success_count << " 个质数,耗时: "
<< duration.count() << " 毫秒" << std::endl;
return true;
}
int get_prime_count() {
sqlite3_stmt* stmt;
const char* sql = "SELECT COUNT(*) FROM primes;";
int count = 0;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) == SQLITE_OK) {
if (sqlite3_step(stmt) == SQLITE_ROW) {
count = sqlite3_column_int(stmt, 0);
}
sqlite3_finalize(stmt);
}
return count;
}
};
int main() {
const int LIMIT = 10000000; // 1000万
std::cout << "正在使用筛法查找 " << LIMIT << " 以内的质数..." << std::endl;
auto sieve_start = std::chrono::high_resolution_clock::now();
PrimeSieve sieve(LIMIT);
std::vector<int> primes = sieve.find_primes();
auto sieve_end = std::chrono::high_resolution_clock::now();
auto sieve_duration = std::chrono::duration_cast<std::chrono::milliseconds>(sieve_end - sieve_start);
std::cout << "找到 " << primes.size() << " 个质数,筛法耗时: "
<< sieve_duration.count() << " 毫秒" << std::endl;
// 测试不带事务的插入
std::cout << "\n=== 测试不带事务的插入 ===" << std::endl;
{
DatabaseManager db_mgr;
if (db_mgr.open_database("primes_no_transaction.db")) {
if (db_mgr.create_table()) {
db_mgr.insert_primes_without_transaction(primes);
std::cout << "数据库中实际存储的质数数量: " << db_mgr.get_prime_count() << std::endl;
}
db_mgr.close_database();
}
}
// 测试带事务的插入
std::cout << "\n=== 测试带事务的插入 ===" << std::endl;
{
DatabaseManager db_mgr;
if (db_mgr.open_database("primes_with_transaction.db")) {
if (db_mgr.create_table()) {
db_mgr.insert_primes_with_transaction(primes);
std::cout << "数据库中实际存储的质数数量: " << db_mgr.get_prime_count() << std::endl;
}
db_mgr.close_database();
}
}
return 0;
}
结果:最终两个.db都是16,940 KB,但是执行插入的耗时大不相同。
正在使用筛法查找 10000000 以内的质数…
找到 664579 个质数,筛法耗时: 6609 毫秒
.
=== 测试不带事务的插入 ===
无事务插入: 664579 个质数,耗时: 2970537 毫秒
数据库中实际存储的质数数量: 664579
.
=== 测试带事务的插入 ===
有事务插入: 664579 个质数,耗时: 6837 毫秒
数据库中实际存储的质数数量: 664579
1271

被折叠的 条评论
为什么被折叠?



