【问题标题】:Why assign a string to DATETIME column would result in so much slower commit speed? Is it reasonable?为什么将字符串分配给 DATETIME 列会导致提交速度慢得多?合理吗?
【发布时间】:2021-11-23 08:25:03
【问题描述】:

最近几天我一直在开发一个非常简单的网页,我决定使用 SQLAlchemy 和 Flask 作为后端堆栈。

表 DDL:

create table if not exists testdb.table_datetime
(
    id int auto_increment
        primary key,
    col1 varchar(50) null,
    col2 varchar(50) null,
    col3 varchar(50) null,
    col4 varchar(50) null,
    col5 varchar(50) null,
    col6 varchar(50) null,
    local_modified datetime null
)
collate=utf8mb4_bin;

我的 ORM 模型:

class DemoClass(Base):
    __tablename__ = 'table_datetime'
    local_modified = Column(DATETIME)

    # __tablename__ = 'table_varchar'
    # local_modified = Column(String)
    id = Column(Integer, primary_key=True)
    col1 = Column(String)
    col2 = Column(String)
    col3 = Column(String)
    col4 = Column(String)
    col5 = Column(String)
    col6 = Column(String)

代码:

with Session() as session:
    db_rows = session.query(DemoClass).all()
    for item in data:
        for db_row in db_rows:
            if item['id'] == db_row.id:
                db_row.col1 = item['col1']
                db_row.col2 = item['col2']
                db_row.col3 = item['col3']
                db_row.col4 = item['col4']
                db_row.col5 = item['col5']
                db_row.col6 = item['col6']

                # db_row.local_modified = item['local_modified'] # Very slow, 6-8s for 500 rows data
                db_row.local_modified = datetime.strptime(item['local_modified'], '%Y-%m-%d %H:%M:%S') # Fast, <1s for 500 rows data
                break
    session.commit()

我发现将字符串 '2021-10-02 11:12:34' 分配给 ORM DATETIME 会比 datetime.strptime(2021-10-02 11:12:34', '%Y-%m-%d %H:%M:%S') 慢得多。

我的测试环境:

Flask==2.0.1
PyMySQL==1.0.2
MySQL==1.0.2
SQLAlchemy==1.4.25
Python 3.6.5 Windows
MySQL remote server, Server version 5.7.33-0ubuntu0.18.04.1

结果是,对于要更新的​​ 500 行,分配一个日期时间字符串需要 6 到 10 秒,而先将日期时间字符串转换为 datetime 然后再分配需要不到 1 秒。

完整的测试代码和设置在这里:https://github.com/ajfg93/sqlalchemy-demo/tree/datetime_slow

我的问题是,性能下降是否合理(小于 1 秒 vs 6 到 10 秒)?

我猜如果我传递的是日期时间字符串而不是 datetime 对象,内部代码(SQLAlchemy 或 MySQL,我不知道谁会做这项工作)可能会尝试猜测字符串格式并得到一个datetime 认为正确的对象?但真的那么慢吗?还有其他原因吗?

如果我不测试代码,我可能永远不会知道它是db_row.local_modified = item['local_modified'] 减慢了整个处理流程。我的意思是,我更希望 SQLAlchemy 引发异常,告诉我数据类型不匹配,而不是导致这种巨大的性能下降。

【问题讨论】:

  • 因为您已经完成了确定格式首先匹配有效日期的工作; SQLAlchemy 中的实际代码首先对字符串运行正则表达式,并指出这比 strptime 快,至少到 Python 2.6;可能只是strptime 现在更快的情况 - 但在添加功能时没有回来:github.com/sqlalchemy/sqlalchemy/blob/… - 它还指出 strptime 本身不支持微秒,但这也可能在较新的版本中发生了变化版本。
  • @MatsLindh 好吧,如果它那么慢,为什么不只是给我一个错误并强迫我决定我的代码中的格式。性能下降是不可接受的。
  • 如前所述(并由评论指出); 在以前的平台上可能并不慢,而且这个问题可能只是最近才出现。我敢肯定,如果可能的话,有人会喜欢一个与今天的工作方式保持兼容性的补丁。附上一个profiler,看看两个不同版本的时间都花在了哪里;也许幕后还有其他事情发生。
  • @MatsLindh 我用 loal mysql 再次测试了它,所有响应时间

标签: python python-3.x sqlalchemy


【解决方案1】:

使用附加的示例,这是因为在日期时间情况下,SQLAlchemy 没有发现对象发生任何变化,也没有向数据库发出更新,这可以通过锁定引擎打印的日志来验证。 如果您将+ timedelta(1) 添加到日期,您应该会看到类似的时间。

同样关于字符串的操作,SQLAlchemy 不解析它们,它们按原样传递给 dbpi。您可以通过锁定日志来验证它,您将在其中看到传递给数据库的参数。

关于所花费的时间,对我来说,所有情况下都不到 1 秒

【讨论】:

    【解决方案2】:

    我重写了一些代码,并确保每次发布的数据都不一样,以确保SQLAlchemy会更新。

    然后我用本地 MySQL 再次测试,所有响应时间

    所以真正的问题原来是我之前连接到远程 MySQL(公共网络)。没想到网络 IO 对整个处理的开销如此之大。

    仅供参考,基准测试结果:

    • bulk_update_mappings: ~80ms
    • 方法 2:约 150 毫秒和 传递日期时间字符串不会增加太多开销
    • session.merge: 500ms~,最慢,符合预期。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-05
      • 1970-01-01
      • 2021-12-14
      • 2019-09-28
      • 2021-06-15
      • 2016-09-03
      • 2018-01-06
      • 1970-01-01
      相关资源
      最近更新 更多