Slide 1

Slide 1 text

テストデータが偏るということについて   2022/05/18 yoku0825 サイボウズ開運研修

Slide 2

Slide 2 text

ごあんない このセッションはデータベース一般の話ではなく、MySQLの話です ググれば出てくるような、「SQLの書き方」や「MySQLの設定」みたいな話は出てきません 今回は「インデックスに紐づくレコード件数の偏りが実行計画に及ぼす影響」の話です これは「あ、いつかそういえばそんなこと聞いたことあるな」と思ってもらうためのセッションです ‐ 最近身の回りでこの手の話が何回か出てきたので、いつかそんな状況になった時に思い出してもらえれば幸いで す ‐ 1/35

Slide 3

Slide 3 text

\こんにちわ/ yoku0825@とある企業のDBA オラクらない ‐ ポスグらない ‐ マイエスキューエる ‐ 生息域 Twitter: @yoku0825 ‐ Blog: 日々の覚書 ‐ 日本MySQLユーザ会 ‐ MySQL Casual ‐ Siriusチームのスレッド ‐ 2/35

Slide 4

Slide 4 text

基本的な話 どんなにダメなテーブル定義をしても、データの件数が少なければ問題にはならない データの件数が大きくなってきても最も効率の良いインデックスで少数の行をフェッチする「得意なア クセスパターン」であれば十分な性能が出る 裏を返すと、テーブルの件数が大きくなって最も効率の良いインデックスで少数の行をフェッチするアクセスパターン でない場合は性能が出なくなってくる ‐ 3/35

Slide 5

Slide 5 text

最も効率の良いインデックス (GROUP BYを除いて) 「走査する行の数 = 結果セットとして送信する行の数」になるインデック スが最良 LIMITなしならWHEREの条件をすべて刈り込めるインデックス LIMITありならWHEREで刈り込みつつORDER BY .. LIMITの最適化を効かせられるインデッ クス See also, MySQLが得意なこと、不得意なこと(仮) ‐ 4/35

Slide 6

Slide 6 text

インデックスの選ばれ方 オプティマイザという名前のブラックボックスに隠されている 「どの実行計画(アクセスパス)が一番行の走査を減らせるか」を計算し続けている 5/35

Slide 7

Slide 7 text

オプティマイザの弱点 データの偏りを完全には把握していない InnoDBの統計情報は1インデックスあたりデフォルト20ページのサンプリングで作る そりゃまあ毎回インデックスを完全にスキャンして偏りを把握させるわけにはいかないだろうけれども ‐ 基本的にテーブル内に一定割合 / 一定行数の更新があった時にバックグラウンドスレッドで統計情報を更新す る 「ギリギリ一定割合 / 一定行数に届かない大量更新」があった時が一番ズレる ‐ 演算は基本的に構ってくれない INT型で桁あふれが発生しない範囲なら、 id - 1 = 0 ⇔ id = 1 … ‐ DATETIME型で桁あふれが発生しない範囲なら、 ORDER BY created_at と ORDER BY created_at + INTERVAL 1 DAY は同じ順番になるはず… ‐ 6/35

Slide 8

Slide 8 text

今回のお題 7/35

Slide 9

Slide 9 text

データの偏り と実行計画 8/35

Slide 10

Slide 10 text

データが偏る #とは あるインデックスに紐づくレコードは均一に分散するとは限らない ex. SELECT COUNT(*) FROM 住民 WHERE 都道府県 = ? ‐ ある偏りについてオプティマイザが同じ実行計画を選ぶとは限らない ex. SELECT * FROM 住民 WHERE 都道府県 = '東京' ORDER BY birthday DESC LIMIT 10 万 vs. SELECT * FROM 住民 WHERE 都道府県 = '鳥取' ORDER BY birthday DESC LIMIT 10万 たぶん東京はORDER BYを狙った方が速くて、鳥取はWHEREを狙った方が速い(2020年の鳥取の人口は全国の0.4%らしい) 都道府県の人口一覧 - Wikipedia ‐ ex. SELECT * FROM 住民 WHERE 都道府県 = '東京' ORDER BY birthday DESC LIMIT 10 vs. SELECT * FROM 住民 WHERE 都道府県 = '東京' ORDER BY birthday DESC LIMIT 1億 LIMIT 1億だと日本総人口にかなり近いので全件フェッチの実行計画を選ぶが、LIMITの早抜けが効かないのでORDER BY狙いの キーは高速化に寄与できない ‐ だが、偏りに気が付かずにオプティマイザが実行計画を選んでしまうことはある 9/35

Slide 11

