简体   繁体   English

python sqlite3,我多久要提交一次?

[英]python sqlite3, how often do I have to commit?

I have a for loop that is making many changes to a database with a sqlite manager class I wrote, but I am unsure about how often I have to commit... 我有一个for循环,它使用我编写的sqlite管理器类对数据库进行了许多更改,但我不确定我多久需要提交...

for i in list:
    c.execute('UPDATE table x=y WHERE foo=bar')
    conn.commit()
    c.execute('UPDATE table x=z+y WHERE foo=bar')
    conn.commit()

Basically my question is whether I have to call commit twice there, or if I can just call it once after I have made both changes? 基本上我的问题是我是否必须在那里调用两次提交,或者我是否可以在完成两次更改后调用它一次?

Whether you call conn.commit() once at the end of the procedure of after every single database change depends on several factors. 是否在每次数据库更改后的过程结束时调用conn.commit()一次取决于几个因素。

What concurrent readers see 并发读者看到了什么

This is what everybody thinks of at first sight: When a change to the database is committed, it becomes visible for other connections. 这是每个人第一眼就想到的:当提交对数据库的更改时,它对其他连接变得可见。 Unless it is committed, it remains visible only locally for the connection to which the change was done. 除非提交,否则它仅在本地可见,用于进行更改的连接。 Because of the limited concurrency features of sqlite , the database can only be read while a transaction is open. 由于sqlite并发功能有限,因此只能在事务打开时读取数据库。

You can investigate what happens by running the following script and investigating its output: 您可以通过运行以下脚本并调查其输出来调查发生的情况:

import os
import sqlite3

_DBPATH = "./q6996603.sqlite"

def fresh_db():
    if os.path.isfile(_DBPATH):
        os.remove(_DBPATH)
    with sqlite3.connect(_DBPATH) as conn:
        cur = conn.cursor().executescript("""
            CREATE TABLE "mytable" (
                "id" INTEGER PRIMARY KEY AUTOINCREMENT, -- rowid
                "data" INTEGER
            );
            """)
    print "created %s" % _DBPATH

# functions are syntactic sugar only and use global conn, cur, rowid

def select():
    sql = 'select * from "mytable"'
    rows = cur.execute(sql).fetchall()
    print "   same connection sees", rows
    # simulate another script accessing tha database concurrently
    with sqlite3.connect(_DBPATH) as conn2:
        rows = conn2.cursor().execute(sql).fetchall()
    print "   other connection sees", rows

def count():
    print "counting up"
    cur.execute('update "mytable" set data = data + 1 where "id" = ?', (rowid,))

def commit():
    print "commit"
    conn.commit()

# now the script
fresh_db()
with sqlite3.connect(_DBPATH) as conn:
    print "--- prepare test case"
    sql = 'insert into "mytable"(data) values(17)'
    print sql
    cur = conn.cursor().execute(sql)
    rowid = cur.lastrowid
    print "rowid =", rowid
    commit()
    select()
    print "--- two consecutive w/o commit"
    count()
    select()
    count()
    select()
    commit()
    select()
    print "--- two consecutive with commit"
    count()
    select()
    commit()
    select()
    count()
    select()
    commit()
    select()

Output: 输出:

$ python try.py 
created ./q6996603.sqlite
--- prepare test case
insert into "mytable"(data) values(17)
rowid = 1
commit
   same connection sees [(1, 17)]
   other connection sees [(1, 17)]
--- two consecutive w/o commit
counting up
   same connection sees [(1, 18)]
   other connection sees [(1, 17)]
counting up
   same connection sees [(1, 19)]
   other connection sees [(1, 17)]
commit
   same connection sees [(1, 19)]
   other connection sees [(1, 19)]
--- two consecutive with commit
counting up
   same connection sees [(1, 20)]
   other connection sees [(1, 19)]
commit
   same connection sees [(1, 20)]
   other connection sees [(1, 20)]
counting up
   same connection sees [(1, 21)]
   other connection sees [(1, 20)]
commit
   same connection sees [(1, 21)]
   other connection sees [(1, 21)]
$

So it depends whether you can live with the situation that a cuncurrent reader, be it in the same script or in another program, will be off by two at times. 所以这取决于你是否能够忍受这样一种情况:一个当前的读者,无论是在同一个剧本中还是在另一个程序中,它有时会被两个人关闭。

When a large number of changes is to be done, two other aspects enter the scene: 当要进行大量更改时,另外两个方面将进入场景:

Performance 性能

The performance of database changes dramatically depends on how you do them. 数据库更改的性能在很大程度上取决于您的操作方式。 It is already noted as a FAQ : 它已作为常见问题解答

Actually, SQLite will easily do 50,000 or more INSERT statements per second on an average desktop computer. 实际上,SQLite很容易在普通的台式计算机上每秒执行50,000或更多INSERT语句。 But it will only do a few dozen transactions per second. 但它每秒只会进行几十次交易。 [...] [...]

It is absolutely helpful to understand the details here, so do not hesitate to follow the link and dive in. Also see this awsome analysis . 了解这里的细节绝对有帮助,所以不要犹豫,请关注链接并深入了解。另请参阅此详细分析 It's written in C, but the results would be similar would one do the same in Python. 它是用C语言编写的,但结果与Python中的结果相似。

Note: While both resources refer to INSERT , the situation will be very much the same for UPDATE for the same arguments. 注意:虽然两个资源都引用INSERT ,但对于相同的参数, UPDATE的情况将大致相同。

Exclusively locking the database 专门锁定数据库

As already mentioned above, an open (uncommitted) transaction will block changes from concurrent connections. 如上所述,open(未提交)事务将阻止并发连接的更改。 So it makes sense to bundle many changes to the database into a single transaction by executing them and the jointly committing the whole bunch of them. 因此,通过执行它们并共同提交整个数据库,将许多更改捆绑到单个事务中是有意义的。

Unfortunately, sometimes, computing the changes may take some time. 不幸的是,有时,计算更改可能需要一些时间。 When concurrent access is an issue you will not want to lock your database for that long. 当并发访问是一个问题时,您不希望长时间锁定您的数据库。 Because it can become rather tricky to collect pending UPDATE and INSERT statements somehow, this will usually leave you with a tradeoff between performance and exclusive locking. 因为以某种方式收集挂起的UPDATEINSERT语句会变得相当棘手,这通常会让您在性能和独占锁定之间进行权衡。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM