PinterestはFlinkに基づいてリアルタイム分析をどのように行いますか?

考えられる問題は次のとおりです。

  1. 実験によりインプレッションの統計が大幅に減少したため、できるだけ早く実験を終了する必要があります。
  2. 対照群と比較して、実験は検索実行の数の大幅な増加につながりました。

画像

図1-信頼区間を使用したリアルタイムの実験指標

上のパネルは、選択したイベントの実験グループとコントロールグループのトラフィック(つまり、アクションの数)と傾向(つまり、一意のユーザーの数)を示しています。実験開始以来、これらのカウントは3日間累積されています。3日後に再ランプが発生した場合(実験グループと対照グループに割り当てられたユーザーの数が増える)、カウントは0にリセットされ、3日の累積が再開されます。

実験群と対照群の比較が統計的に有効であることを確認するために、いくつかの統計的検定を行いました。インジケーターはリアルタイムで配信されるため、新しいレコードを順番に受信するたびにこれらのチェックを実行する必要があります。これには、従来の固定視野検査とは異なる方法が必要です。そうしないと、偽陽性率が高くなります。ギャンブラー破産、ベイジアンA / Bテスト、アルファ消費関数法など、いくつかの逐次テスト法を検討しました。数値的安定性を確保するために、t検定+ボフェローニ補正(ケースを複数のテストとして処理)から開始し、最初の実装のテスト数を事前に決定します。

ハイエンドデザイン

画像

図2-リアルタイム実験パイプラインの高レベル設計

リアルタイム実験パイプラインには、次の主要コンポーネントが含まれています。

  • 最近のランプの実験的なグループ割り当て→5分ごとにCSVファイルをS3の場所に公開します。このCSVは、過去3日間に割り当てられたユーザーが増加した実験グループのスナップショットです。この情報は、実験メタデータをホストしている内部AnalyticsアプリケーションのMySQLデータベースにクエリを実行することで取得できます。
  • イベントの割り当てをフィルタリングする→Pinterestでの何百ものユーザーアクションを分析しました。このジョブは、「filtered_events」Kafkaトピックに挿入された最も重要なビジネスイベントのみを保持します。これらのイベントから不要なフィールドが削除されるため、filtered_eventsトピックは非常に軽量です。ジョブはFlink処理時間内に実行され、Flinkの増分チェックポイントを通過して、進行状況を5秒ごとにHDFSに保存します。
  • 実験のフィルタリングアクティベーションジョブ→ユーザーが実験を入力するようにトリガーされるたびに、アクティベーションレコードが作成されます。トリガールールは実験ロジックに依存し、ユーザーは何百回も実験に参加するようにトリガーできます。過去3日間に開始された、またはグループ割り当てが増加した実験のアクティベーションレコードのみが必要です。

アクティベーションレコードをフィルタリングするために、このジョブはFlinkのブロードキャストステータスモードを使用します。10秒ごとに、「最近のランプ実験グループ」ジョブによって公開されたCSVの変更を確認し、アクティベーションも消費するKeyedBroadcastProcessFunctionのすべてのパーティションに公開します。

KeyedBroadcastProcessFunctionは、ブロードキャストCSVとアクティベーションストリームを組み合わせて、過去3日間にランプアップ実験が行われていないアクティベーションレコードを除外できます。さらに、「group-ramp-up-time」がアクティベーションレコードに追加され、「filtered_experiment_activations」kafkaトピックに挿入されました。

図3-Scalaオブジェクトが中間層のKafkaトピックに挿入されている


図4-リアルタイム実験の累積操作図

上記は、リアルタイムアグリゲーション(アグリゲーション)Flinkジョブの概要です。ここでは、いくつかの演算子について簡単に説明しますが、他の演算子については後で詳しく説明します。ソースオペレーターはKafkaからデータを読み取り、シンクはRESTインターフェースを使用してデータを内部Analyticsストアに書き込みます。

重複するイベントの削除→ここでは、(event.user_id、event.event_type、event.timestamp)をキーとしてKeyedProcessFunctionを使用して実装されています。ここでの考え方は、同じユーザーからの同じイベントタイプのイベントが同じタイムスタンプを持っている場合、それらは重複イベントであるということです。このような最初のイベントはダウンストリームに送信されますが、その状態で5分間キャッシュされます。それ以降のイベントはすべて破棄されます。5分後、タイマーが開始し、ステータスをクリアします。ここでの前提は、すべての反復イベント間の間隔が5分以内であるということです。

