将更改同步到数据库时,SQLAlchemy
ORM使用工作单元模式。这种模式远远超出了简单的数据“插入”。它包括使用属性检测系统接收在对象上分配的属性,该系统会跟踪对象进行更改时的更改,包括在标识图中跟踪所有插入的行这样做的结果是,对于每行,SQLAlchemy必须检索其“最后插入的ID”(如果尚未给出),并且还涉及对要插入的行进行扫描并根据需要对相关性进行排序。对象也要经过一定程度的记账才能保持所有这些运行,这对于大量行一次而言可能会浪费大量时间处理大型数据结构,因此最好对它们进行分块。
基本上,工作单元是高度自动化的,以实现将复杂对象图持久化到没有显式持久性代码的关系数据库中的任务,并且这种自动化是有代价的。
因此,ORM基本上不适合用于高性能批量插入。这就是SQLAlchemy具有 两个
单独的库的全部原因,如果您查看http://docs.sqlalchemy.org/zh-
CN/latest/index.html,您会注意到,您会在索引页面上看到两个不同的部分-
一个用于ORM,一个用于Core。如果您不了解两者,就无法有效地使用SQLAlchemy。
对于快速批量插入的用例,SQLAlchemy提供了core,它是ORM在其之上构建的SQL生成和执行系统。有效地使用该系统,我们可以生产出与原始SQLite版本相比具有竞争力的INSERT。下面的脚本对此进行了说明,还提供了一个预分配主键标识符的ORM版本,以便ORM可以使用executemany()插入行。两种ORM版本也一次将刷新记录分块进行,这会对性能产生重大影响。
这里观察到的运行时是:
SqlAlchemy ORM: Total time for 100000 records 16.4133379459 secsSqlAlchemy ORM pk given: Total time for 100000 records 9.77570986748 secsSqlAlchemy Core: Total time for 100000 records 0.568737983704 secssqlite3: Total time for 100000 records 0.595796823502 sec
脚本:
import timeimport sqlite3from sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import Column, Integer, String, create_enginefrom sqlalchemy.orm import scoped_session, sessionmakerbase = 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 conndef 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
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)