Slide 11 text

データが偏る #と何が起こるのか 期待したレスポンスタイムが得られない場合がある 1行をフェッチするSELECTと100万行をフェッチするSELECTのレスポンスタイムは当然桁違いになる ‐ 1行をフェッチするのに最適な実行計画と、100万行をフェッチするのに最適な実行計画は違うことがある ‐ オプティマイザが実行計画を取り違える場合もあれば、この実行計画の差を認識せずにインデックスが足りない場 合もある ‐ 期待したロック範囲が得られない場合がある InnoDBのネクストキーロックは「走査したインデックスレコードとそのギャップ」にロックを置く ‐ 必要最小限の行フェッチで済ませられないとロック範囲は大きくなる ‐ 10/35

Slide 12

Slide 12 text

TL;DR 実際問題、データは偏る ライトユーザー vs. ヘビーユーザー ‐ 偏ったデータが本番環境に及ぼす影響を把握する そもそもベストな状況は何なのかをある程度認識しないといけない ‐ 最大の結果セット、最小の結果セット、あり得ない(はずの)結果セットで比較する ‐ LIMITの値、INの数も最大と最小を比較するのが吉 ‐ せっかく本番相当のデータを用意しても「よくあるケース」だけのテストでは性能テストにならない ‐ 11/35

Slide 13

Slide 13 text

