How to choose the right index in MySQL

How to choose the right index in MySQL

Let’s take a look at a chestnut first

EXPLAIN select * from employees where name > 'a'; 

If you use the name index to search for data, you need to traverse the name field joint index tree, and then use the traversed primary key value to find the final data in the primary key index tree. The cost is higher than scanning the entire table.

You can use covering index optimization, so that you only need to traverse the joint index tree of the name field to get all the results.

EXPLAIN select name,age,position from employees where name > 'a'; 

It can be seen that the fields selected are covering indexes, and MySQL uses index optimization at the bottom layer. Looking at another case:

EXPLAIN select * from employees where name > 'zzz'; 

For the above two execution results of name>'a' and name>'zzz', whether MySQL finally chooses to use the index or a table involves multiple indexes, how MySQL finally chooses the index, you can use the trace tool to check it out. Turning on the trace tool will affect MySQL performance, so it can only be used to temporarily analyze SQL usage and needs to be closed immediately after use.

SET SESSION optimizer_trace="enabled=on",end_markers_in_json=on; -- Enable trace
SELECT * FROM employees WHERE name > 'a' ORDER BY position;
SELECT * FROM information_schema.OPTIMIZER_TRACE;

Look at the trace field:

{
 "steps": [
 {
  "join_preparation": { --First stage: SQL preparation stage "select#": 1,
  "steps": [
   {
   "expanded_query": "/* select #1 */ select `employees`.`id` AS `id`,`employees`.`name` AS `name`,`employees`.`age` AS `age`,`employees`.`position` AS `position`,`employees`.`hire_time` AS `hire_time` from `employees` where (`employees`.`name` > 'a') order by `employees`.`position`"
   }
  ] /* steps */
  } /* join_preparation */
 },
 {
  "join_optimization": { --Second stage: SQL optimization stage "select#": 1,
  "steps": [
   {
   "condition_processing": { --condition processing "condition": "WHERE",
    "original_condition": "(`employees`.`name` > 'a')",
    "steps": [
    {
     "transformation": "equality_propagation",
     "resulting_condition": "(`employees`.`name` > 'a')"
    },
    {
     "transformation": "constant_propagation",
     "resulting_condition": "(`employees`.`name` > 'a')"
    },
    {
     "transformation": "trivial_condition_removal",
     "resulting_condition": "(`employees`.`name` > 'a')"
    }
    ] /* steps */
   } /* condition_processing */
   },
   {
   "table_dependencies": [ -- table dependency details {
    "table": "`employees`",
    "row_may_be_null": false,
    "map_bit": 0,
    "depends_on_map_bits": [
    ] /* depends_on_map_bits */
    }
   ] /* table_dependencies */
   },
   {
   "ref_optimizer_key_uses": [
   ] /* ref_optimizer_key_uses */
   },
   {
   "rows_estimation": [ --Estimated target access cost {
    "table": "`employees`",
    "range_analysis": {
     "table_scan": { --Full table scan "rows": 3, --Number of scanned rows "cost": 3.7 --Query cost} /* table_scan */,
     "potential_range_indices": [ --Query the possible indexes to be used {
      "index": "PRIMARY", --Primary key index "usable": false,
      "cause": "not_applicable"
     },
     {
      "index": "idx_name_age_position", -- auxiliary index "usable": true,
      "key_parts": [
      "name",
      "age",
      "position",
      "id"
      ] /* key_parts */
     },
     {
      "index": "idx_age",
      "usable": false,
      "cause": "not_applicable"
     }
     ] /* potential_range_indices */,
     "setup_range_conditions": [
     ] /* setup_range_conditions */,
     "group_index_range": {
     "chosen": false,
     "cause": "not_group_by_or_distinct"
     } /* group_index_range */,
     "analyzing_range_alternatives": { ‐‐Analyze the cost of using each index "range_scan_alternatives": [
      {
      "index": "idx_name_age_position",
      "ranges": [
       "a < name"
      ] /* ranges */,
      "index_dives_for_eq_ranges": true,
      "rowid_ordered": false,
      "using_mrr": false,
      "index_only": false, - whether to use covering index "rows": 3, --- the number of rows to scan for index "cost": 4.61, -- index usage cost "chosen": false, - whether to choose this index "cause": "cost"
      }
     ] /* range_scan_alternatives */,
     "analyzing_roworder_intersect": {
      "usable": false,
      "cause": "too_few_roworder_scans"
     } /* analyzing_roworder_intersect */
     } /* analyzing_range_alternatives */
    } /* range_analysis */
    }
   ] /* rows_estimation */
   },
   {
   "considered_execution_plans": [
    {
    "plan_prefix": [
    ] /* plan_prefix */,
    "table": "`employees`",
    "best_access_path": {
     "considered_access_paths": [
     {
      "access_type": "scan",
      "rows": 3,
      "cost": 1.6,
      "chosen": true,
      "use_tmp_table": true
     }
     ] /* considered_access_paths */
    } /* best_access_path */,
    "cost_for_plan": 1.6,
    "rows_for_plan": 3,
    "sort_cost": 3,
    "new_cost_for_plan": 4.6,
    "chosen": true
    }
   ] /* considered_execution_plans */
   },
   {
   "attaching_conditions_to_tables": {
    "original_condition": "(`employees`.`name` > 'a')",
    "attached_conditions_computation": [
    ] /* attached_conditions_computation */,
    "attached_conditions_summary": [
    {
     "table": "`employees`",
     "attached": "(`employees`.`name` > 'a')"
    }
    ] /* attached_conditions_summary */
   } /* attaching_conditions_to_tables */
   },
   {
   "clause_processing": {
    "clause": "ORDER BY",
    "original_clause": "`employees`.`position`",
    "items": [
    {
     "item": "`employees`.`position`"
    }
    ] /* items */,
    "resulting_clause_is_simple": true,
    "resulting_clause": "`employees`.`position`"
   } /* clause_processing */
   },
   {
   "refine_plan": [
    {
    "table": "`employees`",
    "access_type": "table_scan"
    }
   ] /* refine_plan */
   },
   {
   "reconsidering_access_paths_for_index_ordering": {
    "clause": "ORDER BY",
    "index_order_summary": {
    "table": "`employees`",
    "index_provides_order": false,
    "order_direction": "undefined",
    "index": "unknown",
    "plan_changed": false
    } /* index_order_summary */
   } /* reconsidering_access_paths_for_index_ordering */
   }
  ] /* steps */
  } /* join_optimization */
 },
 {
  "join_execution": { --Phase 3: SQL execution phase "select#": 1,
  "steps": [
   {
   "filesort_information": [
    {
    "direction": "asc",
    "table": "`employees`",
    "field": "position"
    }
   ] /* filesort_information */,
   "filesort_priority_queue_optimization": {
    "usable": false,
    "cause": "not applicable (no LIMIT)"
   } /* filesort_priority_queue_optimization */,
   "filesort_execution": [
   ] /* filesort_execution */,
   "filesort_summary": {
    "rows": 3,
    "examined_rows": 3,
    "number_of_tmp_files": 0,
    "sort_buffer_size": 200704,
    "sort_mode": "<sort_key, additional_fields>"
   } /* filesort_summary */
   }
  ] /* steps */
  } /* join_execution */
 }
 ] /* steps */
}

