{"id":21305,"date":"2022-10-21T10:00:00","date_gmt":"2022-10-21T17:00:00","guid":{"rendered":"https:\/\/coderpad.io\/?p=21305"},"modified":"2023-06-05T14:01:03","modified_gmt":"2023-06-05T21:01:03","slug":"how-to-use-indexes-to-increase-mysql-database-performance","status":"publish","type":"post","link":"https:\/\/coderpad.io\/blog\/development\/how-to-use-indexes-to-increase-mysql-database-performance\/","title":{"rendered":"How to Use Indexes to Increase MySQL Database Performance"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">What are Indexes?<\/h2>\n\n\n\n<p>Have you ever tried finding a chapter in a huge book, and you head over to the \u201cTable of Contents\u201d or Index page and seek the page number for the chapter? That\u2019s a similar way DBMS use indexes to speed up data retrieval. Indexes are made up of keys from one or more columns in a table and they contain pointers that tell MySQL where a particular row of data is stored in the database. This enables the MySQL server to skip searching long rows of a table to find that piece of data thus boosting query speed. Additionally, indexes enable data to be <a href=\"https:\/\/coderpad.io\/blog\/development\/optimize-mysql-database-schema\/\">better organized on disk<\/a>. The <a href=\"https:\/\/coderpad.io\/blog\/development\/optimize-query-performance-mysql\/\">MySQL Join Optimizer<\/a> also uses indexes to speed up queries that involve joins.<\/p>\n\n\n\n<p>Indexing is one of the most powerful features of a database. It\u2019s basically the most important thing database engineers consider when optimizing databases for speed.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Indexing basics<\/h2>\n\n\n\n<p>Creating an index in MySQL is done using the <code>CREATE INDEX<\/code> command, and has the following syntax:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-keyword\">CREATE<\/span> <span class=\"hljs-keyword\">INDEX<\/span> index_name <span class=\"hljs-keyword\">ON<\/span> table_name ( column1, column2,...)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You can also set up indexes when creating a new table using the <code>INDEX<\/code> statement:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-keyword\">CREATE<\/span> <span class=\"hljs-keyword\">TABLE<\/span> Customer (\n    last_name  <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">30<\/span>)   <span class=\"hljs-keyword\">not<\/span> <span class=\"hljs-literal\">null<\/span>,\n    first_name <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">30<\/span>)   <span class=\"hljs-keyword\">not<\/span> <span class=\"hljs-literal\">null<\/span>,\n    email      <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">50<\/span>)   <span class=\"hljs-keyword\">not<\/span> <span class=\"hljs-literal\">null<\/span>,\n    <span class=\"hljs-keyword\">INDEX<\/span>(email)\n);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You can also use a <code>PRIMARY KEY<\/code> statement instead of an <code>INDEX<\/code> statement. However, note that a table can only contain a single <code>PRIMARY KEY<\/code>, and the key must contain <code>UNIQUE<\/code> values and no <code>NULL<\/code> values:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-keyword\">CREATE<\/span> <span class=\"hljs-keyword\">TABLE<\/span> Customer ( <span class=\"hljs-keyword\">id<\/span> <span class=\"hljs-built_in\">int<\/span> <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>, last_name <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">30<\/span>) <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>, first_name <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">30<\/span>) <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>, email <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">50<\/span>)   <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>,    <span class=\"hljs-keyword\">CONSTRAINT<\/span> PK_Customer PRIMARY <span class=\"hljs-keyword\">KEY<\/span> (<span class=\"hljs-keyword\">id<\/span>,last_name));<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>For existing tables, you can also add indexes as follows:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-keyword\">ALTER<\/span> <span class=\"hljs-keyword\">TABLE<\/span> Customer <span class=\"hljs-keyword\">ADD<\/span> <span class=\"hljs-keyword\">INDEX<\/span> (email);\n<span class=\"hljs-keyword\">ALTER<\/span> <span class=\"hljs-keyword\">TABLE<\/span> Customer <span class=\"hljs-keyword\">ADD<\/span> PRIMARY <span class=\"hljs-keyword\">KEY<\/span> (<span class=\"hljs-keyword\">id<\/span>);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Deleting existing indexes is simple, just use the <code>DROP INDEX<\/code> statement:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-keyword\">ALTER<\/span> <span class=\"hljs-keyword\">TABLE<\/span> Customer <span class=\"hljs-keyword\">DROP<\/span> <span class=\"hljs-keyword\">INDEX<\/span> (email);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Types of Indexes<\/h2>\n\n\n\n<p>MySQL offers many types of indexes, usually categorized according to their data structure. Most of the indexes created in MySQL are stored in B-trees. These include <code>PRIMARY KEY<\/code>, <code>INDEX<\/code>, <code>FULLTEXT<\/code>, and <code>UNIQUE<\/code> indexes. For Spatial Data types, MySQL uses R-trees to store their indexes. Memory tables use hash indexes by default, but B-tree indexes are also supported.<\/p>\n\n\n\n<p>In the following subheadings, we\u2019ll explore all the different types of indexes, their benefits, and their disadvantages.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">B-Tree indexes<\/h3>\n\n\n\n<p>Most storage engines in MySQL use B-tree indexes by default. B-tree indexes can be used for column comparisons in expressions that use the <code>=<\/code>, <code>&gt;<\/code>, <code>&gt;=<\/code>, <code>&lt;<\/code>, <code>&lt;=<\/code>, or <code>BETWEEN<\/code> operators. They can also be used in <code>LIKE<\/code> comparisons if the argument is a constant string that does not start with a wildcard <code>(<\/code>%<code>,<\/code>_<code>)<\/code> character.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">B-Tree Indexes Characteristics<\/h4>\n\n\n\n<p>B-Trees are search trees commonly used by large databases to access data stored on the disk. Because of its properties, searching for data in a data set can be achieved in significantly less time than otherwise. A B-tree stores all its values in leaves that are sorted in increasing order. All leaves are at the same level and have the same distance from the root node.&nbsp;<\/p>\n\n\n\n<p>When the storage engine performs a lookup on a table, it doesn\u2019t need to scan all the rows to find the desired data instead, it will traverse the B-tree starting from the root node. The root node holds pointers to child nodes. The storage engine uses these pointers to find a leaf page that contains pointers to the indexed data. This process is fast because the indexed values are arranged in order. For example, when looking up an index for a column that contains text values, the storage engine will traverse the tree in alphabetical order. We summarize the steps MySQL\u2019s storage engine will need to take to locate a piece of indexed data by utilizing a B-tree below:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Start at the root node and proceed to the next level in the tree.<\/li>\n\n\n\n<li>Find a node that contains a range of values between a lower and upper limit (for example, every country whose name begins with S through T).<\/li>\n\n\n\n<li>Traverse the tree until it finds the node with the closest range using pointers between nodes.<\/li>\n\n\n\n<li>If the node is found, it traverses through the leaf pages using pointers until the leaf page with the indexed data is found.<\/li>\n<\/ol>\n\n\n\n<h4 class=\"wp-block-heading\">Considerations When Using B-Tree Indexes<\/h4>\n\n\n\n<p>Earlier, we stated that B-tree indexes can be used in <code>LIKE<\/code> comparisons. In other to fully utilize indexes, we have to ensure that the argument is a constant string and must not include a wildcard character. For example, the following <code>SELECT<\/code> statements are using indexes:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-keyword\">SELECT<\/span> * <span class=\"hljs-keyword\">FROM<\/span> customer <span class=\"hljs-keyword\">WHERE<\/span> country <span class=\"hljs-keyword\">LIKE<\/span> <span class=\"hljs-string\">'Spain%'<\/span>;\n<span class=\"hljs-keyword\">SELECT<\/span> * <span class=\"hljs-keyword\">FROM<\/span> customer <span class=\"hljs-keyword\">WHERE<\/span> country <span class=\"hljs-keyword\">LIKE<\/span> <span class=\"hljs-string\">'Sp%_in%'<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In the first statement, only rows with <code>'Spain' &lt;= country &lt; 'Spaio'<\/code> are considered. While the second statement will consider only rows with <code>'Sp' &lt;= country &lt; 'Sq'<\/code>.<\/p>\n\n\n\n<p>Note that MySQL is checking the last character of the string in alphabetical order; the letter \u201c<strong>o<\/strong>\u201d comes after the letter \u201c<strong>n<\/strong>\u201d.<\/p>\n\n\n\n<p>The following <code>SELECT<\/code> statements will not use indexes:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-keyword\">SELECT<\/span> * <span class=\"hljs-keyword\">FROM<\/span> customer <span class=\"hljs-keyword\">WHERE<\/span> country <span class=\"hljs-keyword\">LIKE<\/span> <span class=\"hljs-string\">'%Spain%'<\/span>;\n<span class=\"hljs-keyword\">SELECT<\/span> * <span class=\"hljs-keyword\">FROM<\/span> customer <span class=\"hljs-keyword\">WHERE<\/span> country <span class=\"hljs-keyword\">LIKE<\/span> another_col;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The first statement starts with a wildcard, so it will fail to use an index, and the second statement will not also because its <code>LIKE<\/code> argument is not a constant string.<\/p>\n\n\n\n<p>Any index that fails to span all <code>AND<\/code> levels in a <code>WHERE<\/code> clause is not used in optimizing the query. In other words, to be able to use an index, a prefix of the index must be used in every <code>AND<\/code> group. For example, the following statements use indexes:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-keyword\">SELECT<\/span> * <span class=\"hljs-keyword\">FROM<\/span> table_name <span class=\"hljs-keyword\">WHERE<\/span> index_part1=<span class=\"hljs-number\">1<\/span> <span class=\"hljs-keyword\">AND<\/span> index_part2=<span class=\"hljs-number\">2<\/span> <span class=\"hljs-keyword\">AND<\/span> other_column=<span class=\"hljs-number\">3<\/span>\n\n<span class=\"hljs-comment\">\/* index = 1 OR index = 2 *\/<\/span>\n<span class=\"hljs-keyword\">SELECT<\/span> * <span class=\"hljs-keyword\">FROM<\/span> table_name <span class=\"hljs-keyword\">WHERE<\/span> <span class=\"hljs-keyword\">index<\/span>=<span class=\"hljs-number\">1<\/span> <span class=\"hljs-keyword\">OR<\/span> A=<span class=\"hljs-number\">10<\/span> <span class=\"hljs-keyword\">AND<\/span> <span class=\"hljs-keyword\">index<\/span>=<span class=\"hljs-number\">2<\/span>;\n\n<span class=\"hljs-comment\">\/* optimized like \"index_part1='hello'\" *\/<\/span>\n<span class=\"hljs-keyword\">SELECT<\/span> * <span class=\"hljs-keyword\">FROM<\/span> table_name <span class=\"hljs-keyword\">WHERE<\/span> index_part1=<span class=\"hljs-string\">'hello'<\/span> <span class=\"hljs-keyword\">AND<\/span> index_part3=<span class=\"hljs-number\">5<\/span>;\n\n<span class=\"hljs-comment\">\/* Can use index on index1 but not on index2 or index3 *\/<\/span>\n<span class=\"hljs-keyword\">SELECT<\/span> * <span class=\"hljs-keyword\">FROM<\/span> table_name <span class=\"hljs-keyword\">WHERE<\/span> index1=<span class=\"hljs-number\">1<\/span> <span class=\"hljs-keyword\">AND<\/span> index2=<span class=\"hljs-number\">2<\/span> <span class=\"hljs-keyword\">OR<\/span> index1=<span class=\"hljs-number\">3<\/span> <span class=\"hljs-keyword\">AND<\/span> index3=<span class=\"hljs-number\">3<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>But these <code>WHERE<\/code> clauses do not use indexes:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-comment\">\/* index_part1 is not used *\/<\/span>\n<span class=\"hljs-keyword\">SELECT<\/span> * <span class=\"hljs-keyword\">FROM<\/span> table_name <span class=\"hljs-keyword\">WHERE<\/span> index_part2=<span class=\"hljs-number\">1<\/span> <span class=\"hljs-keyword\">AND<\/span> index_part3=<span class=\"hljs-number\">2<\/span>\n\n<span class=\"hljs-comment\">\/*  Index is not used in both parts of the WHERE clause  *\/<\/span>\n<span class=\"hljs-keyword\">SELECT<\/span> * <span class=\"hljs-keyword\">FROM<\/span> table_name <span class=\"hljs-keyword\">WHERE<\/span> <span class=\"hljs-keyword\">index<\/span>=<span class=\"hljs-number\">1<\/span> <span class=\"hljs-keyword\">OR<\/span> A=<span class=\"hljs-number\">10<\/span>\n\n<span class=\"hljs-comment\">\/* No index spans all rows  *\/<\/span>\n<span class=\"hljs-keyword\">SELECT<\/span> * <span class=\"hljs-keyword\">FROM<\/span> table_name <span class=\"hljs-keyword\">WHERE<\/span> index_part1=<span class=\"hljs-number\">1<\/span> <span class=\"hljs-keyword\">OR<\/span> index_part2=<span class=\"hljs-number\">10<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Sometimes the MySQL Query Optimizer does not use indexes in optimizing queries. This is because the optimizer may have estimated that the index would require MySQL to access a large number of rows than otherwise. To fully understand how the MySQL Optimizer treats queries that utilize indexes, I would recommend you <a href=\"https:\/\/coderpad.io\/blog\/development\/optimize-query-performance-mysql\/\">check this article on query performance optimization<\/a>. <\/p>\n\n\n\n<p>In summary, it\u2019s a good practice to check and confirm if your queries actually use the indexes you created in the tables. In case the optimizer is wrong, you can always resort to using <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/index-hints.html\" target=\"_blank\" rel=\"noopener\">Index hints<\/a> to overwrite the default behavior. We discussed all of that in the <a href=\"https:\/\/coderpad.io\/blog\/development\/optimize-query-performance-mysql\/\">suggested article<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Hash Indexes<\/h3>\n\n\n\n<p>Hash indexes are also used by MySQL to improve data access speed, just like B-tree indexes. The differences, however, are:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Hash indexes can only be used in equality comparisons that use the <code>=<\/code> or <code>&lt;=&gt;<\/code> operators.<\/li>\n\n\n\n<li>They can not be used to optimize <code>ORDER BY<\/code> operations because hash indexes cannot be used to search the next entry in order.<\/li>\n\n\n\n<li>They are not so good for ranges, as MySQL cannot determine the number of rows between two values.<\/li>\n\n\n\n<li>Hash indexes can not also be used for sorting because they don\u2019t store rows in order.<\/li>\n\n\n\n<li>Unlike B-tree indexes that can use any leftmost prefix of a key to find rows, Hash indexes can only use whole keys to find rows.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Advantages of indexes in MySQL<\/h2>\n\n\n\n<p>By now you should know that the primary benefit of indexes is in speeding up search queries by enabling the MySQL server to navigate to a particular location in a table much quicker. Other benefits include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Uniqueness: Indexes such as <code>PRIMARY KEY<\/code> indexes and <code>UNIQUE<\/code> key indexes can help reduce data duplication.<\/li>\n\n\n\n<li>Full-Text Search: <code>FULLTEXT<\/code> indexes allow MySQL to perform full-text search capabilities. Meaning you can search against a large amount of text located in any field.<\/li>\n\n\n\n<li>Reducing the number of Disk I\/Os (page fetches).<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Disadvantages of indexes in MySQL<\/h2>\n\n\n\n<p>Indexes are great for faster data lookup, however, this comes with some drawbacks. The main drawbacks of using indexes are as follows:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Slower Writes: Indexes slow down <code>INSERT<\/code>, <code>UPDATE<\/code>, and <code>DELETE<\/code> queries. This is because when an indexed field is updated, the index also needs to be updated together with it.<\/li>\n\n\n\n<li>Increased disk usage: Using indexes, you store more data.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Maintaining indexes to improve query performance and reduce resource consumption<\/h2>\n\n\n\n<p>This section covers index maintenance concepts, such as index fragmentation, and the impact they have on query performance and resource consumption. You will learn how and when to rebuild or repair tables and indexes. You will also learn how to identify and remove index fragmentation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Index Fragmentation<\/h3>\n\n\n\n<p>Index fragmentation wastes disk space and can hinder performance. Fragmentation means that the physical ordering of the index pages on the disk is not close to the index ordering of the records on the pages. It also means that many unused pages in the 64-page blocks were allocated to the index. For example, when MySQL deletes rows of data from a table, it leaves an empty space on the disk. Over time this space increases and causes gaps (fragments) in the space allocated for that table. Sometimes MySQL will try to use the spaces when inserting new data, but the gaps may still persist, and this eventually leads to fragmentation.<\/p>\n\n\n\n<p>Using the <code>SHOW TABLE STATUS<\/code> command, we can check if fragmentation has occurred in a table. To demonstrate, let\u2019s create a database and a table and then perform some writes to the table:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\">mysql&gt; <span class=\"hljs-keyword\">CREATE<\/span> <span class=\"hljs-keyword\">DATABASE<\/span> coderpad;\nQuery OK, 1 row affected (0.01 sec)\n\nmysql&gt; <span class=\"hljs-keyword\">USE<\/span> coderpad;\nDatabase changed\n\nmysql&gt; <span class=\"hljs-keyword\">CREATE<\/span> <span class=\"hljs-keyword\">TABLE<\/span> frag_table (col1 <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">64<\/span>)) <span class=\"hljs-keyword\">ENGINE<\/span>=MyISAM;\nQuery OK, 0 rows affected (0.59 sec)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Let\u2019s insert some rows of data to our new table:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\">mysql&gt; <span class=\"hljs-keyword\">INSERT<\/span> <span class=\"hljs-keyword\">INTO<\/span> frag_table <span class=\"hljs-keyword\">VALUES<\/span> (<span class=\"hljs-string\">'row 1'<\/span>);\nQuery OK, 1 row affected (0.02 sec)\n\nmysql&gt; <span class=\"hljs-keyword\">INSERT<\/span> <span class=\"hljs-keyword\">INTO<\/span> frag_table <span class=\"hljs-keyword\">VALUES<\/span> (<span class=\"hljs-string\">'row 2'<\/span>);\nQuery OK, 1 row affected (0.02 sec)\n\nmysql&gt; <span class=\"hljs-keyword\">INSERT<\/span> <span class=\"hljs-keyword\">INTO<\/span> frag_table <span class=\"hljs-keyword\">VALUES<\/span> (<span class=\"hljs-string\">'row 3'<\/span>);\nQuery OK, 1 row affected (0.04 sec)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Let\u2019s check if fragmentation is present in the table:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\">mysql&gt; <span class=\"hljs-keyword\">SHOW<\/span> <span class=\"hljs-keyword\">TABLE<\/span> <span class=\"hljs-keyword\">STATUS<\/span> <span class=\"hljs-keyword\">FROM<\/span> coderpad\\G\n\n*************************** <span class=\"hljs-number\">1.<\/span> <span class=\"hljs-keyword\">row<\/span> ***************************\n           <span class=\"hljs-keyword\">Name<\/span>: frag_table\n         <span class=\"hljs-keyword\">Engine<\/span>: MyISAM\n        <span class=\"hljs-keyword\">Version<\/span>: <span class=\"hljs-number\">10<\/span>\n     Row_format: Dynamic\n           <span class=\"hljs-keyword\">Rows<\/span>: <span class=\"hljs-number\">3<\/span>\n Avg_row_length: <span class=\"hljs-number\">20<\/span>\n    Data_length: <span class=\"hljs-number\">60<\/span>\nMax_data_length: <span class=\"hljs-number\">281474976710655<\/span>\n   Index_length: <span class=\"hljs-number\">1024<\/span>\n      Data_free: <span class=\"hljs-number\">0<\/span>\n Auto_increment: <span class=\"hljs-number\">1<\/span>\n    Create_time: <span class=\"hljs-number\">2022<\/span><span class=\"hljs-number\">-10<\/span><span class=\"hljs-number\">-21<\/span> <span class=\"hljs-number\">09<\/span>:<span class=\"hljs-number\">04<\/span>:<span class=\"hljs-number\">33<\/span>\n    Update_time: <span class=\"hljs-number\">2022<\/span><span class=\"hljs-number\">-10<\/span><span class=\"hljs-number\">-21<\/span> <span class=\"hljs-number\">09<\/span>:<span class=\"hljs-number\">04<\/span>:<span class=\"hljs-number\">33<\/span>\n     Check_time: <span class=\"hljs-literal\">NULL<\/span>\n      <span class=\"hljs-keyword\">Collation<\/span>: utf8mb4_0900_ai_ci\n       <span class=\"hljs-keyword\">Checksum<\/span>: <span class=\"hljs-literal\">NULL<\/span>\n Create_options: \n        <span class=\"hljs-keyword\">Comment<\/span>: \n<span class=\"hljs-number\">1<\/span> <span class=\"hljs-keyword\">row<\/span> <span class=\"hljs-keyword\">in<\/span> <span class=\"hljs-keyword\">set<\/span> (<span class=\"hljs-number\">0.01<\/span> sec)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>Data_free<\/code> column shows any empty spaces or gaps in our table. At the moment, its value is 0, meaning there\u2019s no fragmentation yet. The value will increase as your table expands and more write operations occur.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Defragmenting a table<\/h3>\n\n\n\n<p>MySQL offers a way to defragment a table using a simple command, <code>OPTIMIZE TABLE<\/code>. We can defragment the table in our previous example as follows:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-keyword\">OPTIMIZE<\/span> <span class=\"hljs-keyword\">TABLE<\/span> frag_table;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>If you use a storage engine that doesn\u2019t support the <code>OPTIMIZE TABLE<\/code> command, you can use a null <code>ALTER TABLE<\/code> operation:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-keyword\">ALTER<\/span> <span class=\"hljs-keyword\">TABLE<\/span> frag_table <span class=\"hljs-keyword\">ENGINE<\/span>=<span class=\"hljs-keyword\">INNODB<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Another way to defragment a table is to dump the table to a text file using <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/mysqldump.html\" target=\"_blank\" rel=\"noopener\">mysqldump<\/a>, drop the table, and then reload it from the file.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Rebuilding or repairing tables or indexes<\/h3>\n\n\n\n<p>Changes to how MySQL handles data types may require us to rebuild or repair tables and indexes. For example, we explored a use case in the section about fragmentation. Other use cases that might require us to rebuild or repair tables might also include error messages in a collation or reports from <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/check-table.html\" target=\"_blank\" rel=\"noopener\">CHECK TABLE<\/a>, <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/mysqlcheck.html\" target=\"_blank\" rel=\"noopener\">mysqlcheck<\/a>, or <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/mysql-upgrade.html\" target=\"_blank\" rel=\"noopener\">mysql_upgrade<\/a>.<\/p>\n\n\n\n<p>There are three methods available for rebuilding a table:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Dump and Reload Method.<\/li>\n\n\n\n<li>Alter Table Method.<\/li>\n\n\n\n<li>Repair Table Method.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Dump and reload method<\/h4>\n\n\n\n<p>This method involves using <code>mysqldump<\/code> to create a dump file and then using MySQL to reload the file.<\/p>\n\n\n\n<p>To rebuild a table named \u201ctable_name\u201d, you can dump it and reload it as follows:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\">mysqldump db_name table_name &gt; dump.sql\nmysql db_name &lt; dump.sql<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>To rebuild all tables in a database, you can omit the table_name as follows:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\">mysqldump db_name &gt; dump.sql\nmysql db_name &lt; dump.sql<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\">ALTER TABLE method<\/h4>\n\n\n\n<p>You can also use the <code>ALTER TABLE<\/code> method as covered in the section titled \u201cDefragmenting a Table\u201d.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-keyword\">ALTER<\/span> <span class=\"hljs-keyword\">TABLE<\/span> frag_table <span class=\"hljs-keyword\">ENGINE<\/span>=<span class=\"hljs-keyword\">INNODB<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\">REPAIR TABLE method<\/h4>\n\n\n\n<p>The <code>REPAIR TABLE<\/code> method is used when the <code>CHECK TABLE<\/code> operation indicates that corruption exists or an update is required. This <code>REPAIR TABLE<\/code> method is only applicable to MyISAM, ARCHIVE, and CSV tables. The syntax is as follows:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\"><span class=\"hljs-keyword\">REPAIR<\/span> <span class=\"hljs-keyword\">TABLE<\/span> table_name;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>For convenience, you can use the <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/mysqlcheck.html\" target=\"_blank\" rel=\"noopener\">mysqlcheck &#8211;repair<\/a> command to access the <code>REPAIR TABLE<\/code> statement via the command line:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\">mysqlcheck <span class=\"hljs-comment\">--repair --databases db_name ...<\/span>\nmysqlcheck <span class=\"hljs-comment\">--repair --all-databases<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\">OPTIMIZE TABLE<\/h4>\n\n\n\n<p>While REPAIR TABLE is used in fixing table corruption problems, OPTIMIZE TABLE is used in removing unused space occupied by the table\u2019s data and associated index data. This improves I\/O efficiency when accessing the table. The changes made by the <code>OPTIMIZE TABLE<\/code> statement depend on the storage engine used in creating the table. Also, note that this statement can only be used for InnoDB, MyISAM, and Archive Tables. For in-memory NDB tables, only dynamic columns are supported. For the other non-supported storage engines, you\u2019ll need to start <code>mysqld<\/code> with the <code>\u2013skip-new<\/code> option. In this case, <code>OPTIMIZE TABLE<\/code> is just mapped to <code>ALTER TABLE<\/code>.<\/p>\n\n\n\n<p>MySQL maps the <code>OPTMIZE TABLE<\/code> statement to <code>ALTER TABLE\u2026FORCE<\/code> for InnoDB tables. And this rebuilds the table to update index statistics and free unused space in the clustered index. An output is also displayed showing the resulting status after running the statement, as follows:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql shcb-wrap-lines\">mysql&gt; <span class=\"hljs-keyword\">OPTIMIZE<\/span> <span class=\"hljs-keyword\">TABLE<\/span> frag_table;\n+<span class=\"hljs-comment\">----------+----------+----------+-------------------------------------------------------------------+<\/span>\n| Table    | Op       | Msg_type | Msg_text                                                          |\n+<span class=\"hljs-comment\">----------+----------+----------+-------------------------------------------------------------------+<\/span>\n| test.frag_table | <span class=\"hljs-keyword\">optimize<\/span> | note     | <span class=\"hljs-keyword\">Table<\/span> does <span class=\"hljs-keyword\">not<\/span> support <span class=\"hljs-keyword\">optimize<\/span>, doing recreate + <span class=\"hljs-keyword\">analyze<\/span> instead |\n| test.frag_table | <span class=\"hljs-keyword\">optimize<\/span> | <span class=\"hljs-keyword\">status<\/span>   | OK                                                                |\n+<span class=\"hljs-comment\">----------+----------+----------+-------------------------------------------------------------------+<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div\n\tclass=\"sandbox-embed responsive-embed  sandbox-embed--full-width\"\n\tstyle=\"padding-top: 125%\"\ndata-block-name=\"coderpad-sandbox-embed\">\n\t<iframe src=\"https:\/\/embed.coderpad.io\/sandbox?question_id=232486&#038;use_question_button\" width=\"640\" height=\"800\" loading=\"lazy\" aria-label=\"Try out the CoderPad sandbox\"><\/iframe>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Final thoughts<\/h2>\n\n\n\n<p>Indexing is one of the most discussed topics in DBMS. It can also be the most powerful tool in your database optimization arsenal if utilized well. <\/p>\n\n\n\n<p>A common mistake developers make is trying to add indexes everywhere. This isn\u2019t recommended because, as we\u2019ve noted earlier in this guide, indexes consume storage space, and they slow down write operations in your database. Therefore, if you know your database will perform lots of write queries, you might want to take your time and decide where indexes are needed the most. The opposite is also true, and if you\u2019ll be performing lots of read operations, you can optimize your queries by adding indexes to the frequently searched columns. <\/p>\n\n\n\n<p>If you\u2019re not sure about index performance in your queries, MySQL has tools that can help you analyze a query and determine if indexes were used in optimizing it. We discussed that in detail in our article on <a href=\"https:\/\/coderpad.io\/blog\/development\/optimize-query-performance-mysql\/\">query optimization<\/a>.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">References<\/h2>\n\n\n\n<p><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/index-btree-hash.html\" target=\"_blank\" rel=\"noopener\">MySQL 8.0 Reference Manual &#8211; Comparison of B-Tree and Hash Indexes<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/innodb-file-defragmenting.html\" target=\"_blank\" rel=\"noopener\">MySQL 8.0 Reference Manual &#8211; Defragmenting a Table<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.0\/en\/rebuilding-tables.html\" target=\"_blank\" rel=\"noopener\">MySQL 8.0 Reference Manual &#8211; Rebuilding or Repairing Tables or Indexes<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Database performance optimization is an essential practice in database management. Indexes speed up data retrieval from a database, thereby improving its performance.<\/p>\n<p>Learn what indexes are and how to use them to increase your MySQL database performance.<\/p>\n","protected":false},"author":1,"featured_media":21425,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[],"persona":[29],"blog-programming-language":[66],"keyword-cluster":[],"class_list":["post-21305","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"_links":{"self":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/21305","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/comments?post=21305"}],"version-history":[{"count":124,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/21305\/revisions"}],"predecessor-version":[{"id":32650,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/21305\/revisions\/32650"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/media\/21425"}],"wp:attachment":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/media?parent=21305"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/categories?post=21305"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/tags?post=21305"},{"taxonomy":"persona","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/persona?post=21305"},{"taxonomy":"blog-programming-language","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/blog-programming-language?post=21305"},{"taxonomy":"keyword-cluster","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/keyword-cluster?post=21305"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}