在asyncio任务中调用Postgres NOW()返回错误时间的问题
PostgreSQL NOW()返回时间偏差,与本地时间不一致的问题解决
问题现象
- 程序每小时定时执行,日志记录的
datetime.datetime.now()时间正确,但PostgreSQL的NOW()函数返回的时间逐渐滞后 - 程序启动后首次执行时
NOW()时间正常,后续每次执行偏差持续增大 - 通过psql、PyCharm等客户端连接数据库时,
NOW()能返回正确的实时时间
问题根源
PostgreSQL的NOW()函数返回的是当前数据库会话的启动时间,也就是连接建立的时间,而非查询执行时的实时时间。
你的程序使用单例模式维护了一个持久化的数据库长连接,每次调用NOW()都会返回这个连接创建时的时间。随着程序运行时长增加,这个时间与实际时间的差距自然会越来越大。而其他客户端每次都是新建连接,所以NOW()能返回正确的实时时间。
解决方案
1. 使用实时时间函数替代NOW()
将查询语句中的SELECT NOW()替换为以下任意一种,获取实时时间:
CURRENT_TIMESTAMP:返回当前事务的开始时间,适合大多数业务场景clock_timestamp():返回查询执行瞬间的精确时间,适合需要极高时间精度的场景
修改后的process_validity函数示例:
def process_validity() -> Generator[DataInterface, None, None]: # 改用CURRENT_TIMESTAMP获取实时时间 db_now: datetime.datetime = db.fetchone("SELECT CURRENT_TIMESTAMP")['current_timestamp'].replace(tzinfo=None) logger.info("CURRENT_TIMESTAMP is:", db_now) for users_set in db.fetchmany("SELECT ...", 100, ()): for user in users_set: yield user
2. (不推荐)定期重建数据库连接
如果必须使用NOW(),可以在每次任务执行前后重建连接,但这会失去长连接的性能优势,仅作为备选方案:
async def interval_handler(): while True: logger.info("Handler:", datetime.datetime.now()) # 每次任务前重建连接 db.connect() for data in process_validity(): pass # 任务完成后关闭连接 db.connection.close() # 等待下一小时的逻辑... delta = datetime.timedelta(hours=1) now = datetime.datetime.now() next_hour = (now + delta).replace(microsecond=0, second=0, minute=0) wait_seconds = (next_hour - now).seconds await asyncio.sleep(1) await asyncio.sleep(wait_seconds)
额外注意事项
- 确认
fetchone方法已正确实现(你的代码中仅定义了fetchmany,需保证fetchone能正确执行单行查询) - 单例模式下的长连接如果长期闲置,可能会被数据库服务器主动断开,建议添加连接健康检查逻辑
内容的提问来源于stack exchange,提问作者kinton
相关产品推荐
相关产品推荐

