热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

文件数据库sqlite3C++线程安全和并发

转载:https:www.cnblogs.comfeng9exep10682567.html(线程安全和并发)转载:https:juejin.impost5b7d8522e51d

转载:https://www.cnblogs.com/feng9exe/p/10682567.html(线程安全和并发)

转载:https://juejin.im/post/5b7d8522e51d4538e5679f5e(WAL模式介绍)

转载:https://blog.csdn.net/vannachen/article/details/8277344(多线程/WAL/锁)

转载:https://blog.csdn.net/wql2rainbow/article/details/73650056(怎么开启wal机制)

转载:https://www.cnblogs.com/cchust/category/802864.html(sqlite 详细教程)

转载:http://www.cnblogs.com/stephen-liu74/archive/2012/01/19/2326309.html(sqlite备份机制)

转载:https://blog.csdn.net/northcan/article/details/7231115

一、SQLite 与线程

SQLite 是线程安全的。

线程模型

SQLite 支持如下三种线程模型

  • 单线程模型 这种模型下,所有互斥锁都被禁用,同一时间只能由一个线程访问。
  • 多线程模型 这种模型下,一个连接在同一时间内只有一个线程使用就是安全的。
  • 串行模型 开启所有锁,可以随意访问。

设置线程模型

SQLite 可以通过以下三种方式进行线程模型的设置,在实际应用中选择任一一项都可以。

  • 编译期设定 通过 SQLITE_THREADSAFE 这个参数进行编译器的设定来选择线程模型
  • 初始化设定 通过调用 sqlite3_config() 可以在 SQLite 初始化时进行设定
  • 运行时设定 通过调用 sqlite3_open_v2() 接口指定数据库连接的数据库模型

SQLite 并发和事务

事务

事务是 SQLite 的核心概念。对数据库的操作 (绝大部分) 会被打包成一个事务进行提交,需要注意的是,这里的打包成事务是自动开启的。举例而言,如果简单在一个 for 循环语句里向数据库中插入 10 条数据,意味着将自动生成 10 个事务。但需要注意的是事务是非常耗时的,一般而言, SQLite 每秒能够轻松支持 50000 条的数据插入,但是每秒仅能够支持几十个事务。一般而言,事务速度受限于磁盘速度。所以在批量插入时需要考虑禁用自动提交,将其用 BEGIN ... COMMIT 打包成一个事务。

回滚模式和 WAL

为了保证写入正确,SQLite 在使用事务进行数据库改写时将拷贝当前数据库文件的备份,即 rollback journal,当事务失败或者发生意外需要回滚时则将备份文件内容还原到数据库中,并同时删除该日志。这是默认的 DELETE 模式。

而后 SQLite 也引入了 WAL 模式,即 Write-Ahead Log。在这种模式下,所有的修改会写入一个单独的 WAL 文件内。这种模式下,写操作甚至可以不去操作数据库,这使得所有的读操作可以在 "写的同时" 直接对数据库文件进行操作,得到更好的并发性能。

锁和并发

SQLite 通过五种锁状态来完成事务。

  • UNLOCKED ,无锁状态。数据库文件没有被加锁。
  • SHARED 共享状态。数据库文件被加了共享锁。可以多线程执行读操作,但不能进行写操作。
  • RESERVED 保留状态。数据库文件被加保留锁。表示数据库将要进行写操作。
  • PENDING 未决状态。表示即将写入数据库,正在等待其他读线程释放 SHARED 锁。一旦某个线程持有 PENDING 锁,其他线程就不能获取 SHARED 锁。这样一来,只要等所有读线程完成,释放 SHARED 锁后,它就可以进入 EXCLUSIVE 状态了。
  • EXCLUSIVE 独占锁。表示它可以写入数据库了。进入这个状态后,其他任何线程都不能访问数据库文件。因此为了并发性,它的持有时间越短越好。

一个线程只有拥有低级别锁时才能够获得更高一级的锁

