You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何SQLAlchemy异步引擎无法感知数据库时区设置变更?

问题分析

PostgreSQL中执行ALTER DATABASE SET timezone仅对后续新建的数据库连接生效,已存在的连接会保留原时区配置。而SQLAlchemy的异步连接池会复用已创建的连接,因此修改时区后直接查询仍会读取旧时区,重启引擎时连接池被重建,新连接才会应用新时区。

解决方案

无需重启引擎,可通过强制回收连接池内所有现有连接,让后续操作使用新创建的连接(自动应用数据库新时区)。具体实现如下:

修改代码流程

将分散的asyncio.run调用整合到一个异步主函数中,在更新时区后调用引擎的dispose()方法回收连接池:

import asyncio
from contextlib import asynccontextmanager
from sqlalchemy.orm import sessionmaker
from sqlalchemy.sql import text
from sqlalchemy.ext.asyncio import (
    create_async_engine,
    AsyncSession,
)


db_uri = "postgresql+psycopg://bigbrother:BigBrother0906!!@10.190.20.72:5432/bigbrother"
db_engine = create_async_engine(db_uri, max_overflow=1, pool_size=2)


@asynccontextmanager
async def db_session_manager(a_db_engine):
    session = sessionmaker(a_db_engine, expire_on_commit=False, class_=AsyncSession)()
    try:
        yield session
        await session.commit()
    except Exception as ex:
        await session.rollback()
        raise ex
    finally:
        await session.close()


async def update_timezone():
    async with db_session_manager(db_engine) as session:
        await session.execute(
            text(
                """
            ALTER DATABASE "bigbrother"
            SET timezone TO 'Europe/Amsterdam'
        """
            )
        )


async def get_timezone():
    async with db_session_manager(db_engine) as session:
        current_timezone = (
            (
                await session.execute(
                    text(
                        """
                SELECT
                    name, abbrev, utc_offset, is_dst
                FROM pg_timezone_names
                WHERE name = current_setting('TIMEZONE')
            """
                    )
                )
            )
            .mappings()
            .one()
        )
        print(current_timezone)


async def main():
    print("timezone before")
    await get_timezone()
    await update_timezone()
    # 回收连接池内所有现有连接,强制后续使用新连接
    await db_engine.dispose()
    print("timezone after update")
    await get_timezone()


asyncio.run(main())

输出验证

执行修改后的代码会得到预期结果:

timezone before
{'name': 'Europe/Bucharest', 'abbrev': 'EEST', 'utc_offset': datetime.timedelta(seconds=10800), 'is_dst': True}
timezone after update
{'name': 'Europe/Amsterdam', 'abbrev': 'CEST', 'utc_offset': datetime.timedelta(seconds=7200), 'is_dst': True}

补充说明

  • db_engine.dispose()会异步关闭连接池中的所有空闲和正在使用的连接,后续请求会创建全新的连接,自动读取数据库的最新时区配置。
  • 如果仅需让当前会话立即生效,可在update_timezone函数中追加执行SET TIMEZONE TO current_setting('TIMEZONE'),但该方法仅影响当前连接,连接池内其他旧连接仍会保留旧时区,适合局部场景:
    async def update_timezone():
        async with db_session_manager(db_engine) as session:
            await session.execute(
                text(
                    """
                ALTER DATABASE "bigbrother"
                SET timezone TO 'Europe/Amsterdam'
            """
                )
            )
            # 让当前会话立即应用新时区
            await session.execute(text("SET TIMEZONE TO current_setting('TIMEZONE')"))
    

内容的提问来源于stack exchange,提问作者Alex Rohozneanu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 17:40:57