最初のトリガー時間を見つける→ここに(experiment_hash、experiment_group、user_id)をキーとするFlinkKeyedProcessFunctionがあります。ここでの前提は、ユーザーに対して受信された最初の実験的アクティベーションレコードは、最初のトリガー時間でのアクティベーションでもあるということです。実験の立ち上げ後、最初に受信したアクティベーションはダウンストリームに送信され、状態として3日間保存されます(実験グループの立ち上げから3日間のカウントが累積されています)。ランプ時間の3日後、タイマーが状態をクリアします。

15分の処理時間タンブリングウィンドウ→イベントが入力され、結果がダウンストリームに送信されると、分子コンピューターと分母コンピューターの両方がカウントを累積します。これは何百万ものレコードを意味しますが、結果をアナリティクスストアに頻繁に送信する必要はありません。処理時間中に15分のFlinkタンブリングウィンドウを実行できます。これはより効率的です。分子コンピューターの場合、このウィンドウは( "experiment_hash"、 "experiment_group"、 "event_type"、 "timestamp")によってキー設定されます。15分後にウィンドウがトリガーされると、max_usersを含むレコードがフェッチされ、ダウンストリームのAnalyticsストアシンクに送信されます。

接続イベントとアクティベーション

図5-ユーザーIDを介してアクティベーションフローとイベントフローを接続する

FlinkのIntervalJoin演算子を使用して、ストリーム間接続を実装します。IntervalJoinは、次の3日間、ユーザーごとに1つのアクティベーションレコードをバッファリングし、一致するすべてのイベントは、アクティベーションレコード内の他の実験メタデータとともにダウンストリームに送信されます。

この方法の制限:

  1. 私たちのニーズでは、IntervalJoin演算子は、その間隔が動的ではなく固定されているため、少し柔軟性がありません。たとえば、ユーザーは実験の開始後2日で参加できますが、IntervalJoinはこのユーザーに対して3日間実行されます。つまり、データの蓄積を停止してから2日間実行されます。3日後にグループが再ランプする場合、ユーザーはそのような接続を2つ持つこともできます。この状況はダウンストリームで処理されます。
  2. 事件和 Activation 不同步:如果 Activation 作业失败并且 Activation 流被延迟,则可能会丢失一些数据,因为没有匹配 Activation 的事件还会继续流动。这将导致计数不足。

我们研究了 Flink 的 IntervalJoin 源代码。它会在“左侧缓冲区”中缓冲 Activation 3 天时间,但事件将被立即删除。目前似乎无法通过配置更改此行为。我们正在研究使用 Flink 的协同处理函数来实现这个 Activation 到事件的连接,该函数是用于流到流连接的更通用的函数。我们可以将事件缓冲 X 分钟,这样即使 Activation 流延迟了 X 分钟,管道也可以处理延迟而不会出现计数不足。这将帮助我们避免同一用户的两次连接,并能形成更加动态的管道,其可以立即感知到实验组的 re-ramp,并支持更多动态行为,例如在组 re-ramp 时自动扩展累积的覆盖范围 。

Join Results Deduplicator

图 6-Join Results Deduplicator

