我爱牛皮糖 发表于 2023-7-28 14:37:08

Mysql高级4-索引的使用规则

一、最左前缀法则

  如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列,如果跳跃某一列,索引将部分失效(后面的字段索引失效)
  示例1:account_transaction表中创建一个联合索引,使用method字段+trader_staff_id字段+operator_staff_id字段三个字段当做联合索引
mysql> <strong>create index mto on</strong><strong> account_transaction(method, trader_staff_id, operator_staff_id);</strong>
Query OK, 0 rows affected (5.29 sec)
Records: 0Duplicates: 0Warnings: 0

mysql><strong> show index from account_transaction;
</strong>+---------------------+------------+-------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table               | Non_unique | Key_name    | Seq_in_index | Column_name       | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------------------+------------+-------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| account_transaction |          0 | PRIMARY   |            1 | id                | A         |   2067077 |   NULL |   NULL |      | BTREE      |         |               | YES   | NULL       |
| account_transaction |          1 | trade_index |            1 | trade_no          | A         |   2249115 |   NULL |   NULL |      | BTREE      |         |               | YES   | NULL       |
| <strong>account_transaction |          1 | mto         |            1 | method            | A         |         3 |   NULL |   NULL |      | BTREE      |         |               | YES   | NULL</strong>   |
|<strong> account_transaction |          1 | mto         |            2 | trader_staff_id   | A         |       31046 |   NULL |   NULL |      | BTREE      |         |               | YES   | NULL</strong>       |
| <strong>account_transaction |          1 | mto         |            3 | operator_staff_id | A         |       15847 |   NULL |   NULL |      | BTREE      |         |               | YES   | NULL      </strong> |
+---------------------+------------+-------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
5 rows in set (0.00 sec)  说明1:mto是一个联合索引,里面包含了三个字段method,trader_staff_id,operator_staff_id三个字段。
  说明2:method是第1索引字段,即也是最左索引,trader_staff_id 是第2索引, operator_staff_id 是第3索引,这个顺序很重要!
 
  案例1:同时按顺序使用三个字段查询一条数据
mysql> <strong>select * from account_transaction where method="CASH" and trader_staff_id=275 and operator_staff_id=12;
</strong>+--------+--------------------+--------+--------+----------------------------+---------------+--------------+--------+---------+-----------------+-------------------+-----------+--------+
| id   | trade_no         | type   | method | time                     | payment       | out_trade_no | amount | balance | trader_staff_id | operator_staff_id | device_id | remark |
+--------+--------------------+--------+--------+----------------------------+---------------+--------------+--------+---------+-----------------+-------------------+-----------+--------+
|   24 | 156384428075000275 | TOP_UP | CASH   | 2019-07-23 01:11:20.221977 | LOCAL_ACCOUNT |            |10000 |   10000 |             275 |                12 | 6         |      |
| 747793 | 157370375171000275 | TOP_UP | CASH   | 2019-11-14 03:55:51.480417 | LOCAL_ACCOUNT |            |10000 |   11000 |             275 |                12 | 6         |      |
+--------+--------------------+--------+--------+----------------------------+---------------+--------------+--------+---------+-----------------+-------------------+-----------+--------+
2 rows in set (0.00 sec)

mysql> explain select * from account_transaction where method="CASH" and trader_staff_id=275 and operator_staff_id=12;
+----+-------------+---------------------+------------+------+---------------+------+---------+-------------------+------+----------+-------+
| id | select_type | table               | partitions | type | possible_keys | key| <strong>key_len</strong> | ref               | rows | filtered | Extra |
+----+-------------+---------------------+------------+------+---------------+------+---------+-------------------+------+----------+-------+
|1 | SIMPLE      | account_transaction | NULL       | ref| mto         | mto| <strong>70</strong>    | const,const,const |    2 |   100.00 | NULL|
+----+-------------+---------------------+------------+------+---------------+------+---------+-------------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)  说明1:使用method,trader_staff_id,operator_staff_id三个字段作为查询条件,查询时间0.00秒以内
  说明2:使用explain关键字查询执行计划,该查询使用的key是mto 即刚创建的联合索引,key_len是70长度。记住这个长度,我们在后面还会用到。
 
  案例2:使用 method 和 trader_staff_id 两个字段作为查询条件
mysql> <strong>select * from account_transaction where method="CASH" and trader_staff_id=275</strong><strong>;</strong>
+--------+--------------------+--------+--------+----------------------------+---------------+--------------+--------+---------+-----------------+-------------------+-----------+--------+
| id   | trade_no         | type   | method | time                     | payment       | out_trade_no | amount | balance | trader_staff_id | operator_staff_id | device_id | remark |
+--------+--------------------+--------+--------+----------------------------+---------------+--------------+--------+---------+-----------------+-------------------+-----------+--------+
|   24 | 156384428075000275 | TOP_UP | CASH   | 2019-07-23 01:11:20.221977 | LOCAL_ACCOUNT |            |10000 |   10000 |             275 |                12 | 6         |      |
| 747793 | 157370375171000275 | TOP_UP | CASH   | 2019-11-14 03:55:51.480417 | LOCAL_ACCOUNT |            |10000 |   11000 |             275 |                12 | 6         |      |
+--------+--------------------+--------+--------+----------------------------+---------------+--------------+--------+---------+-----------------+-------------------+-----------+--------+
2 rows in set (0.00 sec)

