前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >表数据量影响MySQL索引选择

表数据量影响MySQL索引选择

作者头像
星哥玩云
发布2022-08-17 15:37:23
1.5K0
发布2022-08-17 15:37:23
举报
文章被收录于专栏:开源部署开源部署

现象

新建了一张员工表,插入了少量数据,索引中所有的字段均在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来查看与分析。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档