性能测试实战指南:从核心指标到瓶颈定位的完整流程

性能测试实战指南:从核心指标到瓶颈定位的完整流程

1. 项目概述:一份来自一线的性能测试实战指南

干了十三年测试,从功能点点点,到自动化脚本满天飞,再到性能测试这个“深水区”,我踩过的坑、熬过的夜、和开发运维“掰扯”过的架,估计能写好几本书。性能测试这东西,听起来高大上,什么高并发、低延迟、TPS、响应时间,一堆专业术语。但说穿了,它的核心目标就一个:在你把系统交给真实用户之前,尽可能模拟出真实世界的压力,看看它到底能扛住多少人、多快的速度,以及什么时候会“撑不住”。这就像给一座新建的大桥做承重实验,你不能等通车了才发现桥会晃。

网上关于性能测试的文章、教程、工具介绍多如牛毛,但很多要么是纯理论,讲一堆概念;要么是“Hello World”级别的demo,离真实项目十万八千里。新手看完往往更懵:工具我会用了,脚本我也能录了,但结果怎么看?瓶颈在哪?怎么跟开发说“这里有问题”?这篇内容,就是我想把过去十三年,尤其是那些在真实项目里摸爬滚打出来的实战经验,系统地整理出来。它不追求面面俱到,但力求把从零搭建环境、到设计场景、执行压测、分析结果、定位瓶颈、输出报告这一整套流程中,最核心、最容易出错的环节讲透。无论你是刚接触性能测试的新手,还是有一定经验想提升实战能力的老手,希望这篇“脱水干货”能让你少走弯路,真正看懂一次性能测试应该怎么做。

2. 性能测试核心思路与全局设计

做性能测试,最怕的就是一上来就打开工具开录脚本、开压。没有清晰的思路和设计,最后得到的数据往往是一团乱麻,无法说明任何问题。所以,在动手之前,我们必须想清楚几个关键问题。

2.1 明确测试目标:我们到底要测什么?

这是所有工作的起点,目标不清,后续全偏。性能测试不是简单地说“测一下系统快不快”,它需要具体、可衡量的目标。通常,这些目标来源于以下几个方面:

  1. 业务需求:这是最根本的。例如,市场部门计划在“双十一”进行大促,预计高峰时段订单创建峰值达到每秒5000笔。那么,我们的性能测试目标就是验证系统在每秒5000笔订单的压力下,响应时间是否满足要求(比如95%的请求在2秒内完成),且错误率低于0.1%。
  2. 容量规划:技术团队需要评估,现有的服务器配置(如4核8G的虚拟机)最多能支撑多少用户在线。这决定了我们需要扩容的时机和规模。
  3. 性能基准:在每次重大版本发布前,跑一套标准的性能测试用例,获取关键接口的性能数据(如TPS、平均响应时间),与上一个版本或基线版本进行对比,确保新版本没有引入性能衰退。
  4. 瓶颈探测:在系统架构调整或引入新技术(如更换缓存中间件、数据库分库分表)后,通过压力测试主动寻找系统的性能瓶颈点,验证架构改进的有效性。

实操心得:一定要把模糊的需求转化为数字指标。当业务方说“系统要快”时,你要追问:“多快?在多少用户同时操作时多快?哪些操作要快?” 最终形成一份双方确认的《性能测试需求说明书》,里面明确列出要测试的业务场景、预期的并发用户数、事务响应时间要求、成功率要求等。这是后续所有工作和“扯皮”的依据。

2.2 关键指标解析:TPS、响应时间、并发数与资源利用率

