☰
SQL Server 性能计数器评估指南:SQL Assessment API 中 Performance Probe 的配置、数据变换与多实例实战
2026/9/25 3:42:48 网站建设 项目流程
  • 示例工程
  • 数据库
  • 教程
  • 后端

【免费下载链接】sql-server-samples

Azure Data SQL Samples - Official Microsoft GitHub Repository containing code samples for SQL Server, Azure SQL, Azure Synapse, and Azure SQL Edge

项目地址:https://gitcode.com/gh_mirrors/sq/sql-server-samples
点击查看免费下载

SQL Assessment API 提供了一套可扩展的机制来评估 SQL Server 实例配置是否符合最佳实践。Performance probe(性能探针)是其中用于采集 Windows 性能计数器(Performance Counter)数据的核心探针类型,其返回的多条样本数据需要配合performance 数据变换(Data Transformation)计算平均值、比率、速率等派生值,最终由检查规则(Rule)的条件表达式判定合规性。读完本文,你将掌握 Performance probe 的完整 JSON 配置语法、计数器别名与实例过滤技巧、五种派生值函数的数学含义与适用场景,以及在同一检查中正确使用多个计数器并规避"多实例结果错位"陷阱的实战方案。

Performance probe 是什么

Performance probe 的类型代码(Type code)为Performance,用于读取目标机器上的性能计数器值(如Buffer Manager、Latches、Databases、Memory Node、Access Methods等性能对象下的计数器)。它并不是一个独立的检查,而是作为数据源供规则引用:探针负责"取数",performance 数据变换负责把多份样本"加工"成单个派生值,规则条件再基于该值做出合规性判断。

从 SQL Assessment API 的探针体系看,Performance是众多探针类型中的一种。在 Probe 文档 中列出的可用探针类型还包括 AzGraph、AzMetadata、CMD、External、PowerShell、Registry、SQL(T-SQL)与 WMI。其中 T-SQL 探针从动态管理视图(DMV)取数,而 Performance 探针与 WMI 探针一样,数据来自操作系统层,因此它适合捕捉 T-SQL 查询无法直接暴露的运行时指标,例如缓冲区命中率、闩锁等待时间、每秒事务数等。

samples/manage/sql-assessment-api/docs/Reference/Probes/Performance.md

Implementation 属性:采样频率与计数器清单

Performance probe 的implementation部分包含三个参数:

参数是否必填类型默认值说明
numberOfSamples可选number2采集的样本数量
interval可选number1两次采样之间的时间间隔(秒)
counters必填object—需要采集的性能计数器清单
  • numberOfSamples:探针会对每个计数器连续采样 N 次。默认 2 次意味着拿到两个样本,足以支撑"差值型"派生值(如 rate、delta_ratio、ratio)的计算;某些场景(如求平均值)可提高采样次数以获得更稳定的统计结果。
  • interval:相邻样本的采集间隔(秒)。默认 1 秒。对于瞬态波动明显的计数器(如Pages Allocated/sec),合理调整采样间隔能反映更真实的负载特征。
  • counters:必填。这是一个 JSON 对象,其每个属性名对应一个性能对象(Performance Object),例如'Latches'、'Databases'、'Buffer Manager';每个性能对象的值又是一个 JSON 对象,其中的每个属性代表该对象下的一个性能计数器。

Performance probe 的整体结构可参考仓库中的结构图 BasicPerformanceProbeStructure.svg:

计数器别名与实例过滤

计数器名称常常包含空格和其他非字母数字字符(例如Pages Allocated/sec),无法直接在条件表达式和消息模板中引用。因此每个计数器都必须指定一个别名(alias)——别名将作为该计数器在检查条件与消息中使用的变量名。

此外,计数器可选地声明一个感兴趣的实例列表(instances)。例如 SQL Server 的Databases性能对象下,Transactions/sec计数器会为每个数据库实例分别报告值,其中_Total代表汇总实例;而Memory Node对象下的计数器则按 NUMA 内存节点实例报告。

示例一:最简单的单对象单计数器

下面的配置为Access Methods对象下的Pages Allocated/sec计数器采样,并把结果以pages_allocated变量提供给检查使用:

{ "type": "Performance", "implementation": { "Counters": { "Access Methods": { "Pages Allocated/sec": "pages_allocated" } } } }

示例二:调整采样次数

为Memory Node对象下的Free Node Memory (KB)计数器只采集 1 个样本,此时该计数器只适合使用average、min、max这类不依赖多个样本的派生函数:

{ "type": "Performance", "implementation": { "NumberOfSamples": 1, "Counters": { "Memory Node": { "Free Node Memory (KB)": "free_node_memory" } } } }