The cost of a full table scan is lower than an index scan, so MySQL will eventually choose a full table scan.

SELECT * FROM employees WHERE name > 'zzz' ORDER BY position;
SELECT * FROM information_schema.OPTIMIZER_TRACE;

{
 "steps": [
 {
  "join_preparation": {
  "select#": 1,
  "steps": [
   {
   "expanded_query": "/* select #1 */ select `employees`.`id` AS `id`,`employees`.`name` AS `name`,`employees`.`age` AS `age`,`employees`.`position` AS `position`,`employees`.`hire_time` AS `hire_time` from `employees` where (`employees`.`name` > 'zzz') order by `employees`.`position`"
   }
  ] /* steps */
  } /* join_preparation */
 },
 {
  "join_optimization": {
  "select#": 1,
  "steps": [
   {
   "condition_processing": {
    "condition": "WHERE",
    "original_condition": "(`employees`.`name` > 'zzz')",
    "steps": [
    {
     "transformation": "equality_propagation",
     "resulting_condition": "(`employees`.`name` > 'zzz')"
    },
    {
     "transformation": "constant_propagation",
     "resulting_condition": "(`employees`.`name` > 'zzz')"
    },
    {
     "transformation": "trivial_condition_removal",
     "resulting_condition": "(`employees`.`name` > 'zzz')"
    }
    ] /* steps */
   } /* condition_processing */
   },
   {
   "table_dependencies": [
    {
    "table": "`employees`",
    "row_may_be_null": false,
    "map_bit": 0,
    "depends_on_map_bits": [
    ] /* depends_on_map_bits */
    }
   ] /* table_dependencies */
   },
   {
   "ref_optimizer_key_uses": [
   ] /* ref_optimizer_key_uses */
   },
   {
   "rows_estimation": [
    {
    "table": "`employees`",
    "range_analysis": {
     "table_scan": {
     "rows": 3,
     "cost": 3.7
     } /* table_scan */,
     "potential_range_indices": [
     {
      "index": "PRIMARY",
      "usable": false,
      "cause": "not_applicable"
     },
     {
      "index": "idx_name_age_position",
      "usable": true,
      "key_parts": [
      "name",
      "age",
      "position",
      "id"
      ] /* key_parts */
     },
     {
      "index": "idx_age",
      "usable": false,
      "cause": "not_applicable"
     }
     ] /* potential_range_indices */,
     "setup_range_conditions": [
     ] /* setup_range_conditions */,
     "group_index_range": {
     "chosen": false,
     "cause": "not_group_by_or_distinct"
     } /* group_index_range */,
     "analyzing_range_alternatives": {
     "range_scan_alternatives": [
      {
      "index": "idx_name_age_position",
      "ranges": [
       "zzz < name"
      ] /* ranges */,
      "index_dives_for_eq_ranges": true,
      "rowid_ordered": false,
      "using_mrr": false,
      "index_only": false,
      "rows": 1,
      "cost": 2.21,
      "chosen": true
      }
     ] /* range_scan_alternatives */,
     "analyzing_roworder_intersect": {
      "usable": false,
      "cause": "too_few_roworder_scans"
     } /* analyzing_roworder_intersect */
     } /* analyzing_range_alternatives */,
     "chosen_range_access_summary": {
     "range_access_plan": {
      "type": "range_scan",
      "index": "idx_name_age_position",
      "rows": 1,
      "ranges": [
      "zzz < name"
      ] /* ranges */
     } /* range_access_plan */,
     "rows_for_plan": 1,
     "cost_for_plan": 2.21,
     "chosen": true
     } /* chosen_range_access_summary */
    } /* range_analysis */
    }
   ] /* rows_estimation */
   },
   {
   "considered_execution_plans": [
    {
    "plan_prefix": [
    ] /* plan_prefix */,
    "table": "`employees`",
    "best_access_path": {
     "considered_access_paths": [
     {
      "access_type": "range",
      "rows": 1,
      "cost": 2.41,
      "chosen": true,
      "use_tmp_table": true
     }
     ] /* considered_access_paths */
    } /* best_access_path */,
    "cost_for_plan": 2.41,
    "rows_for_plan": 1,
    "sort_cost": 1,
    "new_cost_for_plan": 3.41,
    "chosen": true
    }
   ] /* considered_execution_plans */
   },
   {
   "attaching_conditions_to_tables": {
    "original_condition": "(`employees`.`name` > 'zzz')",
    "attached_conditions_computation": [
    ] /* attached_conditions_computation */,
    "attached_conditions_summary": [
    {
     "table": "`employees`",
     "attached": "(`employees`.`name` > 'zzz')"
    }
    ] /* attached_conditions_summary */
   } /* attaching_conditions_to_tables */
   },
   {
   "clause_processing": {
    "clause": "ORDER BY",
    "original_clause": "`employees`.`position`",
    "items": [
    {
     "item": "`employees`.`position`"
    }
    ] /* items */,
    "resulting_clause_is_simple": true,
    "resulting_clause": "`employees`.`position`"
   } /* clause_processing */
   },
   {
   "refine_plan": [
    {
    "table": "`employees`",
    "pushed_index_condition": "(`employees`.`name` > 'zzz')",
    "table_condition_attached": null,
    "access_type": "range"
    }
   ] /* refine_plan */
   },
   {
   "reconsidering_access_paths_for_index_ordering": {
    "clause": "ORDER BY",
    "index_order_summary": {
    "table": "`employees`",
    "index_provides_order": false,
    "order_direction": "undefined",
    "index": "idx_name_age_position",
    "plan_changed": false
    } /* index_order_summary */
   } /* reconsidering_access_paths_for_index_ordering */
   }
  ] /* steps */
  } /* join_optimization */
 },
 {
  "join_execution": {
  "select#": 1,
  "steps": [
   {
   "filesort_information": [
    {
    "direction": "asc",
    "table": "`employees`",
    "field": "position"
    }
   ] /* filesort_information */,
   "filesort_priority_queue_optimization": {
    "usable": false,
    "cause": "not applicable (no LIMIT)"
   } /* filesort_priority_queue_optimization */,
   "filesort_execution": [
   ] /* filesort_execution */,
   "filesort_summary": {
    "rows": 0,
    "examined_rows": 0,
    "number_of_tmp_files": 0,
    "sort_buffer_size": 200704,
    "sort_mode": "<sort_key, additional_fields>"
   } /* filesort_summary */
   }
  ] /* steps */
  } /* join_execution */
 }
 ] /* steps */
}

