23. confidential
Mobility Technologies Co., Ltd.
GPSをつけたユーザーが平均的にどの位置にいるかを求めるクエリを考える。
ユーザーの位置はuser_posテーブルに15秒ごとに書き込まれる。
ユーザーが位置トラッキングをOFFにしているときは、300秒ごとにuser_busy_timingテーブルにロ
グが書き込まれる。
Case 1. アルゴリズム的に無駄な処理をしている
23
WITH busy as
(
SELECT
user_id,
t - 150 as start_ts,
t + 150 as end_ts
FROM user_busy_timing
)
SELECT
user_id,
ST_GEOGPOINT(AVG(lon), AVG(lat)) AS point
FROM user_pos as t
WHERE
NOT EXISTS (
SELECT user_id FROM busy AS p
WHERE t.ts BETWEEN p.start_ts AND p.end_ts
AND t.user_id = p.user_id)
GROUP BY user_id
実際はもっと長いSQLクエリの一部だが、プロファイリン
グの結果、右のクエリの周辺がボトルネックであることが
判明。
25. confidential
Mobility Technologies Co., Ltd.
重い処理の特定
25
READ
$510, $511, $512, $513, $514, $515
FROM __stage0B_output
READ
$90, $91
FROM __stage09_output
AGGREGATE
GROUP BY $540 := $535, $541 := $531
$390 := SHARD_AVG($533)
$391 := SHARD_AVG($532)
$392 := SUM($534)
JOIN
ANTI HASH JOIN EACH WITH EACH ON $511 = $91
WITH busy as(
SELECT
user_id,
t - 150 as start_ts,
t + 150 as end_ts
FROM user_busy_timing
)
SELECT
user_id,
ST_GEOGPOINT(AVG(lon), AVG(lat)) AS point
FROM user_pos
WHERE
NOT EXISTS (
SELECT user_id FROM busy AS p
WHERE ts BETWEEN p.start_ts AND p.end_ts
AND t.user_id = p.user_id)
GROUP BY user_id
Stageの統計情報や、処理詳細から重い処理を特定する。
1. 重いのはcompute処理 (ST_GEOGPOINTやSHARD_AVGやANTI HASH JOIN)
2. ステージ詳細を見ると、ST_GEOGPOINTはこのstageには含まれていない
3. ボトルネックはSHARD_AVGか、ANTI HASH JOIN。
avg max
read 0 ms 0 ms
compute 168,634 ms 544,593 ms
write 20 ms 759 ms
26. confidential
Mobility Technologies Co., Ltd.
ステージ詳細には自分で書いた覚えのない処理が出てくる。
ドキュメントがあまりないが、おそらく処理内容は以下の通り。
(捕捉) ステージ詳細に出てくる関数
26
処理名 内容
SHARD_AVG SHARD単位での平均処理
ROOT_AVG SHARD単位の結果をまとめた全体の平均処理
EACH WITH ALL broadcast joinを実行している
EACH WITH EACH Broadcast出ないjoinを実行している
ANTI HASH JOIN NOT EXISTSを使った時に走る処理
27. confidential
Mobility Technologies Co., Ltd.
NOT EXISTSをコメントアウトすると高速化したのでANTI HASH JOINが遅いことがわかった。
JOINの高速化はJOIN前に件数を減らせないか考える。
高速化
27
WITH busy as
(
SELECT
user_id,
t - 150 as start_ts,
t + 150 as end_ts
FROM user_busy_timing
)
SELECT
user_id,
ST_GEOGPOINT(AVG(lon), AVG(lat)) AS point
FROM user_pos
WHERE
NOT EXISTS (
SELECT user_id FROM busy AS p
WHERE ts BETWEEN p.start_ts AND p.end_ts
AND t.user_id = p.user_id)
GROUP BY user_id
busyサブクエリは300秒ごとに来ているログなので、時間的
に連続性があるためマージできる。
120秒~420秒で
トラッキングOFF
420秒~720秒で
トラッキングOFF
120秒~720秒で
トラッキングOFF
NOT EXISTS前に、トラッキングOFFエントリを
mergeする処理を追加することで
約2倍の高速化を達成
この件数が多い
28. confidential
Mobility Technologies Co., Ltd.
Self-joinはテーブル内のデータ間の関係を分析するのに使えるが、BigQueryではアンチパターン
とされている。
以下のように、自分自身をJOINするのがself-joinで、意外と使い道がある。
Case 2. BQ的に無駄な処理をしている
28
https://cloud.google.com/bigquery/docs/best-practices-performance-patterns
WITH src AS (
…
)
SELECT
…
FROM src AS left JOIN src AS right
ON …
同じサブクエリが二回くる
29. confidential
Mobility Technologies Co., Ltd.
Self-joinは直感に反して、同じ処理を2回行うためslot消費が激しくなる。
Self-joinの実行モデル
29
Stage1
src結果
WITH src AS (
…
)
SELECT
…
FROM src AS left JOIN src AS right
ON … Stage2
イメージするself-join
Stage1
src結果
Stage2
実際のself-join
Stage1’
src結果
src処理 src処理 src処理
36. confidential
Mobility Technologies Co., Ltd.
Stageあたりの出力sink数をbigqueryのアルゴリズムを騙して増やす。
並列 read トリック
36
WITH USER_POINTS AS (
複雑な処理
), MULTIPLE_READ AS (
SELECT * FROM USER_POINTS WHERE MOD(user_id,3)=0
UNION ALL
SELECT * FROM USER_POINTS WHERE MOD(user_id,3)=1
UNION ALL
SELECT * FROM USER_POINTS WHERE MOD(user_id,3)=2
)
SELECT
area_centers.area_id,
COUNT(user_points.user_id)
FROM
MULTIPLE_READ, area_centers
WHERE
ST_DISTANCE(user_point, area_point) <= 1000
GROUP BY area_id
左のように、出力sink数が少ない処理 (ここで
はUSER_POINTS) を何度も読みだし、UNION
ALL で結合することで、sink数を結合数分増や
すことができる。
左の例では出力sink数が3倍となり、処理の並
列度が向上する。
実際のクエリだと12並列readを行うことで、処
理時間が4分の1になった。
40. confidential
Mobility Technologies Co., Ltd.
SQLに定数を埋めこみたい時、複数の実現手段が考えられる。
どれも一長一短がある。
定数埋め込み技法
40
SELECT技法
With const as (
100 AS MAGIC_NO
)
SCRIPT技法
DECLARE MAGIC_NO INT64
DEFAULT 100;
UDF技法
CREATE TEMP FUNCTION
MAGIC_NO () AS (100)
これらをST_DISTANCE(point, mesh_point) <= MAGIC_NO のように用いた場合、
処理時間に大きく差が出る。
技法 時間
SELECT 132秒
SCRIPT 49秒
UDF 353秒
41. confidential
Mobility Technologies Co., Ltd.
上記クエリはMAGIC_NOとしてスクリプト式か、定数を直接埋め込んだ時のみ、専用の最適化が
走るっぽい。以下のようなJOIN方式に切り替わり高速化される。(あくまで推測)
なぜ定数の埋め込み方でパフォーマンスに差が?
41
FROM
AGGREGATED_POINT as ap, MESH as m
WHERE
ST_DISTANCE(point, mesh_point) <= MAGIC_NO
CROSS EACH WITH EACH ON st_dwithin($1581, $1586, 2400)