SQLAlchemy"默认"vs"server_default"性能

ben*_*nvc 17 python sql postgresql performance sqlalchemy

使用SQLAlchemy和PostgreSQL 时,使用default而不是server_default映射表列默认值时是否存在性能优势(或缺点)?

我的理解是defaultINSERT(通常)中呈现表达式server_default并将表达式放在CREATE TABLE语句中.似乎server_default类似于直接在db中对默认值的典型处理,例如:

CREATE TABLE example (
    id serial PRIMARY KEY,
    updated timestamptz DEFAULT now()
);
Run Code Online (Sandbox Code Playgroud)

...但我不清楚是否更有效地处理默认值INSERT或通过表创建.

如果default下面示例中的每个参数都更改为server_default?行插入是否会有任何性能改进或降级?

from uuid import uuid4
from sqlalchemy import Column, Boolean, DateTime, Integer
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func

Base = declarative_base()

class Item(Base):
    __tablename__ = 'item'

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
    count = Column(Integer, nullable=False, default=0)
    flag = Column(Boolean, nullable=False, default=False)
    updated = Column(DateTime(timezone=True), nullable=False, default=func.now())
Run Code Online (Sandbox Code Playgroud)

注意:到目前为止我发现的最佳解释是什么时候使用defaultserver_default不是解决性能问题(参见Mike Bayer关于这个问题的答案).我对该解释的简要概述default是优先于server_default......

  • db无法处理您需要或想要用于默认值的表达式.
  • 您不能或不想直接修改架构.

......所以问题仍然是在选择default和之间是否应该考虑表现server_default

Mar*_*ers 25

不可能给你一个'这是更快'的答案,因为每个默认值表达式的性能可以在服务器和Python中广泛变化.检索当前时间的函数与标量默认值的行为不同.

接下来,您必须意识到可以通过五种不同的方式提供默认值:

请注意,当涉及到确定默认值的SQL表达式时,无论是客户端SQL表达式,服务器端DLL表达式还是触发器,它对于默认值表达式来自的数据库几乎没有什么区别. .查询执行器需要知道如何为给定列生成值,一旦从DML语句或模式定义中解析出来,服务器仍然必须为每一行执行表达式.

在这些选项之间进行选择很少仅基于性能,性能最多只能是您考虑的多个方面之一.这里涉及很多因素:

  • default使用标量或Python函数直接生成Python默认值,然后在插入时将新值发送到服务器.在将数据插入数据库之前,Python代码可以访问默认值.
  • 客户端SQL表达式,server_default值和服务器端隐式默认值和触发器都使服务器生成默认值,如果您希望能够在同一SQLAlchemy会话中访问它,则必须由客户端提取该默认值.在将对象插入数据库之前,无法访问该值.

    根据确切的查询和数据库支持,SQLAlchemy可能必须进行额外的 SQL查询,以便在INSERT语句之前生成默认值,或者SELECT之后单独运行以获取已插入的默认值.您可以控制何时发生这种情况(直接插入或在刷新后首次访问时使用eager_defaults映射器配置).

  • 如果在不同平台上有多个客户端访问同一个数据库,则server_default附加到架构的一个或其他默认值(例如触发器)可确保所有客户端都使用相同的默认值,而不管如何在Python中实现的默认值都无法访问其他平台.

使用PostgreSQL时,SQLAlchemy可以使用DML语句RETURNING子句,这使客户端可以在一个步骤中访问服务器端生成的默认值.

因此,当使用server_default列默认值为每行计算一个新值(而不是标量值)时,可以节省少量的Python端时间,并节省少量网络带宽,因为您不会为该列发送数据到数据库.数据库可以更快地创建相同的值,或者可能更慢; 它在很大程度上取决于操作的类型.如果您需要从Python访问生成的默认值,那么在同一事务中,您必须等待SQLAlchemy解析出的数据返回流.然而,与插入或更新行周围发生的所有其他细节相比,所有这些细节变得微不足道.

请注意,ORM 不适合用于高性能批量行插入或更新 ; 引用SQAlchemy Performance FAQ条目:

SQLAlchemy ORM在将更改同步到数据库时使用工作单元模式.这种模式远远超出了数据的简单"插入".它包括使用属性检测系统接收在对象上分配的属性,该系统跟踪对象的更改,包括插入的所有行都在标识映射中跟踪,这对于每行SQLAlchemy必须检索其"最后插入的id"如果还没有给出,还涉及要插入的行被扫描并根据需要对依赖项进行排序.对象也受到相当程度的簿记,以便保持所有这些运行,这对于大量的行一次可以创建大量数据结构花费的大量时间,因此最好将这些块化.

基本上,工作单元是一个很大程度的自动化,以便自动执行将复杂对象图持久化到没有显式持久性代码的关系数据库的任务,并且这种自动化具有代价.

ORM基本上不适用于高性能批量插入 - 这是SQLAlchemy除了将ORM作为一流组件之外还提供Core的全部原因.

因为像SQLAlchemy这样的ORM具有高昂的开销价格,所以服务器端或Python端默认值之间的任何性能差异很快就会在ORM操作的噪声中消失.

因此,如果您担心大量插入或更新操作的性能,您可能希望对这些操作使用批量操作,并使psycopg2批处理执行帮助程序真正获得速度提升.使用这些批量操作时,我希望服务器端默认设置只是通过节省带宽将行数据从Python移动到服务器来提高性能,但是多少取决于默认值的确切性质.

如果ORM在批量操作之外插入和更新性能对您来说是一个大问题,则需要测试您的特定选项.我会与开始的SQLAlchemy examples.performance,并添加自己的测试套件使用两种机型只在一个方面不同server_defaultdefault配置.

  • 重点是_因为像 SQLAlchemy 这样的 ORM 带来了高昂的开销,服务器端或 Python 端默认值之间的任何性能差异很快就会消失_,巧妙地总结了这里的所有有用见解。特别是在选择 `default` 或 `server_default` 时出现任何重大性能问题的可能性通常很低,并且考虑到默认值的性质、与访问默认值相关的需求以及最适合 SQLAlchemy 的应用程序要求ORM 或 Core,或两者的混合可能最好地告知该决定。 (2认同)

Jay*_*Pow 5

还有其他重要的事情,而不仅仅是比较两者的性能

如果您需要向create_at (Not Null)现有表中User添加一些数据的新列,default将无法使用。

如果使用default,则在升级数据库期间,将发生错误,提示无法将Null值插入表中的现有数据。如果您要维护数据(甚至仅用于测试),这将造成严重的麻烦。

并且当使用时server_default,在升级数据库期间,数据库会将当前的DateTime值插入到所有以前的现有测试数据中。

因此,在这种情况下,仅server_default会起作用。

  • 或者,您可以在更改列约束之前通过迁移步骤(例如,由 Alembic 提供)填充这些列。 (9认同)