首发于 MySQL进阶学习
MySQL查询(select)优化--各种索引的使用

MySQL查询(select)优化--各种索引的使用

1、SQL提示

SQL提示是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。

例如,使用索引(一种建议手段,若MySQL认为这种索引不合适就不会使用):

explain select * from tb_user use index(idx_user_pro) where profession="软件工程";

不使用哪个索引:

explain select * from tb_user ignore index(idx_user_pro) where profession="软件工程";

必须使用哪个索引:

explain select * from tb_user force index(idx_user_pro) where profession="软件工程";

use 是建议,实际使用哪个索引 MySQL 还会自己权衡运行速度去更改,force就是无论如何都强制使用该索引。


2、索引覆盖&&回表查询

尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能找到),减少 select *

例如:

 -- 这里首先给出一个数据表
 mysql> select * from tb_user;
 +----+-----------------+----------------+-------------+--------------------------+------+--------+--------+---------------------+
 | id | name            | email          | phone       | profession               | age  | gender | status | create_time         |
 +----+-----------------+----------------+-------------+--------------------------+------+--------+--------+---------------------+
 |  1 | 孙悟空          | 1111@qq.com    | 11111111111 | 软件工程                 |  500 |      1 |      1 | 2022-06-20 11:35:11 |
 |  2 | 周瑜            | 1121@qq.com    | 11111111112 | 软件工程2                |   50 |      1 |      0 | 2022-06-20 11:35:11 |
 |  3 | 孙尚香          | 1113@qq.com    | 11111111113 | 计算机科学与技术         |   30 |      2 |      2 | 2022-06-20 11:35:11 |
 |  4 | 白龙马          | 1114@qq.com    | 11111111114 | 飞翔科学                 |  300 |      1 |      0 | 2022-06-20 11:35:11 |
 |  5 | 唐曾            | 1115@qq.com    | 11111111115 | 软件工程                 |   50 |      1 |      5 | 2022-06-20 11:35:11 |
 +----+-----------------+----------------+-------------+--------------------------+------+--------+--------+---------------------+
 
 -- 里面有三个索引,主键索引 PRIMARY ,根据name的索引idx_user_name和根据phone的索引idx_user_phone
 mysql> show index from tb_user;
 +---------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
 | Table   | Non_unique | Key_name              | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible |
 +---------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
 | tb_user |          0 | PRIMARY               |            1 | id          | A         |          12 |     NULL |   NULL |      | BTREE      |         |               | YES     |
 | tb_user |          0 | idx_user_phone        |            1 | phone       | A         |          12 |     NULL |   NULL | YES  | BTREE      |         |               | YES     |
 | tb_user |          1 | idx_user_name         |            1 | name        | A         |          11 |     NULL |   NULL | YES  | BTREE      |         |               | YES     |
 +---------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
 6 rows in set (0.05 sec)
 
 
 
 -- 情况1
 -- 使用explain执行计划查看下列语句,其中Extra为Using index,并且key为idx_user_name,这说明使用到了索引
 -- 这是因为索引idx_user_name是一个非聚簇索引,在索引的叶子存放了当前数据的id,索引数据就是name,在SQL中,也只需要name和id两个字段,所以就不需要回表了。
 mysql> explain select id,name from tb_user where name = '孙尚香';
 +----+-------------+---------+------------+------+---------------+---------------+---------+-------+------+----------+-------------+
 | id | select_type | table   | partitions | type | possible_keys | key           | key_len | ref   | rows | filtered | Extra       |
 +----+-------------+---------+------------+------+---------------+---------------+---------+-------+------+----------+-------------+
 |  1 | SIMPLE      | tb_user | NULL       | ref  | idx_user_name | idx_user_name | 123     | const |    1 |   100.00 | Using index |
 +----+-------------+---------+------------+------+---------------+---------------+---------+-------+------+----------+-------------+
 1 row in set, 1 warning (0.00 sec)
 
 
 
 -- 情况2
 -- 在下面的explain执行计划中Extra 为NULL,说明整个操作要么没有使用索引,要么有回表操作,由key为idx_user_name可以看出使用name索引,但是在索引结构中没有SQL中需要的gender值,所以就必须回表操作才能回去到gender值。
 mysql> explain select id,name,gender from tb_user where name = '孙尚香';
 +----+-------------+---------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
 | id | select_type | table   | partitions | type | possible_keys | key           | key_len | ref   | rows | filtered | Extra |
 +----+-------------+---------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
 |  1 | SIMPLE      | tb_user | NULL       | ref  | idx_user_name | idx_user_name | 123     | const |    1 |   100.00 | NULL  |
 +----+-------------+---------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
 1 row in set, 1 warning (0.00 sec)
 
 -- 注意,数据库版本不同,环境不同时,有没有回表操作在Extra中的表现形式是不一样的。
 -- 常见的还有using index condition:查找使用了索引,但是需要回表查询数据;using where; using index;:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询。
 -- 笔者这里是Using index和NULL

回表的详细过程请参考

悄悄绽放的海棠花:MySQL索引分类?在InnoDB 存储引擎中,使用聚簇索引和非聚簇索引的查询机制?

3、前缀索引

当字段类型为字符串(varchar, text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率,此时可以只降字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

语法:create index idx_xxxx on table_name(column(n)); 前缀长度:可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。 求选择性公式:

show index 里面的sub_part可以看到接取的长度


前缀索引查询流程如下:

4、单例索引VS联合索引

单列索引:即一个索引只包含单个列 联合索引:即一个索引包含了多个列 在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引。

 -- 使用phone和email字段创建联合索引
 mysql> create index idx_tb_user_phone_name on tb_user(phone,email);
 Query OK, 0 rows affected (0.08 sec)
 Records: 0  Duplicates: 0  Warnings: 0
 
 -- 查看索引 idx_tb_user_phone_name
 mysql> show index from tb_user;
 +---------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
 | Table   | Non_unique | Key_name               | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible |
 +---------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
 | tb_user |          0 | PRIMARY                |            1 | id          | A         |          12 |     NULL |   NULL |      | BTREE      |         |               | YES     |
 | tb_user |          0 | idx_user_phone         |            1 | phone       | A         |          12 |     NULL |   NULL | YES  | BTREE      |         |               | YES     |
 | tb_user |          1 | idx_user_name          |            1 | name        | A         |          11 |     NULL |   NULL | YES  | BTREE      |         |               | YES     |
 | tb_user |          1 | idx_tb_user_phone_name |            1 | phone       | A         |          12 |     NULL |   NULL | YES  | BTREE      |         |               | YES     |
 | tb_user |          1 | idx_tb_user_phone_name |            2 | email       | A         |          12 |     NULL |   NULL | YES  | BTREE      |         |               | YES     |
 +---------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
 9 rows in set (0.01 sec)
 
 -- 我们可以看出在tb_user中,存在phone和name各自的单例索引(idx_user_phone和idx_user_name),也有他们两个组成的联合索引(idx_tb_user_phone_name)
 
 -- 单列索引情况:从explain执行计划中可以看出,Extra为NULL,说明这个SQL存在回表操作,key为idx_user_phone,说明只是使用了phone形成的单列索引,这样效率会不高
 mysql> explain select id, phone, name from tb_user where phone = '11111111113' and name = '孙尚香';
 +----+-------------+---------+------------+-------+-----------------------------------------------------+----------------+---------+-------+------+----------+-------+
 | id | select_type | table   | partitions | type  | possible_keys                                       | key            | key_len | ref   | rows | filtered | Extra |
 +----+-------------+---------+------------+-------+-----------------------------------------------------+----------------+---------+-------+------+----------+-------+
 |  1 | SIMPLE      | tb_user | NULL       | const | idx_user_phone,idx_user_name,idx_tb_user_phone_name | idx_user_phone | 63      | const |    1 |   100.00 | NULL  |
 +----+-------------+---------+------------+-------+-----------------------------------------------------+----------------+---------+-------+------+----------+-------+
 1 row in set, 1 warning (0.00 sec)
 
 -- 联合索引的情况,Extra为Using where表示没有回表,使用了idx_tb_user_phone_name联合索引,执行效率会高于使用单列索引。
 mysql> explain select id, phone, name from tb_user use index(idx_tb_user_phone_name) where phone = '11111111113' and name = '孙尚香';
 +----+-------------+---------+------------+------+------------------------+------------------------+---------+-------+------+----------+-------------+
 | id | select_type | table   | partitions | type | possible_keys          | key                    | key_len | ref   | rows | filtered | Extra       |
 +----+-------------+---------+------------+------+------------------------+------------------------+---------+-------+------+----------+-------------+
 |  1 | SIMPLE      | tb_user | NULL       | ref  | idx_tb_user_phone_name | idx_tb_user_phone_name | 63      | const |    1 |     9.09 | Using where |
 +----+-------------+---------+------------+------+------------------------+------------------------+---------+-------+------+----------+-------------+
 1 row in set, 1 warning (0.00 sec)


使用联合索引查询流程如下:

5、索引设计原则

  1. 针对于数据量较大,且查询比较频繁的表建立索引
  2. 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引
  3. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
  4. 如果是字符串类型的字段,字段长度较长,可以针对于字段的特点,建立前缀索引
  5. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
  6. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价就越大,会影响增删改的效率
  7. 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询

代做工资流水公司烟台签证工资流水办理江门打个人流水济宁代开银行流水修改襄阳个人工资流水 代开孝感打贷款流水东莞银行流水PS开具洛阳薪资流水单办理遵义签证银行流水 公司黄冈自存银行流水多少钱泰安个人工资流水 报价南宁打公司银行流水金华代做签证工资流水大庆银行流水PS开具潮州签证工资流水费用绍兴办理车贷流水柳州打印背调银行流水绵阳流水单代办滁州购房银行流水代办包头对公流水费用扬州查询入职银行流水嘉兴自存流水样本银川贷款银行流水报价深圳签证工资流水代开淄博办车贷银行流水阜阳公司银行流水打印宁波流水账单多少钱铜陵房贷收入证明打印汕头个人工资流水 公司温州查贷款流水湘潭打印车贷银行流水香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声卫健委通报少年有偿捐血浆16次猝死汪小菲曝离婚始末何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言男子被猫抓伤后确诊“猫抓病”周杰伦一审败诉网易中国拥有亿元资产的家庭达13.3万户315晚会后胖东来又人满为患了高校汽车撞人致3死16伤 司机系学生张家界的山上“长”满了韩国人?张立群任西安交通大学校长手机成瘾是影响睡眠质量重要因素网友洛杉矶偶遇贾玲“重生之我在北大当嫡校长”单亲妈妈陷入热恋 14岁儿子报警倪萍分享减重40斤方法杨倩无缘巴黎奥运考生莫言也上北大硕士复试名单了许家印被限制高消费奥巴马现身唐宁街 黑色着装引猜测专访95后高颜值猪保姆男孩8年未见母亲被告知被遗忘七年后宇文玥被薅头发捞上岸郑州一火锅店爆改成麻辣烫店西双版纳热带植物园回应蜉蝣大爆发沉迷短剧的人就像掉进了杀猪盘当地回应沈阳致3死车祸车主疑毒驾开除党籍5年后 原水城县长再被查凯特王妃现身!外出购物视频曝光初中生遭15人围殴自卫刺伤3人判无罪事业单位女子向同事水杯投不明物质男子被流浪猫绊倒 投喂者赔24万外国人感慨凌晨的中国很安全路边卖淀粉肠阿姨主动出示声明书胖东来员工每周单休无小长假王树国卸任西安交大校长 师生送别小米汽车超级工厂正式揭幕黑马情侣提车了妈妈回应孩子在校撞护栏坠楼校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变老人退休金被冒领16年 金额超20万西藏招商引资投资者子女可当地高考特朗普无法缴纳4.54亿美元罚金浙江一高校内汽车冲撞行人 多人受伤

代做工资流水公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化