性能测试报告里一堆指标,新手容易看花眼。我们抓住最核心的四个,理解了它们,就看懂了系统性能的“心电图”。

  1. TPS(Transaction Per Second,每秒事务数):这是衡量系统处理能力的核心指标。注意,事务不等于请求。一个“用户登录”事务,可能包含了访问登录页(1个请求)、提交表单(1个请求)、跳转首页(1个请求)等多个HTTP请求。TPS关注的是业务完成的速率。系统能稳定达到的最高TPS,基本上就是它的性能天花板。

  2. 响应时间(Response Time):用户从发起请求到收到完整响应所花费的时间。我们通常不只看平均响应时间,更要关注百分位数,比如P95(95%的请求响应时间小于此值)或P99。平均时间可能很好看,但如果有5%的请求慢到10秒,用户体验就是灾难。P95/P99能更好地反映尾部延迟,即大多数用户的体验。

  3. 并发用户数(Concurrent Users):这是一个最容易产生误解的指标。它通常指在同一时刻向服务器发送请求的用户数量,而不是简单的“在线用户数”。一个在线用户可能在看页面,并没有发起请求。在性能测试工具中,我们通过控制并发线程数来模拟这个“并发用户数”。但要注意,线程数并不完全等于实际并发用户,它还受到思考时间(用户操作间隔)、脚本逻辑的影响。

  4. 资源利用率:这是系统内部的“健康指标”。主要包括:

    • CPU使用率:通常,如果应用是CPU密集型(如大量计算、编解码),CPU使用率是首要关注点。长期超过70%-80%可能成为瓶颈。
    • 内存使用率:关注使用量及趋势,是否有内存泄漏(使用量持续增长不释放)。
    • 磁盘I/O:读写吞吐量和等待时间。数据库操作、日志写入频繁的系统需重点关注。
    • 网络I/O:带宽使用率和网络连接数。
    • 数据库指标:慢查询数量、连接数、锁等待时间等。

核心逻辑关系:随着我们施加的并发压力(并发用户数)逐渐增加,系统的TPS会先线性上升,达到一个最大值后趋于平稳或下降;同时,响应时间会从平稳状态开始逐渐增长,当系统过载时响应时间会急剧上升。我们的目标就是找到那个TPS最高、响应时间尚可接受的“最佳并发点”。

2.3 测试环境规划:如何搭建一个“像样”的压测环境

测试环境的真实性直接决定了测试结果的价值。一个常见的误区是,在开发本地环境或低配测试环境跑出漂亮数据,就以为生产环境没问题。

  1. 环境隔离:性能测试环境必须独立,避免与其他测试活动相互干扰。最好能独占一套与生产环境架构类似(可以等比例缩容)的环境。
  2. 数据准备:这是性能测试的“粮草”。数据量级和分布要尽可能模拟生产。
    • 量级:如果生产库有1亿用户,测试库至少要有百万级,否则数据库查询的执行计划可能完全不同,导致性能差异巨大。
    • 分布:数据不能是均匀的“假数据”。例如,热门商品的数据访问频率应该远高于冷门商品。需要构造符合业务特征的数据(如使用正态分布生成用户活跃度)。
    • 数据构造工具:可以自己写脚本,也可以使用专业工具。我常用的是通过数据库存储过程或Python脚本,结合Faker库来批量生成符合业务逻辑的测试数据。
  3. 网络考虑:确保压测机(施压端)与被测系统之间的网络带宽足够,且延迟稳定。避免因网络成为瓶颈而误判。通常,压测机和服务器应在同一个局域网或机房内。

注意:绝对不可以在生产环境直接进行未经验证的高压力压测!这被称为“混沌测试”或“红蓝对抗”,需要极其严谨的预案和管控,不属于常规性能测试范畴。我们讨论的都是在前置的、独立的测试环境中进行。

3. 核心工具选型与脚本开发实战

工欲善其事,必先利其器。选择一款合适的工具,并写出真实模拟用户行为的脚本,是性能测试成功的一半。

3.1 主流压测工具对比与选型建议

目前主流的开源压测工具是JMeterLocust,商业工具有 LoadRunner、阿里云PTS等。对于大多数互联网公司和团队,我首推JMeter

特性Apache JMeterLocust
类型开源,Java桌面应用开源,基于Python的代码驱动框架
优点1. 功能全面,支持HTTP、TCP、JDBC等多种协议。
2. GUI界面友好,易于录制和调试脚本。
3. 插件生态丰富,报告详细。
4. 社区庞大,资料和解决方案多。
1. 用Python写脚本,非常灵活,能模拟任何复杂逻辑。
2. 分布式部署简单,支持海量并发。
3. 资源消耗相对较低。
缺点1. 单机并发能力有限(数千级别),需要分布式部署应对更高并发。
2. GUI消耗资源,大规模压测建议用命令行模式。
3. 复杂逻辑需要配合BeanShell等脚本,学习成本稍高。
1. 无GUI,脚本调试对编程能力要求高。
2. 默认报告比较简单,需要二次开发或集成其他工具。
3. 协议支持不如JMeter原生丰富(但可通过代码扩展)。
适用场景绝大多数Web应用、API接口的性能测试。适合测试团队、需要快速上手和丰富报告的场景。需要极高并发、或测试逻辑极其复杂灵活的场景。适合开发人员或具有较强编码能力的测试人员。

