我创建了以下WebApp.test()
来测试对数据库的并发访问,以模拟在后台任务更新数据库时执行某些查询的浏览器。
执行使用相同连接的新查询时后台任务崩溃。我做错了什么?
@cherrypy.expose
def test(self, x=''):
if x == 'connect':
WebApp.con = sqlite3.connect('db', check_same_thread=False)
return 'connected'
if x == 'insert':
with WebApp.con:
cur = WebApp.con.cursor()
cur.execute('drop table if exists x')
cur.execute('create table x(x)')
for i in range(10000):
cur.execute('insert into x values (%s)' % i)
WebApp.con.commit()
return 'Inserted %s rows' % i
if x == 'query':
with WebApp.con:
cur = WebApp.con.cursor()
cur.execute('select * from x where x < 20')
data = cur.fetchall()
return 'result: %s' % data
我按顺序导航到以下3个页面,每个页面之间只需几秒钟:
test?x=connect
test?x=insert
test?x=query
第一个创建连接。
第二个开始一个非常长的周期,模拟在服务器上运行几分钟的后台作业。
第三个进行简单查询。
当我导航到第三页时,查询结果按预期返回,但仍在运行的循环因以下错误而中断:
WebApp.con.commit()
sqlite3.OperationalError: cannot commit - no transaction is active
答案 0 :(得分:0)
一个问题是您在每种情况下都使用连接对象作为上下文管理器。 docs提到连接将在最后提交或回滚。无论如何,你都明确地提交了它(几乎否定了使用上下文管理器开始的任何好处)。
因此,您的第三个查询会提交连接,这会导致您的insert
脚本出现问题,因为现在已经提交了连接。 (...或者甚至是你在10000次迭代循环中所做的显式提交)
Sqlite也mentions,你不能在线程之间共享连接和游标。我想你每次都想使用新的连接。
来自connect
方法的docs:
&#34;当多个连接访问数据库,并且其中一个进程修改数据库时,SQLite数据库将被锁定,直到提交该事务为止。 timeout参数指定连接在引发异常之前等待锁定消失的时间。 timeout参数的默认值为5.0(五秒)。&#34;
答案 1 :(得分:0)
不可能同时在不同的线程中使用相同的连接。这两个线程将共享相同的事务,相同的游标和其他资源。
可以在不同的线程中使用相同的连接(不是同时)。例如,SqlAlchemy使用连接池。
在简单的Web应用程序(如我的)中,可以在每次调用时创建一个连接,但开销很小。