SQLAlchemy 'bulk_save_objects' 与 'add_all' 底层逻辑差异?

Ann*_*wan 19 python sqlalchemy

考虑以下三种使用sqlalchemy ORM插入对象的方法:

(1)

for obj in objects:
    session.add(obj)
Run Code Online (Sandbox Code Playgroud)

(2)

session.add_all(objects)
Run Code Online (Sandbox Code Playgroud)

(3)

session.bulk_save_objects(objects)
Run Code Online (Sandbox Code Playgroud)

假设长度objects[]50000

  • 方法 (1) 是否形成并发送50000插入 SQL 查询?
  • 方法 (2) 是否仅形成和发送1SQL 查询?
  • 方法 (3) 是否仅形成和发送1SQL 查询?

我知道这三种方法在速度上差别很大。但是底层实现细节有什么区别?

Ilj*_*ilä 19

(2) 基本上实现为 (1),如果 ORM 必须获取生成的值,例如主键,则两者都可能在刷新期间发出 50,000 次插入。如果这 50,000 个对象具有级联关系,它们甚至可能发出更多信息。

In [4]: session.add_all([Foo() for _ in range(5)])

In [5]: session.commit()
BEGIN (implicit)
INSERT INTO foo DEFAULT VALUES RETURNING foo.id
{}
... (repeats 3 times)
INSERT INTO foo DEFAULT VALUES RETURNING foo.id
{}
COMMIT
Run Code Online (Sandbox Code Playgroud)

如果您事先提供主键和其他数据库生成的值,则Session可以在参数匹配时将单独的插入组合到单个“executemany”操作中。

In [8]: session.add_all([Foo(id=i) for i in range(5)])

In [9]: session.commit()
BEGIN (implicit)
INSERT INTO foo (id) VALUES (%(id)s)
({'id': 0}, {'id': 1}, {'id': 2}, {'id': 3}, {'id': 4})
COMMIT
Run Code Online (Sandbox Code Playgroud)

如果您的 DB-API 驱动程序使用允许它发出包含多个数据的单个语句的方法实现executemany()等效,那么它可能会导致单个查询。例如在executemany_mode='values'为上述启用Postgresql 日志包含后

LOG: statement: INSERT INTO foo (id) VALUES (0),(1),(2),(3),(4)
Run Code Online (Sandbox Code Playgroud)

批量操作会跳过大部分Session机制——例如持久化相关对象——以换取性能提升。例如,默认情况下它不获取默认值,例如主键,这允许它尝试批量更改操作和参数匹配的较少“executemany”操作。

In [12]: session.bulk_save_objects([Foo() for _ in range(5)])
BEGIN (implicit)
INSERT INTO foo DEFAULT VALUES
({}, {}, {}, {}, {})

In [13]: session.commit()
COMMIT
Run Code Online (Sandbox Code Playgroud)

它可能仍会发出多个语句,同样取决于数据和正在使用的 DB-API 驱动程序。该文档是一个很好的阅读。

随着psycopg2快速执行助理启用PostgreSQL的日志上面产生

LOG: statement: INSERT INTO foo DEFAULT VALUES;INSERT INTO foo DEFAULT VALUES;INSERT INTO foo DEFAULT VALUES;INSERT INTO foo DEFAULT VALUES;INSERT INTO foo DEFAULT VALUES
Run Code Online (Sandbox Code Playgroud)

换句话说,多个语句已加入到发送到服务器的“单个”语句中。

因此,最终所有 3 个问题的答案都是“视情况而定”,这当然看起来令人沮丧。

  • `psycopg2` 和其他 DB-API 驱动程序非常相关,因为它们是 SQLAlchemy 用于实际与数据库通信的东西。其中任何一个固有的缓慢都会影响另一个,在“executemany()”的情况下,驱动程序实现最终决定了发出的语句数量。 (3认同)