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

如何禁用SQLAlchemy查询缓存?PostgreSQL性能测试遇缓存干扰

SQLAlchemy缓存导致PostgreSQL性能测试失真的解决办法

问题背景

用Python的SQLAlchemy做PostgreSQL性能测试,逻辑是循环迭代测试不同数据量([100000,150000,200000,250000]行)的查询耗时。第一次迭代正常,但后续迭代中SQLAlchemy会缓存查询,导致测试结果不准。试过session.expires_all()、调整execution_options、创建新Session、修改隔离级别等方法,均无效。

相关代码与日志

查询函数示例

@get_exec_time
def select_from_data(session_obj, field_name, field_value, mode="json"):
    tgt_table = None

    if mode not in ("json", "jsonb"):
        raise TypeError("Значение mode должно быть равно json/jsonb")

    if mode == "json":
        tgt_table = JsonMedicalData
        filter_expr = tgt_table.json_data.op("->>")(field_name).cast(Integer) == field_value

    elif mode == "jsonb":
        tgt_table = JsonbMedicalData
        filter_expr = tgt_table.jsonb_data.op("@>")({field_name: field_value})

    with session_obj.begin() as session:
        session.expire_all()
        result = session.query(tgt_table).filter(filter_expr).all()

测试流水线代码

row_numbers = [100000, 150000, 200000, 250000]

results_dict = {}

# 生成测试数据
json_rows_max, jsonb_rows_max = data_generator(row_numbers[-1], mode="json"), data_generator(row_numbers[-1], mode="jsonb")

for rows in row_numbers:
    json_rows, jsonb_rows = json_rows_max[:rows], jsonb_rows_max[:rows]

    # JSON数据测试流程
    json_insert_time = insert_test(json_rows, Session)
    results_dict_insert(results_dict, key="insert", row_number=rows, mode="json", exec_time=json_insert_time)

    select_where_time = select_from_data(Session, field_name="select_test", field_value=5, mode="json")
    results_dict_insert(results_dict, key="select", row_number=rows, mode="json", exec_time=select_where_time)

    summarize_data_time = summarize_data_field(Session, field_name="зарегистрировано всего", mode="json")
    results_dict_insert(results_dict, key="summarize", row_number=rows, mode="json", exec_time=summarize_data_time)

    json_delete_time = delete_test(JsonMedicalData, Session)
    results_dict_insert(results_dict, key="delete", row_number=rows, mode="json", exec_time=json_delete_time)

    # JSONB数据测试流程
    jsonb_insert_time = insert_test(jsonb_rows, Session)
    results_dict_insert(results_dict, key="insert", row_number=rows, mode="jsonb", exec_time=jsonb_insert_time)

    select_where_time = select_from_data(Session, field_name="select_test", field_value=5, mode="jsonb")
    results_dict_insert(results_dict, key="select", row_number=rows, mode="jsonb", exec_time=select_where_time)

    summarize_data_time = summarize_data_field(Session, field_name="зарегистрировано всего", mode="jsonb")
    results_dict_insert(results_dict, key="summarize", row_number=rows, mode="jsonb", exec_time=summarize_data_time)

    # GIN索引测试流程
    gin_index_create_time = gin_index_create(Session, "gin_index")
    results_dict_insert(results_dict, key="gin_create", row_number=rows, mode="jsonb_gin", exec_time=gin_index_create_time)

    select_where_time = select_from_data(Session, field_name="select_test", field_value=5, mode="jsonb")
    results_dict_insert(results_dict, key="select", row_number=rows, mode="jsonb_gin", exec_time=select_where_time)

    summarize_data_time = summarize_data_field(Session, field_name="зарегистрировано всего", mode="jsonb")
    results_dict_insert(results_dict, key="summarize", row_number=rows, mode="jsonb_gin", exec_time=summarize_data_time)

    gin_index_delete_time = gin_index_delete(Session, index_name="gin_index")
    results_dict_insert(results_dict, key="gin_delete", row_number=rows, mode="jsonb_gin", exec_time=gin_index_delete_time)

    jsonb_delete_time = delete_test(JsonbMedicalData, Session)
    results_dict_insert(results_dict, key="delete", row_number=rows, mode="jsonb", exec_time=jsonb_delete_time)

缓存相关日志片段

2023-06-21 12:23:18,175 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-06-21 12:23:18,175 INFO sqlalchemy.engine.Engine SELECT sum(CAST(jsonb_medical_data.jsonb_data ->> %(jsonb_data_1)s AS INTEGER)) AS sum_1 
FROM jsonb_medical_data
2023-06-21 12:23:18,175 INFO sqlalchemy.engine.Engine [cached since 39.2s ago] {'jsonb_data_1': 'зарегистрировано всего'}
2023-06-21 12:23:18,200 INFO sqlalchemy.engine.Engine COMMIT
2023-06-21 12:23:18,201 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-06-21 12:23:18,201 INFO sqlalchemy.engine.Engine DROP INDEX gin_index
2023-06-21 12:23:18,201 INFO sqlalchemy.engine.Engine [cached since 37.55s ago] {}
2023-06-21 12:23:18,201 INFO sqlalchemy.engine.Engine COMMIT
2023-06-21 12:23:18,205 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-06-21 12:23:18,205 INFO sqlalchemy.engine.Engine DELETE FROM jsonb_medical_data
2023-06-21 12:23:18,205 INFO sqlalchemy.engine.Engine [cached since 37.54s ago] {}
2023-06-21 12:23:18,232 INFO sqlalchemy.engine.Engine COMMIT

核心问题分析

日志中的[cached since ...]表明,问题根源是SQLAlchemy的语句编译缓存(Statement Cache),而非Session级别的ORM对象缓存。之前尝试的expire_all()等方法只针对ORM对象缓存,无法解决语句缓存的问题。

解决办法

1. 关闭SQL语句编译缓存

可以全局或单查询禁用语句缓存,强制每次都重新编译并发送SQL到数据库:

  • 全局关闭(创建Engine时):
engine = create_engine(
    "postgresql://user:password@host/database",
    execution_options={"compiled_cache": None}
)
  • 单查询禁用:
    在查询语句中添加execution_options(compiled_cache=None):
result = session.query(tgt_table).filter(filter_expr).execution_options(compiled_cache=None).all()

2. 迭代使用全新Session

每次测试迭代都创建独立的Session,避免Session级别的对象缓存干扰:

for rows in row_numbers:
    # 每次迭代创建新Session
    with Session() as session:
        json_rows, jsonb_rows = json_rows_max[:rows], jsonb_rows_max[:rows]
        
        # 后续所有测试操作都使用这个新Session
        json_insert_time = insert_test(json_rows, session)
        # ... 其他测试步骤

3. 禁用PostgreSQL查询计划缓存

PostgreSQL自身也会缓存查询计划,可通过设置参数强制生成新计划:

# 在每次查询前执行
session.execute("SET plan_cache_mode = 'force_custom_plan';")

4. 彻底清理Session状态

如果仍存在ORM对象缓存问题,可在每次测试后执行session.rollback()或session.close(),但最彻底的方式还是使用全新Session。

内容的提问来源于stack exchange,提问作者Nikita Smirnov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 04:22:03