范围查询 range级别 继续优化思路

问题:

这几天工作遇到了一个问题。千万级别的表,每秒钟产生很多数据,select count(id) from table where flag = 1 and create_time < 2023.11.07;分区表,range级别,已经是走create_time列上的索引,flag的值只有0,1。厂商业务卡死在这条sql语句。有什么办法还能再通过什么手段优化吗。大家不妨想一想。

初看已经走索引,range级别好像已经够优秀了,但是执行计划产生了回表,看查找的列不是select *。很好我们可以通过创建联合索引避免回表。

create index inx_caf on table(create_time,flag);

实验1(name的索引选择性还不错),验证具体有没有优化:

-- 创建结构相似的表
create table lian(id int primary key,name varchar(10),flag varchar(10),unique key inx_name(name),key inx_flag(flag)
)
-- 创建存储过程,插入flag分别为0,1的数据
delimiter $$  # 定义结束符
drop procedure if exists addTestData; # 存储过程名叫:addTestData
create procedure addTestData()
begin
declare number int;
set number = 20001;
while number <= 30000 #插入N条数据
do
insert into lian
values(null,number+'1',0);  # 为了区分姓名,我们加上后缀
set number = number + 1;
end
while;
end $$;
select count(*) from lian;
select count(*) from lian where flag = 0;
select count(*) from lian where flag = 1;

已知:现在表中name列和flag列分别有单列索引,我们按找卡死的sql语句运行。

explain
select count(id) from lian   where flag = 1 and name < '1000';

explain format=json
select count(id) from lian   where flag = 1 and name < '1000';
{"query_block": {"select_id": 1,"cost_info": {"query_cost": "5.21"},"table": {"table_name": "lian","access_type": "range","possible_keys": ["inx_name","inx_flag"],"key": "inx_name","used_key_parts": ["name"],"key_length": "13","rows_examined_per_scan": 3,"rows_produced_per_join": 0,"filtered": "10.00","index_condition": "(`test2`.`lian`.`name` < '1000')","cost_info": {"read_cost": "5.15","eval_cost": "0.06","prefix_cost": "5.21","data_read_per_join": "9"},"used_columns": ["id","name","flag"],"attached_condition": "(`test2`.`lian`.`flag` = 1)"}}
}

可以看到这就如厂商卡到的sql语句一样,并且范围查询如果 范围过大的话,执行计划就会变成全表扫描。执行代价5.21

创建联合索引:

create index inx_naf on lian(name,flag);

可以看到联合索引消除了回表操作。让我们看看执行代价:

{"query_block": {"select_id": 1,"cost_info": {"query_cost": "2.22"},"table": {"table_name": "lian","access_type": "range","possible_keys": ["inx_name","inx_flag","inx_naf"],"key": "inx_naf","used_key_parts": ["name"],"key_length": "13","rows_examined_per_scan": 3,"rows_produced_per_join": 0,"filtered": "10.00","using_index": true,"cost_info": {"read_cost": "2.16","eval_cost": "0.06","prefix_cost": "2.22","data_read_per_join": "9"},"used_columns": ["id","name","flag"],"attached_condition": "((`test2`.`lian`.`flag` = 1) and (`test2`.`lian`.`name` < '1000'))"}}
}

2.22比之走单列索引的5.21小了很多(我后续做了实验,小这么多的原因是因为name列是唯一约束,或者说name列的唯一值特别多。后续也做了唯一值特别少的代价实验,请往后看)

联合索引的考量

众所周知联合索引设在前谁在后是有考量的,规则就是谁的选择性好(相对来说唯一值多)谁就放在前面 ,所以我先入为主就把create_time放在了前面。让我们测试下flag在前面的情况。

-- 还没删除naf
create index inx_fan on lian(flag,name);
explain 
select count(id) from lian   where flag = 1 and name < '1000';
explain format=json
select count(id) from lian   where flag = 1 and name < '1000';

 可以看到在两个索引上还是选择了naf。

删除naf:

 优化器没有选择fan,而是选择了最初的单列索引,产生回表。

让我们强制走fan,看看执行代价:

explain format=json
select count(id) from lian force index(inx_fan) where flag = 1 and name < '1000';explain
select count(id) from lian force index(inx_fan) where flag = 1 and name < '1000';

 变成了索引树扫描而非range。

