如何在Python中使用sqlite3连接

0 投票
2 回答
633 浏览
提问于 2025-04-18 15:43

我创建了一个叫 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

我依次访问了以下三个页面,每个页面之间只等了几秒钟:

test?x=connect
test?x=insert
test?x=query

第一个页面是用来建立连接的。

第二个页面开始了一个非常长的循环,模拟一个将在服务器上运行几分钟的后台任务。

第三个页面则是执行一个简单的查询。

当我访问第三个页面时,查询的结果如预期返回了,但仍在运行的循环却被中断,并出现了以下错误:

    WebApp.con.commit()
sqlite3.OperationalError: cannot commit - no transaction is active

2 个回答

0

一个问题是你在每个情况下都把连接对象当作上下文管理器来用。文档提到,连接在结束时会被提交或者回滚。你其实是明确地在提交,这样就几乎没什么好处了,因为你本来是想用上下文管理器的。

所以你的第三个查询提交了连接,这就导致了你的insert脚本出现问题,因为连接现在已经被提交了。(...或者可能是你在10000次循环中做的那些明确提交造成的)

Sqlite还提到,你不能在多个线程之间共享连接和游标。我觉得你每次都应该使用一个新的连接。

关于connect方法的文档中提到:"当多个连接访问数据库时,如果其中一个进程修改了数据库,SQLite数据库会被锁定,直到那个事务被提交。timeout参数指定连接应该等待多长时间,直到锁被释放,否则就会抛出异常。timeout参数的默认值是5.0(五秒)。"

0

经过一些研究,我得出了以下结论:

在不同的线程中,不能同时使用同一个连接。因为这样的话,这两个线程会共享同一个事务、同一个游标和其他资源。

不过,可以在不同的线程中使用同一个连接,只要不是同时使用。例如,SqlAlchemy 就使用了连接池。

对于简单的网页应用(像我这个),每次调用时创建一个连接是可以的,这样的开销非常小。

撰写回答