SQLite3是否可以安全地处理从同一数据库读取/写入的多个进程的并发访问?有平台例外吗?
SQLite3是否可以安全地处理从同一数据库读取/写入的多个进程的并发访问?有平台例外吗?
Answers:
如果大多数并发访问是读取的(例如SELECT),则SQLite可以很好地处理它们。但是,如果您开始同时进行写入,则锁争用可能会成为问题。然后,这很大程度上取决于文件系统的速度,因为SQLite引擎本身非常快,并且具有许多巧妙的优化方法可以最大程度地减少争用。特别是SQLite 3。
对于大多数台式机/笔记本电脑/平板电脑/电话应用程序,SQLite足够快,因为并发性不足。(Firefox将SQLite广泛用于书签,历史记录等。)
对于服务器应用程序,前段时间有人说,在典型情况下(例如,博客,论坛),SQLite数据库可以完美地处理每天少于10万的页面浏览量,而我还没有看到任何相反的证据。实际上,使用现代磁盘和处理器,95%的网站和Web服务都可以在SQLite上正常工作。
如果您想要真正的快速读写访问,请使用内存中的SQLite数据库。RAM比磁盘快几个数量级。
是的,SQLite可以很好地处理并发,但是从性能的角度来看并不是最好的。据我所知,没有例外。详细信息在SQLite的网站上:https : //www.sqlite.org/lockingv3.html
这条语句很有趣:“分页器模块确保所有更改都立即发生,要么所有更改都发生,要么全部不发生,两个或多个进程不会尝试以不兼容的方式同时访问数据库”
是的,它确实。让我们找出原因
SQLite中单个事务中的所有更改要么完全发生,要么根本不发生
这种ACID支持以及并发读/写以两种方式提供-使用所谓的日志记录(称为“旧方法”)或预写日志记录(称为“新方法”)
在这种模式下,SQLite使用DATABASE-LEVEL 锁定。这是要理解的关键点。
这意味着,每当需要读取/写入某些内容时,它都会首先获得ENTIRE数据库文件的锁。多个阅读器可以共存并并行阅读
在写入过程中,请确保已获取排他锁,并且没有其他进程同时在进行读/写操作,因此写操作是安全的。
因为它每次都需要锁定整个数据库,并且每个人都在等待处理写并发的过程,所以这种并发的写/读性能相当低。
在将某些内容写入数据库文件之前,SQLite首先将要更改的块保存在临时文件中。如果在写入数据库文件的过程中发生故障,它将选择该临时文件并从中还原更改
在这种情况下,所有写操作都会附加到一个临时文件(预写日志)中,并且此文件会定期与原始数据库合并。当SQLite搜索某些内容时,它将首先检查该临时文件,如果找不到任何内容,则继续处理主数据库文件。
结果,读者不会与作家竞争,并且与旧方式相比,性能要好得多。
SQlite严重依赖于底层文件系统锁定功能,因此应谨慎使用,此处有更多详细信息
您还可能会遇到数据库锁定错误,尤其是在日志记录模式下,因此在设计应用程序时应考虑到此错误
似乎没有人提到WAL(预写日志)模式。确保事务被正确组织并设置为WAL模式,在人们进行更新的同时阅读内容时,无需保持数据库锁定。
唯一的问题是,在某些时候需要将WAL重新合并到主数据库中,并在与数据库的最后一个连接关闭时执行此操作。对于一个非常繁忙的站点,您可能会发现所有连接都需要花费几秒钟的时间,但是每天点击10万次也不成问题。
database is locked
而作家将提出该错误
在2019年,有两个新的并发写入选项尚未发布,但可以在单独的分支中使用。
与常规的“ wal”模式相比,此日志模式的优势在于,编写者可以继续写入一个wal文件,而另一个则被检查点。
开始同步-链接到详细文档
BEGIN CONCURRENT增强功能允许多个写程序在数据库处于“ wal”或“ wal2”模式下同时处理写事务,尽管系统仍对COMMIT命令进行序列化。
当使用“ BEGIN CONCURRENT”打开写事务时,实际上将锁定数据库推迟到执行COMMIT之前。这意味着以BEGIN CONCURRENT开始的任何数量的事务都可以同时进行。系统使用乐观的页面级别锁定来防止发生冲突的并发事务。
它们一起出现在begin-concurrent-wal2或每个出现在单独的自己的分支中。
SQLite在数据库级别具有读者-作者锁定。多个连接(可能属于不同的进程)可以同时从同一数据库读取数据,但是只有一个可以写入数据库。
SQLite支持无限数量的同时读取器,但它只能在任何时刻允许一个写入器。在许多情况下,这不是问题。作家排队。每个应用程序都会快速完成其数据库的工作并继续运行,并且锁定不会持续超过几十毫秒。但是有些应用程序需要更多的并发性,而这些应用程序可能需要寻求不同的解决方案。-适用于SQLite @ SQLite.org
读写器锁启用独立的事务处理,并使用数据库级别的排他和共享锁来实现。
在连接上对数据库执行写操作之前,必须获得排他锁。获得排他锁后,其他连接的读取和写入操作都将被阻止,直到再次释放该锁为止。
SQLite具有一个锁定表,该锁定表有助于在写操作期间尽可能晚地锁定数据库,以确保最大程度的并发性。
初始状态为UNLOCKED,在此状态下,连接尚未访问数据库。当进程连接到数据库,甚至使用BEGIN启动了事务时,该连接仍处于UNLOCKED状态。
在UNLOCKED状态之后,下一个状态是SHARED状态。为了能够从数据库读取(不写入)数据,连接必须首先通过获得SHARED锁进入SHARED状态。多个连接可以同时获取和维护SHARED锁,因此多个连接可以同时从同一数据库读取数据。但是,即使仅释放一个SHARED锁,也没有连接可以成功完成对数据库的写入。
如果连接要写入数据库,则必须首先获得RESERVED锁。
尽管多个SHARED锁可以与一个RESERVED锁共存,但一次只能激活一个RESERVED锁。RESERVED与PENDING的不同之处在于,当有RESERVED锁时,可以获取新的SHARED锁。-文件锁定和并发在SQLite的第3版@ SQLite.org
一旦连接获得RESERVED锁,它就可以开始处理数据库修改操作,尽管这些修改只能在缓冲区中完成,而不能实际写入磁盘。对读取内容所做的修改将保存在内存缓冲区中。当连接要提交修改(或事务)时,必须将RESERVED锁升级为EXCLUSIVE锁。为了获得锁,您必须首先将锁提升到PENDING锁。
PENDING锁意味着持有该锁的进程希望尽快写入数据库,并且正在等待所有当前SHARED锁清除,以便可以获取EXCLUSIVE锁。如果PENDING锁处于活动状态,则不允许对数据库使用任何新的SHARED锁,尽管允许现有的SHARED锁继续。
需要EXCLUSIVE锁才能写入数据库文件。文件上仅允许一个EXCLUSIVE锁,并且不允许任何其他类型的锁与EXCLUSIVE锁共存。为了最大化并发性,SQLite致力于最小化持有EXCLUSIVE锁的时间。-文件锁定和并发在SQLite的第3版@ SQLite.org
因此,您可能会说SQLite安全地通过多个进程写入同一数据库来安全地处理并发访问,因为它不支持它!当第二个作者达到重试限制时,您将获得SQLITE_BUSY
或SQLITE_LOCKED
第二个作者的报酬。
这个线程很旧,但是我认为最好分享在sqlite上完成的测试结果:我运行了2个python程序实例(同一程序的不同进程),在事务中执行了SELECT和UPDATE sql命令,且EXCLUSIVE锁定和超时设置为10秒钟获得锁定,结果令人沮丧。每个实例都在10000个步骤循环中执行:
即使sqlite授予了事务独占锁定的权限,实际执行的周期总数也不等于20000,而是更少(两个进程都计入单个计数器的迭代总数)。Python程序几乎没有引发任何单个异常(在选择20次执行期间仅一次)。测试时的sqlite修订版是3.6.20和python v3.3 CentOS 6.5。在我看来,最好为这种工作找到更可靠的产品,或者将对sqlite的写入限制为单个唯一的进程/线程。
with con
已经足够了。