Skip to content

Instantly share code, notes, and snippets.

@yudoufu
Created October 20, 2019 12:46
Show Gist options
  • Save yudoufu/65752ef2399bcd04b898e51c41e8947d to your computer and use it in GitHub Desktop.
Save yudoufu/65752ef2399bcd04b898e51c41e8947d to your computer and use it in GitHub Desktop.
<?php
$rows = 10 * 10000;
$host = '127.0.0.1';
$dbname = 'test';
$username = 'root';
$password = '';
$dsn = "mysql:dbname=$dbname;host=$host;charset=utf8mb4";
try {
$pdo = new PDO($dsn, $username, $password);
$pdo->exec('DROP TABLE IF EXISTS secondry_test');
$pdo->exec('CREATE TABLE secondry_test (
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
deliver_id INT NOT NULL,
flag TINYINT NOT NULL,
message VARCHAR(255) NOT NULL,
delivered_at DATETIME default NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
PRIMARY KEY (id),
KEY `user_deliverd` (user_id, delivered_at, deliver_id, message),
KEY `user_flag` (user_id, flag, deliver_id, message)
) ENGINE=InnoDB');
$stmt = $pdo->prepare('INSERT INTO secondry_test (user_id, deliver_id, flag, message, delivered_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, NOW(), NOW())');
for ($i = 1; $i <= $rows; $i++) {
$stmt->execute([
mt_rand(1, 100),
mt_rand(1, 100),
(mt_rand(0, 10) / 5 ? 0 : 1),
sha1(mt_rand(0, 100 * 10000)),
mt_rand(0, 10) / 5 ? NULL : date("Y-m-d H:i:s"),
]);
if ($i % 10000 === 0) {
echo $i . "\n";
}
}
echo 'done';
} catch (PDOException $e) {
var_dump($e);
}
@yudoufu
Copy link
Author

yudoufu commented Oct 20, 2019

各パターンのEXPLAIN

delivered_at のIS NULL

mysql> EXPLAIN FORMAT=JSON SELECT user_id,deliver_id FROM secondry_test WHERE user_id = 50 AND deliver_id = 88 AND delivered_at IS NULL;
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| {
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "2.32"
    },
    "table": {
      "table_name": "secondry_test",
      "access_type": "ref",
      "possible_keys": [
        "user_deliverd",
        "user_flag"
      ],
      "key": "user_deliverd",
      "used_key_parts": [
        "user_id",
        "delivered_at",
        "deliver_id"
      ],
      "key_length": "14",
      "ref": [
        "const",
        "const",
        "const"
      ],
      "rows_examined_per_scan": 16,
      "rows_produced_per_join": 16,
      "filtered": "100.00",
      "using_index": true,
      "cost_info": {
        "read_cost": "0.72",
        "eval_cost": "1.60",
        "prefix_cost": "2.32",
        "data_read_per_join": "16K"
      },
      "used_columns": [
        "user_id",
        "deliver_id",
        "delivered_at"
      ],
      "attached_condition": "(`test`.`secondry_test`.`delivered_at` is null)"
    }
  }
} |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

delivered_at のIS NOT NULL

mysql> EXPLAIN FORMAT=JSON SELECT user_id,deliver_id FROM secondry_test WHERE user_id = 50 AND deliver_id = 88 AND delivered_at IS NOT NULL;
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| {
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "21.97"
    },
    "table": {
      "table_name": "secondry_test",
      "access_type": "range",
      "possible_keys": [
        "user_deliverd",
        "user_flag"
      ],
      "key": "user_deliverd",
      "used_key_parts": [
        "user_id",
        "delivered_at"
      ],
      "key_length": "10",
      "rows_examined_per_scan": 94,
      "rows_produced_per_join": 9,
      "filtered": "10.00",
      "using_index": true,
      "cost_info": {
        "read_cost": "21.03",
        "eval_cost": "0.94",
        "prefix_cost": "21.97",
        "data_read_per_join": "9K"
      },
      "used_columns": [
        "user_id",
        "deliver_id",
        "delivered_at"
      ],
      "attached_condition": "((`test`.`secondry_test`.`deliver_id` = 88) and (`test`.`secondry_test`.`user_id` = 50) and (`test`.`secondry_test`.`delivered_at` is not null))"
    }
  }
} |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

flagの0

mysql> EXPLAIN FORMAT=JSON SELECT user_id,deliver_id FROM secondry_test WHERE user_id = 50 AND deliver_id = 88 AND flag = 0;
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| {
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "2.32"
    },
    "table": {
      "table_name": "secondry_test",
      "access_type": "ref",
      "possible_keys": [
        "user_deliverd",
        "user_flag"
      ],
      "key": "user_flag",
      "used_key_parts": [
        "user_id",
        "flag",
        "deliver_id"
      ],
      "key_length": "9",
      "ref": [
        "const",
        "const",
        "const"
      ],
      "rows_examined_per_scan": 16,
      "rows_produced_per_join": 16,
      "filtered": "100.00",
      "using_index": true,
      "cost_info": {
        "read_cost": "0.72",
        "eval_cost": "1.60",
        "prefix_cost": "2.32",
        "data_read_per_join": "16K"
      },
      "used_columns": [
        "user_id",
        "deliver_id",
        "flag"
      ]
    }
  }
} |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

