見出し画像

MySQL クエリのパフォーマンス改善テクニック -その5- IN述語の引数リストの並べ方

フルスタックエンジニア()な clown の note にようこそ。

はじめに

今回やること

MySQL のクエリパフォーマンス改善のひとつとして、簡単にできるクエリ改修をします。

対象者

  • 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 句の引数リストの並べ方

WHERE 句で複数のレコードを取得するとき、こんな感じに IN 句を使っていませんか?

SELECT
    `id`
  , `username`
  FROM
    `sumple_users`
 WHERE
    `id` IN (200, 10, 100, 20, 30, 300);

クエリは動けばいい。という観点ではこれで正解とは言えます。
が、パフォーマンスが悪化します。

なぜか?

IN 句では、その引数を左から右へ走査していって、ヒットしたところで true を返します。
ということは、ヒットした引数以降は走査しないので、ヒットする可能性が高い順に並べた方がいい。と言えます。

じゃあどうする?

今回のテスト用テーブルで収集したいデータは `id` を基準にしているので、数値が小さいほうを先に並べたほうが良さそうです。

SELECT
    `id`
  , `username`
  FROM
    `sumple_users`
 WHERE
    `id` IN (10, 20, 30, 100, 200, 300);

支援のお願い


素材引用元

アイコン

見出し


この記事が気に入ったらサポートをしてみませんか?