mysql> <strong>explain select * from account_transaction where method="CASH" and trader_staff_id=275</strong><strong>;</strong>
+----+-------------+---------------------+------------+------+---------------+------+---------+-------------+------+----------+-------+
| id | select_type | table               | partitions | type | possible_keys | key| key_len | ref         | rows | filtered | Extra |
+----+-------------+---------------------+------------+------+---------------+------+---------+-------------+------+----------+-------+
|1 | SIMPLE      | account_transaction | NULL       | ref| mto         | mto| 66      | const,const |    2 |   100.00 | NULL|
+----+-------------+---------------------+------------+------+---------------+------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)  说明1:通过explain执行计划,可以查看使用的key仍然是mto,但是key_len只有66,比上一条的key_len少了4位。说明operator_staff_id的索引失效,并且operator_staff_id的长度为4
 
  案例3:使用method+operator_staff_id查询
mysql> <strong>explain select * from account_transaction where method="CASH" and operator_staff_id=12</strong><strong>;</strong>
+----+-------------+---------------------+------------+------+---------------+------+---------+-------+-------+----------+-----------------------+
| id | select_type | table               | partitions | type | possible_keys | key| key_len | ref   | rows| filtered | Extra               |
+----+-------------+---------------------+------------+------+---------------+------+---------+-------+-------+----------+-----------------------+
|1 | SIMPLE      | account_transaction | NULL       | ref| mto         | mto| 62      | const | 39916 |    10.00 | Using index condition |
+----+-------------+---------------------+------------+------+---------------+------+---------+-------+-------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)  说明1:使用了method+operator_staff_id作为查询条件,跳过了trader_staff_id字段,但是最左前缀method有使用,所以依然触发了mto索引。
  说明2:key_len=62说明索引字段又变短了,那是因为从跳过的trader_staff_id字段,所以trader_staff_id及之后的索引字段就失效,案例2中的key_len是66,而现在又变成了62,说明trader_staff_id的索引长度也为4
    
  案例4:使用trader_staff_id + operator_staff_id查询
mysql> <strong>select * from account_transaction where trader_staff_id=275 and operator_staff_id=12;
</strong>+---------+--------------------+---------------+--------+----------------------------+---------------+--------------+--------+---------+-----------------+-------------------+-----------+--------------------------+
| id      | trade_no         | type          | method | time                     | payment       | out_trade_no | amount | balance | trader_staff_id | operator_staff_id | device_id | remark                   |
+---------+--------------------+---------------+--------+----------------------------+---------------+--------------+--------+---------+-----------------+-------------------+-----------+--------------------------+
|      24 | 156384428075000275 | TOP_UP      | CASH   | 2019-07-23 01:11:20.221977 | LOCAL_ACCOUNT |            |10000 |   10000 |             275 |                12 | 6         |                        |
|747793 | 157370375171000275 | TOP_UP      | CASH   | 2019-11-14 03:55:51.480417 | LOCAL_ACCOUNT |            |10000 |   11000 |             275 |                12 | 6         |                        |
| 1993075 | 160454902688000275 | REFUND      | WEB    | 2020-11-05 04:03:46.980204 | LOCAL_ACCOUNT |            |-3200 |       0 |             275 |                12 | 43      |                        |
| 3764809 | 162122330931000275 | TOP_UP      | CHEQUE | 2021-05-17 03:48:29.748154 | LOCAL_ACCOUNT |            |10000 |   10000 |             275 |                12 | 6         |                        |
| 4791205 | 162856536047000275 | CONSUME_LUNCH | WEB    | 2021-08-04 04:46:17.000000 | LOCAL_ACCOUNT |            |    200 |    9400 |             275 |                12 | 35      | 管理后台补充消费         |
| 4791211 | 162856542884000275 | CONSUME_LUNCH | WEB    | 2021-08-05 04:46:17.000000 | LOCAL_ACCOUNT |            |    200 |    9200 |             275 |                12 | 35      | 管理后台补充消费         |
| 4791217 | 162856543723000275 | CONSUME_LUNCH | WEB    | 2021-08-06 04:46:17.000000 | LOCAL_ACCOUNT |            |    200 |    9000 |             275 |                12 | 35      | 管理后台补充消费         |
+---------+--------------------+---------------+--------+----------------------------+---------------+--------------+--------+---------+-----------------+-------------------+-----------+--------------------------+
11 rows in set (4.58 sec)

