SQLite并发访问

时间:2010-10-30 21:33:05

标签: sqlite

SQLite3是否安全地处理多个进程的并发访问 从同一个DB读/写?有没有平台例外?

7 个答案:

答案 0 :(得分:97)

如果大多数并发访问都是读取(例如SELECT),SQLite可以很好地处理它们。但如果你开始同时写,锁争用可能会成为一个问题。然后很大程度上取决于文件系统的速度,因为SQLite引擎本身非常快,并且有许多聪明的优化来最小化争用。特别是SQLite 3。

对于大多数桌面/笔记本电脑/平板电脑/手机应用程序,SQLite足够快,因为没有足够的并发性。 (Firefox广泛使用SQLite来存储书签,历史等)。

对于服务器应用程序,前段时间有人说在一般情况下(例如博客,论坛)SQLite数据库可以完美地处理每天少于100K的页面浏览量,而我还没有看到任何相反的证据。事实上,使用现代磁盘和处理器,95%的网站和Web服务可以与SQLite一起使用。

如果您想要非常快速的读/写访问权限,请使用内存中的SQLite数据库。 RAM比磁盘快几个数量级。

答案 1 :(得分:35)

是的,SQLite很好地处理并发性,但从性能角度来看并不是最好的。据我所知,没有例外。详细信息在SQLite的网站上:https://www.sqlite.org/lockingv3.html

此声明很有意义:“寻呼机模块确保一次更改发生,所有更改都发生或者没有发生任何更改,两个或更多进程不会尝试以不兼容的方式访问数据库时间“

答案 2 :(得分:28)

似乎没有人提到WAL(Write Ahead Log)模式。确保事务处理正确并且启用了WAL模式,在进行更新时人们正在阅读内容时无需锁定数据库。

唯一的问题是,在某些时候需要将WAL重新合并到主数据库中,并且在最后一次连接到数据库时关闭它。对于一个非常繁忙的站点,您可能会发现所有连接都需要几秒钟才能关闭,但每天100K的点击量应该不是问题。

答案 3 :(得分:19)

是的。 让我们找出原因

SQLite是transactional

  

SQLite中单个事务中的所有更改都会发生   完全或根本没有

这种ACID支持以及并发读/写以两种方式提供 - 使用所谓的日记(让我们称之为“旧方式”)或< strong>预先记录(让我们称之为“新方式”)

日记(旧路)

在此模式下,SQLite使用 DATABASE-LEVEL locking。 这是理解的关键点。

这意味着无论何时需要读取/写入内容,它都会首先获取 ENTIRE 数据库文件的锁定。 多个读者可以共存并且并行读取内容

在写入期间,它确保获得独占锁定并且没有其他进程同时读/写,因此写入是安全的。

这就是here他们说SQlite实施serializable次交易的原因

故障

因为它需要每次都锁定整个数据库并且每个人都在等待进程处理写入并发遭受损失并且此类并发写入/读取性能相当低

回滚/中断

在向数据库文件写入内容之前,SQLite会首先将要更改的块保存在临时文件中。如果在写入数据库文件的过程中出现问题,它将获取此临时文件并从中恢复更改

预写日志或WAL(新方式)

在这种情况下,所有写入都附加到临时文件(write-ahead log),并且此文件会定期与原始数据库合并。 当SQLite搜索某些内容时,它会首先检查此临时文件,如果没有找到,则继续使用主数据库文件。

因此,读者不会与作家竞争,与旧方式相比,表现要好得多。

注意事项

SQlite在很大程度上取决于底层文件系统锁定功能,因此应谨慎使用,更多细节here

您还可能遇到数据库被锁定错误,尤其是在日记模式下,因此您的应用需要设计时会记住此错误

答案 4 :(得分:9)

2019年,有两个新的并发写入选项尚未发布,但可以在单独的分支中使用。

"PRAGMA journal_mode = wal2"

  

与普通的“ wal”模式相比,此日志模式的优势在于,编写者可以继续写入一个wal文件,而另一个则被检查点。

BEGIN CONCURRENT-链接到详细文档

  

BEGIN CONCURRENT增强功能允许多个写程序在数据库处于“ wal”或“ wal2”模式下同时处理写事务,尽管系统仍对COMMIT命令进行序列化。

     

