mysql 索引之索引基础

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhang_referee/article/details/83045903

mysql  索引在 mysql 优化中来说是非常重要的一个环节 。索引本质上不难,但要构建高效的索引却又不是那么容易的。在这里打算分三个环节来描述下索引:

  1. 索引基础
  2. 索引的使用
  3. 索引优化

其中索引基础,就是这篇文章要说的问题,第二部分索引的使用包括前缀索引,全文索引等内容,第三部分想要说的是索引的优化,这里提下全文索引,一起使用全文索引的时候基本上是myql 结合 sphinx 或者 elasticsearch 全文搜索引擎的使用,不过在mysql 5.7 中已内置 ngram 分词插件。

下面是这篇文章的概述:

  • 1.理解索引
  • 2.索引的类型
  • 3.索引的管理
  • 4.索引的优缺点
  • 5.索引使用的原则

1.理解索引

   在说索引前,我们先在脑海中回忆下,我们以前使用新华词典的过程。想一下,我们从词典查找某个词的释义的过程 -- 大多数情况下是先从拼音或部首开始查到该词大概所在的页数,然后把词典翻到相应到页数查到我们需要的数据。如果把这一过程抽象出来,那么我们可以把拼音或部首称之为索引。

   细想下,我们要查 "木" 这个词的释义过程,先把词典翻到拼音部分,找到 "m" 部分,然后再在''m" 部分查找 "u" ,再然后找到 "木" 这个词,从而找到 "木" 这个词位于词典的第几页,然后就能迅速地找到改词的释义(也就是我们要查找的数据)。我们把词典想象成mysql数据表,拼音或部首想象成索引,我们可以用上面查词典的过程在脑海中描绘出mysql查数据的过程,是不是很简单(虽然并不很准确,但却很容易理解)。

  索引是从数据中提取关键字,并与数据记录建立对应关系的数据结构。索引的本质是数据结构。

2.索引的类型

名称 语法 关键词
主键索引 primary key 要求关键字不能重复,也不能为NULL。同时增加主键约束
唯一索引 unique index 要求关键字不能重复。同时增加唯一约束
全文索引 fulltext key 关键字的来源不是所有字段的数据,而是从字段中提取的特别关键词
普通索引 index 对关键字没有要求

关键词可以使记录的部分数据(某个字段,某些字段,某个字段的一部分),当是某些字段的时候,那就是复合索引;当是某个字段的一部分的时候,那就是前缀索引。前面四种索引类型都可以为复合索引。

3. 索引的管理

 3.1创建索引

      3.1.1 更新表结构

       先看一下,现有表结构:

       

mysql> show create table dye_production_schedules \G;
*************************** 1. row ***************************
       Table: dye_production_schedules
Create Table: CREATE TABLE `dye_production_schedules` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '染订单id',
  `order_detail_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '染订单分录id',
  `dye_code` varchar(64) NOT NULL DEFAULT '' COMMENT '染订单编号',
  `product_id` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT '产品id',
  `batch_num` varchar(64) NOT NULL DEFAULT '' COMMENT '缸号',
  `customer_color_name` varchar(128) NOT NULL DEFAULT '' COMMENT '客户颜色',
  `is_delete` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除,默认 0 没有删除, 1 -删除',
  `scheduling_bacth` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '投胚卷数',
  `scheduling_qty` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '投胚数量,以最小单位来计算,比如单位是kg ,那么就存g',
  `finished_batch` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '成品卷数',
  `finished_qty` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '成品重量,以最小单位来计算,比如单位是kg ,那么就存g',
  `prodction_state` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '生产进度状态',
  `dye_factory_id` smallint(5) unsigned NOT NULL DEFAULT '0' COMMENT '染厂id',
  `is_pCode` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '0 没有疋号,1 有疋号',
  `is_need_knit` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否需要领胚布,0 不需要 ,1 - 需要',
  `remark` text COMMENT '备注',
  `created_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1011 DEFAULT CHARSET=utf8

       更新表结构,加入以下索引:

  alter table dye_production_schedules
  add index `order_id_order_detail_id` (`order_id`,`order_detail_id`), -- 复合索引
  add index `dye_factory_id` (`dye_factory_id`),
  add index `dye_code` (`dye_code`),
  add index `order_id_batch_num` (`order_id`,`batch_num`) ,
  add fulltext index `customer_color_name` (`customer_color_name`);
  其中`dye_factory_id` 和 `dye_code` 是普通索引,`customer_color_name` 是全文索引

