MySQL クエリのパフォーマンス改善テクニック -その8- IN を適切に使え
フルスタックエンジニア()な clown の note にようこそ。
はじめに
今回やること
MySQL のクエリパフォーマンス改善のひとつとして、簡単にできるクエリ改修をします。
今回は IN を適切に使う方法を扱います。
対象者
RSDB を使ってて、ユーザから重いって言われてるあなた
DB 担当になったけど、何から手を付けていいかわかんないあなた
MySQL を使っているあなた(他 DB でも基本はそんなに違わないです)
ざっくり説明
今日も DBMS と格闘してますか?
正直、クエリなんて究極は動けばいいんですけど、IN 句を適切に使えるかどうかだけの差で、パフォーマンスが変わります。
あなたの書いたこのクエリ。いつまでも、値がヒットしなくてパフォーマンスが悪いよね?
なんていうお小言を貰っちゃったりします。
じゃあ、IN 句を適切に使ったクエリを書けばいいじゃない。
というのが今回の話。
そんなことも知らないの?とか言われるのが嫌!
クエリが遅いのは判ってるのに、なんら手をうてない自分が嫌!
という悩みを解決するのがこの記事です。
準備
下記のテーブルを用意します。
前段となる「MySQL で explain してますか?」の記事を読んでいただくと、より味わいが深くなります。
CREATE TABLE `sumple_users` (
`id` int(11) NOT NULL AUTO_INCREMENT
,`username` varchar(64) NOT NULL DEFAULT ''
,`email` varchar(255) NOT NULL
,`password` varchar(255) NOT NULL
,`login_hash` varchar(255) NOT NULL DEFAULT ''
,`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
,`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
このテーブルには、こんなデータが入っています。
mysql> select * from `sumple_users`;
+-------+------------+----------------------+----------+------------+---------------------+---------------------+
| id | username | email | password | login_hash | created_at | updated_at |
+-------+------------+----------------------+----------+------------+---------------------+---------------------+
| 1 | clown1 | clown1@email.com | abc | def | 2023-08-13 00:00:00 | 0000-00-00 00:00:00 |
| 2 | clown2 | clown2@email.com | abc | def | 2023-08-13 00:00:00 | 0000-00-00 00:00:00 |
| 3 | clown3 | clown3@email.com | abc | def | 2023-08-13 00:00:00 | 0000-00-00 00:00:00 |
| 4 | clown4 | clown4@email.com | abc | def | 2023-08-13 00:00:00 | 0000-00-00 00:00:00 |
| 5 | clown5 | clown5@email.com | abc | def | 2023-08-13 00:00:00 | 0000-00-00 00:00:00 |
~
中略
~
| 9995 | clown9995 | clown9995@email.com | abc | def | 2023-08-13 00:00:00 | 0000-00-00 00:00:00 |
| 9996 | clown9996 | clown9996@email.com | abc | def | 2023-08-13 00:00:00 | 0000-00-00 00:00:00 |
| 9997 | clown9997 | clown9997@email.com | abc | def | 2023-08-13 00:00:00 | 0000-00-00 00:00:00 |
| 9998 | clown9998 | clown9998@email.com | abc | def | 2023-08-13 00:00:00 | 0000-00-00 00:00:00 |
| 9999 | clown9999 | clown9999@email.com | abc | def | 2023-08-13 00:00:00 | 0000-00-00 00:00:00 |
| 10000 | clown10000 | clown10000@email.com | abc | def | 2023-08-13 00:00:00 | 0000-00-00 00:00:00 |
+-------+------------+----------------------+----------+------------+---------------------+---------------------+
10000 rows in set (0.01 sec)
IN 述語を使う
IN 述語が適切ではないクエリ
こんなクエリ(↓)があります。
SELECT
`id`
, `username`
FROM
`sumple_users`
WHERE
`id` IN (10000, 5000, 7500, 2500, 1);
比較的シンプルなクエリで、レコード数がそれほど多いわけでもないので、このクエリでパフォーマンスが悪いと実感することはほぼないですが、こういう感じに IN 述語を使っていると、パフォーマンスが落ちます。
なぜなら、IN はその括弧内に書かれた値の順番の若い順に評価して、ヒットしたタイミングでそれ以降の引数を見ない処理になります。
ということはヒットする可能性が高い値をできるだけ括弧内の左側に書いた方が良い。ということになります。
なので、ベターな書き方は…(↓)
IN 述語を適切に使うクエリ
こんな感じ(↓)になります。
SELECT
`id`
, `username`
FROM
`sumple_users`
WHERE
`id` IN (1, 2500, 5000, 7500, 10000);
と、このように IN の引数を指定してあげると、ヒット率が上がってパフォーマンスを改善できることになります。
大量の引数を扱う場合には特に効果があって、これを気を付けるだけでクエリパフォーマンスを上げることが可能です。