flagの1

mysql> EXPLAIN FORMAT=JSON SELECT user_id,deliver_id FROM secondry_test WHERE user_id = 50 AND deliver_id = 88 AND flag = 1;
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| EXPLAIN                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| {
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "0.48"
    },
    "table": {
      "table_name": "secondry_test",
      "access_type": "ref",
      "possible_keys": [
        "user_deliverd",
        "user_flag"
      ],
      "key": "user_flag",
      "used_key_parts": [
        "user_id",
        "flag",
        "deliver_id"
      ],
      "key_length": "9",
      "ref": [
        "const",
        "const",
        "const"
      ],
      "rows_examined_per_scan": 2,
      "rows_produced_per_join": 2,
      "filtered": "100.00",
      "using_index": true,
      "cost_info": {
        "read_cost": "0.28",
        "eval_cost": "0.20",
        "prefix_cost": "0.48",
        "data_read_per_join": "2K"
      },
      "used_columns": [
        "user_id",
        "deliver_id",
        "flag"
      ]
    }
  }
} |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

@yudoufu
Copy link
Author

yudoufu commented Oct 20, 2019

着目すべきは used_key_parts

delivered_at のIS NULL

      "used_key_parts": [
        "user_id",
        "delivered_at",
        "deliver_id"
      ],

delivered_atのIS NOT NULL

      "used_key_parts": [
        "user_id",
        "delivered_at"
      ],

flagの0

      "used_key_parts": [
        "user_id",
        "flag",
        "deliver_id"
      ],

flagの1

      "used_key_parts": [
        "user_id",
        "flag",
        "deliver_id"
      ],

delivered_atのIS NOT NULL だけ deliver_id を使えていない

@yudoufu
Copy link
Author

yudoufu commented Oct 20, 2019

なお通常のEXPLAINはこう。

mysql> EXPLAIN SELECT user_id,deliver_id FROM secondry_test WHERE user_id = 50 AND deliver_id = 88 AND delivered_at IS NULL;
+----+-------------+---------------+------------+------+-------------------------+---------------+---------+-------------------+------+----------+--------------------------+
| id | select_type | table         | partitions | type | possible_keys           | key           | key_len | ref               | rows | filtered | Extra                    |
+----+-------------+---------------+------------+------+-------------------------+---------------+---------+-------------------+------+----------+--------------------------+
|  1 | SIMPLE      | secondry_test | NULL       | ref  | user_deliverd,user_flag | user_deliverd | 14      | const,const,const |   16 |   100.00 | Using where; Using index |
+----+-------------+---------------+------------+------+-------------------------+---------------+---------+-------------------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> EXPLAIN SELECT user_id,deliver_id FROM secondry_test WHERE user_id = 50 AND deliver_id = 88 AND delivered_at IS NOT NULL;
+----+-------------+---------------+------------+-------+-------------------------+---------------+---------+------+------+----------+--------------------------+
| id | select_type | table         | partitions | type  | possible_keys           | key           | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+---------------+------------+-------+-------------------------+---------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | secondry_test | NULL       | range | user_deliverd,user_flag | user_deliverd | 10      | NULL |   94 |    10.00 | Using where; Using index |
+----+-------------+---------------+------------+-------+-------------------------+---------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> EXPLAIN SELECT user_id,deliver_id FROM secondry_test WHERE user_id = 50 AND deliver_id = 88 AND flag = 0;
+----+-------------+---------------+------------+------+-------------------------+-----------+---------+-------------------+------+----------+-------------+
| id | select_type | table         | partitions | type | possible_keys           | key       | key_len | ref               | rows | filtered | Extra       |
+----+-------------+---------------+------------+------+-------------------------+-----------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | secondry_test | NULL       | ref  | user_deliverd,user_flag | user_flag | 9       | const,const,const |   16 |   100.00 | Using index |
+----+-------------+---------------+------------+------+-------------------------+-----------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> EXPLAIN SELECT user_id,deliver_id FROM secondry_test WHERE user_id = 50 AND deliver_id = 88 AND flag = 1;
+----+-------------+---------------+------------+------+-------------------------+-----------+---------+-------------------+------+----------+-------------+
| id | select_type | table         | partitions | type | possible_keys           | key       | key_len | ref               | rows | filtered | Extra       |
+----+-------------+---------------+------------+------+-------------------------+-----------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | secondry_test | NULL       | ref  | user_deliverd,user_flag | user_flag | 9       | const,const,const |    2 |   100.00 | Using index |
+----+-------------+---------------+------------+------+-------------------------+-----------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

delivered_at IS NOT NULLだけtype=rangeになるのがわかる。

ちなみに、delivered_at IS NULLはUsing whereが出ていてattached_conditionに delivered_at IS NULLをlookupに使っている・・・と書いているのだけど、一方でused_key_partsではdeliver_idまで使えているしrefで解決できていて、ちょっと何を信じていいかわからない。
MySQLのマニュアルを信じるなら IS NULLはindexをつかえる ということでsecondry indexをlookupするというちょっと変わったアクションなのかもしれない。(想像)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment