PythonSQLite3与并发
我有一个使用“线程”模块的Python程序。每隔一秒钟,我的程序就会启动一个新线程,从网络上获取一些数据,并将这些数据存储到硬盘上。我想使用sqlite3来存储这些结果,但我无法让它工作。问题似乎与以下方面有关:PythonSQLite3与并发,python,sqlite,Python,Sqlite,我有一个使用“线程”模块的Python程序。每隔一秒钟,我的程序就会启动一个新线程,从网络上获取一些数据,并将这些数据存储到硬盘上。我想使用sqlite3来存储这些结果,但我无法让它工作。问题似乎与以下方面有关: conn = sqlite3.connect("mydatabase.db") 如果我将这行代码放在每个线程中,就会得到一个错误,告诉我数据库文件已锁定。我猜这意味着另一个线程通过sqlite3连接打开了mydatabase.db并将其锁定 如果我将这行代码放在主程序中,并将连接对
conn = sqlite3.connect("mydatabase.db")
- 如果我将这行代码放在每个线程中,就会得到一个错误,告诉我数据库文件已锁定。我猜这意味着另一个线程通过sqlite3连接打开了mydatabase.db并将其锁定
- 如果我将这行代码放在主程序中,并将连接对象(conn)传递给每个线程,我会得到一个编程错误,即在线程中创建的SQLite对象只能在同一个线程中使用
以前我将所有结果存储在CSV文件中,没有任何文件锁定问题。希望这在sqlite中是可能的。有什么想法吗?您可以使用消费者-生产者模式。例如,您可以创建线程之间共享的队列。从web获取数据的第一个线程将此数据排入共享队列。另一个拥有数据库连接的线程从队列中取出数据并将其传递给数据库 我喜欢Evgeny的答案——队列通常是实现线程间通信的最佳方式。为完整起见,以下是一些其他选项:
- 生成的线程使用完DB连接后,关闭它。这将修复您的
,但由于性能开销,像这样打开和关闭连接通常是不允许的操作错误
- 不要使用子线程。如果每秒一次的任务是相当轻量级的,那么您可以不进行获取和存储,然后一直睡到合适的时间。这是不可取的,因为获取和存储操作可能需要>1秒的时间,并且您将失去使用多线程方法获得的多路资源的好处
或者,如果你像我一样懒惰,你可以使用。它将为您处理线程,()并且它的处理方式是均匀的
另外,如果/当您意识到/决定将Sqlite用于任何并发应用程序都将是一场灾难时,您不必将代码更改为使用MySQL、Postgres或其他任何东西。您只需切换即可。您需要为您的程序设计并发性。SQLite有明确的限制,您需要遵守这些限制,请参见(还有下面的问题)。您不应该为此使用线程。这对你来说是一项微不足道的任务,无论如何,这可能会让你走得更远 只使用一个线程,并让请求完成后触发一个事件进行写入 twisted将负责调度、回调等。。。为你。它将把整个结果作为字符串传递给您,或者您可以通过流处理器运行它(我有一个和一个,当结果仍在下载时,它们都会向调用方触发事件) 根据您对数据所做的操作,您可以在数据完成时将完整结果转储到sqlite中,对其进行烹调并转储,或者在读取时进行烹调并在最后转储
我有一个非常简单的应用程序,它可以在github上实现您想要的功能。我称之为(并行提取)。它在一个时间表上抓取不同的页面,将结果流式传输到一个文件,并在成功完成每个页面后选择性地运行脚本。它还可以做一些奇特的事情,比如条件get,但仍然可以为您所做的任何事情打下良好的基础。似乎是我问题的潜在答案。它的主页描述了我的具体任务。(虽然我还不确定代码有多稳定。)我想看看用于数据持久性的y_串行Python模块: 它处理围绕单个SQLite数据库的死锁问题。如果对并发性的需求越来越大,可以很容易地设置多个数据库的类场,以在随机时间内分散负载 希望这有助于您的项目。。。它应该足够简单,可以在10分钟内实现。在
我找到了解决办法。我不知道为什么python文档对这个选项只字不提。因此,我们必须向连接函数添加一个新的关键字参数 我们可以用它在不同的线程中创建游标。因此,请使用:
sqlite.connect(":memory:", check_same_thread = False)
这对我很合适。当然,从现在起,我需要小心
对数据库的安全多线程访问。无论如何,感谢您的帮助。与流行的观点相反,sqlite3的更新版本确实支持多线程访问 这可以通过可选的关键字参数
检查相同的线程来启用:
sqlite.connect(":memory:", check_same_thread=False)
使用锁定的数据库出现错误的最可能原因是您必须发出
conn.commit()
import threading, sqlite3
class InsertionThread(threading.Thread):
def __init__(self, number):
super(InsertionThread, self).__init__()
self.number = number
def run(self):
conn = sqlite3.connect('yourdb.db', timeout=5)
conn.execute('CREATE TABLE IF NOT EXISTS threadcount (threadnum, count);')
conn.commit()
for i in range(1000):
conn.execute("INSERT INTO threadcount VALUES (?, ?);", (self.number, i))
conn.commit()
# create as many of these as you wish
# but be careful to set the timeout value appropriately: thread switching in
# python takes some time
for i in range(2):
t = InsertionThread(i)
t.start()