示例三:多对象、别名与实例过滤的组合

同时采集Memory Node与Access Methods两个性能对象;其中Free Node Memory (KB)通过对象式写法指定alias为free_node_memory,且instances限定为["000"](仅取编号为000的 NUMA 节点实例),而Target Node Memory (KB)则使用字符串简写形式:

{ "type": "Performance", "implementation": { "NumberOfSamples": 1, "Counters": { "Memory Node": { "Free Node Memory (KB)": { "alias": "free_node_memory", "instances": ["000"] }, "Target Node Memory (KB)": "target_node_memory" }, "Access Methods": { "Pages Allocated/sec": "pages_allocated" } } } }

可见计数器值的两种写法等价:字符串简写("计数器名": "别名")与对象写法("计数器名": { "alias": "...", "instances": [...] })。需要过滤实例时使用对象写法。

完整 ruleset 示例:五个基于性能计数器的检查

下面是一份完整可用的规则集 JSON(与文档保持一致),它同时定义了 5 个针对Server目标的规则,以及一个名为PerformanceProbe的探针定义。通过这个例子可以完整看到"规则(rules)+ 探针(probes)+ 变换(transform)"三者如何协作:

{ "schemaVersion": "1.0", "name": "Performance Checks Example", "version": "1.0", "rules":[ { "target": { "type": "Server" }, "id": "TotalPages", "itemType": "definition", "displayName": "Buffer Manager Total pages", "description": "Use \"average\", \"min\", or \"max\" for counter type PERF_COUNTER_LARGE_RAWCOUNT(65792).", "message": "Total pages (@{total_pages}) is greater than 0", "condition": { "lt": ["@total_pages", 0] }, "probes": [ { "id": "PerformanceProbe", "transform": { "type": "performance", "counters": { "total_pages": "average" } } } ] }, { "target": { "type": "Server" }, "id": "CacheHitRatio", "itemType": "definition", "displayName": "Buffer Manager cache hit ratio", "description": "Use \"ratio\" for counter type PERF_LARGE_RAW_FRACTION(537003264) and specify base PERF_LARGE_RAW_BASE(1073939712).", "message": "Cache hit ratio (@{cache_hit_ratio:P0}) is greater than 0", "condition": { "lt": ["@cache_hit_ratio", 0] }, "probes": [ { "id": "PerformanceProbe", "transform": { "type": "performance", "counters": { "cache_hit_ratio": { "type": "ratio", "base": "cache_hit_ratio_base" } } } } ] }, { "target": { "type": "Server" }, "id": "LatchWaitTime", "itemType": "definition", "displayName": "Average Latch Wait Time", "description": "Use \"delta_ratio\" for counter type PERF_AVERAGE_BULK(1073874176) and base PERF_LARGE_RAW_BASE(1073939712).", "message": "Average Latch Wait Time (@{latch_wait_time}ms.) is greater than 0", "condition": { "lt": ["@latch_wait_time", 0] }, "probes": [ { "id": "PerformanceProbe", "transform": { "type": "performance", "counters": { "latch_wait_time": { "type": "delta_ratio", "base": "latch_wait_time_base" } } } } ] }, { "target": { "type": "Server" }, "id": "TransactionsPerSec", "itemType": "definition", "displayName": "Database Transactions per sec", "description": "Use \"rate\" for counter type PERF_COUNTER_BULK_COUNT(272696576).", "message": "Transactions per sec (@{transactions_sec:0.##}sec.) for database @{instance_name} is greater than 0", "condition": { "or": [{"eq": ["@instance_name", "_Total"]}, {"le": ["@transactions_sec", 0]}] }, "probes": [ { "id": "PerformanceProbe", "transform": { "type": "performance", "counters": { "transactions_sec": "rate" } } } ] }, { "target": { "type": "Server" }, "id": "TotalTransactionsPerSec", "itemType": "definition", "displayName": "Total Database Transactions per sec", "description": "Use \"rate\" for counter type PERF_COUNTER_BULK_COUNT(272696576).", "message": "Total Transactions per sec (@{transactions_sec:0.##}sec.) is greater than 0", "condition": { "lt": ["@transactions_sec", 0] }, "probes": [ { "id": "PerformanceProbe", "transform": { "type": "performance", "counters": { "transactions_sec": { "type": "rate", "instance": "_Total" } } } } ] } ], "probes":{ "PerformanceProbe": [{ "type": "Performance", "implementation": { "Counters": { "Buffer Manager": { "Buffer cache hit ratio": "cache_hit_ratio", "Buffer cache hit ratio base": "cache_hit_ratio_base", "Target pages": "total_pages" }, "Latches": { "Average Latch Wait Time (ms)": "latch_wait_time", "Average Latch Wait Time Base": "latch_wait_time_base" }, "Databases": { "Transactions/sec": "transactions_sec" } } } }] } }