{"query_block": {"select_id": 1,"cost_info": {"query_cost": "35755.00"},"table": {"table_name": "lian","access_type": "index","possible_keys": ["inx_fan"],"key": "inx_fan","used_key_parts": ["flag","name"],"key_length": "26","rows_examined_per_scan": 29795,"rows_produced_per_join": 993,"filtered": "3.33","using_index": true,"cost_info": {"read_cost": "35556.39","eval_cost": "198.61","prefix_cost": "35755.00","data_read_per_join": "31K"},"used_columns": ["id","name","flag"],"attached_condition": "((`test2`.`lian`.`flag` = 1) and (`test2`.`lian`.`name` < '1000'))"}}
}

 执行代价35755特别大,所以联合索引不可以把flag放在最前面,索引选择性特别低。

实验2(name列的索引选择度也特别低)

create table lian3(id int primary key,name varchar(10),flag varchar(10),key inx_flag(flag)
)delimiter $$  # 定义结束符
drop procedure if exists addTestData; # 存储过程名叫:addTestData
create procedure addTestData()
begin
declare number int;
set number = 40001;
while number <= 45000 #插入N条数据
do
insert into lian3
values(null,'h',0);  # 为了区分姓名,我们加上后缀
set number = number + 1;
end
while;
end $$;call addTestData();

分别看name和flag的索引选择度


select count(distinct(name))/count(*),count(distinct(name)),count(*) from lian3;
select count(distinct(flag))/count(*),count(distinct(flag)),count(*) from lian3;

 

 

explain
select count(id) from lian3  where flag = 1 and name < 'e';
explain format=json
select count(id) from lian3  where flag = 1 and name < 'e';

 全表扫描执行代价:

{"query_block": {"select_id": 1,"cost_info": {"query_cost": "6035.00"},"table": {"table_name": "lian3","access_type": "ALL","possible_keys": ["inx_flag"],"rows_examined_per_scan": 29690,"rows_produced_per_join": 989,"filtered": "3.33","cost_info": {"read_cost": "5837.09","eval_cost": "197.91","prefix_cost": "6035.00","data_read_per_join": "30K"},"used_columns": ["id","name","flag"],"attached_condition": "((`test2`.`lian3`.`flag` = 1) and (`test2`.`lian3`.`name` < 'e'))"}}
}

创建name单列索引,执行计划:

create index inx_name on lian3(name);

 因为name的选择度特别低,所以必须强制走索引

explain
select count(id) from lian3 force index(inx_name)  where flag = 1 and name < 'e';explain format=json
select count(id) from lian3 force index(inx_name)  where flag = 1 and name < 'e';

{"query_block": {"select_id": 1,"cost_info": {"query_cost": "20784.01"},"table": {"table_name": "lian3","access_type": "range","possible_keys": ["inx_name"],"key": "inx_name","used_key_parts": ["name"],"key_length": "13","rows_examined_per_scan": 14845,"rows_produced_per_join": 1484,"filtered": "10.00","index_condition": "(`test2`.`lian3`.`name` < 'e')","cost_info": {"read_cost": "20487.11","eval_cost": "296.90","prefix_cost": "20784.01","data_read_per_join": "46K"},"used_columns": ["id","name","flag"],"attached_condition": "(`test2`.`lian3`.`flag` = 1)"}}
}

创建联合索引naf,执行代价:

create index inx_naf on lian3(name,flag);

{"query_block": {"select_id": 1,"cost_info": {"query_cost": "5993.18"},"table": {"table_name": "lian3","access_type": "range","possible_keys": ["inx_flag","inx_name","inx_naf"],"key": "inx_naf","used_key_parts": ["name"],"key_length": "13","rows_examined_per_scan": 14845,"rows_produced_per_join": 1484,"filtered": "10.00","using_index": true,"cost_info": {"read_cost": "5696.29","eval_cost": "296.90","prefix_cost": "5993.19","data_read_per_join": "46K"},"used_columns": ["id","name","flag"],"attached_condition": "((`test2`.`lian3`.`flag` = 1) and (`test2`.`lian3`.`name` < 'e'))"}}
}

创建联合索引fan,并且删除naf,执行代价:

create index inx_fan on lian3(flag,name);
drop index inx_naf on lian3;

