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 做显示事务,减少多次的自动事务消耗

参考

SQLite 是否是线程安全的

Using SQLite In Multi-Threaded Applications

SQLite在多线程环境下的应用

SQLite 插入太慢

SQLite 自动提交

YapDatabase wiki 详解

https://blog.csdn.net/u011342466/article/details/79740086

SQLite 线程安全和并发的更多相关文章

  1. 文件数据库sqlite3 C++ 线程安全和并发

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

  2. 设计模式:基于线程池的并发Visitor模式

    1.前言 第二篇设计模式的文章我们谈谈Visitor模式. 当然,不是简单的列个的demo,我们以电商网站中的购物车功能为背景,使用线程池实现并发的Visitor模式,并聊聊其中的几个关键点. 一,基 ...

  3. java线程安全之并发Queue

    关闭 原 java线程安全之并发Queue(十三) 2017年11月19日 23:40:23 小彬彬~ 阅读数:12092更多 所属专栏: 线程安全    版权声明:本文为博主原创文章,未经博主允许不 ...

  4. 性能测试:深入理解线程数,并发量,TPS,看这一篇就够了

    并发数,线程数,吞吐量,每秒事务数(TPS)都是性能测试领域非常关键的数据和指标. 那么他们之间究竟是怎样的一个对应关系和内在联系? 测试时,我们经常容易将线程数等同于表述为并发数,这一表述正确吗? ...

  5. 如何理解 jmeter 的线程数与并发数之间的关系

    https://blog.csdn.net/weixin_39955351/article/details/110548162 多个线程组的并发是如何计算的?

  6. sqlite线程模式的设置

    (1)编译阶段 这几种模式可以通过参数SQLITE_THREADSAFE在编译阶段指定,可以取值0,1,2,默认是1.这三种取值的含义如下: 0:单线程模式,即内部不做mutex保护,多线程运行sql ...

  7. SQLite多线程下的并发操作

    标签: sqlite多线程数据库跨平台嵌入式class 2011-04-14 13:29 26939人阅读 评论(2) 收藏 举报 这两天一直在捣鼓SQLite数据库,基本的操作就不说了,比较简单,打 ...

  8. Netty : writeAndFlush的线程安全及并发问题

    使用Netty编程时,我们经常会从用户线程,而不是Netty线程池发起write操作,因为我们不能在netty的事件回调中做大量耗时操作.那么问题来了 – 1, writeAndFlush是线程安全的 ...

  9. 线程池——JAVA并发编程指南

    TPS00-J. 用线程池实现应用在流量暴涨时优雅降级 很多程序都要解决这样一个问题——处理一系列外来的请求.Thread- Per-Message这种设计模式是最简单的并发策略了,它为每一个请求创建 ...

随机推荐

  1. springMVC_11拦截器实现登录

    一.   思路 controller实现核对用户名和密码,如果核对正确则保存到session中并且跳转到主页 系统中包含诸多界面,部分界面不需要登录即可进行访问,通过拦截器实现判断是否是不需要登录的界 ...

  2. 积累一些不太常用的C/C++语言知识(不断更新)

    这里积累一些日常编程用得比较少的知识,不断添加. scanf("%c%*c%c",&a,&b); 其中的*表示跳过,本来输入三个数字,结果中间那个读入后被抛弃,a和 ...

  3. 【Java每日一题】20170329

    20170328问题解析请点击今日问题下方的“[Java每日一题]20170329”查看(问题解析在公众号首发,公众号ID:weknow619) package Mar2017; public cla ...

  4. 【Java每日一题】20170210

    20170209问题解析请点击今日问题下方的“[Java每日一题]20170210”查看(问题解析在公众号首发,公众号ID:weknow619) package Feb2017; public cla ...

  5. js 简单日历

    源地址:https://jingyan.baidu.com/article/546ae185fa4f721149f28cbf.htm 文件:index.htm <!DOCTYPE html> ...

  6. blfs(systemv版本与systemd版本均适用)学习笔记-从主机挂载lfs的方法

    我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 运行以下命令,挂载并进入lfs分区即可 su export LFS=/mnt/lfs mount -v -t ext4 /dev ...

  7. 如何用ABP框架快速完成项目(3) - 为什么要使用ABP和ABP框架简介

    首先先讲为什么要使用ABP? 当然是因为使用ABP可以快速完成项目啦. 时间就是金钱, 效率就是生命嘛   有了ABP, 你就节省了写如下模块的时间: CRUD数据库基本操作 校验 异常处理 日志 权 ...

  8. Vue 组件之间传值

    一.父组件向子组件传递数据 在 Vue 中,可以使用 props 向子组件传递数据. 子组件部分: 这是 header.vue 的 HTML 部分,logo 是在 data 中定义的变量. 如果需要从 ...

  9. SQL分组函数

    分组函数是对表中的多行进行操作,而每组返回一个计算结果.常用的分组函数包括: 函数 语法格式 函数描述以及注意事项 AVG AVG([distinct|all] expr) 返回一个数字列或计算列的平 ...

  10. 什么是CSR以及CSR的作用和生成

    什么是CSR以及CSR的作用和生成 来源:https://www.trustasia.com/news-201801-what-is-the-role-and-generation-of-csr-an ...