流处理中的水印是一种用于跟踪事件时间进度和处理事件乱序到达系统中延迟数据的机制。事件时间是指事件实际发生时的时间戳,而不是处理时的时间。水印标志着系统预期所有早期事件都已到达的时间点。例如,如果水印设置为时间戳 T
,系统会假定在此之后不会再有早于 T
的时间戳的事件到达。这使得处理引擎能够完成对直到 T
的事件的计算(如窗口聚合),即使之后可能仍会到达一些滞后事件。水印对于平衡实时系统中的延迟和准确性至关重要。
一个实际例子是处理用户点击流数据。假设事件带有指示用户点击按钮的时间戳,但网络延迟导致某些事件晚了几分钟到达。如果没有水印,系统可能会无限期地等待所有事件,从而延迟结果。使用水印,系统可以设置一个阈值(例如,“等待 10 秒钟处理延迟数据”)。如果最新事件的时间戳是下午 3:00,水印可能会设置为下午 3:00 减去 10 秒。一旦此水印超过一个时间窗口的结束时间(例如,一个在下午 3:00 结束的 1 分钟窗口),系统就会触发该窗口的聚合,即使之后会到达一些延迟的点击。超出水印的延迟事件可以单独处理,例如路由到侧输出进行特殊处理。
实现水印需要配置生成策略。在 Apache Flink 等框架中,开发者可以选择周期性水印(按固定间隔更新)或标点水印(由特定事件触发)。例如,Flink 的 BoundedOutOfOrdernessTimestampExtractor
设置了固定的延迟(例如 5 秒)来处理延迟数据。开发者必须权衡水印延迟:延迟过短可能导致忽略有效的延迟数据,而延迟过长则会增加处理延迟。此外,空闲数据源(例如,暂时离线的传感器)可能会导致水印停滞,因此一些系统使用心跳机制即使在不活动期间也能推进水印。正确调整水印可以确保及时获得结果,同时最大限度地减少由于延迟到达数据引起的误差。