我有一个使用"线程"模块的Python程序.每隔一秒,我的程序就会启动一个新的线程,从网络中获取一些数据,并将这些数据存储到我的硬盘中.我想使用sqlite3来存储这些结果,但我无法让它工作.问题似乎与以下几行有关:
conn = sqlite3.connect("mydatabase.db")
如果我将这行代码放在每个线程中,我会得到一个OperationalError,告诉我数据库文件已被锁定.我想这意味着另一个线程通过sqlite3连接打开了mydatabase.db并锁定了它.
如果我将这行代码放在主程序中并将连接对象(conn)传递给每个线程,我会得到一个ProgrammingError,说在一个线程中创建的SQLite对象只能在同一个线程中使用.
以前我将所有结果存储在CSV文件中,并且没有任何这些文件锁定问题.希望这可以用sqlite实现.有任何想法吗?
与流行的看法相反,较新版本的sqlite3 确实支持来自多个线程的访问.
这可以通过可选的关键字参数启用check_same_thread
:
sqlite.connect(":memory:", check_same_thread=False)
您可以使用消费者 - 生产者模式.例如,您可以创建线程之间共享的队列.从Web获取数据的第一个线程将此数据排入共享队列.另一个拥有数据库连接的线程将队列中的数据从队列中取出并将其传递给数据库.
在mail.python.org.pipermail.1239789
上找到以下内容我找到了解决方案.我不知道为什么python文档没有关于这个选项的单一词.所以我们必须在连接函数中添加一个新的关键字参数,我们将能够在不同的线程中创建游标.所以使用:
sqlite.connect(":memory:", check_same_thread = False)
对我来说很完美.当然,从现在开始,我需要处理对数据库的安全多线程访问.无论如何,所有的尝试帮助.
你根本不应该使用线程.对于扭曲而言,这是一项微不足道的任务,无论如何这可能会让你更进一步.
仅使用一个线程,并完成请求触发事件以进行写入.
twisted会为你安排调度,回调等等.它会将整个结果作为字符串传递给您,或者您可以通过流处理器运行它(我有一个Twitter API和一个friendfeed API,它们都会在调用结果仍在下载时向调用者发出事件).
根据您对数据的处理方式,您可以将完整的结果转储到sqlite中,完成后,将其煮熟并转储,或者在读取时将其煮熟并在最后转储.
我有一个非常简单的应用程序,它在github上做了一些你想要的东西.我称之为pfetch(并行获取).它按计划抓取各种页面,将结果流式传输到文件,并可选择在成功完成每个脚本后运行脚本.它也做了一些奇特的东西,如条件GET,但仍然可以成为你正在做的任何事情的良好基础.
切换到多处理.它更好,扩展性好,可以通过使用多个CPU来超越使用多个内核,并且接口与使用python线程模块相同.
或者,正如Ali建议的那样,只需使用SQLAlchemy的线程池机制.它将自动为您处理所有事情并具有许多额外功能,仅引用其中一些:
SQLAlchemy包括SQLite,Postgres,MySQL,Oracle,MS-SQL,Firebird,MaxDB,MS Access,Sybase和Informix的方言; IBM还发布了DB2驱动程序.因此,如果您决定放弃SQLite,则无需重写应用程序.
工作单元系统是SQLAlchemy的对象关系映射器(ORM)的核心部分,它将待处理的创建/插入/更新/删除操作组织到队列中,并在一个批处理中将它们全部刷新.为了实现这一点,它对队列中的所有修改项执行拓扑"依赖性排序",以便遵守外键约束,并将冗余语句组合在一起,有时甚至可以进一步批处理它们.这产生了最高效率和交易安全性,并最大限度地减少了死锁的可能性.
或者,如果你像我一样懒惰,你可以使用SQLAlchemy.它将为您处理线程(使用线程本地和一些连接池),它的工作方式甚至可配置.
如果/当您意识到/决定使用Sqlite进行任何并发应用程序将是一场灾难时,您将不必更改代码以使用MySQL,Postgres或其他任何东西.你可以切换.