逐规则要点解读

  • TotalPages:Target pages属于PERF_COUNTER_LARGE_RAWCOUNT(65792)类型的原始计数值,适合用average(或min/max)派生;条件lt: ["@total_pages", 0]即"总页数小于 0 则告警"。
  • CacheHitRatio:Buffer cache hit ratio属于PERF_LARGE_RAW_FRACTION(537003264)比例型计数器,必须配套其 base 计数器Buffer cache hit ratio base(类型PERF_LARGE_RAW_BASE(1073939712)),在变换中使用ratio类型并指定base。消息模板@{cache_hit_ratio:P0}中的P0表示按百分比格式显示。
  • LatchWaitTime:Average Latch Wait Time (ms)属于PERF_AVERAGE_BULK(1073874176)平均型计数器,同样需要 base 计数器,使用delta_ratio类型派生。
  • TransactionsPerSec:Transactions/sec属于PERF_COUNTER_BULK_COUNT(272696576)速率型计数器,使用rate派生。由于它按数据库实例分别报告,条件用or组合:@instance_name等于_Total时跳过,或@transactions_sec <= 0时告警。@{transactions_sec:0.##}表示保留两位小数的数值格式。
  • TotalTransactionsPerSec:在变换层用instance: "_Total"直接过滤出汇总实例,条件只需比较单一值。

关于condition中使用的运算符(lt、or、eq、le等),可参考 Operators 参考文档,其中还包含match(正则匹配)、interval(区间取值)等更复杂的判定手段。规则的其他属性(message模板、displayName、description、itemType)的完整语义见 Rule 文档。

performance 数据变换:五种派生值函数

Performance probe 返回的是包含多次样本的表格,而检查需要一个派生值。performance变换(详见 DataTransformation/performance.md)为每个计数器计算指定的派生值。相关符号约定:

  • n— 该计数器的样本数量
  • c₁、c₂— 该计数器的样本值
  • t₁、t₂— 样本的时间戳
  • b₁、b₂— base 计数器的样本值
类型参数公式适用计数器类型
average—(1/n) · ΣcᵢPERF_COUNTER_LARGE_RAWCOUNT(65792)等原始计数值
delta_ratiobase_counter(c₂ − c₁) / (b₂ − b₁)PERF_AVERAGE_BULK(1073874176)(如平均闩锁等待时间),需配 base
min/max—所有样本的最小值 / 最大值原始计数值,用于极值监控
rate—(c₂ − c₁) / (t₂ − t₁)PERF_COUNTER_BULK_COUNT(272696576)(每秒计数类)
ratiobase_counter(c₂/b₂ + c₁/b₁) / 2PERF_LARGE_RAW_FRACTION(537003264)(如缓存命中率),需配 base

要点:

  • rate 与 average 的区别:rate计算的是"两个样本的差值 ÷ 时间戳差值",即真正的每秒速率,适用于Transactions/sec这类本身就是"每秒次数"的计数器;average则是多个样本的算术平均,适用于静态量(如总页数)。
  • ratio 与 delta_ratio 的区别:ratio是两次采样的比值取平均;delta_ratio是两次采样差值的比值,两者都要求指定base计数器,且 base 计数器必须与主计数器同时出现在探针的Counters配置中(如示例中的Buffer cache hit ratio base)。
  • 在performance变换中指定派生类型有两种写法:字符串简写("total_pages": "average")或对象写法("cache_hit_ratio": { "type": "ratio", "base": "cache_hit_ratio_base" }),后者还可附加instance参数(如{ "type": "rate", "instance": "_Total" })在变换层过滤实例。

变换的整体结构见 BasicPerformanceTransformStructure.svg:

一个检查使用多个计数器:多实例结果的"错位"陷阱

在实际使用中,同一检查往往需要同时观察多个计数器(例如批量请求速率batch_request_sec与锁请求速率lock_requests_sec)。但当这些计数器来自不同的实例时,直接合并会得到意外结果。

看下面的反例:把两个计数器放进同一个 probe reference,batch_request_sec不指定实例,lock_requests_sec指定_Total:

"probes": [ { "id": "PerformanceProbe", "alias": "b", "transform": { "type": "performance", "counters": { "batch_request_sec": "rate", "lock_requests_sec": { "type": "rate", "instance": "_Total" } } } } ]

变换结果返回两行数据,每行只填充了其中一个计数器的值:

batch_request_seclock_requests_secinstance_name
123snull''(未选择实例)
Null456s'_Total'

条件表达式是按行逐一求值的:第一行对batch_request_sec求值并可能触发一条消息,第二行对lock_requests_sec求值并触发另一个错误——这显然不符合"两个指标共同判定"的预期。

解决方案:用别名(alias)拆分引用

正确做法是将同一 Performance probe 引用两次,各自通过变换只取一个计数器,并用alias区分结果。alias是探针引用的任意别名,其机制详见 ProbeReference 文档中的 alias 一节:

"probes": [ { "id": "PerformanceProbe", "alias": "b", "transform": { "type": "performance", "counters": { "batch_request_sec": "rate" } } }, { "id": "PerformanceProbe", "alias": "l", "transform": { "type": "performance", "counters": { "lock_requests_sec": { "type": "rate", "instance": "_Total" } } } } ]

此时传给检查的数据结构变为:

b::batch_request_secb::instance_namel::lock_requests_secl::instance_name
123s''(无实例)456s'_Total'

两个计数器各自独立成行,条件可以同时、无歧义地引用它们。变量名有三种等价写法(在无歧义场景下):

@PerformanceProbe::batch_request_sec @b::batch_request_sec @batch_request_sec

其中@b::batch_request_sec形式通过"别名 + 双冒号"精确定位到指定引用产生的数据,是处理"同一探针被多次调用"时的标准做法(ProbeReference 文档 中还有把探针 A 的数据作为参数传给探针 B 的进阶用法)。

引擎视角:Performance probe 的执行语义

了解探针引擎的执行语义有助于写出高效的规则。根据 Probe 文档 的说明:

  • 探针是 JSON 属性,值为实现数组:属性名即探针 ID,值是一个探针实现(probe implementation)数组。引擎会选择第一个与目标模式(target pattern)匹配的实现,因此实现顺序很重要。Performance probe 也可以与其他类型的实现(如 T-SQL、WMI)混合构成一个探针。
  • 探针应设计为无副作用(side-effect free)的函数:引擎不保证探针的调用顺序,甚至可能为降低对目标 SQL Server 的负载而重排调用;当没有任何检查需要某个探针的数据时,该探针根本不会被调用。默认规则集中的探针只读取元数据,不读取用户表数据,也不向实例写入任何内容。
  • 性能计数器的采样开销:Performance probe 需要按numberOfSamples × interval的时间窗口在目标机器上持续采样,这与其他一次性取数的探针(如单条 T-SQL 查询)不同。在自定义规则时,应合理控制采样次数与间隔,避免在高负载实例上叠加额外的采样开销。

与默认规则集的衔接

仓库中的 DefaultRuleset.csv 提供了默认规则集的可读版本(455 行规则,可直接在仓库中浏览与筛选),其中包含大量与性能相关的检查,例如:

  • HighCPUUsage:评估服务器整体 CPU 与 SQL Server 进程 CPU 占用(阈值参数默认 70);
  • PlansUseRatio:评估计划缓存中单次使用计划的比例,用于判断是否应启用 "Optimize for ad hoc workloads";
  • 一系列 TempDB 文件配置检查(TempDBFilesNotLess8、TempDBFiles1PerCPU等)。

这类运行时指标类检查正是 Performance probe 与 performance 变换的典型应用场景。读者可以结合 MakingCustomChecks_sample.json(自定义规则集示例)与 DisablingBuiltInChecks_sample.json(按 ID / 标签禁用内置规则示例)来编写自己的性能监控规则。更完整的概念体系(探针、规则、变换、消息模板、局部变量)可继续阅读 Customization 目录 下的文档。

小结

Performance probe 是 SQL Assessment API 中连接"操作系统性能计数器"与"配置合规性判定"的桥梁。本文覆盖了其三个 implementation 参数(numberOfSamples、interval、counters)、计数器别名与实例过滤的两种 JSON 写法、五种派生值函数(average、min/max、rate、ratio、delta_ratio)的公式与计数器类型对应关系,以及多计数器多实例场景下"按行求值"的陷阱与基于别名拆分的标准解法。在动手自定义性能规则时,建议对照 performance 数据变换文档 与 ProbeReference 文档 协同阅读,并结合默认规则集观察真实生产级规则的写法。

  • 示例工程
  • 数据库
  • 教程
  • 后端

【免费下载链接】sql-server-samples

Azure Data SQL Samples - Official Microsoft GitHub Repository containing code samples for SQL Server, Azure SQL, Azure Synapse, and Azure SQL Edge

项目地址:https://gitcode.com/gh_mirrors/sq/sql-server-samples
点击查看免费下载
上一篇:Rust跨平台编译终极指南:使用Cross实现零配置多架构支持
下一篇:MCP代理内存使用分析终极指南:Valgrind实战教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询