MySQL 索引优化实践(单表)

news2024/11/22 14:17:41

目录

    • 一、前言
    • 二、表数据准备
    • 三、常见业务无索引查询耗时测试
      • 3.1、通过订单ID / 订单编号 查询指定订单
      • 3.2、查询订单列表
    • 四、订单常见业务索引优化实践
      • 4.1、通过唯一索引和普通索引优化通过订单编号查询订单信息
      • 4.2、通过普通联合索引优化订单列表查询
        • 4.2.1、分析查询字段的查询场景
        • 4.2.2、优化各场景查询和原因分析
          • 4.2.2.1、需要根据订单编号查询
          • 4.2.2.2、需要根据客户编号查询
          • 4.2.2.3、需要根据创建时间查询 和 需要根据订单状态查询
    • 五、索引优化实践
      • 4.1 联合索引第一个字段用范围查询可能不会走索引
      • 4.2 强制走索引(联合索引第一个字段用范围查询不会走索引这里强制使用索引)
      • 4.3 使用覆盖索引优化查询
      • 4.4 in和or在表数据量比较大的情况会走索引,在表记录不多的情况下会选择全表扫描
      • 4.5 like KK% 一般情况也是可以走索引的
      • 4.6 分页查询索引使用和优化
      • 4.7 order by查询索引使用和优化
        • 4.7 总结
    • 六、索引设计原则

一、前言

      索引是为了高效查询排好序的数据结构,当表数据量到达一个量级没有对应索引帮助查询耗时会很长,MySQL资源开销也会非常大,当然索引也不能随意创建,要做到尽量少的索引解决尽量多的问题,这里会对一些业务场景做索引优化演示,这篇文中只介绍单表索引优化,如果单表问题能解决多表关联查询优化就简单多了。

如果对MySQL索引原理还有explain SQL分析工具不是很熟悉的可以看看几篇文章:

  • MySQL 索引底层 B+Tree 原理解析
  • MySQL explain SQL分析工具详解与最佳实践
  • MySQL 索引介绍和最佳实践

二、表数据准备

这里要准备100w数据左右的表,表数据尽量多一些或者列多一些,如果数据太少,测试的时候可能看不到效果。

创建订单信息表