/*

** Lock the file with the lock specified by parameter eFileLock - one

** of the following:

**

**     (1) SHARED_LOCK

**     (2) RESERVED_LOCK

**     (3) PENDING_LOCK

**     (4) EXCLUSIVE_LOCK

**

** Sometimes when requesting one lock state, additional lock states

** are inserted in between.  The locking might fail on one of the later

** transitions leaving the lock state different from what it started but

** still short of its goal.  The following chart shows the allowed

** transitions and the inserted intermediate states:

**

**    UNLOCKED -> SHARED

**    SHARED -> RESERVED

**    SHARED -> (PENDING) -> EXCLUSIVE

**    RESERVED -> (PENDING) -> EXCLUSIVE

**    PENDING -> EXCLUSIVE

**

** This routine will only increase a lock.  Use the sqlite3OsUnlock()

** routine to lower a locking level.

*/

总结

综上所述,要保证数据库使用的安全,一般可以采用如下几种模式

  • SQLite 采用单线程模型,用专门的线程/队列(同时只能有一个任务执行访问) 进行访问
  • SQLite 采用多线程模型,每个线程都使用各自的数据库连接 (即 sqlite3 *)
  • SQLite 采用串行模型,所有线程都公用同一个数据库连接。

因为写操作的并发性并不好,当多线程进行访问时实际上仍旧需要互相等待,而读操作所需要的 SHARED 锁是可以共享的,所以为了保证最高的并发性,推荐

  • 使用多线程模式
  • 使用 WAL 模式
  • 单线程写,多线程读 (各线程都持有自己对应的数据库连接)
  • 避免长时间事务
  • 缓存 sqlite3_prepare 编译结果
  • 多语句通过 BEGIN 和 COMMIT 做显示事务,减少多次的自动事务消耗

二、WAL 机制的原理是:

     修改并不直接写入到数据库文件中,而是写入到另外一个称为 WAL 的文件中;如果事务失败,WAL 中的记录会被忽略,撤销修改;如果事务成功,它将在随后的某个时间被写回到数据库文件中,提交修改。 同步 WAL 文件和数据库文件的行为被称为 checkpoint(检查点),它由 SQLite 自动执行,默认是在 WAL 文件积累到 1000 页修改的时候;当然,在适当的时候,也可以手动执行 checkpoint,SQLite 提供了相关的接口。执行 checkpoint 之后,WAL 文件会被清空。 在读的时候,SQLite 将在 WAL 文件中搜索,找到最后一个写入点,记住它,并忽略在此之后的写入点(这保证了读写和读读可以并行执行);随后,它确定所要读的数据所在页是否在 WAL 文件中,如果在,则读 WAL 文件中的数据,如果不在,则直接读数据库文件中的数据。 在写的时候,SQLite 将之写入到 WAL 文件中即可,但是必须保证独占写入,因此写写之间不能并行执行。

2.1 wal工作原理

在引入WAL机制之前,SQLite使用rollbackjournal机制实现原子事务。

rollback journal机制的原理是:在修改数据库文件中的数据之前,先将修改所在分页中的数据备份在另外一个地方,然后才将修改写入到数据库文件中;如果事务失败,则将备份数据拷贝回来,撤销修改;如果事务成功,则删除备份数据,提交修改。

WAL机制的原理是:修改并不直接写入到数据库文件中,而是写入到另外一个称为WAL的文件中;如果事务失败,WAL中的记录会被忽略,撤销修改;如果事务成功,它将在随后的某个时间被写回到数据库文件中,提交修改。

2.2 wal优点:

1.      读和写可以完全地并发执行,不会互相阻塞(但是写之间仍然不能并发)。

2.      WAL在大多数情况下,拥有更好的性能(因为无需每次写入时都要写两个文件)。

3.      磁盘I/O行为更容易被预测。

2.3 wal缺点:

1.      访问数据库的所有程序必须在同一主机上,且支持共享内存技术。

2.      每个数据库现在对应3个文件:.db,-wal,-shm。

3.      当写入数据达到GB级的时候,数据库性能将下降。

4.      3.7.0之前的SQLite无法识别启用了WAL机制的数据库文件。

2.4 wal如何记录数据--checkpoint

