自动提交模式有效吗



我使用的是python3.7和sqlite3模块;自动提交";模式(即通过将isolation_level设置为None来创建连接对象(。我想检查一下这是否尽可能高效?这就是我通过连接对象创建的方式。

conn = sqlite3.connect(DATABASE_NAME, check_same_thread=False, isolation_level=None)

Pythonsqlite3模块在事务方面有一些奇怪的行为,并且关于该主题的文档相当混乱。其引用";自动提交模式";特别令人困惑——我认为这个术语在Python中的含义与底层SQLite库中使用的术语不同。在这个答案中,我将完全避免使用这个词。

如果isolation_level不是None(实际上默认值是"",而不是None(,则事务有时会自动启动。您可以通过显式调用COMMITROLLBACK或将连接用作上下文管理器来提交或回滚它们:

# Approach 1: transactions start automatically
conn = sqlite3.connect(path_to_db)
with conn:
values = [(row[0], row[1]) for row in conn.execute("SELECT a, b FROM Foo")]
# transaction starts here - in between the two statements!
conn.execute("UPDATE Foo SET a=? WHERE b=?", (values[0][0] + 1, values[0][1]))
# At this point, with conn: will automatically commit/rollback the transaction

(顺便说一句,这是一种非常糟糕的增加值的方法。你不需要读取所有行,甚至不需要单独的SELECTUPDATE语句!但这是为了说明事务是如何工作的,而不是如何编写好的SQL。(

在上面的示例中,Python在到达SELECT语句时将NOT启动事务,但在到达INSERT语句时will将启动事务。with conn:上下文管理器将确保事务在块末尾提交(如果没有异常(或回滚(如果有异常(。

我可以详细说明事务何时以及为什么自动启动,但我的建议是将isolation_level设置为None,这将阻止Python自动启动事务。然后你可以自己手动开始,正如progmatico的回答所说:

# Approach 2: you manually start transactions in your code
conn = sqlite3.connect(path_to_db, isolation_level=None)
with conn:
conn.execute("BEGIN")  # Starts the transaction
values = [(row[0], row[1]) for row in conn.execute("SELECT a, b FROM Foo")]
conn.execute("UPDATE Foo SET a=? WHERE b=?", (values[0][0] + 1, values[0][1]))
# At this point, with conn: will automatically commit/rollback the transaction

或者根本不用它们:

# Approach 3: no explicit transactions at all
conn = sqlite3.connect(path_to_db, isolation_level=None)
values = [(row[0], row[1]) for row in conn.execute("SELECT a, b FROM Foo")]
conn.execute("UPDATE Foo SET a=? WHERE b=?", (values[0][0] + 1, values[0][1]))

在上一个代码片段中,我们从未启动过事务(Python也没有!(,因此底层SQLite库会自动将这两个语句中的每一个放入它们自己的隐式事务中。(这就是底层SQLite库文档所称的"自动提交模式"。哎呀,我又提到了这个词!(

因此,希望您能看到,询问isolation_level=None是否会提高代码的效率是没有意义的。它允许您选择何时开始和完成事务(如果有的话(,就像方法2和方法3一样。但当你这样做的时候,它仍然取决于你,这就是影响你的程序效率的因素。如果要插入大量记录,那么在一个事务中执行这些操作会更高效,就像方法2一样。但您不想让事务打开太长时间,因为这可能会阻止其他线程/进程尝试使用该数据库。在事务中执行SELECT语句通常不会带来太多的速度优势,因此方法3可能是可行的(但事务可能无论如何都是正确性所必需的,具体取决于应用程序(。

问题的答案就写在这里。

它基本上说,您可以阻止sqlite3Python模块在您发送的任何数据修改语句上隐式启动事务,从而允许您作为用户控制代码中包含BEGINROLLBACKSAVEPOINTRELEASE语句的底层sqlite库中的事务。

这对于将应该作为单个事务的一部分执行的多个语句分组非常有用,而不是让每个修改语句都在自己的隐式事务中执行。

最新更新