🔩 ねじき教室 Go と Web の教室

【課題 1】計測結果からどこを直すか決める

読了目安 約2分

初期実装のアクセスログとスロークエリログを読んで、最初に手をつける場所を選びます。

この章の目次

初期実装のまま 60 秒走らせて、アクセスログとスロークエリログを集計しました。

アクセスログ(合計時間順)

alp の出力です。 SUM は、そのエンドポイントに費やした時間の合計です。

テキスト
+-------+--------+-------------------------------------------+-------+--------+----------+
| COUNT | METHOD |                    URI                    |  AVG  |  MAX   |   SUM    |
+-------+--------+-------------------------------------------+-------+--------+----------+
| 1987  | GET    | /api/player/player/[^/]+                  | 0.594 | 6.300  | 1180.979 |
| 1488  | GET    | /api/player/competition/[^/]+/ranking     | 0.672 | 6.292  |  999.642 |
| 18    | GET    | /api/admin/tenants/billing                | 5.882 | 12.269 |  105.882 |
| 87    | POST   | /api/organizer/competition/[^/]+/score    | 1.001 | 6.032  |   87.106 |
| 12    | POST   | /api/organizer/players/add                | 1.287 | 2.520  |   15.444 |
| 39    | GET    | /api/organizer/billing                    | 0.126 | 1.288  |    4.932 |
| 215   | GET    | /api/player/competitions                  | 0.010 | 0.040  |    2.052 |
+-------+--------+-------------------------------------------+-------+--------+----------+

スロークエリログ(合計時間順)

slp の出力です。 SUM(ROWSEXAMINED) は、そのクエリが読んだ行数の合計です。

テキスト
+-------+----------------+----------------+-------------------+--------------------------------+
| COUNT | SUM(QUERYTIME) | AVG(QUERYTIME) | SUM(ROWSEXAMINED) |             QUERY              |
+-------+----------------+----------------+-------------------+--------------------------------+
| 2430  |     107.899189 |       0.044403 |          45459630 | SELECT player_id,MIN(created_a |
|       |                |                |                   | t) FROM visit_history WHERE te |
|       |                |                |                   | nant_id = N AND competition_id |
|       |                |                |                   |  = 'S' GROUP BY player_id      |
| 17914 |      49.106648 |       0.002741 |                 0 | REPLACE INTO id_generator (stu |
|       |                |                |                   | b) VALUES ('S')                |
|  1470 |       4.596556 |       0.003127 |                 0 | INSERT INTO visit_history (... |
|  4018 |       1.506151 |       0.000375 |              4017 | SELECT * FROM tenant WHERE nam |
|       |                |                |                   | e = 'S'                        |
+-------+----------------+----------------+-------------------+--------------------------------+

課題

この 2 つの表から、次の 3 つに答えてください。

  1. 最初に手をつけるべき場所はどこか。数字を根拠に挙げる
  2. /api/admin/tenants/billing は 18 件しか来ていないのに、なぜ無視できないか
  3. この 2 つの表に出てこない処理は何か

解答例

1. visit_history の集計

スロークエリログの 1 位が、他を引き離しています。 2430 回で 108 秒、1 回あたり 44 ミリ秒です。

決め手は SUM(ROWSEXAMINED) です。 4545 万行を 2430 回で割ると、1 回のクエリが約 1 万 9 千行を読んでいます。 返している行数は参加者の数ぶんしかないので、読みすぎです。

2. 1 件あたりが異常に遅いから

/api/admin/tenants/billing は平均 5.9 秒、最大 12.3 秒です。 合計時間では 3 位でも、1 件あたりでは断トツです。

ベンチマーカーはタイムアウトした リクエストを失敗として扱います。 このまま負荷が上がると、失敗が増えて減点されます。

3. SQLite へのクエリ

スロークエリログは MySQL の機能です。 テナント DB は SQLite なので、player_score を何行読もうが 1 行もログに出ません。

アクセスログの /api/player/player/[^/]+ が平均 0.594 秒かかっている理由は、この 2 つの表からは分かりません。 計測に出ないものがあるという前提で読む必要があります。

ログに出ない処理を測る道具が pprof です。この Part では第 6 章で使います。

次の一手

順番は、根拠の強さと直しやすさで決めます。

  1. visit_history の読みすぎ(根拠が数字ではっきりしている)
  2. REPLACE INTO id_generator の 17914 回(回数が異常)
  3. SQLite 側(計測が無いので、まずコードを読む)

上から順に片付けます。