使用WAL模式时,改写操作是附加(append)到WAL文件,而不改动数据库文件,因此数据库文件可以被同时读取。当执行checkpoint操作时,WAL文件的内容会被写回数据库文件。当WAL文件达到SQLITE_DEFAULT_WAL_AUTOCHECKPOINT(默认值是1000)页(默认大小是1KB)时,会自动使用当前COMMIT的线程来执行checkpoint操作。也可以关闭自动checkpoint,改为手动定期checkpoint。 

为了避免读取的数据不一致,查询时也需要读取WAL文件,并记录一个结尾标记(end mark)。这样的代价就是读取会变得稍慢,但是写入会变快很多。要提高查询性能的话,可以减小WAL文件的大小,但写入性能也会降低。 需要注意的是,低版本的SQLite不能读取高版本的SQLite生成的WAL文件,但是数据库文件是通用的。这种情况在用户进行iOS降级时可能会出现,可以把模式改成delete,再改回WAL来修复。 

要对一个数据库连接启用WAL模式,需要执行“PRAGMA journal_mode=WAL;”这条命令,它的默认值是“journal_mode=DELETE”。执行后会返回新的journal_mode字符串值,即成功时为"wal",失败时为之前的模式(例如"delete")。一旦启用WAL模式后,数据库会保持这个模式,这样下次打开数据库时仍然是 WAL模式。 要停止自动checkpoint,可以使用wal_autocheckpoint指令或sqlite3_wal_checkpoint()函数。手动执行 checkpoint可以使用wal_checkpoint指令或sqlite3_wal_checkpoint()函数。

三、开启WAL机制

 int DataSource::InitDataBaseToWal(std::string sPath, bool isWal)
    {
        char* zErrMsg;
 
        sqlite3* db = NULL;
 
        int rc = sqlite3_open_v2(sPath.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX, NULL);
 
        if (rc != SQLITE_OK)
        {
            Logger::LogD("DataSource::sqlite [%s] or [%s] open failed", sPath.c_str(), sqlite3_errmsg(db));
            Logger::LogO("DataSource::sqlite [%s] or [%s] open failed", sPath.c_str(), sqlite3_errmsg(db));
 
            sqlite3_close(db);
 
            return -1;
        }
 
        if(isWal == true)
        {
            rc = sqlite3_exec(db, "PRAGMA journal_mode=WAL;", NULL, 0, &zErrMsg);
 
            if (rc != SQLITE_OK)
            {
                sqlite3_free(zErrMsg);
 
                sqlite3_close(db);
 
                return -1;
            }
 
            rc = sqlite3_exec(db, "PRAGMA wal_autocheckpoint=100;", NULL, 0, &zErrMsg);
 
            if (rc != SQLITE_OK)
            {
                sqlite3_free(zErrMsg);
 
                sqlite3_close(db);
 
                return -1;
            }
        }
        else
        {
            rc = sqlite3_exec(db, "PRAGMA journal_mode=DELETE;", NULL, 0, &zErrMsg);
 
                   if (rc != SQLITE_OK)
               {
                sqlite3_free(zErrMsg);
 
                sqlite3_close(db);
 
                 return -1;
              }
            }
 
        return true;
      }
   

 


推荐阅读
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • Firefox火狐浏览器关闭到http://detectportal.firefox.com的流量问题解决办法
    本文介绍了使用Firefox火狐浏览器时出现关闭到http://detectportal.firefox.com的流量问题,并提供了解决办法。问题的本质是因为火狐默认开启了Captive portal技术,当连接需要认证的WiFi时,火狐会跳出认证界面。通过修改about:config中的network.captive-portal-service.en的值为false,可以解决该问题。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 解决VS写C#项目导入MySQL数据源报错“You have a usable connection already”问题的正确方法
    本文介绍了在VS写C#项目导入MySQL数据源时出现报错“You have a usable connection already”的问题,并给出了正确的解决方法。详细描述了问题的出现情况和报错信息,并提供了解决该问题的步骤和注意事项。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
author-avatar
手机用户2502871761
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有