ben*_*nvc 17 python sql postgresql performance sqlalchemy
使用SQLAlchemy和PostgreSQL 时,使用default
而不是server_default
映射表列默认值时是否存在性能优势(或缺点)?
我的理解是default
在INSERT
(通常)中呈现表达式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)
注意:到目前为止我发现的最佳解释是什么时候使用default
而server_default
不是解决性能问题(参见Mike Bayer关于这个问题的答案).我对该解释的简要概述default
是优先于server_default
......
......所以问题仍然是在选择default
和之间是否应该考虑表现server_default
?
Mar*_*ers 25
不可能给你一个'这是更快'的答案,因为每个默认值表达式的性能可以在服务器和Python中广泛变化.检索当前时间的函数与标量默认值的行为不同.
接下来,您必须意识到可以通过五种不同的方式提供默认值:
0
或True
.该值用于INSERT
语句中.INSERT
语句中省略的任何列的值,或者DEFAULT
在INSERT
或UPDATE
语句中设置列值时.请注意,当涉及到确定默认值的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_default
和default
配置.
还有其他重要的事情,而不仅仅是比较两者的性能
如果您需要向create_at (Not Null)
现有表中User
添加一些数据的新列,default
将无法使用。
如果使用default
,则在升级数据库期间,将发生错误,提示无法将Null值插入表中的现有数据。如果您要维护数据(甚至仅用于测试),这将造成严重的麻烦。
并且当使用时server_default
,在升级数据库期间,数据库会将当前的DateTime值插入到所有以前的现有测试数据中。
因此,在这种情况下,仅server_default
会起作用。