选型建议:如果你是初学者或团队以功能测试转性能测试为主,从JMeter开始是最好的选择,它的学习曲线相对平缓,可视化操作能帮你快速建立性能测试的直观感受。当你遇到JMeter无法满足的极端复杂场景时,再考虑Locust

3.2 JMeter脚本开发精要:超越“录制与回放”

很多人用JMeter就是“录制-回放”,但这只能应对最简单场景。要模拟真实用户,必须深入脚本。

  1. 参数化与关联:这是让脚本“活”起来的关键。

    • 参数化:用户的登录名、搜索关键词、商品ID等不能每次都一样。你需要使用CSV Data Set Config组件,从外部文件读取数据,实现多用户使用不同数据。
    • 关联:下一个请求经常依赖于上一个请求的响应。例如,登录后服务器返回一个token,后续所有请求都要带上这个token。你需要用正则表达式提取器JSON提取器,从登录响应中提取token,并保存到一个变量(如${auth_token})中,在后续请求的Header或参数里引用它。
  2. 思考时间与集合点

    • 思考时间(Timer):真实用户操作之间有间隔。添加高斯随机定时器,可以更真实地模拟用户操作停顿,避免给服务器施加不合理的瞬时压力。
    • 集合点(Synchronizing Timer):用于模拟“瞬间爆发”的场景,比如秒杀开始的那一刻。它让所有虚拟用户在一个点等待,直到达到指定数量后同时释放,制造并发峰值。
  3. 断言与事务控制器

    • 断言:检查服务器返回是否正确,比如响应码是否为200,响应体是否包含特定文本。这是判断请求成功与否的依据,直接影响成功率统计。
    • 事务控制器:将多个请求步骤(如:访问首页->登录->搜索商品->下单)组合成一个业务事务。JMeter会统计这个事务整体的响应时间和成功率,这对于从业务角度衡量性能至关重要。

实操示例:一个简单的登录-查询流程脚本结构

测试计划 ├─ 线程组 (模拟100个并发用户,循环10次) │ ├─ CSV Data Set Config (读取 user.csv,变量:username, password) │ ├─ 事务控制器 (名称:用户登录事务) │ │ ├─ HTTP请求:POST /api/login │ │ │ ├─ 参数:username=${username}, password=${password} │ │ │ └─ 正则表达式提取器:提取 `"token":"(.+?)"` 到变量 `auth_token` │ │ └─ 响应断言:检查响应文本包含 "success" │ ├─ 高斯随机定时器 (固定延迟:1000ms,偏差:200ms) │ └─ 事务控制器 (名称:查询用户信息) │ ├─ HTTP请求:GET /api/user/profile │ │ └─ Header: Authorization: Bearer ${auth_token} │ └─ 响应断言:检查响应码等于 200 └─ 监听器 (查看结果树、聚合报告、图形结果等)

3.3 分布式压测部署:突破单机瓶颈

当需要模拟数千、数万级别并发时,单台压测机的网络、CPU、内存或端口数可能成为瓶颈。此时需要采用分布式压测

  1. 原理:由一台机器作为控制机(Master),负责管理测试计划和收集结果;其他多台机器作为压力机(Slave),接收控制机的指令,实际执行测试脚本并向被测服务器发送请求。
  2. JMeter分布式配置步骤
    • 在所有压力机上安装相同版本的JMeter和Java。
    • 启动压力机上的JMeter Server(执行jmeter-server.batjmeter-server)。
    • 在控制机的jmeter.properties文件中,配置remote_hosts为所有压力机的IP地址和端口(默认1099)。
    • 在控制机的GUI中,运行 -> 远程启动,即可选择指定的压力机集群执行测试。
  3. 注意事项
    • 确保控制机与所有压力机、压力机与被测服务器之间网络通畅。
    • 脚本依赖的文件(如CSV数据文件)需要手动同步到所有压力机的相同路径下。
    • 使用命令行模式(jmeter -n -t testplan.jmx -r)进行分布式压测更稳定,资源消耗更低。

