线程安全是如何SQLite3的?

时间:2016-03-01 07:43:07

标签: database multithreading sqlite thread-safety

我对SQLite3中的多线程有疑问。在我的场景中,我有几个进程,它们想要写入相同的SQLite3数据库。

manual中,它说

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. 

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

CREATE TABLE IF NOT EXISTS StatusTable (ID INTEGER PRIMARY KEY, Thread TEXT NOT NULL, Module TEXT NOT NULL, Status INTEGER NOT NULL);

INSERT是

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

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

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

  1. 将10个线程与共享sqlite3对象一起使用,每个插入100行:不会丢失插入且不会发生超时。
  2. 使用10个线程,每个线程都有一个单独的sqlite3对象,每个线程插入100行:某些插入不成功(大约0.5到1%),因为数据库已被锁定。
  3. 使用案例1的两个过程:与案例2相同,有一些损失(约0.1%)。
  4. 我正在使用PRAGMA journal_mode=WAL;PRAGMA busy_timeout=10000;

    现在我的问题:

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

2 个答案:

答案 0 :(得分:6)

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

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

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

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

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

答案 1 :(得分:3)

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

  

多个线程可以安全地使用SQLite,没有任何限制。

线程安全控制在compile-time。您可以使用以下命令查询SQLite发行版的线程模式:

pragma COMPILE_OPTIONS