{"query_block": {"select_id": 1,"cost_info": {"query_cost": "6035.00"},"table": {"table_name": "lian3","access_type": "index","possible_keys": ["inx_flag","inx_name","inx_fan"],"key": "inx_fan","used_key_parts": ["flag","name"],"key_length": "26","rows_examined_per_scan": 29690,"rows_produced_per_join": 1484,"filtered": "5.00","using_index": true,"cost_info": {"read_cost": "5738.10","eval_cost": "296.90","prefix_cost": "6035.00","data_read_per_join": "46K"},"used_columns": ["id","name","flag"],"attached_condition": "((`test2`.`lian3`.`flag` = 1) and (`test2`.`lian3`.`name` < 'e'))"}}
}

实验2总结:

全表扫描:6035

单列name索引:20784

naf:5993.18

fan:6035

可以看到naf和fan相差不大,这和name列的选择度低有关,但是就算选择度低,naf也是最优解,所以选择naf是最好的。

实验3(flag列选择性好):

这就根本不用做实验了,flag等值查询,选择性还好肯定放在联合索引的左侧。

综上所述,不管是什么做实验时硬道理,实践出真理。也要直到range级别了也可以继续优化,优化的一个思路就是创建联合索引避免回表。

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

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

相关文章

springboot宠物店管理系统-计算机毕设 附源码 32041

SpringBoot宠物店管理系统 摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;宠物行业当然也不例外。宠物店管理系统是以实际运用为开发背景&#xff0c;运用软件工程原理…

多级嵌套vue同步调用用法

//需求 要求同步调用initGame2方法 //调用方法 this.initSocket(); //定义方法为同步 async initSocket() { //调用为同步 await this.initGame2(); //定义方法为同步 async initGame2() {const e await w({ url: //定义w方法 const w e.create({ baseURL: http://my_url:8…

python pdf转txt文本、pdf转json

文章目录 一、前言二、实现方法1. 目录结构2. 代码 一、前言 此方法只能转文本格式的pdf&#xff0c;如果是图片格式的pdf需要用到ocr包&#xff0c;以后如果有这方面需求再加这个方法 二、实现方法 1. 目录结构 2. 代码 pdf2txt.py 代码如下 #!/usr/bin/env python # -*- …

H5ke12--3--iframe--编辑邮箱的制作

下面我们来window.iframes[] frames是一个全局变量&#xff0c;它是一个对象数组&#xff0c;其中包含当前窗口中的所有框架&#xff08;如果存在&#xff09;。 在这段代码中&#xff0c;let frameframes[0];是将第一个框架赋值给变量frame。通过frame.document.designMode&q…

【ArcGIS Pro微课1000例】0037:ArcGIS Pro中模型构建器的使用---以shp批量转kml/kmz为例

文章目录 一、ArcGIS Pro模型构建器介绍二、shp批量转kml/kmz1. 打开模型构建器2. 添加工作空间4. 添加【创建要素图层】工具5. 添加【图层转kml】工具6. 输出文件命名7. 运行模型三、模型另存为1.py文件2. 保存为工具一、ArcGIS Pro模型构建器介绍 模型构建器是一种可视化编程…

感冒的六大经方2

3 葛根汤 处方内容是&#xff1a;葛根15克&#xff0c;麻黄5克&#xff0c;桂枝10克&#xff0c;白芍10克&#xff0c;生薑二片&#xff0c; 炙甘草10克&#xff0c;大枣十枚 每付药加入六碗水使用大火来煮成二碗&#xff0c;成人于每三小时空腹时喝一碗&#xff0c;小孩减半…

C语言从入门到精通之【表达式和语句】

1 表达式 表达式由运算符和运算对象组成&#xff0c;最简单的表达式一个单独的运算对象。每个表达式都有一个值&#xff0c;并且是根据运算符优先级规定的顺序来执行&#xff0c;以下是一些表达式&#xff1a; 4 -6 421 a*(b c/d)/20 q 5*2 x q % 3 #q > 3 2 语句 语句…

yolov5从英伟达平台移植到华为昇腾开发板上的思路

作者&#xff1a;朱金灿 来源&#xff1a;clever101的专栏 为什么大多数人学不会人工智能编程&#xff1f;>>> 最近需要将yolov5代码从英伟达平台移植到华为昇腾开发板上。搜了一些代码和资料&#xff0c;大致明白了二者的差别。 1.二者使用的模型文件不一样 yolov…

【unity实战】实现一个放置3d物品建造装修系统(附项目源码)