Looking at the trace field, we can see that the cost of index scanning is lower than the cost of full table scanning, so MySQL finally chooses index scanning.

SET SESSION optimizer_trace="enabled=off"; -- Disable trace

Summarize

The above is my introduction to how to choose a suitable index for MySQL. I hope it will be helpful to you. If you have any questions, please leave me a message and I will reply to you in time. I would also like to thank everyone for their support of the 123WORDPRESS.COM website! If you find this article helpful, please feel free to reprint it and please indicate the source. Thank you!

You may also be interested in:
  • Why does MySQL database index choose to use B+ tree?
  • Exploring MySQL optimizer's choice of index and JOIN order
  • Will mysql's in invalidate the index?
  • Detailed explanation of MySQL combined index and leftmost matching principle
  • How to add indexes to MySQL
  • A case study to thoroughly understand how to correctly use MySQL inndb joint index
  • Detailed analysis of the situations in which database indexes will fail in MySQL
  • In-depth analysis of the leftmost matching principle of MySQL joint index
  • The correct way to use MySQL indexes and detailed explanation of index principles
  • MySQL Index Detailed Explanation
  • MySQL index usage instructions (single-column index and multi-column index)

<<:  CentOS7 network configuration under VMware virtual machine (host wireless Internet access)

>>:  JS implements a detailed plan for the smooth version of the progress bar

Recommend

How to install and configure WSL on Windows

What is WSL Quoting a passage from Baidu Encyclop...

The most basic code for web pages

◆Add to favorites illustrate Click to add your we...

File upload via HTML5 on mobile

Most of the time, plug-ins are used to upload fil...

jQuery achieves the effect of advertisement scrolling up and down

This article shares the specific code of jQuery t...

Detailed explanation of CSS float property

1. What is floating? Floating, as the name sugges...

How to install Odoo12 development environment on Windows 10

Preface Since many friends say they don’t have Ma...

Solution to ONLY_FULL_GROUP_BY error in Mysql5.7 and above

Recently, during the development process, the MyS...

MySQL data insertion efficiency comparison

When inserting data, I found that I had never con...

Practical basic Linux sed command example code

The Linux stream editor is a useful way to run sc...

Detailed explanation of chmod command usage in Linux

chmod Command Syntax This is the correct syntax w...

Vue commonly used high-order functions and comprehensive examples

1. Commonly used high-order functions of arrays S...

CSS3 realizes the graphic falling animation effect

See the effect first Implementation Code <div ...