偏ったデータ まあシンプルなやつ mysql> SHOW CREATE TABLE post\G *************************** 1. row *************************** Table: post Create Table: CREATE TABLE `post` ( `post_id` int NOT NULL, `user_id` int NOT NULL, `post_text` text COLLATE utf8mb4_ja_0900_as_cs NOT NULL, `registered` datetime NOT NULL, PRIMARY KEY (`post_id`), KEY `user_id` (`user_id`), KEY `registered` (`registered`), CONSTRAINT `post_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_ja_0900_as_cs 1 row in set (0.01 sec) mysql> SELECT user_id, COUNT(*) FROM post GROUP BY user_id; +---------+----------+ | user_id | COUNT(*) | +---------+----------+ | 1 | 100001 | | 2 | 1 | +---------+----------+ 2 rows in set (0.04 sec) 12/35

Slide 14

Slide 14 text

偏ったデータ +---------+----------+ | user_id | COUNT(*) | +---------+----------+ | 1 | 100001 | | 2 | 1 | +---------+----------+ mysql> EXPLAIN DELETE FROM post WHERE user_id = 2; +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+- ---------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+- ---------+-------------+ | 1 | DELETE | post | NULL | range | user_id | user_id | 4 | const | 1 | 100.00 | Using where | +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+- ---------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> DELETE FROM post WHERE user_id = 2; 13/35

Slide 15

Slide 15 text

偏ったデータ +---------+----------+ | user_id | COUNT(*) | +---------+----------+ | 1 | 100001 | | 2 | 1 | +---------+----------+ mysql> EXPLAIN DELETE FROM post WHERE user_id = 1; +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----- -----+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----- -----+-------------+ | 1 | DELETE | post | NULL | ALL | user_id | NULL | NULL | NULL | 99875 | 100.00 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----- -----+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> DELETE FROM post WHERE user_id = 1; 14/35

Slide 16

Slide 16 text

偏ったデータ対策 「可能な限り本番データと同じものでテストする」だけではちょっと足りてない 本番データだろうと user_id = 2 みたいなやつを対象にしていたら性能は十分足りるはず ‐ 我々があぶりだしたいのは user_id = 1 みたいに偏ったやつ ‐ ユニットテストではなく、「性能試験」や「負荷試験」と呼ばれるものの対策 目的が違うので… ‐ 15/35

Slide 17

Slide 17 text

偏ったデータ対策 「あり得る最大の行」「あり得る最小の行」「あり得ない行」の3パターンでテストするとまあまあ検出 できるはず SELECT COUNT(*) FROM post WHERE user_id = ? AND registered BETWEEN ? AND ? + INTERVAL 1 MONTH; SELECT * FROM post WHERE user_id = ? AND registered BETWEEN ? AND ? + INTERVAL 1 MONTH O RDER BY registered LIMIT 100 FOR UPDATE; 16/35

Slide 18

Slide 18 text

データの偏りを調べる user_idだけで見ると mysql> SELECT user_id, COUNT(*) FROM post GROUP BY user_id; +---------+----------+ | user_id | COUNT(*) | +---------+----------+ | 1 | 100001 | | 2 | 1 | +---------+----------+ 2 rows in set (0.03 sec) 17/35

Slide 19

Slide 19 text

データの偏りを調べる registeredは元のクエリが1か月単位で切りそうだったから年月に丸めてみた もとのクエリが INTERVAL 1 MONTH だから、 2021/1/30 ~ 2021/2/28 みたいなまたいだものも本来 はあるけれども ‐ mysql> SELECT DATE_FORMAT(registered, '%Y%m') AS yyyymm, COUNT(*) FROM post GROUP BY yyyymm ORDER BY COUNT(*) ASC LI MIT 3; +--------+----------+ | yyyymm | COUNT(*) | +--------+----------+ | 200302 | 28 | | 200102 | 28 | | 200202 | 28 | +--------+----------+ 3 rows in set (0.72 sec) mysql> SELECT DATE_FORMAT(registered, '%Y%m') AS yyyymm, COUNT(*) FROM post GROUP BY yyyymm ORDER BY COUNT(*) DESC LIMIT 3; +--------+----------+ | yyyymm | COUNT(*) | +--------+----------+ | 202205 | 91847 | | 200005 | 31 | | 200003 | 31 | +--------+----------+ 3 rows in set (0.70 sec) 18/35

Slide 20

Slide 20 text

データの偏りを調べる もうちょっと真面目にやるなら、 user_id = 1 は実は「2020年に引退したからそれ以降のレコー ドはない」とかいう偏りもある しかしテストとしてそれを調べ始めると調べるだけで日が暮れてしまうので簡易に推測することにす る… 最大の結果セット 最小の結果セット 存在しない結果セット user_id 1 2 3 registered 2022/05/x 2003/02/x 1999/01/x 19/35

Slide 21

Slide 21 text

データの偏りによる違い 最大の結果セットになりそうな user_id = 1 && regstered = 2022/05/x のパターン mysql> EXPLAIN SELECT COUNT(*) FROM post WHERE user_id = 1 AND registered BETWEEN '2022-05-01' AND '2022-05-01' + INTERVAL 1 M ONTH; +----+-------------+-------+------------+------+--------------------+---------+---------+-------+-------+----------+--------- ----+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+--------------------+---------+---------+-------+-------+----------+--------- ----+ | 1 | SIMPLE | post | NULL | ref | user_id,registered | user_id | 4 | const | 49937 | 50.00 | Using where | +----+-------------+-------+------------+------+--------------------+---------+---------+-------+-------+----------+--------- ----+ mysql> EXPLAIN SELECT * FROM post WHERE user_id = 1 AND registered BETWEEN '2022-05-01' AND '2022-05-01' + INTERVAL 1 MONTH OR DER BY registered LIMIT 100 FOR UPDATE; +----+-------------+-------+------------+-------+--------------------+------------+---------+------+-------+----------+------ ------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+--------------------+------------+---------+------+-------+----------+------ ------------------------------+ | 1 | SIMPLE | post | NULL | range | user_id,registered | registered | 5 | NULL | 49937 | 50.00 | Using index condition; Using where | +----+-------------+-------+------------+-------+--------------------+------------+---------+------+-------+----------+------ ------------------------------+ 20/35

Slide 22

Slide 22 text

データの偏りによる違い 最小の結果セットになりそうな user_id = 2 && regstered = 2003/02/x のパターン mysql> EXPLAIN SELECT COUNT(*) FROM post WHERE user_id = 2 AND registered BETWEEN '2003-02-01' AND '2003-02-01' + INTERVAL 1 MONTH; +----+-------------+-------+------------+------+--------------------+---------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+--------------------+---------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | post | NULL | ref | user_id,registered | user_id | 4 | const | 1 | 5.00 | Using where | +----+-------------+-------+------------+------+--------------------+---------+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> EXPLAIN SELECT * FROM post WHERE user_id = 2 AND registered BETWEEN '2003-02-01' AND '2003-02-01' + INTERVAL 1 MONTH ORDER BY re gistered LIMIT 100 FOR UPDATE; +----+-------------+-------+------------+------+--------------------+---------+---------+-------+------+----------+-------------------- ---------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+--------------------+---------+---------+-------+------+----------+-------------------- ---------+ | 1 | SIMPLE | post | NULL | ref | user_id,registered | user_id | 4 | const | 1 | 5.00 | Using where; Using filesort | +----+-------------+-------+------------+------+--------------------+---------+---------+-------+------+----------+-------------------- ---------+ 1 row in set, 1 warning (0.00 sec) 21/35

Slide 23

Slide 23 text

データの偏りによる違い 表示上の違い、表示が違っても最適化の効き方の違い 22/35

Slide 24

Slide 24 text

データの偏りによる違い user_id = 3 による空振り mysql> EXPLAIN SELECT COUNT(*) FROM post WHERE user_id = 3 AND registered BETWEEN '2022-05-01' AND '2022-05-01' + INTERVAL 1 MONTH; +----+-------------+-------+------------+------+--------------------+---------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+--------------------+---------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | post | NULL | ref | user_id,registered | user_id | 4 | const | 1 | 50.00 | Using where | +----+-------------+-------+------------+------+--------------------+---------+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> EXPLAIN SELECT * FROM post WHERE user_id = 3 AND registered BETWEEN '2022-05-01' AND '2022-05-01' + INTERVAL 1 MONTH ORDER BY re gistered LIMIT 100 FOR UPDATE; +----+-------------+-------+------------+------+--------------------+---------+---------+-------+------+----------+-------------------- ---------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+--------------------+---------+---------+-------+------+----------+-------------------- ---------+ | 1 | SIMPLE | post | NULL | ref | user_id,registered | user_id | 4 | const | 1 | 50.00 | Using where; Using filesort | +----+-------------+-------+------------+------+--------------------+---------+---------+-------+------+----------+-------------------- ---------+ 1 row in set, 1 warning (0.00 sec) 23/35

Slide 25

Slide 25 text

データの偏りによる違い registered = 1999/01/x による空振り mysql> EXPLAIN SELECT COUNT(*) FROM post WHERE user_id = 1 AND registered BETWEEN '1999-01-01' AND '1999-01-01' + INTERVAL 1 MONTH; +----+-------------+-------+------------+-------+--------------------+------------+---------+------+------+----------+----------------- -------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+--------------------+------------+---------+------+------+----------+----------------- -------------------+ | 1 | SIMPLE | post | NULL | range | user_id,registered | registered | 5 | NULL | 1 | 50.00 | Using index condition; Using where | +----+-------------+-------+------------+-------+--------------------+------------+---------+------+------+----------+----------------- -------------------+ 1 row in set, 1 warning (0.00 sec) mysql> EXPLAIN SELECT * FROM post WHERE user_id = 1 AND registered BETWEEN '1999-01-01' AND '1999-01-01' + INTERVAL 1 MONTH ORDER BY re gistered LIMIT 100 FOR UPDATE; +----+-------------+-------+------------+-------+--------------------+------------+---------+------+------+----------+----------------- -------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+--------------------+------------+---------+------+------+----------+----------------- -------------------+ | 1 | SIMPLE | post | NULL | range | user_id,registered | registered | 5 | NULL | 1 | 50.00 | Using index condition; Using where | +----+-------------+-------+------------+-------+--------------------+------------+---------+------+------+----------+----------------- -------------------+ 1 row in set, 1 warning (0.00 sec) 24/35

Slide 26

Slide 26 text

データの偏りによる違い これらの空振りは全部遅くはならないパターン(特に「存在しない値でやる」と遅くなるパターンもある。 よく観測するのはLEFT JOIN) 空振りFOR UPDATEは想定よりロック範囲が広くなりがちなので、どのインデックスを使ってロックされるかは重要は重 要 ‐ 25/35

Slide 27

Slide 27 text

データの偏りによる違い MySQLとインデックスと私 JOINだともっと顕著に出てきたりする ‐ 26/35

Slide 28

Slide 28 text

データの偏りによる違い MySQLとインデックスと私 空振りロックのネクストキーロック ‐ 27/35

Slide 29

Slide 29 text

偏ったように見えないデータも mysql> SHOW CREATE TABLE user\G *************************** 1. row *************************** Table: user Create Table: CREATE TABLE `user` ( `user_id` int NOT NULL, `unique_identifier` varchar(32) COLLATE utf8mb4_ja_0900_as_cs NOT NULL, `registered` datetime NOT NULL, PRIMARY KEY (`user_id`), UNIQUE KEY `unique_identifier` (`unique_identifier`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_ja_0900_as_cs 1 row in set (0.01 sec) mysql> SELECT * FROM user; +---------+-------------------+---------------------+ | user_id | unique_identifier | registered | +---------+-------------------+---------------------+ | 1 | yoku0825 | 2022-05-06 23:51:13 | | 2 | yoku0826 | 2022-05-06 23:51:19 | +---------+-------------------+---------------------+ 2 rows in set (0.00 sec) 28/35

Slide 30

Slide 30 text

偏ったように見えないデータも mysql> EXPLAIN DELETE FROM user WHERE user_id = 1; +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+ | 1 | DELETE | user | NULL | range | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | Using where | +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> DELETE FROM user WHERE user_id = 1; Query OK, 1 row affected (0.80 sec) mysql> EXPLAIN DELETE FROM user WHERE user_id = 2; +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+ | 1 | DELETE | user | NULL | range | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | Using where | +----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) mysql> DELETE FROM user WHERE user_id = 2; Query OK, 1 row affected (0.00 sec) 29/35

Slide 31

Slide 31 text

偏ったように見えないデータ CASCADE や SET NULL な外部キー制約 FKは基本的に1対多で動くので、「見えない子側の偏り」が発生しがち ‐ UPDATE Trigger, DELETE Trigger 1対1なら問題ないけど、1対多(トリガーがあるのが1側)だと外部キー制約と同じようなパターンがある ‐ 多対1(トリガーがあるのが多側)だとこの問題には当たらないけれど、1側にロックが集中するのでまた別の問題は ある ‐ 30/35

Slide 32

Slide 32 text

ちょっと別口の偏り SELECT COUNT(*) FROM post WHERE user_id = ? AND registered BETWEEN ? AND ? + INTERVAL 1 MONTH; SELECT * FROM post WHERE user_id = ? AND registered BETWEEN ? AND ? + INTERVAL 1 MONTH O RDER BY registered LIMIT ? FOR UPDATE; DELETE FROM post WHERE post_id IN (?, ?, .., ?); 31/35

Slide 33

Slide 33 text

ちょっと別口の偏り(1) LIMITの値が 100 → 10000 で実行計画は変わることがある mysql> EXPLAIN SELECT * FROM post WHERE user_id = 1 AND registered BETWEEN '2022-05-01' AND '2022-05-01' + INTERVAL 1 MONTH ORDER BY registere d LIMIT 100 FOR UPDATE; +----+-------------+-------+------------+-------+--------------------+------------+---------+------+-------+----------+--------------------- ---------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+--------------------+------------+---------+------+-------+----------+--------------------- ---------------+ | 1 | SIMPLE | post | NULL | range | user_id,registered | registered | 5 | NULL | 49938 | 50.00 | Using index condition; Using where | +----+-------------+-------+------------+-------+--------------------+------------+---------+------+-------+----------+--------------------- ---------------+ 1 row in set, 1 warning (0.00 sec) mysql> EXPLAIN SELECT * FROM post WHERE user_id = 1 AND registered BETWEEN '2022-05-01' AND '2022-05-01' + INTERVAL 1 MONTH ORDER BY registere d LIMIT 10000 FOR UPDATE; +----+-------------+-------+------------+------+--------------------+---------+---------+-------+-------+----------+------------------------ -----+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+--------------------+---------+---------+-------+-------+----------+------------------------ -----+ | 1 | SIMPLE | post | NULL | ref | user_id,registered | user_id | 4 | const | 49938 | 50.00 | Using where; Using filesort | +----+-------------+-------+------------+------+--------------------+---------+---------+-------+-------+----------+------------------------ -----+ 1 row in set, 1 warning (0.00 sec) 32/35

Slide 34

Slide 34 text

ちょっと別口の偏り(2) WHERE .. IN .. の要素の数で実行計画は変わることがある mysql> DELETE FROM post WHERE post_id IN (?, ?, ?, .., 15477); +----+-------------+-------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+ | 1 | SIMPLE | post | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 15747 | 100.00 | Using where | +----+-------------+-------+------------+-------+---------------+---------+---------+------+-------+----------+-------------+ 1 row in set, 1 warning (0.06 sec) mysql> DELETE FROM post WHERE post_id IN (?, ?, ?, .., 15478); +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+ | 1 | DELETE | post | NULL | ALL | NULL | NULL | NULL | NULL | 99877 | 100.00 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+ 1 row in set, 2 warnings (0.06 sec) | Warning | 3170 | Memory capacity of 8388608 bytes for 'range_optimizer_max_mem_size' exceeded. Range optimization was not done for this query. | 33/35

Slide 35

Slide 35 text

まとめ 実際問題、データは偏る ライトユーザー vs. ヘビーユーザー ‐ 偏ったデータが本番環境に及ぼす影響を把握する そもそもベストな状況は何なのかをある程度認識しないといけない ‐ 最大の結果セット、最小の結果セット、あり得ない(はずの)結果セットで比較する ‐ LIMITの値、INの数も最大と最小を比較するのが吉 ‐ せっかく本番相当のデータを用意しても「よくあるケース」だけのテストでは性能テストにならない ‐ 34/35

Slide 36

Slide 36 text

Any Questions and/or Suggestions? 35/35