注:这里没有列出主键索引是之前建表就指定了主键索引,我这边数据库表数据是采用定时任务执行脚本往数据表插数据,如果建立唯一索引,也会对表数据加入唯一约束,所以这里暂不讨论唯一索引,建立唯一索引只需要在index 前加上关键词  unique 即可!

       3.1.2 建表时创建索引

  create table index_test_table(

    id int auto_increment,
    order_id int  unsigned not null default 0 comment '染订单id',
    order_detail_id int unsigned not null default 0 comment '染订单分录id',
    dye_code varchar(64) not null default '' comment '染订单编号',
    product_id smallint unsigned not null default 0 comment '产品id',
    batch_num varchar(64) not null default '' comment '缸号',
    customer_color_name varchar(128) not null default '' comment '客户颜色',
    is_delete tinyint unsigned not null default  0 comment '是否删除,默认 0 没有删除, 1 -删除',
    scheduling_bacth int unsigned not null default 0 comment '投胚卷数',
    scheduling_qty bigint unsigned not null default 0 comment '投胚数量,以最小单位来计算,比如单位是kg ,那么就存g',
    `finished_batch` int unsigned not null default 0 comment '成品卷数',
    `finished_qty` bigint unsigned not null default 0 comment '成品重量,以最小单位来计算,比如单位是kg ,那么就存g',
    prodction_state  tinyint unsigned not null default 0 comment '生产进度状态',
    `dye_factory_id`  smallint unsigned  not null default 0 comment '染厂id',
    is_pCode  tinyint unsigned not null default 0 comment '0 没有疋号,1 有疋号',
    is_need_knit tinyint unsigned not null default 0 comment '是否需要领胚布,0 不需要 ,1 - 需要',
    `remark` text comment '备注',
    created_at timestamp,
    primary key(id),
    index `order_id_order_detail_id` (`order_id`,`order_detail_id`), -- 复合索引
    index `dye_factory_id` (`dye_factory_id`),
    index `dye_code` (`dye_code`),
    index `order_id_batch_num` (`order_id`,`batch_num`) ,
    fulltext index `customer_color_name` (`customer_color_name`)

  )engine = InnoDB default charset = utf8;

如果你使用过mysql 5.6之前的版本,相信你肯定会对上面的ddl 语句有所疑问(可能在想,不是说mysql innodb 不支持全文索引么?),我这里使用的是mysql 5.7 ,mysql InnoDB 是在5.6后开始支持全文索引的。

在建立索引时,不一定要指定索引名,如果不指定,那么mysql 会自己指定,默认是字段名。我一般喜欢在索引名和字段名加上反引号(``,键盘上 tab 上面的键),这样可以降低出错的概率。

3.2 删除索引  

alter table dye_production_schedules
  drop index `order_id_order_detail_id`,
  drop index `dye_factory_id`,
  drop index `dye_code`,
  drop index `order_id_batch_num`,
  drop index `customer_color_name`;

 

4.索引的优缺点

  在说具体的优缺点前,先看下有索引和没索引查询相应时间的一个区别。

  由于数据量小体现不出差异,所以这里写了个定时任务,插入了60多万条数据。使用show index from tableName 可以查看tableName 的索引。

注:本机配置:8G 内存 + SATA 老硬盘, + ubuntu 16.04 系统。

这里是使用主键查询的

mysql> select count(*) from dye_production_schedules;
+----------+
| count(*) |
+----------+
|   622070 |
+----------+
1 row in set (1.86 sec)
mysql> select id ,dye_code ,customer_color_name from dye_production_schedules where id = 40000 ;
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
| id    | dye_code       | customer_color_name                                                                                       |
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
| 40000 | 2c43a6d572b97a | 0oPYaZ5cqI;她的头略略偏右仰着,嘴唇轻轻的动着,嘴唇以上,尽是微笑。唱Wshyk                               |
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> explain select id ,dye_code ,customer_color_name from dye_production_schedules where id = 40000 ;
+----+-------------+--------------------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table                    | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------------------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | dye_production_schedules | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+--------------------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

