SQLite 3 优化批量数据存储操作---事务transaction机制

0、事务操作

事务的目的是为了保证数据的一致性和完整性。

事务(Transaction)具有以下四个标准属性,通常根据首字母缩写为 ACID:

  • 原子性(Atomicity):确保工作单位内的所有操作都成功完成,否则,事务会在出现故障时终止,之前的操作也会回滚到以前的状态。
  • 一致性(Consistency):确保数据库在成功提交的事务上正确地改变状态。
  • 隔离性(Isolation):使事务操作相互独立和透明。
  • 持久性(Durability):确保已提交事务的结果或效果在系统发生故障的情况下仍然存在。

开启事务操作相比不开启事务操作的优势:

①、性能提升:可以避免频繁地访问磁盘,因为磁盘I/O操作比内存操作慢得多。将数据先写入缓存可以批量处理磁盘写入,减少磁盘I/O次数,从而提高效率。

②、事务管理:缓存机制有助于实现事务的回滚功能。如果事务执行过程中出现错误,可以轻松地从缓存中清除未提交的更改,而不必去磁盘上进行复杂的恢复操作。

③、持久化:当事务被提交时,缓存中的数据会被写入到数据库文件中,这个过程称为"刷盘"。这样做的目的是为了确保数据的持久性,即一旦事务提交,数据更改就成为永久的,即使系统崩溃,数据也不会丢失。

1、事务开始

事务可以是延迟的、立即的或独占的,默认事务行为是延迟的。

DEFERRED:直到第一次访问数据库时事务才真正开始。

IMMEDIATE:使数据库连接立即开始新的写入,而不等待写入语句。如果另一个写事务已经在另一个数据库连接上处于活动状态, BEGIN IMMEDIATE 可能会失败并返回SQLITE_BUSY

EXCLUSIVE:与 IMMEDIATE 类似,但是更加强制,等待所有读取和写入操作完成。

事务类型在BEGIN命令中指定:

BEGIN [ DEFERRED | IMMEDIATE | EXCLUSIVE ] TRANSACTION;

cpp 复制代码
/**
  * @brief  数据库事务开始
  * @param  db:数据库文件描述符
  * @retval 成功返回0, 失败返回-1
  */
int database_transaction_begin(sqlite3 *db)
{
    char* err_msg = NULL;
    char* beginTransactionCmd = "BEGIN TRANSACTION;";
    int beginRet = sqlite3_exec(db, beginTransactionCmd, NULL, NULL, &err_msg);
    if (beginRet != SQLITE_OK) 
    {
        int sqliteErrCode = sqlite3_errcode(db);
        DBUG_SHOW("Begin transaction errorcode:%d\terror: %s\n", sqliteErrCode, err_msg);
        sqlite3_free(err_msg);
        return -1;
    }
    DBUG_SHOW("Transaction started successfully.\n");
    return 0;
}

2、事务提交

事务的目的是为了保证数据的一致性和完整性,而 COMMIT 命令则是将事务中的修改写入磁盘,以保证数据的持久性。

END TRANSACTION 是 COMMIT 的别名。

cpp 复制代码
/**
  * @brief  数据库事务提交
  * @param  db:数据库文件描述符
  * @retval 成功返回0, 失败返回-1
  */
int database_transaction_commit(sqlite3* db)
{
    char* err_msg = NULL;
    char commitCmd[] = "COMMIT;";
    int commitRet = sqlite3_exec(db, commitCmd, NULL, NULL, &err_msg);
    if (commitRet != SQLITE_OK) 
    {
        int sqliteErrCode = sqlite3_errcode(db);
        DBUG_SHOW("Commit transaction errorcode:%d\terror: %s\n", sqliteErrCode, err_msg);
        sqlite3_free(err_msg);
        return -1;
    }
    DBUG_SHOW("Transaction committed successfully.\n");
    
    return 0;
}

3、事务回滚

事务是一组数据库操作,它们被视为一个单独的工作单元,要么全部成功执行,要么全部回滚。