DROP TABLE IF EXISTS `order_info`;
CREATE TABLE `order_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
  `order_no` varchar(100)  NOT NULL COMMENT '订单编号',
  `customer_id` bigint(20) NOT NULL COMMENT '客户编号',
  `goods_id` bigint(20) NOT NULL COMMENT '商品ID',
  `goods_title` varchar(100) DEFAULT NULL COMMENT '商品标题',
  `order_status` tinyint(4) NOT NULL DEFAULT '1' COMMENT '订单状态 1:待支付 2:已支付 3:已发货 4、已收货',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='订单信息表';

使用存储过程插入100w条数据

## 创建一个插入数据的存储过程
DROP PROCEDURE IF EXISTS insert_procedure;
delimiter;;
CREATE PROCEDURE insert_procedure () 
BEGIN
  DECLARE i INT DEFAULT 1;
  DECLARE goods_id BIGINT DEFAULT CEIL(RAND() * 100);
  DECLARE t_error INTEGER DEFAULT 0;
  DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET t_error=1;
  START TRANSACTION;
  WHILE ( i <= 1000000 ) DO
    INSERT INTO `order_info`(`order_no`,`customer_id`, `goods_id`, `goods_title`, `order_status`, `create_time`) VALUES (CONCAT('ON00000',i), CEIL(RAND() * 100000), goods_id, CONCAT('笔记本电脑',goods_id), MOD(i, 4)+1, NOW());
    SET i = i + 1;
    SET goods_id = CEIL(RAND() * 100);
  END WHILE;
  IF t_error=1 THEN
    ROLLBACK;
  ELSE
    COMMIT;
  END IF;
END;;
delimiter;

# 调用存储过程插入数据 我本地插入100w条数据耗时200s
CALL insert_procedure ();

三、常见业务无索引查询耗时测试

我电脑的配置32G内存500G固态,MySQL配置全用默认,自己测试先看看自己MySQL配置的innodb_buffer_pool_size设置的是多少,默认是128MB,查看命令SHOW VARIABLES LIKE 'innodb_buffer_pool_size';配置里的单位是字节,InnoDB使用一个缓冲池来保存索引和原始数据,innodb_buffer_pool_size就是控制这个缓冲池的大小,这个缓冲池在一些情况下对查询性能影响非常大,线上建议设置成MySQL能使用内存的80%,这里不深入。

3.1、通过订单ID / 订单编号 查询指定订单

  • 通过订单ID查询订单
SELECT * FROM order_info WHERE id = 955;

在这里插入图片描述

  • 通过订单编号查询订单
SELECT * FROM order_info WHERE order_no = 'ON000009999';

在这里插入图片描述
这里已经可以看到查询耗时明显的差距,我们这里的ID是主键,MySQL InnoDB存储引擎会自动将表中的主键设置为主键索引,同时也是一个聚簇索引叶子节点携带数据,而订单编号是没有索引的会进行全表扫描,会将ON000009999和这个表中每行数据的订单编号都进行比对然后取出满足条件的数据行,100w数据查询一个订单信息耗时已经到了0.6秒左右。

3.2、查询订单列表

查询订单列表一般查询条件比较多,如订单编号、客户编号、订单状态、创建时间、创建时间倒序、是否分页做几个演示,查询条件的内容自己看看存储过程插入的数据长什么样。

  • 1、查询客户编号为111的订单列表不分页,不根据创建时间倒序
SELECT * FROM order_info WHERE customer_id = 111 AND create_time >= '2023-10-02 09:57:24' AND create_time <= '2023-10-02 10:00:46';

在这里插入图片描述

  • 2、查询客户编号为111订单列表分页,不根据创建时间倒序
SELECT * FROM order_info WHERE customer_id = 111 AND create_time >= '2023-10-02 09:57:24' AND create_time <= '2023-10-02 10:00:46' LIMIT 3;

在这里插入图片描述

  • 3、查询客户编号为111订单列表分页,根据创建时间倒序
SELECT * FROM order_info WHERE customer_id = 111 AND create_time >= '2023-10-02 09:57:24' AND create_time <= '2023-10-02 10:00:46' ORDER BY create_time DESC LIMIT 3;

在这里插入图片描述

这里三个查询只有第2个查询相对较快一点,耗时110毫秒,其它两个查询耗时基本上都接近500毫秒,从我们给定的条件来看区别就在分页或者不分页,排序或者不排序,一般认为分页肯定比不分页查询要快,但是我们看1和2查询分页比不分页耗时相差接近5倍相差可以说是巨大,然后在查询3中分页但是根据创建时间倒序,这里耗时和查询1相近和查询2耗时相差接近5倍,这其中原理挺有趣的会在下面索引优化实践中举例说明这一些问题。

四、订单常见业务索引优化实践

这里会对一些业务场景举例说明,也会对索引的一些特性做讲解。

4.1、通过唯一索引和普通索引优化通过订单编号查询订单信息

类似通过订单编号查询订单信息的业务有很多,都是通过一个编号信息如客户编号配送员编号查询一个一对一的详情数据,这一类查询都有一个特性编号唯一,并且编号类的数据大多都是字符串类型,这里优化可以考虑唯一索引和普通索引,一般我们会给这一类编号数据设置一个唯一索引,既保证了数据的唯一性也保证了通过编号查询的性能问题。

  • 1、添加唯一索引
ALTER TABLE `order_info` ADD UNIQUE INDEX `idx_orderNo`(`order_no`);
  • 2、添加索引后查询
SELECT * FROM order_info WHERE order_no = 'ON000009999';

在这里插入图片描述
无索引测试的时候耗时0.538s,添加索引后查询性能提升十几倍,数据量越大提升比例越高。

4.2、通过普通联合索引优化订单列表查询

      在上面无索引查询我们列举了查询订单列表的三个例子,查询耗时除了第2个都差不多耗时0.5s,不算太慢但是对MySQL性能开销其实是很大的,如果数据量在大一些到500w 1000w,查询时间也会增加到接受不了数值,所以必须要优化。

4.2.1、分析查询字段的查询场景

      优化前第一要考虑的就是需要一些什么字段,如我们例子中会使用订单编号、客户编号、订单状态、创建时间。

分析通过这四个字段查询的场景:

  • 1、需要根据订单编号查询
  • 2、需要根据客户编号查询
  • 3、需要根据创建时间查询
  • 4、需要根据订单状态查询

这里我给这四个字段分了四个查询场景,为什么这么分我在下面会详细说明。

4.2.2、优化各场景查询和原因分析

对于这种列表查询使用索引一定要知道索引的一个特性就是最左前缀原则,索引的匹配一定是从最左边第一个字段开始匹配的,不能跳过中间字段匹配,在索引优化实践中会详细说明。

4.2.2.1、需要根据订单编号查询

      在一个订单列表如果需要根据订单编号查询,那么一定是要查询一个唯一的订单,如果我们有索引那么我们可以通过一个订单编号快速定位到一条数据不用进行全表扫描,竟然能快速定位到一条数据那么就算还携带别的条件那直接回表取出行数据再去判断即可。
      所以这里只需要创建一个订单编号的索引来适配所有带订单编号的查询,我们在本文的4.1的优化通过订单编号查询订单信息中创建过一个订单编号的唯一索引,我们这里就用这个唯一索引就行。

  • 1、需要根据订单编号查询测试
SELECT * FROM order_info WHERE order_no = 'ON000009999' AND order_status=4;

在这里插入图片描述
执行计划 EXPLAIN
在这里插入图片描述

这里可以看到查询耗时为0.037s,执行计划中使用到了订单编号的索引,扫描估计行数为1。

4.2.2.2、需要根据客户编号查询

      在需要根据客户编号查询的业务中一定是以客户编号为主要条件的,还有可能会携带订单状态,创建时间等,一个客户可能会下很多单,想想自己这些年网购和点外面应该也有个100单以上了把,那么这里就不能像订单编号只用一个单字段索引了,我们需要把订单状态和创建时间也加上,其实就算不加性能也不会差太多因为一个客户订单本来也不会太多,单表几十条数据和几百条数据查询差距不会很大。

  • 1、创建以客户编号起头的普通联合索引
ALTER TABLE `order_info` ADD INDEX `idx_customerId_orderStatus_createTime`(`customer_id`, `order_status`, `create_time`);

我这里会把订单状态和创建时间也带上,某购物APP查询自己订单是不是都有状态选择,时间字段可以用作排序和检索。

  • 2、需要根据客户编号查询测试
SELECT * FROM order_info WHERE customer_id = 111 AND order_status=4 AND create_time >= '2023-10-02 09:57:24' AND create_time <= '2023-10-02 10:00:46';

在这里插入图片描述
执行计划 EXPLAIN
在这里插入图片描述
这里可以看到使用到了我们创建的联合索引,并且三个字段全用到了,客户编号bigint类型不能为空占用8字节,订单状态tinyint不能为空占用1字节,创建时间datetime类型占用5个字节因为创建时间可为空所以多加一个字节,创建时间占用6个字节,合集15个字节和key_len相等。

4.2.2.3、需要根据创建时间查询 和 需要根据订单状态查询

      在查询订单列表时经常会查询某个状态某个时间有多少订单,状态值只有4个如果要通过状态值建立索引的话显然是不可行的,通过状态索引查找某个类型数据可能得到的是几十万行数据,然后还需要回表获取聚簇索引数据,所以对于这种状态值创建单独索引时还需要带上时间字段,在单独查询某个时间内全部订单时也可以使用这个索引,通过in查询将状态值全部包含满足最左前缀原则就能使用该索引查询指定时间段的全部订单。

  • 1、添加索引前查询某个时间段内全部订单
SELECT * FROM order_info WHERE order_status IN (1,2,3,4) AND create_time >= '2023-10-02 09:57:30' AND create_time <= '2023-10-02 09:57:31';

在这里插入图片描述
因为我们是批量插入的时间间隔比较相近1秒有好几千条数据,自己查询测试的时候最好控制区间在2s的样子。

  • 2、创建订单状态和创建时间的普通索引
ALTER TABLE `order_info` ADD INDEX `idx_orderStatus_createTime`(`order_status`,`create_time`);
  • 3、查询某个时间段内全部状态订单
SELECT * FROM order_info WHERE order_status IN (1,2,3,4) AND create_time >= '2023-10-02 09:57:30' AND create_time <= '2023-10-02 09:57:40' ;

在这里插入图片描述
执行计划 EXPLAIN

在这里插入图片描述
我们这里数据是批量插入的,每秒会插入几千条数据,查询时间间隔不能太大了最好在2s的样子,不然索引可能是会失效的,要查询某个状态的订单只有把订单状态的的IN查询换成=查询效果是一样的,满足最左前缀原则即可。

五、索引优化实践

在订单常见业务索引优化实践中简单的介绍了一下在一些场景下创建一些什么索引能提升查询效率,但是还有很多可变因素会影响到索引的使用,也有很多场景可以使用更好的索引,以及索引中很重要的左前缀原则,这里会对一些场景做举例说明。

删除之前创建的索引,创建新的测试联合索引

# 删除上面创建的三个索引,避免测试时被其它索引干扰,要是没有创建则不用删除
ALTER TABLE `order_info` DROP INDEX `idx_orderNo`;
ALTER TABLE `order_info` DROP INDEX `idx_customerId_orderStatus_createTime`;
ALTER TABLE `order_info` DROP INDEX `idx_orderStatus_createTime`;
# 创建新的测试联合索引
ALTER TABLE `order_info` ADD INDEX `idx_goodsTitle_customerId_orderStatus`(`goods_title`, `customer_id`, `order_status`);

4.1 联合索引第一个字段用范围查询可能不会走索引

EXPLAIN SELECT * FROM order_info WHERE goods_title > '笔记本电脑9' AND customer_id = 9 AND order_status = 4;

在这里插入图片描述

  • 给一个区间值
EXPLAIN SELECT * FROM order_info  WHERE goods_title > '笔记本电脑9' AND goods_title < '笔记本电脑99' AND customer_id = 9 AND order_status = 4;

MySQL5.7
在这里插入图片描述

MySQL8.0
在这里插入图片描述

联合索引第一个字段就用范围查找可能不会走索引,对于MySQL5.7来说只要第一个字段用范围查找不会走索引,但是对于MySQL8.0来说给个查询区间还是可能会走索引的,前提是区间也不能太大不然也不会走索引,MySQL内部可能觉得第一个字段用范围,结果集应该很大,回表效率不高,还不如就全表扫描,创建联合索引时千万别把时间这类型字段放第一个了。

4.2 强制走索引(联合索引第一个字段用范围查询不会走索引这里强制使用索引)

EXPLAIN SELECT * FROM order_info FORCE INDEX(idx_goodsTitle_customerId_orderStatus) WHERE goods_title > '笔记本电脑9' AND customer_id = 9 AND order_status = 4;

在这里插入图片描述
这里确认是使用了我们指定的索引,然后再来看看查询性能怎么样

  • 不指定强制走索引
    在这里插入图片描述

  • 指定强制走索引
    在这里插入图片描述

经过对比发现强制走索引查询时间能缩短近10倍,所以有时候MySQL自身并不一定能选择到性能最高的索引使用方式,需要自己不断的尝试对比出最好的方式。

4.3 使用覆盖索引优化查询

EXPLAIN SELECT goods_title,customer_id,order_status FROM order_info WHERE goods_title > '笔记本电脑9' AND customer_id = 9 AND order_status = 4;

在这里插入图片描述
查询结果字段和条件字段都在同一个索引中,查询可以完全使用索引中字段不用回表则称为覆盖索引,覆盖索引因为能避免回表就算联合索引第一个字段范围查询也能走索引。

4.4 in和or在表数据量比较大的情况会走索引,在表记录不多的情况下会选择全表扫描

  • 100w数据表测试
EXPLAIN SELECT * FROM order_info  WHERE goods_title IN('笔记本电脑8','笔记本电脑9') AND customer_id = 9 AND order_status = 4;

在这里插入图片描述

EXPLAIN SELECT * FROM order_info  WHERE (goods_title = '笔记本电脑8' OR goods_title = '笔记本电脑9') AND customer_id = 9 AND order_status = 4;

在这里插入图片描述

  • 根据order_info创建一个只有3条数据order_info_copy表进行测试
    MySQL5.7
    在这里插入图片描述
    在这里插入图片描述
    MySQL8.0
    在这里插入图片描述
    在这里插入图片描述

这里可以看到MySQL5.7中会进行全表扫描,但是MySQL8.0还是会走索引,现在MySQL8.0市场上用的已经比较多了,它执意要走索引肯定有它的道理,而且要通过索引优化查询肯定是要测试比较查询效率的,在实际业务中多测试再看看执行计划,只要实践才知道加的索引是否好用。

4.5 like KK% 一般情况也是可以走索引的

  • like 笔记本电脑999
EXPLAIN SELECT * FROM order_info WHERE goods_title LIKE '笔记本电脑999%' AND customer_id = 9 AND order_status = 4;

在这里插入图片描述

  • like 笔记本电脑9
EXPLAIN SELECT * FROM order_info WHERE goods_title LIKE '笔记本电脑9%' AND customer_id = 9 AND order_status = 4;

在这里插入图片描述

使用like前缀查询在结果集较少的时候是会走索引的,如果MySQL认为结果集较大还是不会走索引,结合以上几个例子可以看出,如果通过索引查询响应结果集过大并且没有满足覆盖索引也很有可能不会走索引,这点MySQL5.0 8.0都是一样的。

4.6 分页查询索引使用和优化

  • 例1:在没有索引且不分页的请求下查询指定客户订单列表数据
EXPLAIN SELECT * FROM order_info WHERE customer_id = 9 ORDER BY goods_title;

-

  • 例2:在没有索引分页查询指定客户订单列表数据
SELECT * FROM order_info WHERE customer_id = 9 LIMIT 0,3;

在这里插入图片描述

SELECT * FROM order_info WHERE customer_id = 9 LIMIT 80,3;

在这里插入图片描述
通过这两个分页查询可以看到查询从第0条开始往后查3条数据执行耗时很短,查询从第80条开始往后查3条数据耗时和不分页差不多,因为MySQL分页查询的时候会根据我们的分页条件找出对应结果集的数据,比如我们分页条件时customer_id = 9 LIMIT 0,3,MySQL会先用customer_id = 9去一条条对比数据,因为我们的分页参数时0,3也就是说找出3条数据即可,只要找到了3条数据就不会往后找了,同理LIMIT 80,3的时候需要找到83条数据就不会往后找了,所以这里我们LIMIT 80,3的时候和不分页耗时差不了多少,不加分页会扫描全表拿出全表的数据。

  • 例3:在没有索引分页查询指定客户订单列表数据并且根据创建时间排序
SELECT * FROM order_info WHERE customer_id = 9 ORDER BY create_time LIMIT 0,3;

在这里插入图片描述
这里加了一个排序条件分页查询后耗时也和不分页差不多,因为如果加了排序字段是需要先扫全表获取全部符合结果的数据才能进行分页。

  • 例4:创建索引分页查询指定客户订单列表数据并且根据创建时间排序(正序)
# 创建索引
ALTER TABLE `order_info` ADD INDEX `idx_customerId_createTime`(`customer_id`, `create_time`);
# 删除索引
ALTER TABLE `order_info` DROP INDEX `idx_customerId_createTime`;
SELECT * FROM order_info WHERE customer_id = 9 ORDER BY create_time LIMIT 0,3;

在这里插入图片描述
执行计划 EXPLAIN
在这里插入图片描述
创建索引后查询快了很多,因为这里是正序的索引默认也是正序的根本不用在进行排序,下面再试试倒序。

  • 例4:创建索引分页查询指定客户订单列表数据并且根据创建时间排序(倒序)
SELECT * FROM order_info WHERE customer_id = 9 ORDER BY create_time DESC LIMIT 0,3;

MySQL 5.7
在这里插入图片描述
MySQL8.0
在这里插入图片描述
查询时间和正序差不多,但是这里MySQL5.7和MySQL8.0的Extra有点区别,MySQL8.0可以反向索引扫描,MySQL5.7应该是将获取到的数据放入内存中排序,也可以创建倒序索引这里不深入。

4.7 order by查询索引使用和优化

  • 例1:
EXPLAIN SELECT * FROM order_info WHERE goods_title = '笔记本电脑9' AND order_status = 4 ORDER BY customer_id;

在这里插入图片描述

利用最左前缀原则:中间字段不能断,因此查询用到了goods_title索引,从key_len=403也能看出,customer_id索引列用在排序过程中,因为Extra字段里没有using filesort,而且索引本来就是正序的无需在排。

  • 列2:
EXPLAIN SELECT * FROM order_info WHERE goods_title = '笔记本电脑9' ORDER BY order_status;

在这里插入图片描述

从explain的执行结果来看:key_len=403,查询使用了goods_title 索引,由于用了order_status进行排序,跳过了
customer_id,出现了Using filesort。

  • 列3:
EXPLAIN SELECT * FROM order_info WHERE goods_title = '笔记本电脑9' ORDER BY customer_id,order_status;

在这里插入图片描述

查找只用到索引goods_title 字段,customer_id和order_status用于排序,无Using filesort。

  • 列4:
EXPLAIN SELECT * FROM order_info WHERE goods_title = '笔记本电脑9' ORDER BY order_status,customer_id;

在这里插入图片描述

和例3中explain的执行结果一样,但是出现了Using filesort,因为索引的创建顺序为goods_title,customer_id,order_status,但是排序的时候customer_id和order_status颠倒位置了。

  • 列5:
EXPLAIN SELECT * FROM order_info WHERE goods_title = '笔记本电脑9' AND customer_id = 9 ORDER BY order_status,customer_id;

在这里插入图片描述

与例4对比,在Extra中并未出现Using filesort,因为customer_id 为常量,在排序中被优化,所以索引未颠倒,不会出现Using filesort。

  • 列6:
EXPLAIN SELECT * FROM order_info WHERE goods_title = '笔记本电脑9' ORDER BY customer_id ASC,order_status DESC;

在这里插入图片描述

虽然排序的字段列与索引顺序一样,且order by默认升序,这里position desc变成了降序,导致与索引的排序方式不同,从而产生Using filesort。

  • 列7:
EXPLAIN SELECT * FROM order_info WHERE goods_title IN ('笔记本电脑9','笔记本电脑10') ORDER BY customer_id,order_status;

在这里插入图片描述
对于排序来说,IN查询也是范围查询,如果排序查询中使用范围查询则索引中只能用到goods_title,后面的字段无法使用。

  • 列8:
EXPLAIN SELECT * FROM order_info WHERE goods_title > '笔记本电脑9' ORDER BY goods_title;

MySQL5.7
在这里插入图片描述

MySQL8.0
在这里插入图片描述
MySQL5.7只要是联合索引第一个字段要走范围查询,那么索引就没法使用,除非覆盖索引,但是在MySQL8.0时却是会去使用索引的,还可以对比一下查询性能,我这里MySQL5.7全表扫描耗时1.3s,MySQL8.0走索引耗时0.8s,两个版本MySQL第一次执行可能时间都需要2s的样子,会将磁盘中的聚簇索引叶子节点数据加载到内存中,多次执行后回表可以直接查内存不用再次读取磁盘。

4.7 总结
  • 1、MySQL支持两种方式的排序filesort和index,Using index是指MySQL扫描索引本身完成排序。index
    效率高,filesort效率低。
  • 2、order by满足两种情况会使用Using index。
    • order by语句使用索引最左前列。
    • 使用where子句与order by子句条件列组合满足索引最左前列。
  • 3、尽量在索引列上完成排序,遵循索引建立(索引创建的顺序)时的最左前缀法则。
  • 4、如果order by的条件不在索引列上,就会产生Using filesort。
  • 5、能用覆盖索引尽量用覆盖索引
  • 6、group by与order by很类似,其实质是先排序后分组,遵照索引创建顺序的最左前缀法则。对于group
    by的优化如果不需要排序的可以加上order by null禁止排序。注意,where高于having,能写在where中
    的限定条件就不要去having限定了。

Using filesort文件排序原理详解

  • filesort文件排序方式
    • 单路排序:是一次性取出满足条件行的所有字段,然后在sort buffer中进行排序;用trace工具可
      以看到sort_mode信息里显示< sort_key, additional_fields >或者< sort_key,
      packed_additional_fields >
    • 双路排序(又叫回表排序模式):是首先根据相应的条件取出相应的排序字段和可以直接定位行
      数据的行 ID,然后在 sort buffer 中进行排序,排序完后需要再次取回其它需要的字段;用trace工具
      可以看到sort_mode信息里显示< sort_key, rowid >
  • MySQL 通过比较系统变量 max_length_for_sort_data(默认1024字节) 的大小和需要查询的字段总大小来
    判断使用哪种排序模式。
    • 如果 字段的总长度小于max_length_for_sort_data ,那么使用 单路排序模式;
    • 如果 字段的总长度大于max_length_for_sort_data ,那么使用 双路排序模式。

六、索引设计原则

  • 1、代码先行,索引后上
    不知大家一般是怎么给数据表建立索引的,是建完表马上就建立索引吗?
    这其实是不对的,一般应该等到主体业务功能开发完毕,把涉及到该表相关sql都要拿出来分析之后再建立
    索引。
  • 2、联合索引尽量覆盖条件
    比如可以设计一个或者两三个联合索引(尽量少建单值索引),让每一个联合索引都尽量去包含sql语句里的
    where、order by、group by的字段,还要确保这些联合索引的字段顺序尽量满足sql查询的最左前缀原
    则。
  • 3、不要在小基数字段上建立索引
    索引基数是指这个字段在表里总共有多少个不同的值,比如一张表总共100万行记录,其中有个性别字段,
    其值不是男就是女,那么该字段的基数就是2。
    如果对这种小基数字段建立索引的话,还不如全表扫描了,因为你的索引树里就包含男和女两种值,根本没
    法进行快速的二分查找,那用索引就没有太大的意义了。
    一般建立索引,尽量使用那些基数比较大的字段,就是值比较多的字段,那么才能发挥出B+树快速二分查
    找的优势来。
  • 4、长字符串我们可以采用前缀索引
    尽量对字段类型较小的列设计索引,比如说什么tinyint之类的,因为字段类型较小的话,占用磁盘空间也会
    比较小,此时你在搜索的时候性能也会比较好一点。
    当然,这个所谓的字段类型小一点的列,也不是绝对的,很多时候你就是要针对varchar(255)这种字段建立
    索引,哪怕多占用一些磁盘空间也是有必要的。
    对于这种varchar(255)的大字段可能会比较占用磁盘空间,可以稍微优化下,比如针对这个字段的前20个
    字符建立索引,就是说,对这个字段里的每个值的前20个字符放在索引树里,类似于 KEY
    index(name(20),age,position)。
    此时你在where条件里搜索的时候,如果是根据name字段来搜索,那么此时就会先到索引树里根据name
    字段的前20个字符去搜索,定位到之后前20个字符的前缀匹配的部分数据之后,再回到聚簇索引提取出来
    完整的name字段值进行比对。
    但是假如你要是order by name,那么此时你的name因为在索引树里仅仅包含了前20个字符,所以这个排
    序是没法用上索引的, group by也是同理。所以这里大家要对前缀索引有一个了解。
  • 5、where与order by冲突时优先where
    在where和order by出现索引设计冲突时,到底是针对where去设计索引,还是针对order by设计索引?到
    底是让where去用上索引,还是让order by用上索引?
    一般这种时候往往都是让where条件去使用索引来快速筛选出来一部分指定的数据,接着再进行排序。
    因为大多数情况基于索引进行where筛选往往可以最快速度筛选出你要的少部分数据,然后做排序的成本可
    能会小很多。
  • 6、基于慢sql查询做优化
    可以根据监控后台的一些慢sql,针对这些慢sql查询做特定的索引优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1057636.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构】HashSet的底层数据结构

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 HashSet 一、 HashSet 集合的底层数据结构二…

GraphPad Prism 10 for Mac(统计分析绘图软件)

GraphPad Prism是一款专业的统计和绘图软件&#xff0c;主要用于生物医学研究、实验设计和数据分析。 以下是 GraphPad Prism 的主要功能和特点&#xff1a; 数据导入和整理&#xff1a;GraphPad Prism 可以导入各种数据格式&#xff0c;并提供直观的界面用于整理、编辑和管理数…

RFID与人工智能的融合:物联网时代的智能化变革

随着物联网技术的不断发展&#xff0c;现实世界与数字世界的桥梁已经被打通。物联网通过各种传感器&#xff0c;将现实世界中的光、电、热等信号转化为有价值的数据。这些数据可以通过RFID技术进行自动收集和传输&#xff0c;然后经由人工智能算法进行分析、建模和预测&#xf…

【LeetCode算法系列题解】第76~80题

CONTENTS LeetCode 76. 最小覆盖子串&#xff08;困难&#xff09;LeetCode 77. 组合&#xff08;中等&#xff09;LeetCode 78. 子集&#xff08;中等&#xff09;LeetCode 79. 单词搜索&#xff08;中等&#xff09; LeetCode 76. 最小覆盖子串&#xff08;困难&#xff09; …

Java下正面解除警告Unchecked cast: ‘java.lang.Object‘ to ‘java.util.ArrayList‘

就是我在反序列化时&#xff0c;遇到这样一个警告&#xff1a; Unchecked cast: java.lang.Object to java.util.ArrayList<com.work1.Student>然后我去网上查&#xff0c;有些人说用SuppressWarnings(“unchecked”)去忽略警告&#xff0c;但是我觉得作为一名合格的程序…

SNERT预备队招新CTF体验赛-Web(SWCTF)

目录 1、F12 2、robots 3、game1-喂青蛙 4、game 2 - flap bird 5、game 3 - Clash 6、Get&Post 7、sql &#xff08;1&#xff09;手工注入 &#xff08;2&#xff09;工具注入 8、命令执行漏洞 9、文件上传漏洞 10、文件泄露 11、php反序列化漏洞 12、PHP绕…

【网络编程】UDP数据报套接字编程和TCP流套接字编程

文章目录 1. 网络编程基础1.1 为什么需要网络编程&#xff1f;1.2 网络编程是什么&#xff1f;1.3 概念 2. Socket套接字3. UDP数据报套接字编程3.1 DatagramSocket API3.2 DatagramPacket API3.3 InetSocketAddress API 4. UDP构建服务端客户端&#xff08;一发一收&#xff0…

QSS之QComboBox

QComboBox在Qt开发过程中经常使用&#xff0c;默认的下载列表风格达不到设计师的要求&#xff0c;本篇介绍基本的QComboBox的qss设置。 属性意思QComboBoxQComboBox基本样式QComboBox:editable右边可选择按钮QComboBox:!editable, QComboBox::drop-down:editable不可编辑或下拉…

Python中匹配模糊的字符串

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 如何使用thefuzz 库&#xff0c;它允许我们在python中进行模糊字符串匹配。 此外&#xff0c;我们将学习如何使用process 模块&#xff0c;该模块允许我们在模糊…

离散数学 学习 之 5.3 一阶逻辑的推理理论

第一个证明中&#xff0c;最后三步的化简很重要&#xff0c;倒数第三步构造出一个可以化简出倒数第二步的公式&#xff0c;最后再化简 上面中的第 1&#xff0c; 2 步 和 3 &#xff0c; 4 步不能换&#xff0c;因为无法保证是同一个 c 尽量弄成前束范式 上面中2&#xff0c;3&…

无状态自动配置 DHCPv6无状态配置 DHCPv6有状态配置

1、无状态自动配置 配置命令 AR1 ipv6 #开启路由器ipv6报文转发功能 interface GigabitEthernet0/0/0 ipv6 enable #开启路由器接口IPv6报文转发功能 ipv6 address FC01::1/64 …

对比两个Series序列中的元素是否不相等,并以Series格式返回结果

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 逐一对比两个Series序列中 元素是否不相等&#xff0c;将结果 以Series格式返回 [太阳]选择题 关于以下代码的说法中正确的是? import pandas as pd a pd.Series([0,1,2],index["x"…

stm32 - 中断/定时器

stm32 - 中断/定时器 概念时钟树定时器类型基准时钟&#xff08;系统时钟&#xff09;预分频器 - 时基单元CNT计数器 - 时基单元自动重装寄存器 - 时基单元基本定时器结构通用定时器计数器模式内外时钟源选择 定时中断基本结构时序预分频器时序计数器时序 概念 时钟树 https:…

vue重修004上部

文章目录 版权声明组件的三大组成部分scoped解决样式冲突scoped原理2.代码演示 组件data函数说明演示 组件通信组件关系分类通信解决方案父子通信流程子向父通信代 props详解props校验props&data、单向数据流 小黑记事本&#xff08;组件版&#xff09;基础组件结构需求和实…

【AI处理器组合】python实现-附ChatGPT解析

1.题目 AI处理器组合 知识点数组 时间限制:1s 空间限制: 256MB 限定语言:不限 题目描述: 某公司研发了一款高性能AI处理器。每台物理设备具备8颗AI处理器,编号分别为0、1、2、3、4、5、6、7。编号0-3的处理器处于同一个链路中,编号4-7的处理器处于另外一个链路中,不通链路中…

【计算机网络笔记九】I/O 多路复用

阻塞 IO 和 非阻塞 IO 阻塞 I/O 和 非阻塞 I/O 的主要区别&#xff1a; 阻塞 I/O 执行用户程序操作是同步的&#xff0c;调用线程会被阻塞挂起&#xff0c;会一直等待内核的 I/O 操作完成才返回用户进程&#xff0c;唤醒挂起线程非阻塞 I/O 执行用户程序操作是异步的&#xf…

阿里云PolarDB数据库详细介绍_3分钟看懂

阿里云PolarDB数据库是阿里巴巴自研的关系型分布式云原生数据库&#xff0c;PolarDB兼容三种数据库引擎&#xff1a;MySQL、PostgreSQL、Oracle&#xff08;语法兼容&#xff09;&#xff0c;目前提供云原生数据库PolarDB MySQL版、云原生数据库PolarDB PostgreSQL版和云原生数…

简单聊聊 TCP 协议

简单聊聊 TCP 协议 如何实现可靠传输 ?完全可靠存在比特差错存在丢包流水线可靠数据传输协议回退N步 (GBN)选择重传 (ARQ) 小结 TCPTCP 连接报文段结构序号和确认号 可靠数据传输避免重传超时时间加倍快速重传回退N步还是选择重传 流量控制连接管理拥塞控制拥塞原因拥塞控制方…

国庆10.03

运算符重载 代码 #include <iostream> using namespace std; class Num { private:int num1; //实部int num2; //虚部 public:Num(){}; //无参构造Num(int n1,int n2):num1(n1),num2(n2){}; //有参构造~Num(){}; //析构函数const Num operator(const Num &other)co…

想用ChatGPT写申请文书?那你肯定会被拒!

美国总统乔拜登&#xff08;Joe Biden&#xff09;、诗人TS艾略特&#xff08;T. S. Eliot&#xff09;和历史学家斯蒂芬安布罗斯&#xff08;Stephen Ambrose&#xff09;&#xff0c;他们的名字都曾与抄袭事件联系在一起。 其中&#xff0c;艾略特的抄袭行为是在他去世后才被…