【问题标题】:Locking a row with SQLite (read lock ?)用 SQLite 锁定一行(读锁?)
【发布时间】:2011-12-31 22:24:12
【问题描述】:

我已经用 Python 开发了一个基本的代理测试器。代理 IP 和端口,以及它们的 date_of_last_test(例如 31/12/2011 10:10:10)和 result_of_last_test(OK 或 KO)存储在单个 SQLite 表中。 (我意识到我可以存储更多关于测试结果的详细信息并保留历史/统计信息,但这个简单的模型适合我的需要)。

这是测试器主循环的简化代码,我在其中循环代理并更新它们的状态:

while True:
    # STEP 1: select
    myCursor.execute("SELECT * from proxy ORDER BY date_of_last_test ASC;")
    row = myCursor.fetchone()

    # STEP 2: update
    if isProxyWorking(row['ip'], row['port']): # this test can last a few seconds
        updateRow(row['ip'], row['port'], 'OK')
    else:
        updateRow(row['ip'], row['port'], 'KO')

我的代码在作为单个进程运行时运行良好。现在,我希望能够运行程序的多个进程,使用相同的 SQLite 数据库文件。 当前代码的问题是缺少一种锁定机制,该机制会阻止多个进程测试同一个代理。

在 STEP 1 / SELECT 时间锁定行的最干净的方法是什么,以便下一个执行 SELECT 的进程获得下一行?


换句话说,我想避免以下情况:

假设现在是晚上 10 点,数据库包含 2 个代理: 代理 A 最后一次测试是晚上 8 点,代理 B 测试是晚上 9 点。

我启动测试器的两个进程来更新它们的状态:

  • 10:00 - 进程 1 获取“最旧”代理来测试它:A
  • 10:01 - 进程 2 获取“最旧”的代理来测试它:!!! A !!! (在这里我会 像过程 2 一样获取代理 B 因为 A 已经在测试中 - 虽然尚未在 db 中更新)
  • 10:10 - 进程 1 对 A 的测试是 结束,其状态在 DB 中更新
  • 10:11 - 进程 2 对 A 的测试是 结束,它的状态在 DB 中被更新(!!! AGAIN !!!)

在这种情况下没有实际的错误/异常,但我想避免浪费时间。

【问题讨论】:

  • 我认为理解代码比 True/False 更容易:P
  • 我有类似的问题,并决定不仅选择最旧的记录,而且选择最旧的记录,其中 id % NUM_OF_THREADS。通过这种方式,我们在工作线程之间划分所有记录,因此不会发生冲突。我知道这会产生一些问题,例如如果某些工作线程无事可做,但统计上可以。 (顺便说一句。KO & OK 不好,因为它很容易出错。这不是防御性编程。但是防御性编程通常很无聊。;))

标签: python sqlite


【解决方案1】:

SQlite 一次只允许一个进程更新数据库中的任何内容,来自FAQ

多个进程可以同时打开同一个数据库。多个进程可以同时执行 SELECT。但是任何时候只有一个进程可以对数据库进行更改,

当 SQLite 尝试访问被另一个进程锁定的文件时,默认行为是返回 SQLITE_BUSY。您可以使用 sqlite3_busy_handler() 或 sqlite3_busy_timeout() API 函数从 C 代码调整此行为。

因此,如果只有少量更新,那么这将起作用,否则您需要更改为功能更强大的数据库。

所以整个数据库只有一把锁

【讨论】:

  • 我知道你的 2 个进程不能同时写入同一个数据库文件。但是,这个限制不应该阻止我运行测试仪的多个进程,只要我在收到 SQLITE_BUSY 异常时实现重试机制。我将编辑我的问题以更好地解释我真正的问题是什么。
  • @Sebastien - 您的问题 h=只是询问如何进行行锁定,而不是您尝试过并遇到错误,如果是,请显示错误是什么
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-01-31
  • 2011-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-28
  • 1970-01-01
相关资源
最近更新 更多