如何在 Node.js 中准确测量 PostgreSQL 查询执行时间

夜芳姑娘_6663

夜芳姑娘_6663

2026-06-18

867人浏览

原创

如何在 Node.js 中准确测量 PostgreSQL 查询执行时间

本文详解为何连续查询的性能测量会出现偏差,并提供基于连接池、预热机制和统计学方法的可靠基准测试方案,避免缓存、jit 编译、连接复用等干扰因素导致的误判。

本文详解为何连续查询的性能测量会出现偏差,并提供基于连接池、预热机制和统计学方法的可靠基准测试方案,避免缓存、jit 编译、连接复用等干扰因素导致的误判。

您遇到的现象——“第二个查询总是比第一个快”——是典型的基准测试陷阱,而非真实性能差异。根本原因并非代码逻辑错误,而是忽略了数据库与运行时环境的多层优化机制:PostgreSQL 的查询计划缓存(plan cache)、操作系统页缓存(page cache)、Node.js V8 的 JIT 预热、以及 TCP 连接建立开销的摊销效应。您每次新建 Client 实例并调用 connect(),看似“隔离”,实则仍共享底层资源(如 socket 复用、DNS 缓存、甚至 PG 的 shared_buffers),导致后续查询天然受益于前序操作的“热身”。

✅ 正确做法:使用连接池 + 预热 + 多轮统计

首先,绝不应在单次基准中反复新建/销毁连接(如您的 new Client() 循环)。这会引入巨大的连接建立/销毁噪声(通常 1–5ms),严重污染毫秒级测量。应改用 pg.Pool 并复用连接:

const { Pool } = require('pg');

const pool = new Pool({
  user: 'postgres',
  host: 'localhost',
  database: 'indexing-is-really-hard',
  password: process.env.DB_PASSWORD,
  port: 5432,
  max: 10, // 控制并发连接数,避免过载
});

// 预热:执行一次 dummy 查询,确保连接就绪、计划缓存填充
await pool.query('SELECT 1');

其次,必须分离“预热阶段”与“测量阶段”。每组查询应独立预热 + 测量,且顺序需随机化以消除顺序偏差:

arcgis-js
arcgis-js

提供 ArcGIS Maps SDK for JavaScript 开发辅助,涵盖 2D/3D 地图视图、图层管理、数据可视化、空间分析等功能模块的参考和示例指导。

下载
async function benchmarkQuery(queryObj, iterations = 1000) {
  // Step 1: Warm-up (5x)
  for (let i = 0; i = 10) times.push(end - start); // skip first 10
  }

  const avg = times.reduce((a, b) => a + b, 0) / times.length;
  const p95 = times.sort((a, b) => a - b)[Math.floor(times.length * 0.95)];

  console.log(
    `${queryObj.queryStr.replace(/[\r\n\s]+/g, ' ').substring(0, 60)}... ` +
    `→ avg: ${avg.toFixed(3)}ms, p95: ${p95.toFixed(3)}ms`
  );
  return { avg, p95, times };
}

// 执行时打乱顺序,避免系统性偏差
const shuffled = [...queries].sort(() => Math.random() - 0.5);
for (const q of shuffled) {
  await benchmarkQuery(q);
}

⚠️ 关键注意事项

  • 勿用 Date.now():精度仅毫秒级,且受系统时钟调整影响;必须使用 performance.now()(微秒级高精度,单调递增)。
  • 禁用自动 prepared statement:pg 默认启用 prepare: true,可能使首次查询变慢(计划生成开销)。如需纯 SQL 比较,显式关闭:
    await pool.query({ text: queryObj.queryStr, values: queryObj.options, prepare: false });
  • 控制变量:确保测试期间无其他负载(如后台备份、监控查询);使用 EXPLAIN (ANALYZE, BUFFERS) 在 psql 中交叉验证执行计划是否一致。
  • 统计有效性:单次 1000 次循环易受 GC 或系统抖动影响。建议运行 3–5 轮完整 benchmark,取各轮平均值的中位数。

? 总结

您观察到的“B 总比 A 快”本质是未控制实验条件的伪相关。真实性能对比必须满足:① 连接复用(池化);② 充分预热;③ 顺序随机化;④ 排除首尾异常值;⑤ 使用高精度计时器。只有这样,测得的 avg 才反映查询本身的计算与 I/O 开销,而非环境噪声。最终结论应始终以 psql 的 EXPLAIN ANALYZE 为金标准,Node.js 测量仅用于验证应用层行为一致性。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

5346

5

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

2023.07.04

1309

3

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

2023.07.04

5264

6

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

2023.09.01

1000

4

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

1936

5

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

2023.09.04

3705

4

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

2023.09.12

4873

6

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

2023.09.20

3200

5

java访问控制修饰符介绍
java访问控制修饰符介绍

java访问控制修饰符有四种,分别是public、protected、private、默认访问修饰符。详细介绍:1、public,public是最宽松的访问控制修饰符,被修饰的类、方法和变量可以被任何其他类访问,当一个类、方法或变量被声明为public时,它们可以在任何地方被访问,无论是同一个包中的类还是不同包中的类;2、protected修饰符等等。

2023.09.20

1028

7

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

RabbitMQ 教程手册
RabbitMQ 教程手册

共0课时 | 0人学习

Node.js 教程
Node.js 教程

共57课时 | 25.6万人学习