这里根据dye_code 这个字段来查询

mysql> select sql_no_cache id ,dye_code ,customer_color_name from dye_production_schedules where dye_code = '2c43a6d572b97a';
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
| id    | dye_code       | customer_color_name                                                                                       |
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
| 40000 | 2c43a6d572b97a | 0oPYaZ5cqI;她的头略略偏右仰着,嘴唇轻轻的动着,嘴唇以上,尽是微笑。唱Wshyk                               |
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (1.66 sec)

mysql> select sql_no_cache id ,dye_code ,customer_color_name from dye_production_schedules where dye_code = '2c43a6d572b97a' limit 1;
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
| id    | dye_code       | customer_color_name                                                                                       |
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
| 40000 | 2c43a6d572b97a | 0oPYaZ5cqI;她的头略略偏右仰着,嘴唇轻轻的动着,嘴唇以上,尽是微笑。唱Wshyk                               |
+-------+----------------+-----------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.10 sec)

mysql> explain select sql_no_cache id ,dye_code ,customer_color_name from dye_production_schedules where dye_code = '2c43a6d572b97a' limit 1;
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table                    | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | dye_production_schedules | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 471005 |    10.00 | Using where |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 2 warnings (0.00 sec)

mysql> 

不知道你还记不记得,前面第一部分内容说理解索引的时候,举的查词典栗子,说根据查词典流程去理解mysql  查询并不很准确,这是因为mysql在一次查询完毕后,会做很多后续工作,其中就会尝试把查询结果缓存起来,这里的sql_no_cache 是指示mysql 不要对这条sql查询结果进行缓存。

通过explain 可以看到,当没有索引查询的时候,哪怕是指定了limit 1 ,也是全表扫描(type 值为ALL ),至于explain 的用法可以参考 https://blog.csdn.net/zhang_referee/article/details/83041301 。

我们再看一个排序的栗子:

我们按照`order_detail_id` 这个字段来排序:

mysql> select sql_no_cache id ,order_id,order_detail_id ,dye_code ,customer_color_name from dye_production_schedules order by order_detail_id desc limit 10;
+--------+----------+-----------------+----------------+-----------------------------------------------------------------------------------------------------------+
| id     | order_id | order_detail_id | dye_code       | customer_color_name                                                                                       |
+--------+----------+-----------------+----------------+-----------------------------------------------------------------------------------------------------------+
| 595950 |     1076 |          100000 | e1004ec21380a5 | 35Fe6vimkq  你为我的捞什子书也费了不少神;第一回让你父亲的男佣人从家HyR4O                               |
| 107857 |     7301 |          100000 | a279af0d418d38 | QOvptosZXN字与字间的时距,我不能指明,只觉比普通人说话延长罢了;最令我USfR9                               |
| 249517 |     5463 |          100000 | a7682ca9b246a9 | 3ySBwHWLbG过的荷塘,在这满月的光里,总该另有一番样子吧。月亮渐渐地升高dsZzQ                               |
| 218765 |     9783 |          100000 | 40a8c5c97ffa0c | ElAJFnO4W7。那边学校当局要我约圣陶去。圣陶来信说:“我们要痛痛快快游西a8k1d                                |
| 566500 |     3779 |           99999 | be6bc4fb7d2e00 | qjvSLdR6b3字。“人”让他站着,“牛”也让它站着;所饶不过的是“女”人,zcTJ9                                     |
| 164685 |     5037 |           99999 | 8b737b07d85bb3 | 8eMkXJrWqS快,不觉七点还欠五分了。这时票子还有许多人没买着,大家都着急06RxO                               |
| 137008 |     1860 |           99999 | 9872f5c73533b5 | C9BaK4wsbm去,直到现在——中间又被朋友拉到福州一次,有一篇《将离》抒写dplOM                                 |
| 370503 |     1334 |           99999 | 5efb7037d40e47 | eH7481tAlK,始终笔直的站着,几乎不曾移过一步,真像石像一般,有着可怕的LN53s                               |
| 597563 |     7425 |           99999 | 0b95c11a4e30ee | 1POtQT2GdX的地方便是护城河,曼衍开去,曲曲折折,直到平山堂,——这是你oMwJb                                 |
| 507070 |     6182 |           99999 | dd3dc56d921e92 | nbfxzOZy62动都像不是他们自己的。好容易费了二虎之力,居然买了几张票,凭cH8Gp                               |
+--------+----------+-----------------+----------------+-----------------------------------------------------------------------------------------------------------+
10 rows in set, 1 warning (3.94 sec)

