【sqlite】BEGIN TRANSACTION没用?NONONO!简简单单使插入效率提高100倍!!!

当你不使用 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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值