面试系列--数据库MySQL优化

为什么要使用索引

  • 可以大大加快数据的检索数据
  • 将随机IO变为顺序IO

索引那么多优点,为什么不是表中每一列都添加一个索引

  • 对表中的数据进行增加、删除和修改的时候,索引也要进行动态的维护
  • 索引需要占物理空间,如果建立聚簇索引,需要的空间更大
  • 创建索引和维护索引耗费时间,而且随着数据量的增加而增加

什么是聚簇索引

       表数据按照索引的顺序来存储的,也就是说索引项的顺序与表中记录的物理顺序一致。对于聚集索引,叶子结点即存储了真实的数据行,不再有另外单独的数据。 在一张表上最多只能创建一个聚集索引,因为真实数据的物理顺序只能有一种,但该索引可以包含多个列(组合索引)。

建立聚簇索引的思想

1.大多数表都应该有聚簇索引或使用分区来降低对表尾页的竞争,在一个高事务的环中,对最后一页的封锁严重影响系统的吞吐量。
2.在聚簇索引下,数据在物理上按顺序排在数据页上,重复值也排在一起,因而在那些包含范围检查 (between、<、<=、>、>=)或使用group by或orderby的查询时,一旦找到具有范围中第一个键值的行,具有后续索引值的行保证物理上毗连在一起而不必进一步搜索,避免了大范围扫描,可以大 大提高查询速度。
3.在一个频繁发生插入操作的表上建立聚簇索引时,不要建在具有单调上升值的列(如IDENTITY)上,否则会经常引起封锁冲突。
4.在聚簇索引中不要包含经常修改的列,因为码值修改后,数据行必须移动到新的位置。
5.选择聚簇索引应基于where子句和连接操作的类型。
       InnoDB 引擎采用了聚簇索引,即将数据存入索引叶子页面上。对于 InnoDB 引擎来说,叶子页面不再存该行对应的地址,而是直接存储数据。

建立索引的注意事项:

  • 在经常需要搜索的列上,可以加快搜索的速度
  • 在经常使用在where子句的列上面创建索引,加快条件的判断速度
  • 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间
  • 在经常用到连接的列上,这些列主要是一些外键,可以加快连接的速度
  • 将打算加索引的列设置为NOT NULL,否则将导致引擎放弃使用索引而进行全表扫描
覆盖索引

       就是select的数据列只用从索引中就能够取得,不必从数据表中读取,换句话说查询列要被所使用的索引覆盖。
不是所有类型的索引都可以成为覆盖索引。覆盖索引必须要存储索引的列,而哈希索引、空间索引和全文索引等都不存储索引列的值,所以MySQL只能使用B-Tree索引做覆盖索引。

几种常见优化场景

1.无WHERE条件的查询优化:
3333
执行计划中,type 为ALL,表示进行了全表扫描
如何改进?优化措施很简单,就是对这个查询列建立索引。如下:

ALERT TABLE t1 ADD KEY(staff_id);
  • 再看一下执行计划
 explain select sql_no_cache count(staff_id) from t1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
         type: index
possible_keys: NULL
          key: staff_id
      key_len: 1
          ref: NULL
         rows: 1023849
      Extra: Using index
row in set (0.00 sec)

       possible_key: NULL,说明没有WHERE条件时查询优化器无法通过索引检索数据,这里使用了索引的另外一个优点,即从索引中获取数据,减少了读取的数据块的数量。 无where条件的查询,可以通过索引来实现索引覆盖查询,但前提条件是,查询返回的字段数足够少,更不用说select *之类的了。毕竟,建立key length过长的索引,始终不是一件好事情。

  • 查询消耗
    在这里插入图片描述
    从时间上看,小了0.13 sec
    2.二次检索优化
