性能测试-其三
-
Prometheus PostgreSQL Exporter 可以监控哪些内容呢?
- 数据库连接指标:如
postgresql_connections,可以帮助了解当前数据库的连接数情况,包括空闲连接数、活跃连接数等,用于判断数据库的负载情况。 - 事务指标:例如
postgresql_xact_count,可以统计数据库中的事务执行次数,通过该指标可以分析数据库的事务处理能力和负载。 - 查询性能指标:能够收集慢查询的相关数据,帮助定位执行时间较长的查询语句,以便进行优化。
- 数据库连接指标:如
-
活跃连接数
活跃连接数直接体现了数据库当前所承受的工作负荷。连接数越多,意味着有更多的客户端请求正在被处理或等待处理,数据库的 CPU、内存等资源消耗也会相应增加。例如,在一个电商网站的数据库中,当有大量用户同时下单、查询商品信息时,活跃连接数会迅速上升,表明数据库正处于高负载状态。
-
响应时间(Response Time)与吞吐量(Throughput)并非总是线性相关
- 场景描述:
- 系统支持高并发(如多线程、异步非阻塞架构),即使单个请求处理时间较长,仍能并行处理大量请求。
- 示例:
- 每个搜索请求耗时 2 秒,但系统支持 100 个并发线程 → 吞吐量 = 100 / 2 = 50 QPS。
- 若仅支持 10 个并发线程 → 吞吐量 = 10 / 2 = 5 QPS。
- 结论:
- 高并发能力掩盖了单请求的耗时问题,吞吐量由并发数主导,而非单个请求的响应时间。
- 场景描述:
-
混合负载
混合负载是指在性能测试或实际生产环境中,模拟多种不同类型请求同时发生的场景。
比如在IC中搜索(60%)下载原图(20%)查看(20%),比例可以通过日志或调研获得
使用动态变量(不同的搜索关键词)避免缓存干扰
依赖关系:搜索结果的图像ID需传递给下载接口。
重要性:
- 暴露隐藏问题:
- 单一接口测试可能表现良好,但混合场景下资源竞争(如数据库连接池、线程池)会导致性能骤降。
- 例如:搜索和下载同时进行时,磁盘IO和网络带宽可能成为瓶颈。
- 真实用户体验:
- 用户不会仅执行单一操作,混合负载更贴近实际使用情况。
- 暴露隐藏问题:
-
Locust和Jmeter
维度 Locust JMeter 脚本编写 Python 代码(需编程基础),支持函数式编程。 图形化界面配置或录制脚本,也支持 CSV/JSON 格式。 协议支持 主要针对 HTTP/HTTPS,需自定义扩展其他协议。 原生支持多种协议(HTTP、数据库、消息中间件等)。 分布式能力 原生支持分布式,通过 Master-Slave 模式扩展。 需要手动配置分布式节点,设置相对复杂。 性能表现 轻量级,单机并发能力强(数万级别)。 依赖 Java 环境,单机并发通常在数千到一万左右。 学习成本 需掌握 Python 编程,适合开发 / 测试团队。 图形化操作易上手,适合无编程基础的测试人员。 扩展性 基于 Python 生态,可灵活调用第三方库。 通过插件扩展功能,但需注意插件兼容性。 实时监控 内置 Web 界面,支持自定义指标展示。 需要配合插件(如 PerfMon)实现服务器监控。 报告生成 支持 CSV 日志和自定义报告脚本。 内置丰富的可视化报告,支持 HTML 导出。 Locust 更适合:
- 需要代码化管理测试逻辑:如动态参数生成、复杂业务流程(用户登录→下单→支付)。
- 高并发分布式测试:模拟数万甚至数十万用户并发访问(如电商大促、秒杀场景)。
- 开发团队自测:开发人员可直接通过 Python 脚本编写测试用例,集成到 CI/CD 流程中。
JMeter 更适合:
- 多协议混合测试:同时测试 Web 接口、数据库、消息队列等多组件系统。
- 快速搭建测试场景:非技术人员通过图形化界面快速完成接口测试、压力测试。
- 企业级性能测试:大型项目中需要生成标准化报告、对接监控平台(如 Prometheus)的场景。
若追求灵活性和高并发,Locust 是高效之选;若需要全面的协议支持和低代码门槛,JMeter 更能满足需求。
-
混合负载实现
考虑所有场景同时开始同时结束,所有线程组持续时间保持一致
使用Jmeter同一个测试计划中,不同的功能选用不同的测试组,通过控制测试组的线程数,实现并发比例
通过控制线程组启动时间(ramp-up time),保证同时开始。
或者通过 吞吐量控制器
![]()
吞吐量控制器只能控制其层级下的取样器
不推荐使用线程组实现混合负载
在 JMeter 中,通过多个线程组(Thread Group)控制不同接口的线程数比例看似直观(例如:搜索接口 60 线程、显示接口 30 线程、下载接口 10 线程),但实际存在 灵活性低、维护成本高、比例失真 等问题。
并且跨线程组传输数据困难
线程组之间独立,报告不好分析
而 吞吐量控制器核心优势
- 比例控制灵活精准
动态权重分配:
无论总线程数多少,Throughput Controller 按百分比分配请求比例(如 60% 搜索、30% 显示、10% 下载),无需手动计算线程数。
示例:
总线程数 50 → 搜索约 30 次/秒,显示 15 次/秒,下载 5 次/秒。
总线程数 100 → 搜索约 60 次/秒,比例自动保持。
- 资源共享与参数化便捷
变量自然传递:
同一线程组内,搜索接口提取的 imageId 可直接用于显示和下载接口,无需额外处理。
统一参数化:
CSV 文件、用户变量在同一线程组内共享,避免跨组同步问题。
- 结果聚合分析简单
所有请求在同一线程组执行:
汇总报告直接展示各接口的统计结果(如平均响应时间、错误率),无需合并数据。
- 更贴近真实用户行为
模拟用户会话连续性:
用户在一次会话中可能依次执行搜索→显示→下载,Throughput Controller 可在单线程组内顺序执行,而多线程组需拆分会话,破坏行为真实性。
- 比例控制灵活精准
-
锁等待时间
锁等待时间是衡量数据库并发性能的关键指标,过长的锁等待通常意味着事务设计不合理、索引缺失或锁争用过高。
锁等待时间的影响
- 性能下降
- 锁等待时间过长会导致事务响应变慢,系统吞吐量下降。
- 例如,若数据库平均查询响应时间为 100ms,但锁等待时间占 50ms,则实际有效执行时间仅 50ms。
- 死锁风险
- 长时间的锁等待可能增加死锁(两个或多个事务互相等待对方释放锁)的概率。
- 死锁会导致数据库强制回滚其中一个事务,进一步影响性能。
- 资源浪费
- 等待锁的事务会占用数据库连接、内存等资源,可能导致资源耗尽。
三、常见原因
- 锁粒度过大
- 例如,使用表级锁而非行级锁,导致更多事务被阻塞。
- 示例:在 MySQL 中,
SELECT ... FOR UPDATE默认对扫描的所有行加锁,若查询条件无索引,可能导致全表锁。
- 长事务
- 事务执行时间过长,导致锁持有时间延长。
- 例如,一个事务包含多个复杂查询或批量更新,可能长时间持有锁。
- 索引缺失
- 无合适索引时,数据库可能进行全表扫描并对所有行加锁。
- 示例:
UPDATE users SET status = 'active' WHERE last_login < '2023-01-01';若无last_login索引,会锁定整个users表。
- 锁争用高
- 高并发场景下,多个事务频繁竞争同一资源的锁。
- 例如,电商系统中大量用户同时抢购同一商品,导致库存行锁竞争激烈。
- 性能下降
-
使用nginx获取静态资源
使用 Nginx 获取静态资源是一种常见的 Web 服务器配置方式,用于高效地提供网站的静态文件(如图片、CSS、JavaScript、HTML 等)。Nginx 以其高性能、低内存占用和强大的静态资源处理能力而闻名。
Nginx 通过
location块配置静态资源的路径映射,直接从文件系统读取并返回文件,无需经过应用服务器(如 Node.js、Tomcat),从而显著提升响应速度。 -
P90和P95
如果希望了解系统对于大多数用户的服务水平,评估系统的整体性能状况,P90 是一个合适的指标。
P95更侧重于捕捉那些相对较少但可能影响到部分用户体验的较长响应时间或异常情况,适合用于发现性能瓶颈。
-
监控吞吐量的意义
可直接反映系统的处理效率
识别性能瓶颈
- 意义:
吞吐量的变化趋势能暴露系统瓶颈:- 资源饱和:当 CPU、内存或磁盘 IO 达到极限时,吞吐量不再随并发增加而提升。
- 锁竞争或慢查询:数据库吞吐量骤降可能因死锁或未优化 SQL。
- 案例:
- 现象:某图像搜索接口的 QPS 在 50 并发时达 200,但 100 并发时仍为 200,且响应时间飙升。
- 根因:数据库连接池耗尽或线程池排队,导致吞吐量达到上限。
优化资源分配
- 意义:
通过吞吐量与资源使用率的关联分析,合理分配资源:- CPU 密集型服务:若 CPU 使用率 90% 但吞吐量低,可能代码存在低效循环。
- IO 密集型服务:吞吐量受限于磁盘或网络带宽时,需升级硬件或优化传输逻辑。
- 应用场景:
- 缓存优化:添加 Redis 缓存后,相同 QPS 下数据库负载下降 70%,说明吞吐量效率提升。
- 线程池调优:调整线程池大小后,QPS 从 100 提升至 300,资源利用率更均衡。
吞吐量需结合其他指标综合解读:
关联指标 分析场景 响应时间 高吞吐量但高延迟:系统处理快但排队严重(如线程池不足)。 错误率 吞吐量高但错误率高:系统超负荷运行,部分请求失败(如数据库连接池耗尽)。 资源利用率 吞吐量未达预期但 CPU 100%:存在代码性能问题(如死循环或未释放资源)。 - 意义:
-
CPU 使用率的细分指标是诊断系统性能问题的关键线索:
- 高
us%:优化应用代码或算法。 - 高
sy%:减少系统调用或内核竞争。 - 高
wa%:解决 I/O 瓶颈(磁盘/网络)。 - 高
si%:优化网络配置或硬件。
- 高
-
内存
物理内存(RAM)
├── Used Memory(已用内存)
│ ├── 应用程序内存
│ └── Buffers/Cache(缓存中不可回收部分)
├── Available Memory(可用内存)
│ ├── Free Memory(空闲内存)
│ └── Reclaimable Cache(可回收缓存)
└── Swap Usage(交换分区使用率)
├── 当物理内存不足时触发
└── 反映内存压力 -
稳定性测试的必要性
** 发现资源泄漏问题**
- 典型问题:
- 内存泄漏:图像处理服务(如OpenCV库)未释放资源,运行3天后内存耗尽,进程崩溃。
- 连接泄漏:数据库连接池未正确回收,最终导致“Too many connections”错误。
- 测试方法:
- 持续运行系统72小时,监控内存、线程、文件句柄等资源变化趋势。
验证长时间高负载下的可靠性
- 场景示例:
- 系统需7×24小时处理用户上传的图像缩略图生成任务。
- 稳定性测试可验证后台队列(如RabbitMQ)是否会在持续压力下堆积消息或丢失任务。
确保数据一致性
- 案例:
- 某系统在长时间运行后,因未处理分布式锁超时,导致同一图像被重复处理,存储冗余数据。
- 稳定性测试可结合异常注入(如网络抖动),验证分布式事务的最终一致性。
- 典型问题:
-
压力测试的必要性
模拟高并发场景,暴露性能瓶颈
- 典型问题:
- 用户同时上传大量高分辨率图像时,服务器带宽或存储IO被占满,导致上传失败。
- 突发搜索请求(如全网热点事件)压垮数据库,响应时间从1秒飙升到10秒。
- 测试价值:
- 验证系统能否支撑设计容量(如1000 QPS的搜索接口)。
- 定位瓶颈点(如数据库连接池不足、线程池排队)。
验证弹性扩展能力
- 场景示例:
- 当系统负载达到80%时,自动触发Kubernetes水平扩容(HPA),新增Pod实例分担流量。
- 压力测试可验证扩容策略是否生效,避免流量洪峰时扩容延迟导致崩溃。
**预防“雪崩效应” **
- 案例:
- 某图像管理系统因缓存击穿(如Redis宕机),大量请求直接穿透到数据库,导致级联故障。
- 压力测试可验证熔断机制(如Sentinel)是否有效触发,保护核心链路。
- 典型问题:
-
基准测试的必要性
单用户系统性能,为后续优化和对比提供客观依据。
从开始避免问题,如果单用户不达标,就无需压测了
实施步骤
- 环境标准化:确保测试环境硬件、软件、数据量与生产环境一致。
- 数据准备:导入百万级测试图像数据,模拟真实数据分布。
- 执行测试:
- 单用户执行关键操作,记录基准指标。
- 多次运行取平均值,排除随机波动。
- 生成报告:对比历史数据,标注性能变化趋势。


浙公网安备 33010602011771号