资讯动态

SQL Server Query Store配置实战:采集间隔、存储与捕获模式调优指南

发布时间:2026/10/14 16:14:54 来源:尧图企业网站定制
1. 为什么Query Store的配置值得单独拿出来讲很多人第一次接触Query Store是在数据库性能突然劣化、执行计划莫名其妙变差的时候。别人告诉你“去查一下Query Store”你打开一看数据是有的但要么保留时间太短关键时间点的信息已经被自动清理了要么采集间隔太大跑了一整天的慢查询只留下几个采样点要么干脆因为没配好把系统库也纳入了采集范围白白吃掉大量空间。这些问题归根结底都指向同一件事Query Store的配置不是打开开关就完事它需要根据你的业务负载特征、存储容量、排查窗口来精细调整。我在实际工作中遇到过不少案例数据库本身配置不差硬件也够用但因为Query Store的INTERVAL_LENGTH_MINUTES设得过大导致一个持续几分钟的突发性能抖动完全被平滑掉了事后根本找不到当时到底跑了什么语句。也有相反的情况采集频率设得太密STALE_QUERY_THRESHOLD_DAYS又给得很长结果几个月下来Query Store自己占了几十个GB反而成了新的性能负担。这篇文章就是要把Query Store的配置项一个一个拆开讲清楚每个参数背后的含义、调整时需要考虑的因素、以及我在实际运维中总结出来的配置组合。不管你是刚开始接触Query Store还是已经用了一段时间但总觉得“数据不太对劲”都能从这里找到可以直接参考的方案。2. Query Store配置项全景解读2.1 配置项的分类逻辑Query Store的配置项可以分成三大类来理解。第一类是开关与模式类决定Query Store是否工作、以什么方式工作包括OPERATION_MODE操作模式。第二类是采集与保留类控制数据怎么写、写多少、留多久包括INTERVAL_LENGTH_MINUTES采集间隔、MAX_STORAGE_SIZE_MB最大存储、STALE_QUERY_THRESHOLD_DAYS陈旧阈值、CLEANUP_POLICY清理策略。第三类是捕获行为类决定哪些查询会被记录、以什么粒度记录包括QUERY_CAPTURE_MODE捕获模式、SIZE_BASED_CLEANUP_MODE基于大小的清理模式、WAIT_STATS_CAPTURE_MODE等待统计捕获模式。这种分类不是学术上的强行划分而是对应了实际运维中的三种决策场景要不要用、用多大代价用、记录哪些东西。你在调整配置时先想清楚当前要解决的是哪一类问题再去动对应的参数比盲目地全改一遍要有效得多。2.2 查看当前配置的方法在动手改任何参数之前先看一眼当前状态。最直接的方式是查系统视图SELECT desired_state_desc, actual_state_desc, readonly_reason, current_storage_size_mb, max_storage_size_mb, interval_length_minutes, stale_query_threshold_days, query_capture_mode_desc, size_based_cleanup_mode_desc, wait_stats_capture_mode_desc FROM sys.database_query_store_options;这个查询返回的每一列都值得仔细看。desired_state_desc是你期望的状态actual_state_desc是实际状态两者不一致时readonly_reason会告诉你原因。我见过好几次actual_state_desc显示为READ_ONLY但很多人没注意到以为Query Store还在正常写入结果排查问题时发现最近几天的数据全是空的。readonly_reason的值对应不同的原因比如空间不足、数据库处于只读状态等需要针对性处理。current_storage_size_mb和max_storage_size_mb的对比也很关键。如果当前大小已经接近上限Query Store会开始清理旧数据这时候你看到的“数据丢失”其实是清理策略在起作用而不是采集出了问题。2.3 操作模式的选择与切换OPERATION_MODE有三个可选值OFF、READ_ONLY、READ_WRITE。OFF就是完全关闭Query Store不采集也不保留任何数据。READ_ONLY表示停止采集新数据但保留已有数据适合在排查完问题后暂时冻结现场。READ_WRITE是正常工作状态。切换模式的语法很简单ALTER DATABASE [你的数据库名] SET QUERY_STORE (OPERATION_MODE READ_WRITE);但这里有一个容易被忽略的细节从READ_ONLY切回READ_WRITE时Query Store不会自动补采中间缺失的数据。也就是说如果你在READ_ONLY状态下运行了三个小时这三个小时的查询信息就是永久缺失的。所以我的习惯是除非有明确的“冻结现场”需求否则不要轻易切到READ_ONLY。如果确实需要暂停采集来减少开销更推荐的做法是调整QUERY_CAPTURE_MODE为NONE这样同样停止采集但恢复时不需要额外的状态切换操作。注意在READ_ONLY模式下Query Store的清理线程仍然会运行也就是说旧数据仍然可能被清理掉。如果你冻结现场的目的是保留证据还需要同时调整STALE_QUERY_THRESHOLD_DAYS为一个较大的值。3. 采集间隔与保留策略的精细调整3.1 INTERVAL_LENGTH_MINUTES的计算逻辑INTERVAL_LENGTH_MINUTES决定了Query Store把运行时统计信息聚合到时间窗口的粒度。默认值是60分钟也就是说每个查询在每个小时内的执行统计会被聚合成一条记录。这个值设得越大数据越“浓缩”存储压力越小但时间分辨率越低设得越小时间分辨率越高但存储增长越快。那怎么确定合适的值我的经验是从两个维度来算。第一个维度是你的排查窗口。如果你经常需要定位“下午两点到两点半之间突然变慢”这类问题那60分钟的聚合粒度显然不够至少需要15分钟甚至5分钟。第二个维度是存储预算。假设你的Query Store分配了2048MB业务系统有大约500个不同的查询语句在跑每个查询每小时产生一条聚合记录每条记录加上计划信息大约占用2KB到10KB不等取决于计划的大小。按平均5KB算500个查询乘以24小时乘以5KB一天大约60MB2048MB可以保留大约34天。如果把间隔改成15分钟数据量直接翻四倍保留天数就降到8天左右。所以这里有一个权衡时间分辨率越高可回溯的历史窗口越短。你需要根据实际排查需求来定。对于大多数OLTP系统15分钟是一个比较平衡的选择。对于批处理系统或者负载变化不频繁的场景30到60分钟也够用。3.2 STALE_QUERY_THRESHOLD_DAYS的取舍STALE_QUERY_THRESHOLD_DAYS定义了“多久没有被执行的查询”会被视为陈旧数据并清理掉。默认值是30天。这个参数的设计意图是如果一个查询30天都没跑过那它的历史统计信息大概率不再有参考价值可以清理掉腾出空间。但这里有一个常见的误区很多人以为这个参数控制的是“数据保留的总天数”其实不是。它控制的是“查询的活跃度阈值”。一个每天都在跑的查询即使跑了三年它的数据也不会因为STALE_QUERY_THRESHOLD_DAYS而被清理。只有那些超过指定天数没有执行记录的查询才会被标记为陈旧并清理。这个参数怎么设如果你的系统有周期性特征比如每月初跑一次月结报表那STALE_QUERY_THRESHOLD_DAYS至少要大于31天否则月结查询的数据会在两次执行之间被清理掉下次再跑的时候就没有历史对比了。对于大多数业务系统我建议设置在30到90天之间。设得太短周期性查询的历史会丢失设得太长那些真正不再使用的查询会一直占着空间。3.3 MAX_STORAGE_SIZE_MB的容量规划MAX_STORAGE_SIZE_MB是Query Store在数据库中占用的最大空间。默认值是100MB这个值对于任何有一定负载的系统来说都太小了。我见过很多案例Query Store打开后没改这个值结果运行几天就满了然后开始自动清理导致历史数据断断续续。容量规划没有一个万能公式但可以按以下步骤估算统计数据库中活跃的查询语句数量。可以通过sys.dm_exec_query_stats或者Query Store自身的sys.query_store_query来获取。估算每条查询每天产生的数据量。这取决于采集间隔和计划大小。一个粗略的经验值是每条查询每小时每条聚合记录大约占用3KB到8KB。用“活跃查询数 × 每天聚合记录数 × 单条记录大小 × 期望保留天数”来估算总需求。举个例子300个活跃查询采集间隔15分钟每天96个聚合窗口单条记录平均5KB期望保留30天。那么总需求大约是300 × 96 × 5KB × 30 ≈ 4.1GB。这个数字看起来很大但实际上很多查询并不是全天都在跑实际占用会小一些。不过为了保险我通常会在这个估算值的基础上再留50%的余量。提示MAX_STORAGE_SIZE_MB的上限是受数据库本身的空间限制的。如果你的数据库设置了最大大小Query Store的空间也包含在内。在调整这个参数之前先确认数据库有足够的可用空间。4. 捕获模式与清理策略的实战配置4.1 QUERY_CAPTURE_MODE的四种模式对比QUERY_CAPTURE_MODE决定了哪些查询会被Query Store记录。它有四个可选值模式行为适用场景ALL捕获所有查询包括那些只执行一次的排查阶段需要完整信息AUTO自动过滤掉执行次数少、资源消耗低的查询日常运维平衡信息量和开销NONE停止捕获新查询但保留已有数据临时冻结采集CUSTOM根据自定义条件捕获较新版本支持精细化控制AUTO是默认值也是大多数场景下的推荐值。它的过滤逻辑是基于执行次数和资源消耗的综合判断把那些“跑了一次就再也不跑”的即席查询过滤掉避免Query Store被大量无意义的数据填满。但AUTO模式有一个问题它可能会过滤掉一些你真正关心的查询。比如某个查询平时跑得很快但偶尔会因为参数嗅探导致一次严重的性能下降如果这次下降只发生了一次AUTO模式可能就不会记录它。所以我的建议是日常用AUTO排查时临时切到ALL。切换的语法ALTER DATABASE [你的数据库名] SET QUERY_STORE (QUERY_CAPTURE_MODE ALL);排查完成后记得切回AUTO否则Query Store的存储增长会明显加快。4.2 SIZE_BASED_CLEANUP_MODE的开关逻辑SIZE_BASED_CLEANUP_MODE控制当Query Store的存储接近MAX_STORAGE_SIZE_MB时是否自动清理最旧的数据。默认是AUTO也就是自动清理。另一个选项是OFF表示不自动清理当空间满了之后Query Store会转为只读状态停止写入新数据。这两个选项怎么选AUTO的好处是Query Store始终可用不会因为空间满而停止工作。坏处是旧数据会被静默清理如果你没有监控存储使用率可能会在需要查历史数据时才发现已经被清掉了。OFF的好处是数据不会被自动清理你可以完全控制什么时候清理、清理多少。坏处是需要你自己监控空间一旦满了Query Store就停止工作新的性能问题不会被记录。我个人的做法是生产环境用AUTO但配合存储使用率的监控告警。当使用率达到70%时触发告警这时候你有足够的时间来决定是扩大MAX_STORAGE_SIZE_MB还是调整保留策略。如果数据库空间确实紧张那就用OFF但必须建立定期清理的机制比如每月手动执行一次清理。4.3 WAIT_STATS_CAPTURE_MODE的取舍WAIT_STATS_CAPTURE_MODE控制是否捕获查询的等待统计信息。默认是ON也就是捕获。等待统计对于分析“查询为什么慢”非常有价值它能告诉你查询的时间花在了哪里是在等锁、等IO、还是等CPU。但捕获等待统计会增加一定的开销尤其是在高并发场景下。如果你的系统CPU压力已经很大可以考虑暂时关闭等待统计捕获来减轻负担ALTER DATABASE [你的数据库名] SET QUERY_STORE (WAIT_STATS_CAPTURE_MODE OFF);但要注意关闭之后你就失去了分析等待类型的能力排查性能问题时只能看到“这个查询跑了5秒”但不知道这5秒花在哪里。所以除非确实有性能压力否则我建议保持ON。5. 一套可直接参考的配置方案5.1 中小型OLTP系统的推荐配置假设你的系统是一个日活几千到几万的中小型OLTP系统数据库大小在几百GB以内服务器配置中等。以下是我在实际项目中验证过的一套配置ALTER DATABASE [你的数据库名] SET QUERY_STORE ( OPERATION_MODE READ_WRITE, INTERVAL_LENGTH_MINUTES 15, MAX_STORAGE_SIZE_MB 2048, STALE_QUERY_THRESHOLD_DAYS 60, QUERY_CAPTURE_MODE AUTO, SIZE_BASED_CLEANUP_MODE AUTO, WAIT_STATS_CAPTURE_MODE ON );这套配置的逻辑是15分钟的采集间隔提供了足够的时间分辨率2048MB的存储空间在大多数中小型系统中可以保留30到60天的数据60天的陈旧阈值覆盖了月度周期性的查询AUTO捕获模式过滤掉了噪音数据自动清理保证了Query Store不会因为空间满而停止工作。5.2 大型高并发系统的调整方向对于高并发、大负载的系统配置需要做几个方向的调整。首先是采集间隔可以适当放宽到30分钟因为高并发系统本身查询数量就多15分钟的粒度会产生大量数据。其次是存储空间要相应扩大具体扩大多少需要根据活跃查询数量来估算。第三是考虑关闭等待统计捕获如果CPU压力确实很大的话。ALTER DATABASE [你的数据库名] SET QUERY_STORE ( OPERATION_MODE READ_WRITE, INTERVAL_LENGTH_MINUTES 30, MAX_STORAGE_SIZE_MB 8192, STALE_QUERY_THRESHOLD_DAYS 30, QUERY_CAPTURE_MODE AUTO, SIZE_BASED_CLEANUP_MODE AUTO, WAIT_STATS_CAPTURE_MODE OFF );这里把陈旧阈值降到30天是因为高并发系统的查询模式变化更快太旧的数据参考价值有限。等待统计关闭是为了减轻CPU开销但代价是失去了等待类型的分析能力需要根据实际情况权衡。5.3 配置变更后的验证步骤改完配置之后不要直接走人做几个验证步骤确认配置生效了SELECT desired_state_desc, actual_state_desc, readonly_reason, current_storage_size_mb, max_storage_size_mb, interval_length_minutes, stale_query_threshold_days, query_capture_mode_desc, size_based_cleanup_mode_desc, wait_stats_capture_mode_desc FROM sys.database_query_store_options;对比desired_state_desc和actual_state_desc是否一致检查各个参数是否已经变成你设置的值。然后等几分钟再查一次current_storage_size_mb看是否有增长确认Query Store确实在写入数据。注意某些配置变更需要数据库重新连接或者等待下一个采集周期才会完全生效。如果发现actual_state_desc和desired_state_desc不一致先等几分钟再查不要急着反复执行ALTER语句。6. 配置过程中容易踩的坑与排查方法6.1 配置不生效的常见原因有时候你执行了ALTER DATABASE ... SET QUERY_STORE但查系统视图发现参数没变。这种情况通常有几个原因。第一个原因是数据库处于只读状态或者有只读副本的限制这时候readonly_reason会给出具体原因。第二个原因是权限不足执行ALTER需要数据库的CONTROL权限。第三个原因是语句本身有语法错误比如参数名拼写错误或者值超出了允许范围但错误信息可能不够直观。排查方法很简单先看readonly_reason如果不是0根据值去查对应的原因。如果是0但参数没变检查执行语句的返回信息看是否有警告或错误。还可以查一下数据库的兼容级别Query Store的某些功能在不同兼容级别下行为不同。6.2 存储空间增长过快的处理如果发现Query Store的存储增长远超预期先查一下是哪些查询占用了最多空间SELECT TOP 20 q.query_id, qt.query_sql_text, SUM(rs.count_executions) AS total_executions, SUM(rs.avg_duration * rs.count_executions) AS total_duration, COUNT(DISTINCT p.plan_id) AS plan_count FROM sys.query_store_query q JOIN sys.query_store_query_text qt ON q.query_text_id qt.query_text_id JOIN sys.query_store_plan p ON q.query_id p.query_id JOIN sys.query_store_runtime_stats rs ON p.plan_id rs.plan_id GROUP BY q.query_id, qt.query_sql_text ORDER BY total_duration DESC;这个查询会列出资源消耗最大的查询。如果发现某个查询有大量的执行计划plan_count很高说明这个查询存在计划频繁变化的问题每个计划都会占用存储空间。这种情况下可以考虑对这个查询使用计划强制Plan Forcing固定一个稳定的计划减少计划数量。另一个常见的原因是QUERY_CAPTURE_MODE被设成了ALL导致大量即席查询被记录。检查一下当前模式如果不是AUTO考虑切回去。6.3 数据断档的排查思路Query Store的数据出现断档表现为某个时间段内没有任何查询记录。这种情况通常有几个原因。第一个原因是Query Store曾经转为只读状态readonly_reason会告诉你原因。第二个原因是清理策略把数据清掉了检查STALE_QUERY_THRESHOLD_DAYS和MAX_STORAGE_SIZE_MB的设置。第三个原因是数据库本身在那个时间段内没有负载比如夜间维护窗口。排查的时候先查sys.database_query_store_options看当前状态和历史状态。如果readonly_reason不为0说明确实发生过只读。然后查一下current_storage_size_mb和max_storage_size_mb的比例如果接近100%说明是空间不足导致的清理。提示Query Store本身没有内置的历史状态记录所以如果你怀疑之前发生过只读但现在已经恢复了readonly_reason可能已经归零。这种情况下需要结合数据库的错误日志和监控数据来综合判断。6.4 配置变更对性能的影响评估调整Query Store配置本身对性能的影响通常很小但在某些极端情况下可能会有感知。比如把INTERVAL_LENGTH_MINUTES从60改到5采集频率提高12倍写入压力会明显增加。如果数据库的IO本身就很紧张可能会加剧IO竞争。评估影响的方法是在变更前后对比几个关键指标数据库的写入延迟、CPU使用率、以及Query Store自身的写入等待。可以通过sys.dm_exec_query_stats查看与Query Store相关的内部查询的耗时变化。如果发现变更后整体性能有明显下降考虑回退到之前的配置或者找一个折中点。我在一次实际调整中把采集间隔从60分钟改到10分钟结果发现数据库的写入延迟从平均2ms上升到了5ms。虽然绝对值不大但对于一个延迟敏感的交易系统来说这个变化是值得关注的。后来把间隔调整到20分钟延迟回落到3ms左右同时时间分辨率也够用。这个例子说明配置调整需要结合实际负载来验证不能只看理论值。7. 配置之外的几个实用技巧7.1 利用Query Store的自动计划修正从较新的版本开始Query Store支持自动计划修正Automatic Plan Correction。这个功能会在检测到某个查询的计划性能退化时自动强制使用之前的良好计划。要启用这个功能需要确保Query Store处于READ_WRITE模式并且数据库兼容级别足够高。ALTER DATABASE [你的数据库名] SET AUTOMATIC_TUNING (FORCE_LAST_GOOD_PLAN ON);这个功能对于计划频繁波动的查询非常有用可以显著减少“昨天还好好的今天就慢了”这类问题。但它也不是万能的对于参数嗅探导致的计划问题自动修正可能无法完全解决还需要配合查询重写或者提示来优化。7.2 定期导出Query Store数据做长期分析Query Store的存储空间有限不可能无限期保留所有历史数据。如果你需要做长期的性能趋势分析可以定期把Query Store的数据导出到另一张表或者另一个数据库中。导出的方式可以是简单的INSERT INTO ... SELECT也可以用SSIS或者脚本定期执行。我通常建议每周导出一次保留最近一个季度的数据。导出的数据可以用来做容量规划、性能基线对比、以及历史问题的回溯分析。导出的时候注意只导出聚合后的统计信息不要导出原始的执行计划否则数据量会很大。7.3 结合扩展事件做互补分析Query Store擅长的是“查询级别的历史统计”但它不记录实时的执行细节。如果你需要分析某个具体查询在某一刻的等待类型、锁信息、执行计划细节扩展事件Extended Events是更好的选择。两者结合使用Query Store提供宏观趋势扩展事件提供微观细节。我的做法是日常靠Query Store监控整体性能趋势发现异常查询后用扩展事件针对性地捕获该查询的详细执行信息。这样既不会因为扩展事件的全量捕获而带来性能压力又能在需要的时候拿到足够细的排查数据。7.4 配置变更的版本管理Query Store的配置不是设一次就永远不变的。随着业务增长、数据量变化、排查需求调整配置也需要相应更新。我建议把每次配置变更记录下来包括变更时间、变更内容、变更原因、以及变更后的效果观察。这样在后续排查问题时可以快速定位到“是不是某次配置变更导致的”。记录的方式可以很简单在团队的运维文档里加一个表格每次变更加一行。内容包括日期、变更项、旧值、新值、操作人、变更后观察。这个习惯看起来不起眼但在几次“性能问题追溯到配置变更”的经历之后你会发现它节省的时间远超记录的成本。8. 个人在实际操作中的几点体会Query Store的配置没有一套放之四海而皆准的参数但有一个原则是通用的先保证数据不丢再考虑数据多不多。很多人一上来就担心Query Store占空间把MAX_STORAGE_SIZE_MB设得很小结果关键时刻查不到数据。我的建议是在空间允许的前提下宁可先给大一点运行一段时间后根据实际使用量再调整。空间是可以回收的但丢失的历史数据找不回来。另一个体会是QUERY_CAPTURE_MODE的切换要养成习惯。排查问题时切到ALL排查完切回AUTO。我见过太多案例ALL模式开了之后忘了关几个月后Query Store膨胀到几十GB然后开始自动清理反而把真正有用的数据清掉了。可以在运维日历上设一个提醒或者写一个简单的脚本定期检查当前模式。最后Query Store的配置调整最好在业务低峰期进行。虽然大多数配置变更不需要重启数据库但采集间隔和捕获模式的变更会立即影响写入行为在高峰期操作可能会带来不必要的性能波动。如果确实需要在高峰期调整建议先在小范围或者测试环境验证效果确认没有明显影响后再在生产环境执行。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