[解決済み] SQLAlchemyのsqliteによる挿入は、なぜsqlite3を直接使うよりも25倍も遅いのですか?
質問
この単純なテストケースの 100,000 行の挿入が、SQLAlchemy を使った場合、sqlite3 ドライバを直接使った場合よりも 25 倍も遅いのはなぜですか? 実際のアプリケーションでも同じような速度低下を見たことがあります。 私は何か間違ったことをしているのでしょうか?
#!/usr/bin/env python
# Why is SQLAlchemy with SQLite so slow?
# Output from this program:
# SqlAlchemy: Total time for 100000 records 10.74 secs
# sqlite3: Total time for 100000 records 0.40 secs
import time
import sqlite3
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
Base = declarative_base()
DBSession = scoped_session(sessionmaker())
class Customer(Base):
__tablename__ = "customer"
id = Column(Integer, primary_key=True)
name = Column(String(255))
def init_sqlalchemy(dbname = 'sqlite:///sqlalchemy.db'):
engine = create_engine(dbname, echo=False)
DBSession.configure(bind=engine, autoflush=False, expire_on_commit=False)
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
def test_sqlalchemy(n=100000):
init_sqlalchemy()
t0 = time.time()
for i in range(n):
customer = Customer()
customer.name = 'NAME ' + str(i)
DBSession.add(customer)
DBSession.commit()
print "SqlAlchemy: Total time for " + str(n) + " records " + str(time.time() - t0) + " secs"
def init_sqlite3(dbname):
conn = sqlite3.connect(dbname)
c = conn.cursor()
c.execute("DROP TABLE IF EXISTS customer")
c.execute("CREATE TABLE customer (id INTEGER NOT NULL, name VARCHAR(255), PRIMARY KEY(id))")
conn.commit()
return conn
def test_sqlite3(n=100000, dbname = 'sqlite3.db'):
conn = init_sqlite3(dbname)
c = conn.cursor()
t0 = time.time()
for i in range(n):
row = ('NAME ' + str(i),)
c.execute("INSERT INTO customer (name) VALUES (?)", row)
conn.commit()
print "sqlite3: Total time for " + str(n) + " records " + str(time.time() - t0) + " sec"
if __name__ == '__main__':
test_sqlalchemy(100000)
test_sqlite3(100000)
私は数多くのバリエーションを試しました ( http://pastebin.com/zCmzDraU )
どのように解決するのですか?
SQLAlchemy ORM は 作業単位 パターンを使います。 このパターンは、単純なデータの挿入をはるかに超えています。 オブジェクトに割り当てられた属性は、オブジェクトの変更が行われるとそれを追跡する属性計装システムを使用して受信されること、挿入されたすべての行が アイデンティティマップ これは、SQLAlchemy が各行に対して "最後に挿入された id" を取得しなければならない、という効果を持ちます。また、挿入される行は、必要に応じてスキャンされ、依存関係のためにソートされます。 オブジェクトはまた、これらすべてを実行し続けるために、かなりの度合いで帳簿をつけることになります。
基本的に、作業単位は、複雑なオブジェクト グラフをリレーショナル データベースに明示的な永続化コードなしで永続化するタスクを自動化するための大規模な自動化であり、この自動化には代償があります。
ですから、ORM は基本的に高性能な一括挿入を意図したものではありません。 これが、SQLAlchemy が に の別ライブラリを持っている理由です。 http://docs.sqlalchemy.org/en/latest/index.html を見ると、インデックスページが2つに分かれているのがわかるでしょう - 1つは ORM 用、もう1つは Core 用です。 SQLAlchemy を効果的に使うには、その両方を理解する必要があります。
高速な一括挿入のユースケースのために、SQLAlchemy は コア これは ORM がその上に構築する、SQL の生成と実行のシステムです。 このシステムを効果的に使えば、生の SQLite バージョンと競争力のある INSERT を作ることができます。 以下のスクリプトは、ORM が行を挿入するために executemany() を使用できるように主キー識別子を事前に割り当てる ORM バージョンと同様に、これを図示しています。 どちらの ORM バージョンも、一度に 1000 レコードでフラッシュをチャンクしており、パフォーマンスに大きな影響を与えます。
ここで観察されたランタイムは次のとおりです。
SqlAlchemy ORM: Total time for 100000 records 16.4133379459 secs
SqlAlchemy ORM pk given: Total time for 100000 records 9.77570986748 secs
SqlAlchemy Core: Total time for 100000 records 0.568737983704 secs
sqlite3: Total time for 100000 records 0.595796823502 sec
スクリプトを使用します。
import time
import sqlite3
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
Base = declarative_base()
DBSession = scoped_session(sessionmaker())
class Customer(Base):
__tablename__ = "customer"
id = Column(Integer, primary_key=True)
name = Column(String(255))
def init_sqlalchemy(dbname = 'sqlite:///sqlalchemy.db'):
global engine
engine = create_engine(dbname, echo=False)
DBSession.remove()
DBSession.configure(bind=engine, autoflush=False, expire_on_commit=False)
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
def test_sqlalchemy_orm(n=100000):
init_sqlalchemy()
t0 = time.time()
for i in range(n):
customer = Customer()
customer.name = 'NAME ' + str(i)
DBSession.add(customer)
if i % 1000 == 0:
DBSession.flush()
DBSession.commit()
print "SqlAlchemy ORM: Total time for " + str(n) + " records " + str(time.time() - t0) + " secs"
def test_sqlalchemy_orm_pk_given(n=100000):
init_sqlalchemy()
t0 = time.time()
for i in range(n):
customer = Customer(id=i+1, name="NAME " + str(i))
DBSession.add(customer)
if i % 1000 == 0:
DBSession.flush()
DBSession.commit()
print "SqlAlchemy ORM pk given: Total time for " + str(n) + " records " + str(time.time() - t0) + " secs"
def test_sqlalchemy_core(n=100000):
init_sqlalchemy()
t0 = time.time()
engine.execute(
Customer.__table__.insert(),
[{"name":'NAME ' + str(i)} for i in range(n)]
)
print "SqlAlchemy Core: Total time for " + str(n) + " records " + str(time.time() - t0) + " secs"
def init_sqlite3(dbname):
conn = sqlite3.connect(dbname)
c = conn.cursor()
c.execute("DROP TABLE IF EXISTS customer")
c.execute("CREATE TABLE customer (id INTEGER NOT NULL, name VARCHAR(255), PRIMARY KEY(id))")
conn.commit()
return conn
def test_sqlite3(n=100000, dbname = 'sqlite3.db'):
conn = init_sqlite3(dbname)
c = conn.cursor()
t0 = time.time()
for i in range(n):
row = ('NAME ' + str(i),)
c.execute("INSERT INTO customer (name) VALUES (?)", row)
conn.commit()
print "sqlite3: Total time for " + str(n) + " records " + str(time.time() - t0) + " sec"
if __name__ == '__main__':
test_sqlalchemy_orm(100000)
test_sqlalchemy_orm_pk_given(100000)
test_sqlalchemy_core(100000)
test_sqlite3(100000)
こちらもご覧ください。 http://docs.sqlalchemy.org/en/latest/faq/performance.html
関連
-
[解決済み] SQLAlchemy で SQLite を使って個別の行を返す
-
[解決済み] なぜ SQLAlchemy の count() は生のクエリよりずっと遅いのでしょうか?
-
[解決済み] なぜC++はPythonよりもstdinからの行の読み込みが遅いのですか?
-
[解決済み】float < integerの比較で4倍遅くなるものがあるのはなぜですか?
-
[解決済み] なぜアーリーリターンは他より遅いのか?
-
[解決済み] googletransがエラー 'NoneType' オブジェクトに 'group' 属性がない、と言って動かなくなった。
-
[解決済み] データフレームをソートした後にインデックスを更新する
-
[解決済み] 古いバージョンのPythonにおける辞書のキーの並び順
-
[解決済み] Ctrl-CでPythonスクリプトを終了できない
-
[解決済み] pycharmがタブをスペースに自動変換する
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] SQLiteのINSERT/per-secondのパフォーマンスを向上させる
-
[解決済み] Djangoで2つの日付の間を選択する
-
[解決済み] バブルソートの宿題
-
[解決済み] Python 3でバイナリデータを標準出力に書き込むには?
-
[解決済み] 値で列挙名を取得する [重複]。
-
[解決済み] 古いバージョンのPythonにおける辞書のキーの並び順
-
[解決済み] 範囲指定された浮動小数点数のランダムな配列を生成します。
-
[解決済み] djangoフレームワークでフォームフィールドから値を取得するには?
-
[解決済み] 単純な文字列からtimedeltaオブジェクトを作成する方法
-
[解決済み] Django filter queryset __in for *every* item in list