Join Results Deduplicator 是一个 Flink KeyedProcessFunction,它由 experiment_hash,experiment_group,event_type,user_id 作为 key。这个 operator 的主要目的是在向下游发送记录时插入“user_first_time_seen”标志——下游 Numerator Computer 使用这个标志来计算倾向编号(# unique users),而无需使用设置的数据结构。

这个 operator 将状态存储到 last-ramp-time+ 3 天,之后状态将被清除。

Numerator Computer

画像

图 7-Numerator Computer

Numerator Computer 是一个 KeyedProcessFunction,由 experiment_hash,experiment_group,event_type 作为 key。它会在最后 2 小时内一直滚动 15 分钟的存储桶(bucket),每当有新记录进入时都会更新这些桶。对于流量来说,每个动作都很重要;因此对于每个事件,动作计数都会增加。对于倾向数字(unique user)——它取决于"first_time_seen”标志(仅在为 true 时递增)。

随着时间的流逝,存储桶会滚动 / 旋转。每次新事件进入时,存储桶数据都会向下游刷新到 15 分钟的 tumbling 窗口中。

它有一个时间为 3 天的计时器(从 ramp-up 时间→3 天),可在触发后清除所有状态,这样就能在 ramp-up3 天后重置 / 清除计数,完成归零。

垃圾消息与处理

为了使我们的流管道具有容错能力,Flink 的增量检查点和 RocksDB 状态后端被用来保存应用程序检查点。我们面临的一项有趣挑战是检查点失败。问题似乎在于检查点流程需要花费很长时间,并且最终会超时。我们还注意到,在发生检查点故障时通常也会有很高的背压。

画像

图 8-Flink UI 中显示的检查点故障

在仔细检查了检查点故障的内部机制之后,我们发现超时是由于某些子任务未将确认发送给检查点协调器而导致的,整个检查点流程都卡住了,如下所示。

画像

图 9- 子任务未发送确认

然后我们针对导致失败的根本原因应用了一些调试步骤:

  1. 检查作业管理日志
  2. 检查在检查点期间卡住的子任务的任务管理器日志
  3. 使用 Jstack 详细查看子任务

原来子任务运行很正常,只是抽不出空来处理消息。结果,这个特定的子任务具有很高的背压,从而阻止了 barrier 通过。没有 barrier 的收据,检查点流程将无法进行。

在进一步检查所有子任务的 Flink 指标之后,我们发现其中一个子任务产生的消息数量比其对等任务多 100 倍。由于消息是通过 user_id 在子任务之间分区的,这表明有些用户产生的消息比其他用户多得多,这就意味着那是垃圾消息。临时查询我们的 spam_adjusted 数据集后也确认了这一结果。

画像

图 10- 不同子任务的消息数

为了缓解该问题,我们在“过滤器事件作业”中应用了一个上限规则:对于一个小时内的用户,如果我们看到的消息多于 X 条,则仅发送前 X 条消息。应用上限规则后,检查点就不再出现故障了。

数据稳健性和验证

数据准确性对于实验指标的计算而言更为重要。为了确保我们的实时实验流程按预期运行,并始终提供准确的指标,我们启动了一个单独的每日工作流,其执行与流作业相同的计算,但使用的是临时方式。如果流作业结果违反以下任一条件,则会提醒开发人员:

  • 在同一累积期间(本例中为 3 天),计数不应减少
  • 如果在第一个累积期之后进行了 re-ramp,则计数应从 0 开始再累积 3 天
  • 流结果与验证流结果之间的差异不应超过某个阈值(在我们的例子中为 2%)。

通过查询实验元数据,我们分别在 3 种情况下对实验进行了验证:

  1. 单次 ramp-up 实验
  2. 在初始累积期间内进行多次 ramp-up 实验
  3. 在初始累积期后进行多次 ramp-up 实验

这一流程如下所示:

画像

图 11- 验证流程

规    模

在这一部分中,我们提供了一些基本统计信息,展示实时实验管道的规模:

  1. 输入主题流量(一天的平均值):

Kafka 主题名称 消息数 / 每秒 MB/ 每秒
experiment_activation 2,513,006.863 1,873.295
event 127,347.091 64.704
filted_experiment_activation 876,906.711 88.237
filtered_backend_event 9,478.253 0.768
  1. 100G 检查点
  2. 200~300 个实验
  3. 8人のマスター、50人のワーカー、それぞれec2 c5d.9xlarge
  4. 計算の並列処理は256です。

将来の計画

  1. PWT(ピナー待機時間)などのより多くのインジケーターをサポートして、実験によってピナーの遅延が異常に増加した場合に、できるだけ早く停止できるようにします。
  2. 「間隔接続」の代わりにFlinkの協調処理機能を使用するようにパイプラインを更新して、イベントストリームとアクティベーションストリーム間の非同期の問題に対処するためにパイプラインをより動的かつ柔軟にすることができます。
  3. パーティション:パーティションは状態を増加させるため、パーティションがサポートできるパーティションのタイプを調査します。
  4. 電子メールまたはSlackを介したリアルタイムアラートをサポートします。

ありがとう

リアルタイムの実験的分析は、Pinterestの本番環境での最初のFlinkベースのアプリケーションです。Flinkプラットフォームを構築し、サービスとして提供してくれたビッグデータプラットフォームチーム(特にSteven Bairos-Novak、Jooseong Kim、Ang Zhang)に感謝します。また、優れた視覚化を提供してくれたAnalytics Platformチーム(Bo Sun)、リアルタイムのデータ抽出を提供してくれたLogging Platformチーム、統計相談をしてくれたData Scienceチーム(Brian Karfunkel)にも感謝します。


おすすめ

転載: blog.51cto.com/15060462/2677034