现象

新建了一张员工表,插入了少量数据,索引中所有的字段均在where条件出现时,正确走到了idx_nap索引,但是where出现部分自左开始的索引时,却进行全表扫描,与MySQL官方所说的最左匹配原则“相悖”。

数据背景

CREATE TABLE `staffs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
`age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
`pos` varchar(20) NOT NULL DEFAULT '' COMMENT '职位',
`add_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
PRIMARY KEY (`id`),
KEY `idx_nap` (`name`,`age`,`pos`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COMMENT='员工记录表'; 表中数据如下:
id name age pos add_time
1 July 23 dev 2018-06-04 16:02:02
2 Clive 22 dev 2018-06-04 16:02:32
3 Cleva 24 test 2018-06-04 16:02:38
4 July 23 test 2018-06-04 16:12:22
5 July 23 pre 2018-06-04 16:12:37
6 Clive 22 pre 2018-06-04 16:12:48
7 July 25 dev 2018-06-04 16:30:17

Explain语句看下执行计划

-- 全匹配走了索引
explain select * from staffs where name = 'July' and age = 23 and pos = 'dev';
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE staffs NULL ref idx_nap idx_nap 140 const,const,const 1 100.00 NULL

开启优化器跟踪优化过程

-- 左侧部分匹配却没有走索引,全表扫描
explain select * from staffs where name = 'July' and age = 23;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE staffs2 NULL ALL idx_nap NULL NULL NULL 6 50.00 Using where
-- 开启优化器跟踪
set session optimizer_trace='enabled=on';
-- 在执行完查询语句后,在执行以下的select语句可以查看具体的优化器执行过程
select * from information_schema.optimizer_trace;

Trace部分的内容

{
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select `staffs`.`id` AS `id`,`staffs`.`name` AS `name`,`staffs`.`age` AS `age`,`staffs`.`pos` AS `pos`,`staffs`.`add_time` AS `add_time` from `staffs` where ((`staffs`.`name` = 'July') and (`staffs`.`age` = 23))"
}
]
}
},
{
"join_optimization": {
"select#": 1,
"steps": [
{
"condition_processing": {
"condition": "WHERE",
"original_condition": "((`staffs`.`name` = 'July') and (`staffs`.`age` = 23))",
"steps": [
{
"transformation": "equality_propagation",
"resulting_condition": "((`staffs`.`name` = 'July') and multiple equal(23, `staffs`.`age`))"
},
{
"transformation": "constant_propagation",
"resulting_condition": "((`staffs`.`name` = 'July') and multiple equal(23, `staffs`.`age`))"
},
{
"transformation": "trivial_condition_removal",
"resulting_condition": "((`staffs`.`name` = 'July') and multiple equal(23, `staffs`.`age`))"
}
]
}
},
{
"substitute_generated_columns": {
}
},
{
"table_dependencies": [
{
"table": "`staffs`",
"row_may_be_null": false,
"map_bit": 0,
"depends_on_map_bits": [
]
}
]
},
{
"ref_optimizer_key_uses": [
{
"table": "`staffs`",
"field": "name",
"equals": "'July'",
"null_rejecting": false
},
{
"table": "`staffs`",
"field": "age",
"equals": "23",
"null_rejecting": false
}
]
},
{
"rows_estimation": [
{
"table": "`staffs`",
"range_analysis": {
"table_scan": {
"rows": 6,
"cost": 4.3
},
"potential_range_indexes": [
{
"index": "PRIMARY",
"usable": false,
"cause": "not_applicable"
},
{
"index": "idx_nap",
"usable": true,
"key_parts": [
"name",
"age",
"pos",
"id"
]
}
],
"setup_range_conditions": [
],
"group_index_range": {
"chosen": false,
"cause": "not_group_by_or_distinct"
},
"analyzing_range_alternatives": {
"range_scan_alternatives": [
{
"index": "idx_nap",
"ranges": [
"July <= name <= July AND 23 <= age <= 23"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"rows": 3,
"cost": 4.61,
"chosen": false,
"cause": "cost"
}
],
"analyzing_roworder_intersect": {
"usable": false,
"cause": "too_few_roworder_scans"
}
}
}
}
]
},
{
"considered_execution_plans": [
{
"plan_prefix": [
],
"table": "`staffs`",
"best_access_path": {
"considered_access_paths": [
{
//可以看到这边MySQL计算得到使用索引的成本为2.6
"access_type": "ref",
"index": "idx_nap",
"rows": 3,
"cost": 2.6,
"chosen": true
},
{
//而全表扫描计算所得的成本为2.2
"rows_to_scan": 6,
"access_type": "scan",
"resulting_rows": 6,
"cost": 2.2,
"chosen": true
}
]
},
//因此选择了成本更低的scan
"condition_filtering_pct": 100,
"rows_for_plan": 6,
"cost_for_plan": 2.2,
"chosen": true
}
]
},
{
"attaching_conditions_to_tables": {
"original_condition": "((`staffs`.`age` = 23) and (`staffs`.`name` = 'July'))",
"attached_conditions_computation": [
],
"attached_conditions_summary": [
{
"table": "`staffs`",
"attached": "((`staffs`.`age` = 23) and (`staffs`.`name` = 'July'))"
}
]
}
},
{
"refine_plan": [
{
"table": "`staffs`"
}
]
}
]
}
},
{
"join_execution": {
"select#": 1,
"steps": [
]
}
}
]
}

增加表数据量

-- 接下来增大表的数据量
INSERT INTO `staffs` (`name`, `age`, `pos`, `add_time`)
VALUES
('July', 25, 'dev', '2018-06-04 16:30:17'),
('July', 23, 'dev1', '2018-06-04 16:02:02'),
('July', 23, 'dev2', '2018-06-04 16:02:02'),
('July', 23, 'dev3', '2018-06-04 16:02:02'),
('July', 23, 'dev4', '2018-06-04 16:02:02'),
('July', 23, 'dev6', '2018-06-04 16:02:02'),
('July', 23, 'dev5', '2018-06-04 16:02:02'),
('July', 23, 'dev7', '2018-06-04 16:02:02'),
('July', 23, 'dev8', '2018-06-04 16:02:02'),
('July', 23, 'dev9', '2018-06-04 16:02:02'),
('July', 23, 'dev10', '2018-06-04 16:02:02'),
('Clive', 23, 'dev1', '2018-06-04 16:02:02'),
('Clive', 23, 'dev2', '2018-06-04 16:02:02'),
('Clive', 23, 'dev3', '2018-06-04 16:02:02'),
('Clive', 23, 'dev4', '2018-06-04 16:02:02'),
('Clive', 23, 'dev6', '2018-06-04 16:02:02'),
('Clive', 23, 'dev5', '2018-06-04 16:02:02'),
('Clive', 23, 'dev7', '2018-06-04 16:02:02'),
('Clive', 23, 'dev8', '2018-06-04 16:02:02'),
('Clive', 23, 'dev9', '2018-06-04 16:02:02'),
('Clive', 23, 'dev10', '2018-06-04 16:02:02');

执行Explain

-- 再次执行同样的查询语句,会发现走到索引上了
explain select * from staffs where name = 'July' and age = 23;
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE staffs NULL ref idx_nap idx_nap 78 const,const 13 100.00 NULL

查看新的Trace内容

-- 再看下优化器执行过程
{
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select `staffs`.`id` AS `id`,`staffs`.`name` AS `name`,`staffs`.`age` AS `age`,`staffs`.`pos` AS `pos`,`staffs`.`add_time` AS `add_time` from `staffs` where ((`staffs`.`name` = 'July') and (`staffs`.`age` = 23))"
}
]
}
},
{
"join_optimization": {
"select#": 1,
"steps": [
{
"condition_processing": {
"condition": "WHERE",
"original_condition": "((`staffs`.`name` = 'July') and (`staffs`.`age` = 23))",
"steps": [
{
"transformation": "equality_propagation",
"resulting_condition": "((`staffs`.`name` = 'July') and multiple equal(23, `staffs`.`age`))"
},
{
"transformation": "constant_propagation",
"resulting_condition": "((`staffs`.`name` = 'July') and multiple equal(23, `staffs`.`age`))"
},
{
"transformation": "trivial_condition_removal",
"resulting_condition": "((`staffs`.`name` = 'July') and multiple equal(23, `staffs`.`age`))"
}
]
}
},
{
"substitute_generated_columns": {
}
},
{
"table_dependencies": [
{
"table": "`staffs`",
"row_may_be_null": false,
"map_bit": 0,
"depends_on_map_bits": [
]
}
]
},
{
"ref_optimizer_key_uses": [
{
"table": "`staffs`",
"field": "name",
"equals": "'July'",
"null_rejecting": false
},
{
"table": "`staffs`",
"field": "age",
"equals": "23",
"null_rejecting": false
}
]
},
{
"rows_estimation": [
{
"table": "`staffs`",
"range_analysis": {
"table_scan": {
"rows": 27,
"cost": 8.5
},
"potential_range_indexes": [
{
"index": "PRIMARY",
"usable": false,
"cause": "not_applicable"
},
{
"index": "idx_nap",
"usable": true,
"key_parts": [
"name",
"age",
"pos",
"id"
]
}
],
"setup_range_conditions": [
],
"group_index_range": {
"chosen": false,
"cause": "not_group_by_or_distinct"
},
"analyzing_range_alternatives": {
"range_scan_alternatives": [
{
"index": "idx_nap",
"ranges": [
"July <= name <= July AND 23 <= age <= 23"
],
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": false,
"rows": 13,
"cost": 16.61,
"chosen": false,
"cause": "cost"
}
],
"analyzing_roworder_intersect": {
"usable": false,
"cause": "too_few_roworder_scans"
}
}
}
}
]
},
{
"considered_execution_plans": [
{
"plan_prefix": [
],
"table": "`staffs`",
"best_access_path": {
"considered_access_paths": [
{
//使用索引的成本变为了5.3
"access_type": "ref",
"index": "idx_nap",
"rows": 13,
"cost": 5.3,
"chosen": true
},
{
//scan的成本变为了6.4
"rows_to_scan": 27,
"access_type": "scan",
"resulting_rows": 27,
"cost": 6.4,
"chosen": false
}
]
},
//使用索引查询的成本更低,因此选择了走索引
"condition_filtering_pct": 100,
"rows_for_plan": 13,
"cost_for_plan": 5.3,
"chosen": true
}
]
},
{
"attaching_conditions_to_tables": {
"original_condition": "((`staffs`.`age` = 23) and (`staffs`.`name` = 'July'))",
"attached_conditions_computation": [
],
"attached_conditions_summary": [
{
"table": "`staffs`",
"attached": null
}
]
}
},
{
"refine_plan": [
{
"table": "`staffs`"
}
]
}
]
}
},
{
"join_execution": {
"select#": 1,
"steps": [
]
}
}
]
}

结论

MySQL表数据量的大小,会影响索引的选择,具体的情况还是通过Explain和Optimizer Trace来查看与分析。

表数据量影响MySQL索引选择的更多相关文章

  1. 单表扫描,MySQL索引选择不正确 并 详细解析OPTIMIZER_TRACE格式

    单表扫描,MySQL索引选择不正确 并 详细解析OPTIMIZER_TRACE格式     一 表结构如下:  万行 CREATE TABLE t_audit_operate_log (  Fid b ...

  2. SOME:收缩数据库日志文件,查看表数据量和空间占用,查看表结构索引修改时间

    ---收缩数据库日志文件 USE [master]ALTER DATABASE yourdatabasename SET RECOVERY SIMPLE WITH NO_WAITALTER DATAB ...

  3. MySQL索引选择及添加原则

    索引选择性就是结果个数与总个数的比值. 用sql语句表示为: SELECT COUNT(*) FROM table_name WHERE column_name/SELECT COUNT(*) FRO ...

  4. sql server编写通用脚本自动统计各表数据量心得

    工作过程中,如果一个数据库的表比较多,手工编写统计脚本就会比较繁琐,于是摸索出自动生成各表统计数据量脚本的通用方法,直接上代码: /* 脚本来源:https://www.cnblogs.com/zha ...

  5. 查询优化百万条数据量的MySQL表

    转自https://www.cnblogs.com/llzhang123/p/9239682.html 1.两种查询引擎查询速度(myIsam 引擎 ) InnoDB 中不保存表的具体行数,也就是说, ...

  6. MySQL单表数据量过千万,采坑优化记录,完美解决方案

    问题概述 使用阿里云rds for MySQL数据库(就是MySQL5.6版本),有个用户上网记录表6个月的数据量近2000万,保留最近一年的数据量达到4000万,查询速度极慢,日常卡死.严重影响业务 ...

  7. 单表扫描,MySQL索引选择不正确 并 详细解析OPTIMIZER_TRACE格式

    一 表结构如下:  万行 CREATE TABLE t_audit_operate_log (  Fid bigint(16) AUTO_INCREMENT,  Fcreate_time int(10 ...

  8. MySQL索引选择不正确并详细解析OPTIMIZER_TRACE格式

    一 表结构如下: CREATE TABLE t_audit_operate_log (  Fid bigint(16) AUTO_INCREMENT,  Fcreate_time int(10) un ...

  9. 大数据量下MySQL插入方法的性能比较

    不管是日常业务数据处理中,还是数据库的导入导出,都可能遇到需要处理大量数据的插入.插入的方式和数据库引擎都会对插入速度造成影响,这篇文章旨在从理论和实践上对各种方法进行分析和比较,方便以后应用中插入方 ...

随机推荐

  1. Python logging模块简介

    logging模块提供logger,handler,filter,formatter. logger:提供日志接口,供应用代码使用.logger最长用的操作有两类:配置和发送日志消息.可以通过logg ...

  2. day_1 Python介绍及计算机组成和系统

    python学习路线 基础语法 - 文件操作 - 函数 - 模块 - 面向对象(类) - 网络编程 - 数据库 - 前段 - 项目 学习方法 wwwh: what-why-where-how #wha ...

  3. ASP.NET MVC下使用AngularJs语言(一):Hello your name

    新春节后,分享第一个教程. 是教一位新朋友全新学习ASP.NET MVC下使用AngularJs语言. 一,新建一个空的Web项目.使用NuGet下载AngularJs和jQuery.二,配置Bund ...

  4. 浅谈数通畅联ECP与EAC的区别

    最近收到很多客户的提问,AEAI ECP企业云联平台是什么产品?为什么AEAI ECP中包括集成套件?EAC也是数通畅联的产品吗?同样涉及集成两者有什么区别呢?诸如此类的问题还有很多. 其实AEAI ...

  5. getaddrinfo 报错 Invalid value for ai_flags

    最近改了游戏的网络层代码,运行 Android 版的时候 getaddrinfo 报错 Invalid value for ai_flags. ai_flags 设置如下: struct addrin ...

  6. 聚簇索引(clustered index )和非聚簇索引(secondary index)的区别

    这两个名字虽然都叫做索引,但这并不是一种单独的索引类型,而是一种数据存储方式.对于聚簇索引存储来说,行数据和主键B+树存储在一起,辅助键B+树只存储辅助键和主键,主键和非主键B+树几乎是两种类型的树. ...

  7. LOJ#6387 「THUPC2018」绿绿与串串 / String (Manacher || hash+二分)

    题目描述 绿绿和 Yazid 是好朋友.他们在一起做串串游戏. 我们定义翻转的操作:把一个串以最后一个字符作对称轴进行翻转复制.形式化地描述就是,如果他翻转的串为 RRR,那么他会将前 ∣R∣−1个字 ...

  8. Git 常用命令备份

    继上次保存了git 多个key共存配置(https://www.cnblogs.com/xiaochangwei/p/9155195.html)后,今天把常见的git命令备份下(最近我的云笔记账号经常 ...

  9. D3.js的一些基础部分 (v3版本)

    最近公司有需求要做一些可视化的功能.之前一直都是用Echarts做的,但是Echarts难以满足我们的需求,经过多方请教,查找发现D3可以满足我们的需求.第一次接触到D3,发现这些图标的可交互性非常丰 ...

  10. LeetCode--No.001 Two Sum

    Two Sum Total Accepted: 262258 Total Submissions: 1048169 Difficulty: Easy Given an array of integer ...