mysql> explain select sql_no_cache id ,order_id,order_detail_id ,dye_code ,customer_color_name from dye_production_schedules order by order_detail_id desc limit 10;
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+--------+----------+----------------+
| id | select_type | table                    | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra          |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+--------+----------+----------------+
|  1 | SIMPLE      | dye_production_schedules | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 471005 |   100.00 | Using filesort |
+----+-------------+--------------------------+------------+------+---------------+------+---------+------+--------+----------+----------------+
1 row in set, 2 warnings (0.01 sec)

该查询不仅耗时久,通过explain  可以发现,使用了文件排序。

下面就添加适当的索引来进行比较:

mysql>   alter table dye_production_schedules
    ->   add index `order_id_order_detail_id` (`order_id`,`order_detail_id`), -- 复合索引
    ->   add index `dye_factory_id` (`dye_factory_id`),
    ->   add index `dye_code` (`dye_code`),
    ->   add index `order_detail_id` (`order_detail_id`),
    ->   add index `order_id_batch_num` (`order_id`,`batch_num`) ,
    ->   add fulltext index `customer_color_name` (`customer_color_name`);
Query OK, 0 rows affected (1 min 26.31 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql>  select sql_no_cache id ,dye_code ,order_id,order_detail_id ,customer_color_name from dye_production_schedules where dye_code = 'b5ac612c7a0139' limit 1;
+--------+----------------+----------+-----------------+-----------------------------------------------------------------------------------------------------------+
| id     | dye_code       | order_id | order_detail_id | customer_color_name                                                                                       |
+--------+----------------+----------+-----------------+-----------------------------------------------------------------------------------------------------------+
| 100423 | b5ac612c7a0139 |     9006 |           54461 | 3CN92UGwlo是逃不了的。我说北平看花,比别处有意思,也正在此。这时候,我Wsrdi                               |
+--------+----------------+----------+-----------------+-----------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain  select sql_no_cache id ,dye_code ,order_id,order_detail_id ,customer_color_name from dye_production_schedules where dye_code = 'b5ac612c7a0139' limit 1;
+----+-------------+--------------------------+------------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table                    | partitions | type | possible_keys | key      | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------------------------+------------+------+---------------+----------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | dye_production_schedules | NULL       | ref  | dye_code      | dye_code | 194     | const |    1 |   100.00 | NULL  |
+----+-------------+--------------------------+------------+------+---------------+----------+---------+-------+------+----------+-------+
1 row in set, 2 warnings (0.00 sec)

可以发现,在dye_code 上加上索引,查询速度快了很多。

我们再次按照`order_detail_id` 这个字段来排序:

mysql>  select sql_no_cache id ,dye_code ,order_id,order_detail_id ,customer_color_name from dye_production_schedules order by order_detail_id desc  limit 10;
+--------+----------------+----------+-----------------+-----------------------------------------------------------------------------------------------------------+
| id     | dye_code       | order_id | order_detail_id | customer_color_name                                                                                       |
+--------+----------------+----------+-----------------+-----------------------------------------------------------------------------------------------------------+
| 595950 | e1004ec21380a5 |     1076 |          100000 | 35Fe6vimkq  你为我的捞什子书也费了不少神;第一回让你父亲的男佣人从家HyR4O                               |
| 249517 | a7682ca9b246a9 |     5463 |          100000 | 3ySBwHWLbG过的荷塘,在这满月的光里,总该另有一番样子吧。月亮渐渐地升高dsZzQ                               |
| 218765 | 40a8c5c97ffa0c |     9783 |          100000 | ElAJFnO4W7。那边学校当局要我约圣陶去。圣陶来信说:“我们要痛痛快快游西a8k1d                                |
| 107857 | a279af0d418d38 |     7301 |          100000 | QOvptosZXN字与字间的时距,我不能指明,只觉比普通人说话延长罢了;最令我USfR9                               |
| 597563 | 0b95c11a4e30ee |     7425 |           99999 | 1POtQT2GdX的地方便是护城河,曼衍开去,曲曲折折,直到平山堂,——这是你oMwJb                                 |
| 566500 | be6bc4fb7d2e00 |     3779 |           99999 | qjvSLdR6b3字。“人”让他站着,“牛”也让它站着;所饶不过的是“女”人,zcTJ9                                     |
| 507070 | dd3dc56d921e92 |     6182 |           99999 | nbfxzOZy62动都像不是他们自己的。好容易费了二虎之力,居然买了几张票,凭cH8Gp                               |
| 370503 | 5efb7037d40e47 |     1334 |           99999 | eH7481tAlK,始终笔直的站着,几乎不曾移过一步,真像石像一般,有着可怕的LN53s                               |
| 164685 | 8b737b07d85bb3 |     5037 |           99999 | 8eMkXJrWqS快,不觉七点还欠五分了。这时票子还有许多人没买着,大家都着急06RxO                               |
| 137008 | 9872f5c73533b5 |     1860 |           99999 | C9BaK4wsbm去,直到现在——中间又被朋友拉到福州一次,有一篇《将离》抒写dplOM                                 |
+--------+----------------+----------+-----------------+-----------------------------------------------------------------------------------------------------------+
10 rows in set, 1 warning (0.11 sec)

mysql> explain  select sql_no_cache id ,dye_code ,order_id,order_detail_id ,customer_color_name from dye_production_schedules order by order_detail_id desc  limit 10;
+----+-------------+--------------------------+------------+-------+---------------+-----------------+---------+------+------+----------+-------+
| id | select_type | table                    | partitions | type  | possible_keys | key             | key_len | ref  | rows | filtered | Extra |
+----+-------------+--------------------------+------------+-------+---------------+-----------------+---------+------+------+----------+-------+
|  1 | SIMPLE      | dye_production_schedules | NULL       | index | NULL          | order_detail_id | 4       | NULL |   10 |   100.00 | NULL  |
+----+-------------+--------------------------+------------+-------+---------------+-----------------+---------+------+------+----------+-------+

通过explain 分析得知,在没有索引的时候,按照`order_detail_id` 这个字段来排序,会做全表扫描文件排序(哪怕是把整个表载入内存做排序也是文件排序,因为explain 并不会告诉你这个区别,具体explain 用法可参考 :https://blog.csdn.net/zhang_referee/article/details/83041301),而在排序字段上加了索引后,mysql 已无需再去做耗时的排序操作。

mysql> select count(*) from dye_production_schedules;
+----------+
| count(*) |
+----------+
|   622070 |
+----------+
1 row in set (0.17 sec)

mysql> explain select count(*) from dye_production_schedules;
+----+-------------+--------------------------+------------+-------+---------------+----------------+---------+------+--------+----------+-------------+
| id | select_type | table                    | partitions | type  | possible_keys | key            | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+--------------------------+------------+-------+---------------+----------------+---------+------+--------+----------+-------------+
|  1 | SIMPLE      | dye_production_schedules | NULL       | index | NULL          | dye_factory_id | 2       | NULL | 471005 |   100.00 | Using index |
+----+-------------+--------------------------+------------+-------+---------------+----------------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

优缺点:

  1. 索引能大大提高了查询速度,却会降低更新表的速度,如对表进行insert、update和delete。因为更新表时,不仅要保存数据,还要维护索引。
  2. 建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会增长很快。

在mysql 优化中,性价比最高的就是建立索引,建立索引不难,建立合适的索引(三星索引,这一说法来自于《高性能mysql》),却不总是那么容易的。

文章参考自

《高性能mysql 第三版》 (下载地址:https://pan.baidu.com/s/1haFdY7c9xb6VNtlfPUaidQ)

  mysql 官方手册: https://dev.mysql.com/doc/refman/5.7/en/explain-output.html

猜你喜欢

转载自blog.csdn.net/zhang_referee/article/details/83045903