4. 场景设计与执行策略

有了脚本,怎么压?是上来就全力猛攻,还是慢慢加码?不同的场景设计,能帮你发现不同的问题。

4.1 经典性能测试场景模型

  1. 基准测试:单用户、单业务场景下的性能测试。目的是获取在无压力情况下,系统单业务操作的最佳性能数据,作为后续测试的对比基线。例如,单用户登录的响应时间应该是毫秒级。
  2. 负载测试:逐步增加并发用户数,观察系统性能指标(TPS、响应时间、资源利用率)的变化趋势,找到系统的“性能拐点”和最大承载能力。这是最常用、最核心的场景。
  3. 压力测试:在超过系统正常负载的情况下持续运行,目的是发现系统在极限压力下的表现(如内存泄漏、连接池耗尽),以及系统的恢复能力。比如,用1.5倍于最大负载的压力,持续压测30分钟。
  4. 稳定性测试(耐力测试):在系统正常负载压力下(通常取最大负载的70%-80%),长时间运行(如24小时、72小时)。目的是检查系统在长期运行下是否有性能衰减、内存泄漏等问题。很多线上问题都是在长时间运行后才会暴露。
  5. 并发测试:模拟特定时刻大量用户同时执行同一操作,如秒杀、抢红包。重点测试系统的锁处理、资源竞争和队列机制。

4.2 负载模型与加压策略

如何控制虚拟用户数随时间变化,这就是负载模型。

  1. 阶梯加压(Ramp-Up):最常用的策略。线程组中设置Ramp-Up Period,让虚拟用户在指定时间内逐步启动。例如,100个线程,Ramp-Up时间为100秒,则每秒启动1个线程。这可以观察系统在压力逐步增大时的表现。
  2. 波浪式加压:模拟业务高峰和低谷。可以通过多个线程组,或使用Ultimate Thread Group插件来实现。例如,先5分钟内加压到500用户,持续10分钟,然后在5分钟内降到100用户,如此循环。这种模型对系统的弹性伸缩能力是很好的考验。
  3. 瞬时高峰:使用集合点,让所有已启动的用户在同一时刻发起请求,模拟瞬时流量冲击。

实操心得:不要一开始就进行复杂场景测试。建议的流程是:基准测试 -> 单场景负载测试(找瓶颈) -> 混合场景负载测试(模拟真实) -> 稳定性/压力测试。在负载测试中,建议采用“小步快跑”的方式,每次增加一定并发数(如50),稳定运行2-3分钟后,观察指标,无异常再继续增加。记录下每次增加压力后的性能数据,最终可以绘制出TPS-并发数、响应时间-并发数的曲线图,直观看到拐点。

4.3 监控体系搭建:眼睛要亮

执行压测时,不能只盯着JMeter的报告。必须全方位监控被测系统的状态。

  1. 服务器资源监控
    • Linux服务器:使用top,vmstat,iostat,netstat等命令。但更推荐使用nmon这个工具,它可以一次性收集CPU、内存、磁盘、网络等所有关键指标,并生成易于分析的报告。
    • 可视化监控:搭建Prometheus + Grafana是当前的主流选择。通过在服务器上部署Node Exporter,可以轻松地将系统资源指标收集到Prometheus,并在Grafana中配置美观的实时监控大盘。
  2. 应用性能监控
    • JVM应用:使用jconsole,jvisualvm或更强大的Arthas。关注堆内存各区域(Eden, Survivor, Old Gen)变化、GC频率和耗时、线程状态等。
    • 全链路APM工具:如SkyWalking,Pinpoint。它们可以帮你追踪一个用户请求经过的所有服务(网关、服务A、服务B、数据库),清晰地看到耗时瓶颈在哪个服务、哪个方法。这对于微服务架构的性能排查是神器。
  3. 中间件与数据库监控
    • 数据库:MySQL可以监控慢查询日志、SHOW PROCESSLISTSHOW ENGINE INNODB STATUS。也可以使用Percona Monitoring and Management等专业工具。
    • Redis:使用INFO命令查看内存、命中率、连接数等。
    • Nginx:监控活跃连接数、请求速率、上游响应时间等。

核心原则:压测执行时,监控必须同步启动。你的眼睛应该同时关注压力曲线(JMeter)系统资源曲线(监控大盘),当TPS上不去或响应时间飙升时,要立刻去资源监控和应用监控中寻找线索。

5. 结果分析与性能瓶颈定位实战

压测跑完了,面对一堆数据和图表,如何快速定位问题?这是最能体现经验价值的部分。

5.1 看懂JMeter核心报告

抛开那些花哨的图形,聚合报告里的几个数字是关键:

  • 样本数:总请求数。
  • 平均值、中位数、P90、P95、P99:反映响应时间分布。重点看P95/P99
  • 异常%:错误请求百分比。理想情况下应为0%,超过1%就需要严肃对待。
  • 吞吐量:即TPS,系统核心能力指标。
  • 接收/发送KB/sec:网络流量。

分析模式:在负载测试中,随着并发数增加,一个健康的系统表现应该是:TPS平稳上升至一个平台,响应时间(P95)缓慢线性增长。当并发数达到某个临界点后,会出现:TPS开始持平或下降,同时P95响应时间急剧上升。这个临界点就是当前系统的性能瓶颈点。

5.2 性能瓶颈定位的“自上而下”法则

当发现性能指标不佳时,不要盲目猜测,遵循科学的排查路径:

  1. 第一步:确认压力是否真实送达应用服务器?

    • 查看JMeter本身的错误日志,是否有连接超时、连接被拒绝等网络错误。
    • 对比JMeter发送的请求速率(TPS)和应用服务器(Nginx/Apache)访问日志统计的请求速率是否基本一致。如果不一致,可能是网络或负载均衡问题。
  2. 第二步:查看服务器整体资源瓶颈(使用nmon/Prometheus)

    • CPU:如果us(用户态)CPU长期高于70%,说明应用计算繁忙;如果sy(系统态)CPU高,可能是系统调用频繁或上下文切换过多。
    • 内存:关注free内存是否持续减少,swap是否被使用。使用率过高可能引发OOM。
    • 磁盘I/O:使用iostat -x 1查看%util(利用率)和await(平均等待时间)。如果%util持续接近100%,说明磁盘已是瓶颈。
    • 网络:检查带宽是否打满,网络连接数是否达到上限。
  3. 第三步:定位到具体进程和服务(使用top/APM)

    • 如果整体CPU高,用top -c命令查看是哪个进程消耗最多。
    • 如果是Java应用,使用jstack导出线程堆栈,或直接用Arthas的thread命令,查看哪些线程长期处于RUNNABLE状态,对应什么代码。
    • 使用APM工具(如SkyWalking),直接定位到本次压测期间,耗时最长的服务接口和方法。
  4. 第四步:深入应用与中间件

    • 数据库:这是最常见的瓶颈。查看慢查询日志,分析执行计划。检查是否存在全表扫描、未用索引、锁等待。使用SHOW FULL PROCESSLIST查看当前执行的SQL。
    • 缓存:检查Redis的命中率。如果命中率低,大量请求穿透到数据库,压力会巨大。
    • 应用代码:是否存在低效算法(如多层循环嵌套)、同步锁竞争激烈、大量日志输出阻塞IO、创建大量临时对象引发频繁GC等问题。
    • JVM GC:频繁的Full GC会导致应用暂停(Stop-The-World),使响应时间出现周期性尖峰。监控GC日志,关注GC频率和耗时。

5.3 常见性能问题与速查表

现象可能原因排查方向
TPS低,响应时间正常1. 施压机本身性能不足或网络带宽打满。
2. 被测应用有频率限制或同步锁。
3. 脚本中思考时间设置过长。
1. 检查施压机CPU/网络。
2. 检查应用日志是否有限流提示。
3. 检查线程堆栈是否有锁等待。
TPS上不去,响应时间随并发线性增长典型瓶颈特征。资源已成为瓶颈,新请求需要排队。1. 检查服务器CPU、内存、磁盘I/O。
2. 检查数据库连接池、线程池是否已满。
3. 检查是否有外部依赖服务响应慢。
响应时间出现周期性尖峰1. JVM进行Full GC。
2. 数据库定时任务或缓存失效风暴。
3. 日志滚动归档。
1. 分析GC日志。
2. 检查数据库慢查询和缓存命中率随时间变化。
3. 检查应用日志文件切换时间。
错误率突然升高1. 连接池耗尽。
2. 数据库连接数满。
3. 内存溢出(OOM)。
4. 第三方服务超时或不可用。
1. 检查应用和中间件连接池配置及监控。
2. 检查服务器内存使用和OOM日志。
3. 检查外部服务调用链。
内存使用率持续升高不下降内存泄漏1. 使用jmap生成堆转储文件,用MAT等工具分析。
2. 检查是否有静态集合类不当引用、未关闭的资源(连接、流)。

实操心得:性能优化是一个“测量->假设->验证->修改”的循环过程。永远不要凭感觉优化。通过监控定位到一个疑似瓶颈点后(比如发现某个SQL慢),做出修改(加索引),然后重新跑一次完全相同的压测场景,对比优化前后的数据。只有数据上的提升,才能证明优化是有效的。

6. 报告编写与性能调优建议落地

测试做完,分析完了,最后一步是把结果和结论清晰地传达出去,并推动问题解决。

6.1 如何编写一份有价值的性能测试报告

报告不是数据的堆砌,而是讲一个“故事”:我们做了什么,发现了什么,这意味着什么,接下来该怎么办。

  1. 报告结构

    • 摘要:一页纸说清核心结论。本次测试是否通过?系统最大支撑能力是多少?主要瓶颈在哪里?建议的优化方向是什么?
    • 测试概述:测试目标、测试范围、测试环境(硬件、软件版本)、测试数据说明。
    • 测试场景与策略:详细描述每个测试场景的业务流程、并发用户模型、加压策略、持续时间。
    • 监控与结果分析:这是核心章节。不要只贴图,要解读。
      • 趋势对比图展示TPS、响应时间随并发数变化的曲线,标出性能拐点。
      • 表格汇总关键场景下的核心指标(P95响应时间、TPS、错误率)。
      • 监控截图展示在瓶颈点发生时,服务器CPU、内存、数据库慢查询等关键指标的状态。
      • 瓶颈分析:明确指出根据数据分析,性能瓶颈定位在何处(如:MySQL CPU使用率95%,发现慢SQL:SELECT * FROM order WHERE ...)。
    • 结论与建议
      • 结论:针对每个测试目标,给出明确的通过/不通过判断。
      • 优化建议:针对每个瓶颈点,给出具体、可操作的建议。例如:“建议为order表的user_id字段添加索引,预计可提升该接口TPS约50%。” 避免说“优化数据库”这种空话。
      • 风险提示:如果测试未通过,说明在预期流量下可能存在的业务风险。
  2. 可视化技巧

    • 使用Grafana等工具制作实时监控大盘的截图,比命令行输出更直观。
    • 使用JMeter的Generate Report功能(jmeter -g result.csv -o report)可以生成一个包含多种图表的HTML报告,可以直接引用。
    • 在报告中高亮显示异常数据(如用红色标出错误率>1%的行)。

6.2 从测试到调优:如何推动问题解决

性能测试工程师的价值,不仅在于发现问题,更在于推动和验证问题的解决。

  1. 沟通技巧:用数据和证据说话。当你把一张显示数据库CPU 100%的监控图,和一条对应的慢SQL语句一起发给开发同事时,沟通效率会高很多。避免使用“系统很慢”、“性能不行”这种模糊表述。
  2. 优化验证闭环:提出优化建议后,主动跟进。在开发同学修改代码或DBA优化SQL后,务必安排一轮回归性能测试。用相同的场景、相同的压力,验证优化是否真的有效,并将优化前后的数据对比图放入最终报告。这形成了“测试->发现->优化->验证”的闭环,体现了性能测试工作的完整价值。
  3. 建立性能基线:将每次重要版本发布前的性能测试数据保存下来,作为性能基线。后续版本迭代时,首先与基线进行对比,确保没有“性能回退”。这能将性能保障左移,避免问题累积到发布前才发现。

性能测试是一个需要技术、耐心和沟通能力的综合性工作。它不像功能测试那样有明确的“对错”,更多是在探索系统的“边界”和“状态”。多年的经验告诉我,保持好奇心,对每一个异常数据刨根问底,对每一个优化方案追测到底,才能真正守护住系统的稳定与流畅。最后分享一个小习惯:在每一次压测执行时,我都会用一个文档实时记录操作步骤、观察到的现象和当时的猜想,这个“压测日志”在后续分析时往往能提供关键的线索。