私が書いた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回だけ呼び出すことができるかどうかです。
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
ステートメントを何らかの方法で収集するのはかなり難しくなる可能性があるため、通常、これによりパフォーマンスと排他ロックの間のトレードオフが生じます。