SQLite3 多个同时访问数据库的读取问题

问题描述 投票:0回答:1

我在使用 SQLite3 数据库读取数据时遇到一些问题。

背景: 我构建了一个 python 程序,它依赖于两个并行运行的脚本:

  • Script1:持续读取(在 time.sleep(30) 上)从 MQTT 代理流式传输的消息,并将一些数据写入 SQLite 数据库的表 1(我们称之为数据库 1)。
  • Script2:有许多依赖于访问数据库1的进程。虽然大多数进程涉及对数据库 1 的其他表进行读/写,但存在偶发问题的进程是在 script1 运行时依赖于读取数据库 1 的表 1 的进程。问题是从 script2 读取 table1 可能会被完全跳过。

从大量谷歌搜索/阅读论坛主题和 SQLite 文档中,我相信这是由于以下事实:当 script1 写入数据库 1 的表 1 时,表 1 被阻止访问。当 script2 的进程与 script1 在同一个数据库表 1 上的写入重合时,这就会成为问题。我注意到,当我只运行 script2 (而不执行 script1)时,我从来没有遇到过这个问题(即从 script2 读取 table1 的所有操作都不会被跳过),并且所有过程都运行顺利。

我尝试了很多事情,包括为我的数据库连接添加超时和pragma busy超时(下面的片段)。两个超时都已应用于我在两个脚本中使用的所有数据库函数,但结果是相同的。

def db_function1(param1,param2,param3):
    con=None
    temp_df='null'
    db=os.path.join(os.getcwd(), './database1.db')
    try:
        con=sqlite3.connect(db, timeout=20) #Increase timeout from default 5sec.
        c = con.cursor()
        c.execute('''pragma busy_timeout=10000''') #Busy timeout changed to 10s (in ms)
        temp_df = pd.read_sql(
            '''SELECT * FROM table1 WHERE .....''' #I skipped the query detail to make this block neater to read
            % (param1,param2,param3), con)
        con.close()
    except Error as e:
        print(e)
    return temp_df

进一步阅读https://www.sqlite.org/faq.html#q5,似乎前进的道路是使用 sqlite3_busy_handler() 或 sqlite3_busy_timeout() 来运行测试,如果表实际上很忙,那么它可以等待并重试(通过在繁忙时暂停依赖于 table1 读取的 script1 进程几秒钟),但我无法让它工作或在网上找到任何应用程序示例。

有什么想法吗?

谢谢!

python database sqlite
1个回答
0
投票

如果没有可重现的问题,很难给出明确的答案。但是,请测试一下它是否可以解决您的问题。我包含了一个 custom_busy_handler 函数,它将返回繁忙处理程序的回调函数和一个 inner_callback,它是 SQLite 遇到繁忙情况时将调用的实际回调。

它会休眠指定的超时时间,并返回 True 重试或返回 False 放弃。 db_function1 已修改为使用 con.set_busy_handler(custom_busy_handler(retries, timeout)) 设置自定义繁忙处理程序。尝试一下重试和超时参数,看看它在您的具体情况下如何工作

def custom_busy_handler(retries, timeout):
    def inner_callback(tries):
        if tries >= retries:
            return False  # Give up if retries exceed a certain limit
        time.sleep(timeout)
        return True

    return inner_callback

def db_function1(param1, param2, param3, retries=5, timeout=1):
    con = None
    temp_df = 'null'
    db = os.path.join(os.getcwd(), './database1.db')
    try:
        con = sqlite3.connect(db, timeout=20)  # Increase timeout from default 5sec.
        con.execute('''pragma busy_timeout=10000''')  # Busy timeout changed to 10s (in ms)

        # Set up a custom busy handler
        con.set_busy_handler(custom_busy_handler(retries, timeout))

        temp_df = pd.read_sql(
            '''SELECT * FROM table1 WHERE ...''',
            % (param1, param2, param3), con)
        con.close()
    except Error as e:
        print(e)
    return temp_df

# Example of using db_function1 with custom busy handler
result = db_function1("value1", "value2", "value3", retries=5, timeout=1)
print(result)
© www.soinside.com 2019 - 2024. All rights reserved.