在一个事务中,可以执行多个SQL语句,如:插入、删除、更新数据操作,但如果在执行过程中出现错误时,可以使用ROLLBACK回滚操作,撤销所有的更改,恢复数据库到上一次提交数据操作的状态。

cpp 复制代码
/**
  * @brief  数据库事务回滚
  * @param  db:数据库文件描述符
  * @retval 成功返回0, 失败返回-1
  */
int database_transaction_rollback(sqlite3* db)
{
    char* err_msg = NULL;
    char rollbackCmd[] = "ROLLBACK;";
    int rollbackRet = sqlite3_exec(db, rollbackCmd, NULL, NULL, &err_msg);
    if (rollbackRet != SQLITE_OK) 
    {
        int sqliteErrCode = sqlite3_errcode(db);
        DBUG_SHOW("Rollback transaction errorcode:%d\terror: %s\n", sqliteErrCode, err_msg);
        sqlite3_free(err_msg);
        return -1;
    }
    DBUG_SHOW("Transaction rolled back.\n");
    return 0;
}

4、读写事务

读取事务仅用于读取,写事务允许读取和写入。

读事务由 SELECT 语句启动,写事务由 CREATE、DELETE、DROP、INSERT 或 UPDATE 等语句(统称为"写语句")启动。

开启了事务操作后,事务的写入并非立即写入到数据库存储文件中,而是被保存在缓存区中,只有进行了事务提交,缓冲区的数据才被写入到事务。

SQLite 支持来自不同数据库连接的多个同时读取事务,可能在不同的线程或进程中,但只支持一个同时写入事务。

cpp 复制代码
/**
  * @brief  写入数据到数据库中
  * @param  db:数据库文件描述符
  * @param  data_cmd:写入数据命令
  * @retval 写入成功返回0,失败-1  
  */
int write_data_to_database(sqlite3* db, char* data_cmd)
{
    //"create table if not exists camera(time_stamp integer primary key, direction text, action text, x integer, y integer, z integer, vx integer, vy integer, vz integer, time integer);"
    //data_cmd内容格式参考:"insert into table values(123456789, 'U', 'T', 100, 200, 50, 10, 5, 30, 25);"
    int ret = 0;
    char* err_msg = NULL;
    ret = sqlite3_exec(db, data_cmd, NULL, NULL, &err_msg);
    if (ret)
    {
        int sqliteErrCode = sqlite3_errcode(db);
        DBUG_SHOW("insert value to table err: code=%d, msg='%s'!\n", sqliteErrCode, err_msg);
        sqlite3_free(err_msg);
        return -1;
    }
    DBUG_SHOW("insert value to table successfully!\n");


    return 0;
}
相关推荐
a努力。6 小时前
国家电网Java面试被问:混沌工程在分布式系统中的应用
java·开发语言·数据库·git·mysql·面试·职场和发展
li_wen016 小时前
文件系统(八):Linux JFFS2文件系统工作原理、优势与局限
大数据·linux·数据库·文件系统·jffs2
wWYy.7 小时前
详解redis(16):缓存击穿
数据库·redis·缓存
JosieBook7 小时前
【数据库】Oracle迁移至KingbaseES:挑战、策略与最佳实践
数据库·oracle
imX2G8 小时前
爆破小游戏2.0
c++
一休哥助手8 小时前
时序数据库选型指南:从核心考量到四大主流数据库深度解析
数据库·时序数据库
Cx330❀8 小时前
【优选算法必刷100题】第41-42题(模拟):Z 字形变换,外观数列
c++·算法
Cx330❀8 小时前
【优选算法必刷100题】第038题(位运算):消失的两个数字
开发语言·c++·算法·leetcode·面试
燃于AC之乐8 小时前
我的算法修炼之路--5——专破“思维陷阱”,那些让你拍案叫绝的非常规秒解
c++·算法·贪心算法·bfs·二分答案·扩展域并查集·动态规划(最长上升子序列)
艾莉丝努力练剑8 小时前
【优选算法必刷100题】第021~22题(二分查找算法):山脉数组的峰顶索引、寻找峰值
数据结构·c++·算法·leetcode·stl