Jey*_*mon 6 python sqlite concurrency
我创建了一个非常基本的脚本,定期将一些数据写入数据库:
测试.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()
Run Code Online (Sandbox Code Playgroud)
现在我可以通过多次运行脚本来实现简单的并发:
python3 test.py foo &
python3 test.py bar &
Run Code Online (Sandbox Code Playgroud)
我尝试阅读一些有关脚本同时写入同一数据库文件的文章,但我仍然不确定我的脚本将如何处理此类事件,并且我不知道如何测试它。
我的期望是,在不太可能发生的情况下,当我的脚本的两个实例尝试在同一毫秒内写入数据库时,后一个实例将只是默默地等待,直到前一个实例完成其工作。
我当前的实施是否满足我的期望?如果没有,发生此类事件时它会如何表现以及如何修复?
这个脚本将满足预期。
当两个脚本实例尝试同时写入的罕见事件发生时,第一个脚本实例会锁定数据库,而第二个脚本实例会静默等待一段时间,直到第一个脚本实例完成其事务,以便数据库解锁以再次写入。
更准确地说,第二个脚本实例等待 5 秒(默认情况下),然后引发OperationalError并显示消息database is locked。正如 @roganjosh 评论的那样,这种行为实际上是 Python SQLite 包装器特有的。该文档指出:
当多个连接访问数据库并且其中一个进程修改数据库时,SQLite 数据库将被锁定,直到提交该事务。timeout 参数指定连接应等待锁定消失的时间,直到引发异常。超时参数的默认值为 5.0(五秒)。
为了演示两个实例的碰撞事件,我修改了函数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()
Run Code Online (Sandbox Code Playgroud)
文档指出数据库将被锁定,直到事务提交为止。因此,只需在交易期间睡觉就足以测试它。
我们运行以下命令:
python3 test.py first 10 & sleep 1 && python3 test.py second 0
Run Code Online (Sandbox Code Playgroud)
第一个实例正在运行,1 秒后第二个实例正在运行。第一个实例创建一个 10 秒长的事务,在此期间第二个实例尝试写入数据库、等待然后引发异常。该日志表明:
1540307088.6203635 first: trying to insert ...
1540307089.6155508 second: trying to insert ...
1540307094.6333485 second: database is locked
1540307098.6353421 first: done
Run Code Online (Sandbox Code Playgroud)
我们运行以下命令:
python3 test.py first 3 & sleep 1 && python3 test.py second 0
Run Code Online (Sandbox Code Playgroud)
第一个实例正在运行,1 秒后第二个实例正在运行。第一个实例创建一个 3 秒长的事务,在此期间第二个实例尝试写入数据库并等待。由于它是在 1 秒后创建的,因此必须等待 3 秒 - 1 秒 = 2 秒,这小于默认的 5 秒,因此两个事务都会成功完成。该日志表明:
1540307132.2834115 first: trying to insert ...
1540307133.2811155 second: trying to insert ...
1540307135.2912169 first: done
1540307135.3217440 second: done
Run Code Online (Sandbox Code Playgroud)
事务完成所需的时间(毫秒)明显小于锁定时间限制(5 秒),因此在这种情况下,脚本确实符合预期。但正如@HarlyH。评论说,事务在队列中等待提交,因此对于频繁使用或非常大的数据库来说,这不是一个好的解决方案,因为与数据库的通信会变得很慢。
| 归档时间: |
|
| 查看次数: |
955 次 |
| 最近记录: |