mysql> <strong>explain select * from account_transaction where trader_staff_id=275 and operator_staff_id=12;
</strong>+----+-------------+---------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table               | partitions | type | possible_keys | <strong>key| key_len</strong> | ref| rows    | filtered | Extra       |
+----+-------------+---------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
|1 | SIMPLE      | account_transaction | NULL       | ALL| NULL          | <strong>NULL | NULL</strong>| NULL | 2249115 |   1.00 | Using where |
+----+-------------+---------------------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)  说明1:查询时间为4.58秒,比之前用时多了很多
  说明2:通过explain执行计划,可以发现该查询语句没有使用索引,是因为不符合最左前缀原则,即索引的最左边的method也就是第一索引列,这一列必须要使用,是触发组合索引的前缀。
 
  案例5:包含最左前缀,但是最左前缀不在最前面
mysql> <strong>select * from account_transaction where trader_staff_id=275 and operator_staff_id=12 and method=</strong><strong>"CASH";</strong>
+--------+--------------------+--------+--------+----------------------------+---------------+--------------+--------+---------+-----------------+-------------------+-----------+--------+
| id   | trade_no         | type   | method | time                     | payment       | out_trade_no | amount | balance | trader_staff_id | operator_staff_id | device_id | remark |
+--------+--------------------+--------+--------+----------------------------+---------------+--------------+--------+---------+-----------------+-------------------+-----------+--------+
|   24 | 156384428075000275 | TOP_UP | CASH   | 2019-07-23 01:11:20.221977 | LOCAL_ACCOUNT |            |10000 |   10000 |             275 |                12 | 6         |      |
| 747793 | 157370375171000275 | TOP_UP | CASH   | 2019-11-14 03:55:51.480417 | LOCAL_ACCOUNT |            |10000 |   11000 |             275 |                12 | 6         |      |
+--------+--------------------+--------+--------+----------------------------+---------------+--------------+--------+---------+-----------------+-------------------+-----------+--------+
2 rows in set (0.00 sec)

mysql> <strong>explain select * from account_transaction where trader_staff_id=275 and operator_staff_id=12 and method=</strong><strong>"CASH";</strong>
+----+-------------+---------------------+------------+------+---------------+------+---------+-------------------+------+----------+-------+
| id | select_type | table               | partitions | type | possible_keys | key| key_len | ref               | rows | filtered | Extra |
+----+-------------+---------------------+------------+------+---------------+------+---------+-------------------+------+----------+-------+
|1 | SIMPLE      | account_transaction | NULL       | ref| mto         | mto| 70      | const,const,const |    2 |   100.00 | NULL|
+----+-------------+---------------------+------------+------+---------------+------+---------+-------------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)    说明1:通过explain发现依然触发了mto索引,虽然最左前缀没有在最左边,但是只要出现了就可以,复合最左前缀法则。
 
二、范围查询


  联合查询索引中,出现范围查询(>, explain select * from account_transaction where trader_staff_id>275 and operator_staff_id=12 and method="CASH";+----+-------------+---------------------+------------+-------+---------------+------+---------+------+-------+----------+----------------------------------+| id | select_type | table               | partitions | type| possible_keys | key| key_len | ref| rows| filtered | Extra                            |+----+-------------+---------------------+------------+-------+---------------+------+---------+------+-------+----------+----------------------------------+|1 | SIMPLE      | account_transaction | NULL       | range | mto         | mto| 66      | NULL | 37708 |    10.00 | Using index condition; Using MRR |+----+-------------+---------------------+------------+-------+---------------+------+---------+------+-------+----------+----------------------------------+1 row in set, 1 warning (0.01 sec)  说明2,在搜索条件中的trader_staff_id是一个范围查询使用的">",因为trader_staff_id在创建索引的时候在第2顺序,所以该查询语句中,处于第三个字段的operator_staff_id字段就失效了,所以key_len是66
mysql> <strong>show index from</strong><strong> account_transaction;</strong>
+---------------------+------------+-------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table               | Non_unique | Key_name    | Seq_in_index | Column_name       | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------------------+------------+-------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| account_transaction |          0 | PRIMARY   |            1 | id                | A         |   2067077 |   NULL |   NULL |      | BTREE      |         |               | YES   | NULL       |
| account_transaction |          1 | trade_index |            1 | trade_no          | A         |   2249115 |   NULL |   NULL |      | BTREE      |         |               | YES   | NULL       |
| account_transaction |          1 | mto         |            1 | method            | A         |         3 |   NULL |   NULL |      | BTREE      |         |               | YES   | NULL       |
| account_transaction |          1 | mto         |            2 | trader_staff_id   | A         |       31046 |   NULL |   NULL |      | BTREE      |         |               | YES   | NULL       |
| account_transaction |          1 | mto         |            3 | operator_staff_id | A         |       15847 |   NULL |   NULL |      | BTREE      |         |               | YES   | NULL       |
+---------------------+------------+-------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
  说明3:如果在不影响业务的时候,最好使用">="或者"
页: [1]
查看完整版本: Mysql高级4-索引的使用规则