1. 从“rea”这个标题说起一个被低估的缩写背后藏着什么第一次看到“rea”这个标题的时候我脑子里蹦出来的第一反应是——这大概率又是一个被缩写玩坏的项目名。做技术的人都有个通病喜欢把长名字砍成三四个字母仿佛名字越短越显得内行。但“rea”这个组合有点意思它不像“api”“sdk”“cli”那样有明确的行业共识也不像“abc”“xyz”那样一看就是随手打的占位符。它更像是一个被刻意压缩过的核心概念背后可能指向好几个完全不同的方向。我后来花了不少时间去琢磨这个标题可能的含义。结合当前技术社区里高频出现的一些讨论方向“rea”最有可能指向的是实时事件分析Real-time Event Analytics这个领域也有人把它理解为响应式事件架构Reactive Event Architecture的缩写。不管是哪一种核心都绕不开三个关键词实时、事件驱动、分析决策。这三个词放在一起基本上就勾勒出了当前很多数据密集型系统的核心诉求——数据产生的那一刻就要被处理处理完立刻要能指导下一步动作中间不能有太长的等待窗口。为什么这个方向值得单独拿出来聊因为我在实际项目里踩过太多坑了。很多团队一上来就说要做实时分析结果架构搭到一半发现数据延迟根本压不下去或者事件量一上来整个管道就堵死了。更常见的是系统倒是能跑但分析结果和业务动作之间是断开的数据看板很漂亮可没人知道该拿这些数字怎么办。所以“rea”这个标题虽然短但它背后牵扯的东西一点都不少——从数据采集、传输、处理、存储到最终的分析呈现和决策触发每一层都有讲究。这篇文章我打算按照一个完整项目的推进逻辑来写从整体设计思路开始然后拆解核心细节接着走一遍实操过程最后把常见问题和排查技巧整理出来。适合正在做实时数据管道、事件驱动系统或者流式分析平台的同行参考也适合刚接触这块但不知道从哪里下手的朋友。我会尽量把每个选择背后的“为什么”讲清楚而不是只丢一堆配置和命令出来。2. 整体设计与思路拆解为什么这么搭不那么搭2.1 核心需求到底在解决什么问题先把场景说清楚。假设你手上有一个系统每天会产生大量的用户行为事件、系统日志、交易记录或者传感器读数。这些数据的特点是持续不断、量大、单条价值低但聚合价值高。传统的做法是先把数据攒起来定时跑批处理比如每小时或者每天跑一次。这个模式的问题在于等你跑完批处理业务窗口可能已经过去了。比如风控场景一笔异常交易发生之后你等一个小时再报警钱早就转走了。实时事件分析要解决的就是这个时间差问题。它的目标是在事件发生后的秒级甚至毫秒级内完成采集、处理、分析并且把结果推送到需要的地方。这里面有几个关键指标需要提前想清楚端到端延迟从事件产生到分析结果可用的时间。这个指标决定了你能做多“实时”的事情。吞吐量每秒能处理多少条事件。这个决定了系统能不能扛住峰值。准确性在追求速度的同时能不能保证不丢数据、不重复计算。可扩展性事件量翻倍的时候系统能不能平滑扩容。这四个指标之间是有取舍的。想要极低的延迟可能就要牺牲一定的吞吐量想要绝对准确可能就要接受稍高的延迟。所以第一步不是选工具而是明确你的业务场景到底更看重哪几个指标。2.2 架构选型的几个关键岔路口在明确了需求之后接下来就是架构选型。我见过太多项目一上来就堆技术栈Kafka、Flink、ClickHouse、Redis 全上结果发现一半的组件根本用不上反而增加了运维负担。所以选型的时候我习惯先问几个问题第一个问题数据是“先存后算”还是“边来边算”如果业务对延迟不敏感比如日报、周报这种那批处理完全够用没必要上流式。但如果业务要求秒级响应那就必须走流式处理路线。这里有个中间态叫微批处理比如 Spark Streaming 那种把流切成小批次来处理延迟在秒级到分钟级之间。很多场景其实微批就够了不一定非要上真正的流式。第二个问题事件需不需要被多个消费者重复消费如果需要那消息队列就是必须的。Kafka 在这个领域基本是默认选项它的分区模型、持久化机制和消费组语义都很成熟。但如果只是简单的点对点传输RabbitMQ 或者云厂商的消息服务也能胜任而且运维更简单。第三个问题分析结果是“预聚合”还是“即席查询”预聚合的意思是提前把指标算好查询的时候直接读结果速度快但灵活性差。即席查询是查询的时候现算灵活但慢。实时分析场景通常两者都需要核心指标预聚合保证看板秒开探索性分析走即席查询允许慢一点。基于这几个问题一个典型的“rea”系统架构大概长这样数据源 → 采集层 → 消息队列 → 流处理引擎 → 存储层 → 查询/分析层 → 告警/动作触发每一层都有多种选择我后面会逐个拆解。这里先强调一个原则不要为了技术而技术。如果业务量不大单机版的方案可能比分布式方案更合适。我见过一个日事件量不到十万的系统硬是搭了一套 Kafka 集群结果运维成本比开发成本还高。2.3 为什么我倾向于“分层解耦”而不是“端到端”早期我做实时分析的时候喜欢把采集、处理、存储写在一个服务里图省事。后来发现这种端到端的写法有几个致命问题一是扩展性差处理逻辑变了要整个服务重新部署二是容错性差一个环节挂了整条链路都断三是复用性差同样的数据想给另一个业务用得重新写一套。分层解耦之后每一层只负责自己的事情。采集层只管把数据收上来不关心数据怎么处理处理层只管计算逻辑不关心数据从哪来存储层只管存好不关心谁去查。层与层之间通过标准化的接口或者消息格式通信。这样做的好处是任何一层需要替换或者扩容都不会影响其他层。当然分层也有代价就是延迟会增加。每经过一层就多一次网络传输和序列化/反序列化的开销。所以如果业务对延迟极其敏感比如高频交易场景可能就需要把某些层合并做一定程度的耦合。但对于大多数场景来说分层带来的可维护性和可扩展性收益远大于那几毫秒的延迟增加。3. 核心细节解析与实操要点每一层到底怎么选怎么配3.1 采集层别小看这一层坑最多采集层看起来简单不就是把数据发出去吗但实际做起来这一层的问题是最多的。常见的问题包括数据格式不统一、采集端资源占用过高、网络抖动导致数据丢失、采集频率和业务节奏不匹配。先说数据格式。我强烈建议在采集层就统一数据格式通常用 JSON 或者 Protobuf。JSON 的好处是可读性好调试方便Protobuf 的好处是体积小、序列化快。如果事件量很大比如每秒几十万条Protobuf 的优势会非常明显。但 Protobuf 的代价是需要维护 schema每次字段变更都要重新生成代码。所以如果团队规模不大JSON 可能更务实。再说采集端资源占用。采集程序通常跑在业务服务器上如果占用太多 CPU 或者内存会影响业务本身。我一般会把采集程序的资源限制设得比较保守比如 CPU 限制在单核的 20% 以内内存限制在 256MB 以内。同时要设置好背压机制当采集端处理不过来的时候要么丢弃低优先级数据要么把数据暂存到本地磁盘等恢复后再补发。还有一个容易被忽略的点是采集频率。有些团队为了追求实时性把采集间隔设得极短比如每 100 毫秒发一次。结果发现网络开销巨大而且很多批次里只有几条数据效率极低。我的经验是采集间隔要根据事件产生速率来定。如果事件产生速率本身就不高比如每秒几十条那采集间隔设成 1 秒甚至 5 秒都完全可以接受。实时性不是靠采集频率堆出来的而是靠整条链路的优化。3.2 消息队列Kafka 不是唯一选择但通常是最好选择消息队列在“rea”架构里扮演的是缓冲和解耦的角色。它的核心作用有三个一是削峰填谷当事件量突然暴增的时候队列可以先把数据攒着让下游按自己的节奏消费二是解耦生产者和消费者生产者不需要知道谁在消费数据三是保证数据不丢即使下游挂了数据还在队列里。Kafka 在这个领域基本是默认选项它的分区模型、副本机制和持久化能力都很成熟。但 Kafka 也不是没有缺点它的运维复杂度相对较高尤其是集群规模大了之后分区再平衡、副本同步这些问题会让人头疼。如果团队没有专门的运维人员可以考虑云厂商提供的托管 Kafka 服务或者用更轻量的替代方案比如 Redpanda 或者 Pulsar。选 Kafka 的时候有几个参数需要特别注意分区数分区数决定了并行消费的能力。分区太少消费端扩不起来分区太多会增加元数据管理的开销。我的经验是分区数至少要是预期消费者数量的 2 到 3 倍同时要考虑未来一年的数据增长。副本数生产环境建议至少 3 副本保证单节点故障不影响数据可用性。保留策略按时间保留还是按大小保留保留多久这些要根据业务需求来定。如果下游有重放需求保留时间就要设长一点。压缩方式Kafka 支持多种压缩算法比如 gzip、snappy、lz4、zstd。压缩率越高CPU 开销越大。我一般用 lz4 或者 zstd在压缩率和性能之间比较平衡。注意Kafka 的默认配置是偏向吞吐量的如果你的场景更看重延迟需要调整一些参数比如把linger.ms设小一点把batch.size也调小。但这样会牺牲一定的吞吐量需要根据实际情况权衡。3.3 流处理引擎Flink 还是 Spark或者别的流处理引擎是“rea”系统的心脏负责所有的计算逻辑。目前主流的选择有 Apache Flink、Apache Spark Streaming、Apache Storm以及云厂商的一些托管服务。如果让我给建议新项目直接上 Flink除非你有特殊的理由不用它。Flink 的优势在于它是真正的流处理引擎不是微批。它的事件时间语义和水位线机制处理乱序数据非常成熟状态管理和恰好一次语义也做得很好。这些特性在实时分析场景里非常关键。比如你统计每 5 分钟的订单量如果数据有延迟事件时间语义可以保证统计结果正确而不是简单地按处理时间算。Spark Streaming 的优势在于生态好如果你已经在用 Spark 做批处理那流处理也用 Spark 可以减少技术栈的复杂度。但 Spark Streaming 本质上是微批延迟通常在秒级做不到 Flink 那种毫秒级。所以如果业务对延迟要求极高Spark Streaming 可能不够用。Storm 现在用得比较少了主要是因为它虽然延迟低但状态管理和恰好一次语义的支持不如 Flink 完善。除非你有非常特殊的低延迟需求否则不太建议新项目用 Storm。用 Flink 的时候有几个核心概念需要吃透时间语义事件时间、处理时间、摄入时间。事件时间是事件实际发生的时间处理时间是事件被处理的时间摄入时间是事件进入 Flink 的时间。实时分析通常用事件时间因为只有事件时间才能反映真实的业务节奏。水位线用来处理乱序数据。水位线是一个时间戳表示“在这个时间之前的数据都已经到了”。当水位线超过某个窗口的结束时间时就触发窗口计算。状态Flink 的状态可以是键控状态或者算子状态。键控状态是按 key 分区的适合做聚合算子状态是跟算子实例绑定的适合做连接器或者缓冲区。检查点Flink 通过检查点机制实现容错。检查点会定期把状态快照保存到外部存储当任务失败重启时可以从最近的检查点恢复。3.4 存储层选对了查询快十倍存储层的选择取决于你的查询模式。如果是预聚合的指标数据量不大用 Redis 或者关系型数据库就够了。如果是原始事件数据量大需要支持灵活的查询那就要用列式存储或者时序数据库。ClickHouse 是目前实时分析场景里非常热门的选择它的列式存储和向量化执行引擎让聚合查询非常快。但 ClickHouse 的短板在于高并发点查和频繁更新它更适合批量写入和聚合查询。如果你的场景是大量单条查询ClickHouse 可能不是最优解。Druid 和 Pinot 也是实时分析领域的老牌选手它们的设计目标就是低延迟的聚合查询支持预聚合和实时摄入。但这两个系统的运维复杂度都不低组件多配置复杂。如果数据是时序数据比如监控指标或者传感器读数那 InfluxDB 或者 TimescaleDB 可能更合适。InfluxDB 的写入性能很好查询语言也针对时序场景做了优化。TimescaleDB 则是基于 PostgreSQL 的好处是可以直接用 SQL学习成本低。我的建议是不要一开始就追求完美的存储方案。先用一个简单的方案把链路跑通比如用 Kafka 做临时存储或者用关系型数据库先顶着。等业务量上来了查询模式也清晰了再根据实际需求换更合适的存储。过早优化存储层往往会导致过度设计。4. 实操过程与核心环节实现从零搭一条实时分析链路4.1 环境准备与基础组件部署假设我们要搭一个最小可用的实时分析系统处理用户行为事件统计每分钟的活跃用户数和事件总量。我用 Docker Compose 来编排基础组件这样环境搭建会快很多。先创建一个docker-compose.yml文件定义 Kafka、Flink 和 ClickHouse 三个服务。Kafka 用单节点模式Flink 用 Session 模式ClickHouse 用单机版。生产环境肯定不能这么简陋但开发和测试阶段这样搭最省事。version: 3.8 services: zookeeper: image: zookeeper:3.8 ports: - 2181:2181 kafka: image: bitnami/kafka:3.5 ports: - 9092:9092 environment: - KAFKA_CFG_ZOOKEEPER_CONNECTzookeeper:2181 - KAFKA_CFG_LISTENERSPLAINTEXT://:9092 - KAFKA_CFG_ADVERTISED_LISTENERSPLAINTEXT://localhost:9092 depends_on: - zookeeper clickhouse: image: clickhouse/clickhouse-server:23.8 ports: - 8123:8123 - 9000:9000 volumes: - ./clickhouse-data:/var/lib/clickhouse flink-jobmanager: image: flink:1.17 ports: - 8081:8081 command: jobmanager environment: - JOB_MANAGER_RPC_ADDRESSflink-jobmanager flink-taskmanager: image: flink:1.17 depends_on: - flink-jobmanager command: taskmanager environment: - JOB_MANAGER_RPC_ADDRESSflink-jobmanager启动命令很简单docker-compose up -d等所有服务起来之后用docker ps确认一下状态。Kafka 和 ClickHouse 可能需要几十秒才能完全就绪可以看日志确认。提示如果本地内存不够可以把 Flink 的 TaskManager 数量减到 1 个或者调小各个容器的内存限制。开发环境不需要太多资源。4.2 创建 Kafka 主题和 ClickHouse 表Kafka 主题的分区数我设成 3副本数设成 1单节点环境没法设多副本。保留时间设成 7 天够开发测试用了。docker exec -it kafka kafka-topics.sh \ --create \ --topic user-events \ --bootstrap-server localhost:9092 \ --partitions 3 \ --replication-factor 1 \ --config retention.ms604800000ClickHouse 这边建两张表一张存原始事件一张存预聚合的分钟级指标。原始事件表用 MergeTree 引擎按天分区按事件时间排序。预聚合表用 SummingMergeTree 或者 AggregatingMergeTree方便做增量聚合。CREATE TABLE user_events_raw ( event_time DateTime, user_id String, event_type String, page_url String, extra String ) ENGINE MergeTree() PARTITION BY toYYYYMMDD(event_time) ORDER BY (event_time, user_id); CREATE TABLE user_events_agg ( minute DateTime, event_type String, event_count UInt64, unique_users UInt64 ) ENGINE SummingMergeTree() ORDER BY (minute, event_type);4.3 写一个简单的 Flink 作业Flink 作业的逻辑不复杂从 Kafka 读数据解析 JSON按分钟窗口聚合然后把结果写到 ClickHouse。我用 Java 写因为 Flink 的 Java API 最成熟。先加依赖在pom.xml里加上 Flink 和 Kafka 连接器dependency groupIdorg.apache.flink/groupId artifactIdflink-streaming-java/artifactId version1.17.0/version /dependency dependency groupIdorg.apache.flink/groupId artifactIdflink-connector-kafka/artifactId version1.17.0/version /dependency dependency groupIdorg.apache.flink/groupId artifactIdflink-connector-jdbc/artifactId version3.1.0-1.17/version /dependency核心代码大概长这样StreamExecutionEnvironment env StreamExecutionEnvironment.getExecutionEnvironment(); env.enableCheckpointing(60000); // 每分钟做一次检查点 Properties kafkaProps new Properties(); kafkaProps.setProperty(bootstrap.servers, localhost:9092); kafkaProps.setProperty(group.id, rea-consumer); DataStreamString rawStream env.addSource( new FlinkKafkaConsumer(user-events, new SimpleStringSchema(), kafkaProps) ); DataStreamEvent events rawStream .map(new JsonToEventMapper()) .assignTimestampsAndWatermarks( WatermarkStrategy.EventforBoundedOutOfOrderness(Duration.ofSeconds(5)) .withTimestampAssigner((event, ts) - event.getEventTime()) ); DataStreamAggResult aggResults events .keyBy(Event::getEventType) .window(TumblingEventTimeWindows.of(Time.minutes(1))) .aggregate(new EventAggregator()); aggResults.addSink( JdbcSink.sink( INSERT INTO user_events_agg (minute, event_type, event_count, unique_users) VALUES (?, ?, ?, ?), (statement, result) - { statement.setTimestamp(1, new Timestamp(result.getMinute())); statement.setString(2, result.getEventType()); statement.setLong(3, result.getEventCount()); statement.setLong(4, result.getUniqueUsers()); }, new JdbcConnectionOptions.JdbcConnectionOptionsBuilder() .withUrl(jdbc:clickhouse://localhost:8123/default) .withDriverName(com.clickhouse.jdbc.ClickHouseDriver) .build() ) ); env.execute(rea-realtime-analysis);这段代码里有几个关键点需要解释一下。enableCheckpointing(60000)是开启检查点间隔 1 分钟。检查点间隔太短会增加开销太长会导致故障恢复时丢失更多数据。1 分钟是个比较平衡的值。forBoundedOutOfOrderness(Duration.ofSeconds(5))是设置水位线允许数据最多乱序 5 秒。这个值要根据实际数据的乱序程度来定。如果数据源本身比较有序可以设小一点比如 2 秒如果乱序严重就要设大一点但代价是窗口触发会延迟。TumblingEventTimeWindows.of(Time.minutes(1))是定义 1 分钟的滚动窗口。滚动窗口不重叠每个事件只属于一个窗口。如果需要重叠的窗口可以用滑动窗口。4.4 验证数据链路是否通畅作业提交之后先往 Kafka 里发几条测试数据看看能不能正常流转到 ClickHouse。echo {event_time:1690000000,user_id:u001,event_type:click,page_url:/home} | \ docker exec -i kafka kafka-console-producer.sh \ --bootstrap-server localhost:9092 \ --topic user-events等几秒钟然后查 ClickHouseSELECT * FROM user_events_agg ORDER BY minute DESC LIMIT 10;如果能看到数据说明链路是通的。如果看不到就要逐层排查Kafka 里有没有数据Flink 作业有没有报错ClickHouse 能不能连上。排查的思路我后面会详细讲。5. 常见问题与排查技巧实录踩过的坑才是真经验5.1 数据延迟突然变大怎么定位这是实时分析系统最常见的问题。表现是看板上的数据不更新了或者更新明显变慢。排查的时候我一般按从后往前的顺序来查。先看存储层。ClickHouse 的写入有没有堆积可以查system.merges和system.mutations表看看有没有长时间没完成的合并或者变更。如果写入堆积了可能是磁盘 IO 瓶颈或者表结构设计不合理导致合并太慢。再看流处理层。Flink 作业的背压情况怎么样在 Flink Web UI 上可以看到每个算子的背压状态。如果某个算子显示 HIGH 背压说明它处理不过来上游数据积压了。这时候要看是 CPU 不够、内存不够还是逻辑本身太复杂。我遇到过一次是因为在map里做了同步的 HTTP 调用每条数据都要等外部接口返回吞吐量直接掉到个位数。后来改成异步 IO 就解决了。最后看消息队列。Kafka 的消费延迟怎么样可以用kafka-consumer-groups.sh查看 consumer group 的 lag。如果 lag 持续增长说明消费速度跟不上生产速度。这时候要么增加消费者实例要么优化消费逻辑。还有一个容易被忽略的点是数据倾斜。如果某个 key 的数据量特别大比如某个热门页面的点击量是其他页面的几百倍那按 key 分区的时候处理这个 key 的算子就会成为瓶颈。解决办法是在 key 前面加随机前缀把热点 key 打散到多个分区然后再做二次聚合。5.2 数据重复或丢失怎么保证一致性实时系统里数据重复和丢失是两个极端但往往同时存在。重复通常是因为至少一次语义丢失通常是因为检查点没配好或者水位线设置不当。Flink 的恰好一次语义需要配合检查点和支持事务的 sink 才能实现。如果 sink 不支持事务那就只能做到至少一次也就是可能重复。对于重复数据可以在存储层做去重比如用事件 ID 做唯一键写入的时候用INSERT ... ON CONFLICT DO NOTHING或者ReplacingMergeTree引擎。数据丢失的常见原因有几个一是水位线设得太激进乱序数据还没到就触发了窗口计算导致晚到的数据被丢弃。解决办法是调大forBoundedOutOfOrderness的值或者用allowedLateness允许窗口延迟触发。二是检查点失败任务重启后从旧的检查点恢复中间的数据就丢了。要确保检查点的存储路径可靠并且监控检查点的成功率。三是Kafka 消费位点提交过早数据还没处理完就提交了位点任务挂了之后从新位点开始消费中间的数据就丢了。Flink 的 Kafka 连接器默认是在检查点完成时才提交位点这个行为是正确的不要改成自动提交。5.3 常见问题速查表问题现象可能原因排查方法解决思路看板数据不更新链路某环节中断逐层检查 Kafka lag、Flink 背压、ClickHouse 写入定位到具体环节后针对性修复数据延迟持续增大消费速度跟不上生产速度查看 consumer lag 和算子背压增加并行度、优化逻辑、扩容资源统计结果偏小水位线太激进导致晚到数据被丢弃检查水位线配置和窗口触发时间调大乱序容忍度或启用 allowedLateness数据重复至少一次语义导致检查 sink 是否支持事务存储层去重或改用支持事务的 sink作业频繁重启检查点失败或 OOM查看 Flink 日志和检查点历史修复检查点存储、调大内存、优化状态大小某个 key 处理特别慢数据倾斜查看各分区的处理速率加随机前缀打散热点 key5.4 几个我踩过的坑和对应的技巧第一个坑Kafka 分区数设得太少。刚开始做的时候我觉得数据量不大就设了 1 个分区。后来数据量涨上来消费端想扩容发现分区数不够最多只能有一个消费者实例。Kafka 的分区数只能增加不能减少所以一开始就要留足余量。我的经验是按预期峰值吞吐量的 3 到 5 倍来设分区数。第二个坑Flink 状态太大导致检查点超时。有一次做用户会话分析状态里存了每个用户的完整会话信息结果状态大小涨到几十 GB检查点每次都要跑好几分钟经常超时失败。后来改成只存必要的聚合信息状态大小降到了几百 MB检查点秒级完成。状态能小则小这是铁律。第三个坑ClickHouse 的JOIN性能很差。ClickHouse 的JOIN实现和传统数据库不一样它会把右表加载到内存里如果右表很大就会 OOM。实时分析场景里尽量避免JOIN能预聚合就预聚合能宽表就宽表。如果非要JOIN把大表放左边小表放右边。第四个坑时区问题导致统计结果对不上。事件时间戳通常是 UTC但业务看板要显示本地时间。如果 Flink 和 ClickHouse 的时区配置不一致统计出来的分钟级数据就会错位。解决办法是在整条链路里统一用 UTC只在最终展示的时候转成本地时间。6. 这套方案还能怎么扩展把基础链路跑通之后后面可以做的事情还有很多。比如加一个告警模块当某个指标超过阈值的时候自动发通知。告警的触发逻辑可以放在 Flink 里用ProcessFunction做状态监控也可以用 ClickHouse 的物化视图加定时查询。我倾向于放在 Flink 里因为延迟更低而且可以利用 Flink 的状态管理做更复杂的告警逻辑比如连续 N 个窗口都超标才触发。再比如加一个数据回放功能。当发现统计结果有问题的时候能够把某段时间的原始数据重新跑一遍验证修复逻辑是否正确。这个功能在排查问题的时候非常有用。实现方式是把 Kafka 的保留时间设长一点然后写一个回放作业从指定的 offset 开始消费。还有一个方向是多租户支持。如果这套系统要给多个业务线用就需要在数据里加租户标识并且在存储和查询层面做隔离。ClickHouse 可以用行级策略做隔离Flink 可以在 keyBy 的时候把租户 ID 加进去。但多租户会带来额外的复杂度如果业务量不大不如每个业务线单独部署一套省事。最后再分享一个小技巧在 Flink 作业里加一个旁路输出把解析失败的数据或者异常数据写到另一个 Kafka 主题里。这样既不会因为脏数据导致作业失败又能保留现场供后续分析。这个旁路输出的成本很低但排查问题的时候能省很多时间。