select sql_no_cache rental_date from t1 where inventory_id<80000;
…
…
| 2005-08-23 15:08:00 |
| 2005-08-23 15:09:17 |
| 2005-08-23 15:10:42 |
| 2005-08-23 15:15:02 |
| 2005-08-23 15:15:19 |
| 2005-08-23 15:16:32 |
+---------------------+
rows in set (0.13 sec)

执行计划

explain select sql_no_cache rental_date from t1 where inventory_id<80000\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
         type: range
possible_keys: inventory_id
          key: inventory_id
      key_len: 3
          ref: NULL
         rows: 153734
        Extra: Using index condition
row in set (0.00 sec)

Extra:Using index condition 表示使用的索引方式为二级检索,即79999个书签值被用来进行回表查询。可想而知,还是会有一定的性能消耗的。
尝试针对这个SQL建立联合索引,如下:

alter table t1 add key(inventory_id,rental_date);
explain select sql_no_cache rental_date from t1 where inventory_id<80000\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
         type: range
possible_keys: inventory_id,inventory_id_2
          key: inventory_id_2
      key_len: 3
          ref: NULL
         rows: 162884
        Extra: Using index
row in set (0.00 sec)

Extra:Using index 表示没有会标查询的过程,实现了索引覆盖
3.分页查询优化

select tid,return_date from t1 order by inventory_id limit 50000,10;
+-------+---------------------+
| tid   | return_date         |
+-------+---------------------+
| 50001 | 2005-06-17 23:04:36 |
| 50002 | 2005-06-23 03:16:12 |
| 50003 | 2005-06-20 22:41:03 |
| 50004 | 2005-06-23 04:39:28 |
| 50005 | 2005-06-24 04:41:20 |
| 50006 | 2005-06-22 22:54:10 |
| 50007 | 2005-06-18 07:21:51 |
| 50008 | 2005-06-25 21:51:16 |
| 50009 | 2005-06-21 03:44:32 |
| 50010 | 2005-06-19 00:00:34 |
+-------+---------------------+
rows in set (0.75 sec)

在未优化之前,我们看到它的执行计划是如此的糟糕

explain select tid,return_date from t1 order by inventory_id limit 50000,10\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1023675
row in set (0.00 sec)

看出是全表扫描。加上而外的排序,性能消耗是不低的
  如何通过覆盖索引优化呢?
  我们创建一个索引,包含排序列以及返回列,由于tid是主键字段,因此,下面的复合索引就包含了tid的字段值。

alter table t1 add index liu(inventory_id,return_date);
select tid,return_date from t1 order by inventory_id limit 50000,10;
+-------+---------------------+
| tid   | return_date         |
+-------+---------------------+
| 50001 | 2005-06-17 23:04:36 |
| 50002 | 2005-06-23 03:16:12 |
| 50003 | 2005-06-20 22:41:03 |
| 50004 | 2005-06-23 04:39:28 |
| 50005 | 2005-06-24 04:41:20 |
| 50006 | 2005-06-22 22:54:10 |
| 50007 | 2005-06-18 07:21:51 |
| 50008 | 2005-06-25 21:51:16 |
| 50009 | 2005-06-21 03:44:32 |
| 50010 | 2005-06-19 00:00:34 |
+-------+---------------------+
rows in set (0.03 sec)

可以发现,添加复合索引后,速度提升0.7s!
我们看一下改进后的执行计划

explain select tid,return_date from t1 order by inventory_id limit 50000,10\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
         type: index
possible_keys: NULL
          key: liu
      key_len: 9
          ref: NULL
         rows: 50010
    Extra: Using index
row in set (0.00 sec)

执行计划也可以看到,使用到了复合索引,并且不需要回表。
索引为甚会这么快,可以参考 [Mysql–基本存储结构][1]
MySQL InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读)。我们可以通过SELECT @@tx_isolation;命令来查看

 show variables like 't%_isolation';

在这里插入图片描述
[1]: https://blog.csdn.net/m0_37941483/article/details/89057073

猜你喜欢

转载自blog.csdn.net/m0_37941483/article/details/89053657