文章目录 最终效果前言绘制开始场景素材开始放置旋转物体扩展优化1. 绘制地图边界&#xff0c;确保放置物品在指定区域内工作2. 让模型所占面积大小更加准确3. 隐藏白色瓦片指示区域 最终效果其他源码参考完结 最终效果 前言 其实3d物品建造装修系统之前就已经做过了&#xff…

2024年天津天狮学院食品质量与安全专业《普通化学》考试大纲

2024年天津天狮学院食品质量与安全专业高职升本入学考试《普通化学》考试大纲 一、考试性质 《普通化学》专业课程考试是天津天狮学院食品质量与安全专业高职升本入学考试 的必考科目之一&#xff0c;其性质是考核学生是否达到了升入本科继续学习的要求而进行的选拔性考试。《…

位姿变换线性变换与坐标变换

位姿,线性变换与坐标变换.yuque 内旋,外旋, 左乘,右乘很容易把人绕晕是因为缺少一种科学的符号。 自己的符号,如果经常变不稳定,对推理和理解都很不利。 一旦问题复杂起来, 直观和直觉靠不住, 只能靠代数符号来推理 约定 P1: A,B,C…是空间中的标准正交基 P2&#xff1a; [a1,…

GZ031 应用软件系统开发赛题第6套

2023年全国职业院校技能大赛 应用软件系统开发赛项&#xff08;高职组&#xff09; 赛题第6套 工位号&#xff1a; 2023年4月 竞赛说明 一、项目背景 党的二十大报告指出&#xff0c;要加快建设制造强国、数字中国&#xff0c;推动制造业高端化、智能化、…

kafka入门(二): 位移提交

位移提交&#xff1a; Kafka的每条消息都有唯一的 offset&#xff0c; 用来表示消息在分区中对应的位置。有的也称之为 “偏移量”。 消费者每次在 poll() 拉取消息&#xff0c;它要返回的是还没有消费过的消息集&#xff0c; 因此&#xff0c;需要记录上一次消费时的消费位…

[计算机网络]运输层概述

虽然我自己也不知道写在前面和前言有什么区别..... 这个系列其实是针对<深入浅出计算机网络>的简单总结,加入了一点个人的理解和浅薄见识,如果您有一些更好的意见和见解,欢迎随时协助我改正,感激不尽啦. 最近心态平和了不少, 和过去也完全做了个割舍吧,既然痛苦和压力的…

记录华为云服务器(Linux 可视化 宝塔面板)-- 安全组篇

文章目录 前言安全组说明安全组的特性安全组的应用场景 进入安全组添加基本规则添加自定义规则如有启发&#xff0c;可点赞收藏哟~ 前言 和windows防火墙类似&#xff0c;安全组是一种虚拟防火墙&#xff0c;具备状态检测和数据包过滤功能&#xff0c;可以对进出云服务器的流量…

typeof,instanceof

1.typeof typeof运算符返回的结果是以小写的字符串表示的变量的类型 2.instanceof instanceof运算符用于判断右边构造函数的原型对象是否在左边对象的原型链上 let arr[]let obj{}let datenew Dateconsole.log(arr instanceof Array)console.log(arr instanceof Object)conso…

Maven 简单配置阿里云镜像

配置步骤&#xff1a; 1、找到 maven 的安装目录&#xff0c;修改settings.xml 2、在文件中找到<mirrors>标签&#xff0c;然后再标签中添加阿里云配置即可 <mirror><id>aliyunmaven</id><mirrorOf>*</mirrorOf><name>阿里云公共…

CocosCreator 面试题(十五)Cocos Creator如何内置protobuf JS版本?

一、说说protobuf 是什么&#xff1f; Protocol Buffers&#xff08;简称为ProtoBuf&#xff09;是一种由Google开发的数据序列化格式。它是一种轻量级、高效且通用的数据交换格式&#xff0c;可用于各种编程语言和平台。 ProtoBuf使用结构化的消息定义语言&#xff08;IDL&a…

巧妙之中见真章:深入解析常用的创建型设计模式

设计模式之创建型设计模式详解 一、设计模式是什么&#xff1f;二、模板方法2.1、代码结构2.2、符合的设计原则2.3、如何扩展代码2.4、小结 三、观察者模式3.1、代码结构3.2、符合的设计原则3.3、如何扩展代码3.4、小结 四、策略模式4.1、代码结构4.2、符合的设计原则4.3、如何…