sqlite3快速多次提交时操作错误

2 投票
1 回答
1287 浏览
提问于 2025-04-15 15:17

我在运行我正在开发的一个应用程序时遇到了这个问题:

sqlite3.OperationalError: SQL logic error or missing database

接下来是一个简化但完整的示例,展示了我遇到的问题。这个示例使用了两个表:一个用来存储用户信息,另一个用来记录用户信息在外部目录系统中是否是最新的。(可以想象,在我的实际应用中,这些表要长得多。)这个示例创建了一些随机用户,然后遍历这些(随机的)用户,把他们添加到第二个表中。

#!/usr/bin/env python

import sqlite3
import random

def random_username():
    # Returns one of 10 000 four-letter placeholders for a username
    seq = 'abcdefghij'
    return random.choice(seq) + random.choice(seq) + \
           random.choice(seq) + random.choice(seq)

connection = sqlite3.connect("test.sqlite")

connection.execute('''CREATE TABLE IF NOT EXISTS "users" (
    "entry_id"              INTEGER PRIMARY KEY AUTOINCREMENT  NOT NULL ,
    "user_id"               INTEGER NOT NULL ,
    "obfuscated_name"       TEXT NOT NULL)''')
connection.execute('''CREATE TABLE IF NOT EXISTS "dir_x_user" (
    "user_id"               INTEGER PRIMARY KEY NOT NULL)''')        

# Create a bunch of random users
random.seed(0)  # get the same results every time
for i in xrange(1500):
    connection.execute('''INSERT INTO users
        (user_id, obfuscated_name) VALUES (?, ?)''',
        (i, random_username()))
connection.commit()

#random.seed()
for i in xrange(4000):
    username = random_username()
    result = connection.execute(
        'SELECT user_id FROM users WHERE obfuscated_name = ?',
        (username, ))
    row = result.fetchone()
    if row is not None:
        user_id = row[0]
        print "  %4d %s" % (user_id, username)
        connection.execute(
            'INSERT OR IGNORE INTO dir_x_user (user_id) VALUES(?)',
            (user_id, ))
    else:
        print "     ? %s" % username
    if i % 10 == 0:
        print "i = %s; committing" % i
        connection.commit()
connection.commit()

特别需要注意的是,接近结尾的那一行:

if i % 10 == 0:

在实际应用中,我是从网络资源中查询数据,并希望不时提交用户信息。修改那一行会改变错误发生的时机;似乎在我提交的时候,有一定几率会出现OperationalError(操作错误)。这似乎和我放入数据库的数据有些关系,但我无法确定具体问题是什么。

大多数情况下,如果我先读取所有数据,然后只提交一次,就不会出现错误。[是的,这里有一个明显的解决方法,但潜在的问题依然存在。]

以下是我电脑上运行示例的最后部分:

     ? cgha
i = 530; committing
     ? gegh
     ? aabd
     ? efhe
     ? jhji
     ? hejd
     ? biei
     ? eiaa
     ? eiib
     ? bgbf
   759 bedd
i = 540; committing
Traceback (most recent call last):
  File "sqlitetest.py", line 46, in <module>
    connection.commit()
sqlite3.OperationalError: SQL logic error or missing database

我使用的是Mac OS X 10.5.8,内置的Python版本是2.5.1,Sqlite3版本是3.4.0。

1 个回答

2

正如名字中的“lite”部分所暗示的,sqlite3 是为了轻量级数据库使用而设计的,并不是像一些大牌数据库那样能处理大量并发请求。我的理解是,当你发出新的请求时,sqlite 可能还没有完成你上一个请求的更改。

所以,我给你一些建议:

  1. 你可以花很多时间去了解 sqlite3 中的文件锁定、并发和事务
  2. 你可以通过让你的应用在第一次失败后重试这个操作来增加一些容错能力,正如这篇 Reddit 文章中提到的,里面有一些建议,比如“如果代码有一个有效的机制可以简单地重试,大部分 sqlite 的并发问题就会消失”和“将 isolation_level=None 传递给连接似乎可以解决这个问题”。
  3. 你可以考虑换用一个更能扩展的数据库,比如 PostgreSQL

(在我看来,第二个或第三个建议更值得考虑。)

撰写回答