web-dev-qa-db-ja.com

python sqlite3、どれくらいの頻度でコミットする必要がありますか?

私が書いたsqliteマネージャークラスを使用してデータベースに多くの変更を加えるforループがありますが、コミットする必要がある頻度がわかりません...

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()

基本的に私の質問は、コミットを2回呼び出す必要があるかどうか、または両方の変更を行った後で1回だけ呼び出すことができるかどうかです。

17
deltaskelta

conn.commit()をプロシージャの最後に1回呼び出すかどうかは、データベースが変更されるたびに行うかどうかは、いくつかの要因によって異なります。

同時閲覧者が見るもの

これは、誰もが一目で考えていることです。データベースへの変更がコミットされると、他の接続から見えるようになります。コミットされない限り、変更が行われた接続のローカルでのみ表示されたままになります。 sqliteの同時実行機能は限られているため、データベースはトランザクションが開いている間のみ読み取ることができます。

次のスクリプト を実行し、その出力を調査することで、何が起こるかを調査できます。

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()

出力:

$ 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)]
$

ですから、同じスクリプトでも別のプログラムでも、流暢な読者が2度にオフになる状況に耐えられるかどうかによって異なります。

多数の変更が行われる場合、他の2つの側面がシーンに入ります。

パフォーマンス

データベース変更のパフォーマンスは、それらをどのように行うかに大きく依存します。 [〜#〜] faq [〜#〜] としてすでに注記されています:

実際、SQLiteは平均的なデスクトップコンピューターで毎秒50,000以上のINSERTステートメントを簡単に実行します。しかし、1秒あたり数十のトランザクションしか実行しません。 [...]

ここで詳細を理解することは絶対に役立ちますので、 link をたどって詳細を確認してください。これも参照してください awsome analysis 。それはCで書かれていますが、Pythonで同じことをするのと同じような結果になります。

注:両方のリソースがINSERTを参照していますが、同じ引数のUPDATEの場合も状況はほとんど同じです。

排他的にデータベースをロックする

すでに前述したように、開いている(コミットされていない)トランザクションは、同時接続からの変更をブロックします。したがって、データベースへの多くの変更を実行し、それらをまとめてコミットすることで、1つのトランザクションにまとめることは理にかなっています。

残念ながら、変更の計算には時間がかかる場合があります。同時アクセスが問題になる場合は、その間データベースをロックする必要はありません。保留中のUPDATEおよびINSERTステートメントを何らかの方法で収集するのはかなり難しくなる可能性があるため、通常、これによりパフォーマンスと排他ロックの間のトレードオフが生じます。

25
flaschbier