Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sqlite/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python中锁定sqlite3数据库(重新询问澄清)_Sqlite - Fatal编程技术网

在Python中锁定sqlite3数据库(重新询问澄清)

在Python中锁定sqlite3数据库(重新询问澄清),sqlite,Sqlite,几周前,我在SO上发布了关于如何用python锁定sqlite3数据库的问题: 然而,我不太相信这个答案是有效的。或者,也许我只是误解了答案 以下是我遇到的情况: 我有一个数据库“测试” 在数据库“test”中有一个表“book” 表“book”中有两列:“title”和“checked_out_by” 然后我有一个函数,它的工作原理如下: def checkout(title, user): con = get_connection_from_db() with con:

几周前,我在SO上发布了关于如何用python锁定sqlite3数据库的问题:

然而,我不太相信这个答案是有效的。或者,也许我只是误解了答案

以下是我遇到的情况:

  • 我有一个数据库“测试”
  • 在数据库“test”中有一个表“book”
  • 表“book”中有两列:“title”和“checked_out_by”
然后我有一个函数,它的工作原理如下:

def checkout(title, user):
    con = get_connection_from_db()
    with con:
        checked_out_by = get_checked_out_by(title)
        if checked_out_by == '': # If NOT checked out:
            checkout(title, user)
            print user, "checked out", title
        elif checked_out_by == 'user':
            print user, "already got it"
        else:
            print user, "can't check it out because", checked_out_by, "has it!"
因此,checkout()函数首先验证该书是否未签出,如果是,则签出该书。请注意,我使用了推荐的“with con:”技巧来确保一切都是事务性的、愉快的、和谐的

然而,我运行了一系列并发测试并发现了问题。具体来说,当我同时运行以下两个调用时:

checkout('foo', 'steve')
checkout('foo', 'tim')
输出表明它工作不太正常。我希望看到以下两种可能的结果之一:

steve checked out foo
tim can't check it out because steve has it!
或:

但偶尔,我会得到以下输出:

tim checked out foo
steve checked out foo

我认为“with con:”技巧可以确保我的DB调用被捆绑在一起。有人能向我解释一下我是怎么弄错的吗?如果是这样的话,有什么办法可以让它工作吗?

需要注意的一点是,当数据库被锁定时,这意味着它不接受多个写入程序。但是,它可以接受多个阅读器

检查事务是否按预期工作的一种简单方法是将值写入数据库,然后在事务代码完成之前引发异常。如果未写入该值,则事务正常工作。否则,就有问题了

数据库事务是一种乐观的并发方法,也就是说,它们只有在即将提交时才会失败。既然你似乎在寻找一种悲观的方法,也许你应该试试:


“带着骗局”不是这里想要的。(或此螺纹锁紧装置)

要获得特定时间段的独占访问权(不仅仅是在进行单个查询/传输时),您需要执行以下操作:

con = sqlite3.connect()
con.isolation_level = 'EXCLUSIVE'
con.execute('BEGIN EXCLUSIVE')
#exclusive access starts here. Nothing else can r/w the db, do your magic here.
con.commit()
con.close()
希望这能把我刚刚经历的搜索/实验中的人救出来


记住,在运行beginexclusive之前,它不是独占的,在关闭(或者运行commit,我认为)之前,它将保持独占状态。如果您不确定,您可以随时使用python解释器/CL sqlite3应用程序进行测试。

这是一个有趣的技巧。理想情况下,我想知道如何获得独占读/写锁,这样我就不必这样做。这可能吗?谢谢你提供锁的信息;你知道如何在Python的sqlite3中正确使用它吗?因为
threading.Lock
对象可以用作上下文管理器,并且上下文管理器可以嵌套,所以我用一个示例更新了我的答案,该示例只添加了一个新的上下文管理器,以便在程序中具有全局数据库锁。看看你的示例,看起来锁将只应用于checkout()函数的调用。但我希望锁应用于数据库本身的所有操作。你的例子能做到吗?不,不能。您需要在每个函数中使用
db_lock
,在这些函数中,您需要将读卡器的数量限制为一个。如果您在很多地方都需要,我建议使用装饰器来避免有太多的样板代码。打开隔离级别为EXCLUSIVE的连接可以解决这个问题吗()?只是澄清一下:请注意,您可以使用“with con”,这样它会将所有内容包装在“EXCLUSIVE access”部分中,然后删除con.commit()-因为这会在with块的末尾自动发生。您仍然需要前面的两行,这两行设置隔离级别并开始独占部分。另外请注意,如果您保持连接打开,在许多情况下,您可能希望将隔离级别设置为非独占。即使没有
con.isolation\u level='EXCLUSIVE'
行,这似乎也可以工作。
import threading
db_lock = threading.Lock()

def checkout(title, user):
    with db_lock:
        con = get_connection_from_db()
        with con:
            checked_out_by = get_checked_out_by(title)
            if checked_out_by == '': # If NOT checked out:
                checkout(title, user)
                print user, "checked out", title
            elif checked_out_by == 'user':
                print user, "already got it"
            else:
                print user, "can't check it out because", checked_out_by, "has it!"
con = sqlite3.connect()
con.isolation_level = 'EXCLUSIVE'
con.execute('BEGIN EXCLUSIVE')
#exclusive access starts here. Nothing else can r/w the db, do your magic here.
con.commit()
con.close()