为什么 SQLAlchemy 使用 sqlite 插入比直接使用 sqlite3 慢 25 倍?

2023-12-05

为什么这个简单的测试用例使用 SQLAlchemy 插入 100,000 行比直接使用 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 的全部原因two单独的库,如果您查看的话您会注意到http://docs.sqlalchemy.org/en/latest/index.html您将在索引页中看到两个不同的部分 - 一个用于 ORM,另一个用于 Core。如果不了解两者,就无法有效地使用 SQLAlchemy。

对于快速批量插入的用例,SQLAlchemy 提供了core,这是 ORM 构建于其之上的 SQL 生成和执行系统。有效地使用这个系统,我们可以生成与原始 SQLite 版本竞争的 INSERT。下面的脚本说明了这一点,以及预先分配主键标识符的 ORM 版本,以便 ORM 可以使用executemany() 来插入行。两个 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

script:

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 SQLAlchemy 使用 sqlite 插入比直接使用 sqlite3 慢 25 倍? 的相关文章

随机推荐

  • 在 C 或 C++ 中以编程方式删除非空目录

    如何在C或C 中删除非空目录 有什么功能吗 rmdir 只删除空目录 请提供一种不使用任何外部库的方法 还告诉我如何用 C 或 C 删除文件 您想要编写一个函数 递归函数是最简单的 但很容易耗尽深层目录上的堆栈空间 来枚举目录的子目录 如果
  • 具有扩展密钥用法的证书仅适用于 Firefox

    我尝试为多个域的服务器生成自签名证书 我在 v3 req 扩展中使用了 openssl 我使用此命令行生成具有多个域和扩展密钥用法的证书 openssl x509 req days 3650 in san domain com csr si
  • atexit 函数的目的是什么?

    我知道函数的地址何时传递给atexit功能 该函数被执行 include
  • 如何在 aws emr 上使用 -libjars?

    Stack Overflow 上有类似的问题 但没有一个回答这个问题 当按照以下链接时出现问题http grepalex com 2013 02 25 hadoop libjars 我们需要使用 export HADOOP CLASSPAT
  • 是否有 OutputDebugString 的 Unicode 替代品?

    OutputDebugString 是原生 ASCII 这意味着它在调用 ASCII 版本之前将输入的 Unicode 字符串转换为本地字符串OutputDebugStringA 有没有什么替代方案OutputDebugString 哪个支
  • .htaccess RewriteRule 有效,但地址栏中的 URL 没有变化?

    我一直在抓着头发试图解决这个问题 但没有任何效果 我有一个网页位于mysite com test php我想做一个简单的 URL 重写并将其更改为mysite com testRewrite 实现这一点的代码应该是 Options Foll
  • 如何将包导入 Scala REPL?

    如何将包导入 Scala 的 REPL 中 我正在尝试导入这个名为 funsets 的包 它有一个名为 FunSets 的对象 我尝试了几种变体import funsets and import funsets 等等 但它仍然没有导入包中的
  • Android后台服务和警报

    最近遇到一个问题Android 4 4杀死了我的应用程序Service and AlarmManager当设备进入睡眠模式时 START STICKY参数没有帮助 我尝试了很多东西 但没有任何效果符合我的需要 在我的任务管理器应用程序中 我
  • 在 OCaml 中打破循环

    我经常需要在 OCaml 中打破循环 至少有两种方法 by exception try for i 0 to 100 do if cond then raise BreakLoop done with BreakLoop gt by whi
  • SQL 按字母顺序排序,小于 10 的数字不带前导零

    是否可以按以下方式在 mySQL 中按字母顺序排序 A1 A2 A3 A5 A10 A11 A20 A23 A24 不幸的是 我收到的订单如下所示 请注意 A10 之前的任何数字如何没有组合在一起 A1 A10 A11 A12A2 A20
  • 由于 AccessibilityNodeInfo getChild 导致出现大量 ANR

    收到大量 ANR 报告 main prio 5 tid 1 可运行 组 主 sCount 0 dsCount 0 标志 0 obj 0x72e8a568 自我 0xe65da000 sysTid 20592 好 0 cgrp 默认调度 0
  • 对 Intellij 如何编译事物和处理资源感到困惑

    我对 Visual Studio 的 IntelliJ 非常陌生 而且我通常对资源文件在 IntelliJ 项目中的工作方式感到非常困惑 我阅读了一些有关将资源添加到类路径以及 IntelliJ 文档的问题和解答 但仍然感到困惑 我还有几个
  • 在 R Markdown 中使用 pandoc 和 bookdown 删除图标题中的冒号

    我正在更改 R Markdown 中图形标题的字体并使用bookdown和 pandoc 这样做 我的问题与以下问题密切相关 如何更改bookdown中的图形标题格式 我能够获得正确的图形编号 并且能够更改标题的 图 1 部分的格式 但是
  • 是否可以从 adf 连接到 databricks deltalake 表

    我正在寻找一种能够从 ADF 和其他 Azure 服务 如数据目录 连接到 Databricks deltalake 表的方法 我没有看到 ADF 数据源中列出的 databricks 数据存储 关于类似的问题 是否可以从 Azure 数据
  • 如何从jni端调用java方法?

    我在 jni 端完成了一些 C 代码函数 并且所有工作正常 public native String getMessage 函数将字符串从 jni 端返回到 java 端 它工作正常 所有其他 jni 代码也工作正常 但问题是如何在 jni
  • 使用 Python 刷新 Excel 外部数据

    我有一个 Excel 文件 我在其上运行 Python 脚本 Excel 文件具有需要在运行 Python 脚本之前刷新的外部数据连接 我指的功能在这里 我使用的是 Python 2 7 并且依靠 Pandas 进行大部分 Excel 数据
  • 如何重命名 DataFrame 中与嵌套 JSON 对应的字段

    我正在尝试使用以下方法处理移动应用程序中收到的 JSON 事件 如点击等 spark 1 5 2 有多个应用程序版本 并且事件的结构因版本而异 假设版本 1 具有以下结构 timestamp ev app appName XYZ appVe
  • 基于数据集的SSRS多值参数过滤器

    假设我在 SSRS 中有一个名为 DataSet1 的数据集 如下所示 CREATE TABLE data ID int Value int UserID varchar 2 INSERT INTO data VALUES 1 1000 A
  • 我什么时候应该使用 Moq 的 .As 方法?

    我们到底什么时候需要使用 AsMoq提供的方法 来自快速入门文档 implementing multiple interfaces in mock var foo new Mock
  • 为什么 SQLAlchemy 使用 sqlite 插入比直接使用 sqlite3 慢 25 倍?

    为什么这个简单的测试用例使用 SQLAlchemy 插入 100 000 行比直接使用 sqlite3 驱动程序慢 25 倍 我在现实应用程序中也看到过类似的减速情况 难道我做错了什么 usr bin env python Why is S