ORM SQLAlchemy używa wzorca jednostki pracy podczas synchronizowania zmian w bazie danych. Ten wzorzec wykracza daleko poza zwykłe „wstawianie” danych. Obejmuje to, że atrybuty przypisane do obiektów są odbierane za pomocą systemu oprzyrządowania atrybutów, który śledzi zmiany w obiektach w trakcie ich tworzenia, w tym wszystkie wstawione wiersze są śledzone na mapie tożsamościco powoduje, że dla każdego wiersza SQLAlchemy musi pobrać swój „ostatnio wstawiony identyfikator”, jeśli nie został jeszcze podany, a także powoduje, że wiersze do wstawienia są skanowane i sortowane pod kątem zależności w razie potrzeby. Obiekty podlegają również dość dużej księgowości, aby wszystko to działało, co w przypadku bardzo dużej liczby wierszy naraz może powodować nadmierną ilość czasu spędzanego z dużymi strukturami danych, dlatego najlepiej jest je podzielić.
Zasadniczo jednostka pracy to duży stopień automatyzacji w celu zautomatyzowania zadania utrwalania złożonego grafu obiektowego w relacyjnej bazie danych bez jawnego kodu trwałości, a ta automatyzacja ma swoją cenę.
Dlatego też ORM zasadniczo nie są przeznaczone do wysokowydajnych wkładek masowych. To jest cały powód, dla którego SQLAlchemy ma dwie oddzielne biblioteki, o których zauważysz, jeśli spojrzysz na http://docs.sqlalchemy.org/en/latest/index.html , zobaczysz dwie odrębne połówki strony indeksu - jeden dla ORM i jeden dla Core. Nie można efektywnie używać SQLAlchemy bez zrozumienia obu.
W przypadku użycia szybkiego wstawiania zbiorczego SQLAlchemy zapewnia rdzeń , czyli system generowania i wykonywania kodu SQL, na którym opiera się ORM. Korzystając z tego systemu skutecznie, możemy stworzyć INSERT, który jest konkurencyjny w stosunku do surowej wersji SQLite. Poniższy skrypt ilustruje to, a także wersję ORM, która wstępnie przypisuje identyfikatory kluczy podstawowych, dzięki czemu ORM może używać funkcji executemany () do wstawiania wierszy. Obie wersje ORM również dzielą rzuty na 1000 rekordów na raz, co ma znaczący wpływ na wydajność.
Obserwowane tutaj okresy działania to:
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
scenariusz:
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)
Zobacz także: http://docs.sqlalchemy.org/en/latest/faq/performance.html