Python中的基本并发SQLite编写器

2024-05-05 20:49:07 发布

您现在位置:Python中文网/ 问答频道 /正文

我创建了一个非常基本的脚本,定期将一些数据写入数据库:

测试.py

import sqlite3
import sys
import time

DB_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS items (item TEXT)'
DB_INSERT = 'INSERT INTO items VALUES (?)'
FILENAME = 'test.db'


def main():
    index = int()
    c = sqlite3.connect(FILENAME)
    c.execute(DB_CREATE_TABLE)
    c.commit()

    while True:
        item = '{name}_{index}'.format(name=sys.argv[1], index=index)
        c.execute(DB_INSERT, (item,))
        c.commit()
        time.sleep(1)
        index += 1

    c.close()


if __name__ == '__main__':
    main()

现在我可以通过多次运行脚本来实现简单的并发:

python3 test.py foo &
python3 test.py bar &

我试着读了一些关于脚本同时写入同一个数据库文件的文章,但是我仍然不确定我的脚本将如何处理这样的事件,我也不知道如何测试它。你知道吗

我的期望是,在不太可能的情况下,当我的脚本的两个实例试图在同一毫秒内写入数据库时,后一个实例将只是静静地等待,直到前一个实例完成它的工作。你知道吗

我当前的实现是否符合我的期望?如果没有,在发生此类事件时,它的行为如何?我如何修复它?你知道吗


Tags: 实例namepytestimport脚本数据库db
1条回答
网友
1楼 · 发布于 2024-05-05 20:49:07

TL;博士

这个脚本将符合预期。你知道吗

解释

当两个脚本实例试图同时写入的不太可能的事件发生时,第一个脚本实例将锁定数据库,第二个脚本实例将静默地等待一段时间,直到第一个脚本实例完成其事务,以便再次解锁数据库以供写入。你知道吗

更准确地说,第二个脚本实例等待5秒(默认情况下),然后用消息database is locked引发OperationalError。正如@roganjosh所评论的,这种行为实际上是Python SQLite包装器特有的。文档states

When a database is accessed by multiple connections, and one of the processes modifies the database, the SQLite database is locked until that transaction is committed. The timeout parameter specifies how long the connection should wait for the lock to go away until raising an exception. The default for the timeout parameter is 5.0 (five seconds).

测试

为了演示这两个实例的冲突事件,我修改了main函数:

def main():
    c = sqlite3.connect(FILENAME)
    c.execute(DB_CREATE_TABLE)
    c.commit()
    print('{} {}: {}'.format(time.time(), sys.argv[1], 'trying to insert ...'))

    try:
        c.execute(DB_INSERT, (sys.argv[1],))
    except sqlite3.OperationalError as e:
        print('{} {}: {}'.format(time.time(), sys.argv[1], e))
        return

    time.sleep(int(sys.argv[2]))
    c.commit()
    print('{} {}: {}'.format(time.time(), sys.argv[1], 'done')) 
    c.close()

文档说明在提交事务之前,数据库一直处于锁定状态。因此,在事务期间简单地休眠应该足以测试它。你知道吗

测试1

我们运行以下命令:

python3 test.py first 10 & sleep 1 && python3 test.py second 0

第一个实例正在运行,1s后第二个实例正在运行。第一个实例创建一个10秒长的事务,第二个实例尝试写入数据库,等待,然后引发异常。日志显示:

1540307088.6203635 first: trying to insert ...
1540307089.6155508 second: trying to insert ...
1540307094.6333485 second: database is locked
1540307098.6353421 first: done

测试2

我们运行以下命令:

python3 test.py first 3 & sleep 1 && python3 test.py second 0

第一个实例正在运行,1s后第二个实例正在运行。第一个实例创建一个3s长的事务,第二个实例尝试写入数据库并等待。因为它是在1s之后创建的,所以它必须等待3s-1s=2s,这比默认的5s小,这样两个事务都将成功完成。日志显示:

1540307132.2834115 first: trying to insert ...
1540307133.2811155 second: trying to insert ...
1540307135.2912169 first: done
1540307135.3217440 second: done

结论

事务完成所需的时间比锁定时间限制(5s)要小得多(毫秒),因此在这种情况下,脚本确实符合预期。但作为哈雷。注释:事务在队列中等待提交,因此对于大量使用或非常大的数据库,这不是一个好的解决方案,因为与数据库的通信将变得缓慢。你知道吗

相关问题 更多 >