线程安全如何是SQLite3?

Fab*_*ian 2 database sqlite multithreading thread-safety

我有一个关于SQLite3中的多线程的问题.在我的场景中,我有几个进程,它们想要写入相同的SQLite3数据库.

手册中,它说

Multi-thread. In this mode, SQLite can be safely used by multiple threads provided that no single database connection is used simultaneously in two or more threads. 
Run Code Online (Sandbox Code Playgroud)

我编写了一个示例程序,其中几个(比如10个)线程使用相同的数据库连接INSERT到同一个数据库的同一个表中.

CREATE TABLE IF NOT EXISTS StatusTable (ID INTEGER PRIMARY KEY, Thread TEXT NOT NULL, Module TEXT NOT NULL, Status INTEGER NOT NULL);
Run Code Online (Sandbox Code Playgroud)

INSERT是

sprintf(acBuffer, "INSERT INTO StatusTable(Thread, Module, Status) VALUES('%s', 'testnumber%d', %d);", acThread, i, thisThread);
Run Code Online (Sandbox Code Playgroud)

where acThread是包含进程ID和线程ID的字符串,i从1到100,并且thisThread是线程ID.线程在while循环中执行此操作100次,然后退出.

根据上述说法,虽然我看不出任何问题,但这并不安全.

  1. 使用带有共享sqlite3对象的10个线程,每个插入100行:不会丢失任何插入且不会发生超时.
  2. 使用10个线程,每个线程都有一个单独的sqlite3对象,每个线程插入100行:某些插入不成功(大约0.5到1%),因为数据库已被锁定.
  3. 使用案例1的两个过程:与案例2相同,存在一些损失(约0.1%).

我正在使用PRAGMA journal_mode=WAL;PRAGMA busy_timeout=10000;.

现在我的问题:

  • 我不应该在线程之间共享连接对象吗?如果我这样做会发生什么坏事?
  • 为什么第一种情况(使用共享数据库连接)工作(所有插入成功,没有超时)?我需要做些什么才能"打破"SQLite?那会发生什么?
  • 我假设每个线程都应该有自己的连接.如何避免插入失败?在我的例子中,只有插入,没有读者.高得离谱的busy_timeout(10秒)没有按预期工作,仍有"数据库被锁定"插入失败.只要插入成功,我不介意每次插入等待10秒.

H. *_*ijt 6

文档(由创建此软件并且多年来生活,呼吸和思考的人员编写)告诉您不要共享数据库连接.你为什么不简单地相信他们?这并不是说创建多个数据库连接实际上非常困难.

  1. 对,是真的.可能发生的最糟糕的事情是未定义的行为,可能包括也可能不包括黑洞,裸体奇点和克苏鲁的召唤.

  2. 你真是幸运.在下一个线程开始之前,测试中的所有插入都可能已经执行.

  3. 你假设正确.您可以通过测试来避免失败,并在遇到错误时重复SQL命令.设置高超时无济于事.

对于任何对数据库进行多线程(或多进程)访问的问题,Sqlite并不是特别匹配,根据我的经验,"数据库被锁定"消息在这种情况下几乎是不可避免的.通过从单个线程访问数据库,并将插入组合到单个语句中(即使用多值插入),可以获得更好的性能.如果这还不够,那么使用多个线程锤击sqlite数据库根本无法提供性能.如果你有一个需要这种方法的用例,你应该考虑安装一个为此目的而构建的数据库,比如PostgreSQL.


Den*_*nie 5

当前的SQLite(3.23.1版)文档指出默认的线程化模式是“序列化”,并且在这种模式下:

SQLite可以不受限制地被多个线程安全地使用。

线程安全性在编译时进行控制。您可以使用以下方法查询SQLite发行版的线程模式:

pragma COMPILE_OPTIONS
Run Code Online (Sandbox Code Playgroud)