为何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
相关产品推荐
相关产品推荐