使用“ BEGIN CONCURRENT”打开写事务时,实际上将锁定数据库推迟到执行COMMIT之前。这意味着以BEGIN CONCURRENT开始的任何数量的事务都可以同时进行。该系统使用乐观的页面级别锁定来防止发生冲突的并发事务。

它们一起出现在begin-concurrent-wal2中,或者每个出现在单独的branch中。

答案 5 :(得分:6)

  

SQLite支持无限数量的同时阅读器,但它只允许一个编写者在任何时刻。在许多情况下,这不是问题。作家排队。每个应用程序都可以快速完成数据库的工作并继续运行,并且锁定持续时间超过几十毫秒。但是有些应用程序需要更多的并发性,而这些应用程序可能需要寻求不同的解决方案。

在DOC中很清楚。

在事务处理中,SQLite通过数据库级​​别的独占锁和共享锁实现独立的事务处理。这就是多个进程可以同时从同一个数据库读取数据的原因,但只有一个可以写入数据库。

必须在进程或线程要对数据库执行写操作之前获取独占锁。获得独占锁后,其他读或写操作不会再次发生。

实现两个写入的详细信息:

SQLite有一个锁表,可以帮助在最后一刻锁定不同的写数据库,以确保最大的并发性。

初始状态为“UNLOCKED”,在此状态下,连接尚未访问数据库。当数据库连接到数据库并且甚至已使用BEGIN启动事务时,连接仍处于“未锁定”状态。

解锁状态的下一个状态是SHARED状态。为了能够从数据库读取(不写入)数据,连接必须首先进入SHARED状态,也就是说,首先获得SHARED锁定。 多个连接可以同时获取和维护SHARED锁,也就是说,多个连接可以同时从同一个数据库读取数据。但即使只发布了一个SHARED锁,也不允许任何连接写入数据库。

如果连接想要写一个数据库,它必须先获得一个RESERVED锁。

  

虽然多个SHARED锁可以与单个RESERVED锁共存,但一次只能激活一个RESERVED锁。 RESERVED与PENDING的不同之处在于,当存在RESERVED锁时,可以获取新的SHARED锁。

一旦连接获得了RESERVED锁,它就可以开始处理数据库修改操作,尽管这些修改只能在缓冲区中完成,而不是实际写入磁盘。对读出内容的修改保存在内存缓冲区中。 当连接想要提交修改(或事务)时,必须将保留锁升级为独占锁。为了获得锁定,您必须首先将锁提升到挂起锁。

  

PENDING锁意味着持有锁的进程想要尽快写入数据库并且只是等待所有当前的SHARED锁清除,以便它可以获得EXCLUSIVE锁。如果PENDING锁处于活动状态,则不允许对数据库使用新的SHARED锁,但允许继续使用现有的SHARED锁。

     

为了写入数据库文件,需要一个EXCLUSIVE锁。文件上只允许一个EXCLUSIVE锁,并且不允许任何其他类型的锁与EXCLUSIVE锁共存。为了最大化并发性,SQLite可以最大限度地减少EXCLUSIVE锁定的持续时间。

因此SQLite安全地处理来自同一个数据库的多个进程的并发访问,因为它不支持它。当第二个编写器达到重试限制时,您将获得SQLITE_BUSYSQLITE_LOCKED

答案 6 :(得分:5)

这个帖子已经老了,但我认为分享我在sqlite上完成的测试结果会很好: 我运行了2个python程序实例(不同进程相同程序)执行语句SELECT和UPDATE sql命令在事务中使用EXCLUSIVE锁定并将超时设置为10秒来获取锁定,结果令人沮丧。每个实例都在10000步循环中完成:

  • 使用独占锁连接到db
  • 选择一行来读取计数器
  • 使用等于计数器增加1
  • 的新值更新行
  • 与db
  • 的紧密连接

即使sqlite在事务上授予了独占锁定,实际执行的周期总数也不等于20 000但更少(两个进程计算的单个计数器的迭代总数)。 Python程序几乎没有抛出任何单个异常(在选择20次执行期间只有一次)。 测试时的sqlite修订版是3.6.20和python v3.3 CentOS 6.5。 在我看来,最好为这类工作找到更可靠的产品,或者将sqlite的写入限制为单一的唯一进程/线程。