{"id":106156,"date":"2025-05-06T06:38:00","date_gmt":"2025-05-06T06:38:00","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=106156"},"modified":"2025-04-08T19:31:41","modified_gmt":"2025-04-08T19:31:41","slug":"mysql-index-overviews-hash-indexes","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/mysql\/mysql-index-overviews-hash-indexes\/","title":{"rendered":"MySQL Index Overviews: Hash Indexes"},"content":{"rendered":"\n<p>Welcome back to the MySQL indexing series! We\u2019ve already covered <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/mysql\/the-nuances-of-mysql-indexes\/\">the nuances surrounding indexes in MySQL<\/a> as well as <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/mysql\/mysql-index-overviews-b-tree-indexes\/\">walked you through B-tree indexes<\/a>.<\/p>\n\n\n\n<p>MySQL, however, has many other index types it can offer for your use case, and one of those indexes are hash indexes, too.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-what-are-hash-indexes\">What are Hash Indexes?<\/h2>\n\n\n\n<p>In MySQL, hash indexes are indexes that are used in queries that use the equality operators like <code>=<\/code> or <code>&lt;=><\/code> (which is the MySQL <code>NULL<\/code> safe equality operator, equivalent to the SQL Standard <code>IS NOT DISTINCT<\/code> <code>FROM<\/code>). Hash indexes are not used in other situations, so they can be useful for things like random or generated PRIMARY KEY values where you are looking up single rows and not needing to order rows or searching for a range of values.<\/p>\n\n\n\n<p><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.4\/en\/index-btree-hash.html#hash-index-characteristics\">As per the documentation<\/a>, MySQL only supports the use of hash indexes for the <code>MEMORY<\/code> storage engine, and these kinds of indexes are known to facilitate blazing-fast single-row searches through data residing in memory. Hash indexes may start to become an option when index structures can save your disk in return for occupying computational power and the memory available on your server.<\/p>\n\n\n\n<p style=\"padding-right:0;padding-left:var(--wp--preset--spacing--md)\">Note: Such indexes can also be defined within other storage engines (I\u2019ll get to that in a second); however, in use cases not revolving around the <code>MEMORY<\/code> storage engine, hash indexes are likely to be turned into B-tree indexes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-the-subtleties-of-hash-indexes\">The Subtleties of Hash Indexes<\/h2>\n\n\n\n<p>Hash indexes are not as widely implemented as B-Tree indexes, and as mentioned, part of that is their narrow use case. But even though they can be useful, there are a few concerns that you need to have to be aware of:<\/p>\n\n\n\n<p><em><strong>For the hash table, one needs to know the number of rows likely to be within the table<\/strong>.<\/em> That helps with determining whether the amount of memory available within our server will be sufficient to store hash indexes on our data.<\/p>\n\n\n\n<p><span style=\"background-color: rgba(0, 0, 0, 0.2);\"><em><b><i>Hash indexes may have to <\/i><\/b><\/em><\/span><strong><em>be rebuilt from time to time for them to be usefu<\/em>l. <\/strong>Rebuilding hash indexes may become necessary when your data changes considerably\u2014if you have larger or smaller tables than expected, are not shutting down your server, and want your index to shrink, hash indexes may have to be rebuilt. <br><br>That\u2019s where we come to the upsides of storing such indexes in the memory of your server because as our data grows, that could add an overhead on our disk I\/O, but because hash indexes primarily work with data residing in memory, the rebuilding of such index types is likely to be significantly faster.<\/p>\n\n\n\n<p><em><strong>Hash indexes are used for in-memory tables.<\/strong> <\/em>Since the only way to acquire and work with in-memory tables within MySQL and its counterparts is the usage of <code>MEMORY<\/code> storage engine, the use cases of hash indexes will also involve <code>MEMORY<\/code>-based tables within MySQL. <\/p>\n\n\n\n<p>Aside from that, it would be a shame not to note that disk-based storage engines such as InnoDB provide support for a type of hash index called Adaptive Hash Indexes. More on this later in the article.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-hash-indexes-in-mariadb\">Hash Indexes in MariaDB<\/h2>\n\n\n\n<p>Now that you\u2019ve familiarized yourself with hash indexes, it\u2019s time to dig into what makes them tick. I\u2019ll use a demo table to demonstrate hash indexes in action. Its structure will be defined like so (<em>note \u2013 here, we don\u2019t denote <\/em><code>MEMORY<\/code><em> as our storage engine to demonstrate how MariaDB \u201cthinks in InnoDB\u201d internally when hash indexes are defined<\/em>):<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"block lang:mysql\" highlight=\"true\" decode=\"true\">DROP TABLE IF EXISTS TABLE `demo_table`;\n\nCREATE TABLE `demo_table` (\n  `id` INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,\n  `first_name` VARCHAR(50) NOT NULL,\n  `last_name` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019,\n  `email` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019,\n  `gender` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019,\n  `ip` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019\n);<\/pre><\/div>\n\n\n\n<p>After we have the InnoDB table up and running, we will also drop a hash-based index on top of it using the <code>CREATE INDEX<\/code> query:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"block lang:mysql\" highlight=\"true\" decode=\"true\">CREATE INDEX `hash_idx` ON `demo_table`(`first_name`) USING HASH;<\/pre><\/div>\n\n\n\n<p>When we combine the <code>CREATE INDEX<\/code> clause with the <code>CREATE TABLE<\/code> query in phpMyAdmin, we have the following result:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1536\" height=\"573\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\August 2024\\Hash Indexes\\Images\\Hash Indexes in MariaDB - Creating a Table.png\" class=\"wp-image-106157\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima.png 1536w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-300x112.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-1024x382.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-768x287.png 768w\" sizes=\"auto, (max-width: 1536px) 100vw, 1536px\" \/><\/figure>\n\n\n\n<p><br> <strong>Image 1 \u2013 Creating Hash Indexes when Creating a Table in MariaDB<\/strong><\/p>\n\n\n\n<p>After we execute the code and then inspect the structure of our table once, we can see that the hash index is indeed a <code>BTREE<\/code> index under the hood \u2013 <em>that happened because the default table storage engine definition for all tables that don\u2019t have the storage engine defined is InnoDB<\/em>:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1145\" height=\"227\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-1.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\August 2024\\Hash Indexes\\Images\\Hash Index = BTREE Index.png\" class=\"wp-image-106158\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-1.png 1145w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-1-300x59.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-1-1024x203.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-1-768x152.png 768w\" sizes=\"auto, (max-width: 1145px) 100vw, 1145px\" \/><\/figure>\n\n\n\n<p><br>  <strong>Image 2 \u2013 The Hash Index you expected to create is actually created as a BTREE Index<\/strong><\/p>\n\n\n\n<p>Let\u2019s now try doing the same thing with a table running the <code>MEMORY<\/code> storage engine instead (uses the same code, with one small change to the <code>CREATE TABLE<\/code> statement to denote the usage of the <code>MEMORY<\/code> storage engine):<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"block lang:mysql\" highlight=\"true\" decode=\"true\">DROP TABLE IF EXISTS TABLE `demo_table_mem`;\n\nCREATE TABLE `demo_table_mem` (\n`id` INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,\n`first_name` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019,\n`last_name` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019,\n`email` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019,\n`gender` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019,\n`ip` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019\n) ENGINE = MEMORY; -- &lt; add this\n\nCREATE INDEX `hash_idx` ON `demo_table_mem`(`first_name`) USING HASH;<\/pre><\/div>\n\n\n\n<p>Now, execute the create statements again, and you will see a different output:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1512\" height=\"643\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-2.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\August 2024\\Hash Indexes\\Images\\Hash Indexes on a MEMORY Table.png\" class=\"wp-image-106159\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-2.png 1512w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-2-300x128.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-2-1024x435.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-2-768x327.png 768w\" sizes=\"auto, (max-width: 1512px) 100vw, 1512px\" \/><\/figure>\n\n\n\n<p><br><strong>Image 3 \u2013 Defining a Hash Index on the MEMORY Storage Engine<\/strong><\/p>\n\n\n\n<p>If we inspect the memory-based table structure now, we will see that <em>all of our indexes are hash indexes instead<\/em>:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1512\" height=\"310\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-3.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\August 2024\\Hash Indexes\\Images\\Indexes on a MEMORY Table.png\" class=\"wp-image-106160\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-3.png 1512w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-3-300x62.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-3-1024x210.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-3-768x157.png 768w\" sizes=\"auto, (max-width: 1512px) 100vw, 1512px\" \/><\/figure>\n\n\n\n<p><br><strong>Image 4 \u2013 Hash Indexes in MariaDB<\/strong><\/p>\n\n\n\n<p>Even the primary key index is a hash index! Interesting, right?<\/p>\n\n\n\n<p>That\u2019s because our table is based on the <code>MEMORY<\/code> storage engine, and <em>as all of its data is stored in memory, MySQL doesn\u2019t have to default to using B-tree indexes and can suggest (and choose) the faster option<\/em> \u2013 a hash-based index \u2013 instead.<\/p>\n\n\n\n<p>The same is true for clustered indexes \u2013 define an <code>AUTO_INCREMENT<\/code> on an <code>ID<\/code> column on a memory-based storage engine, and you will have a hash-based clustered index, too \u2013 it will be hyper-fast to its clustered nature and hash-based structure. Cool, right?<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1100\" height=\"158\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\2024\\August 2024\\Hash Indexes\\Images\\AutoIncrement on MEMORY Table - Query.png\" class=\"wp-image-106161\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe.png 1100w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-300x43.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-1024x147.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-768x110.png 768w\" sizes=\"auto, (max-width: 1100px) 100vw, 1100px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>Image 5 \u2013 Creating an AUTO_INCREMENT Column Using the MEMORY Storage Engine<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1072\" height=\"83\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-1.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\2024\\August 2024\\Hash Indexes\\Images\\AutoIncrement on MEMORY Table - Results.png\" class=\"wp-image-106162\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-1.png 1072w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-1-300x23.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-1-1024x79.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-1-768x59.png 768w\" sizes=\"auto, (max-width: 1072px) 100vw, 1072px\" \/><\/figure>\n\n\n\n<p><br> <strong>Image 6 \u2013 the AUTO_INCREMENT Column on a MEMORY Table is a HASH Index<\/strong><\/p>\n\n\n\n<p>Now, we will insert some data into our table and review how such indexes work.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-hash-indexes-in-action\">Hash Indexes in Action<\/h2>\n\n\n\n<p>First, let\u2019s insert some data into our in-memory-based table \u2013 for that, we can make use of ordinary <code>INSERT<\/code> or <code>LOAD DATA INFILE<\/code> statements without inventing anything revolutionary either (you can see the details for loading the data in the Appendix):<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1356\" height=\"136\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-4.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\August 2024\\Hash Indexes\\Images\\LOAD DATA INFILE on MEMORY table.png\" class=\"wp-image-106163\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-4.png 1356w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-4-300x30.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-4-1024x103.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-4-768x77.png 768w\" sizes=\"auto, (max-width: 1356px) 100vw, 1356px\" \/><\/figure>\n\n\n\n<p><br><strong>Image 7 &#8211; LOAD DATA INFILE Into a MEMORY Table<\/strong><\/p>\n\n\n\n<p>Everything works as usual \u2013 <code>COUNT(*)<\/code> queries too:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"611\" height=\"184\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-5.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\August 2024\\Hash Indexes\\Images\\SELECT COUNT.png\" class=\"wp-image-106164\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-5.png 611w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-5-300x90.png 300w\" sizes=\"auto, (max-width: 611px) 100vw, 611px\" \/><\/figure>\n\n\n\n<p><br><strong>Image 8 &#8211; COUNT(*) On a MEMORY Table<\/strong><\/p>\n\n\n\n<p>One thing I would direct your attention to is the time taken by MariaDB to complete each query \u2013 the first query was completed in 0.01 sec., and the second one in 0.002 sec., and that\u2019s not because we heavily optimized our database, but because our data resides in memory and can be accessed much faster. This can be confirmed once we run some basic tests on an InnoDB table and one running the <code>MEMORY<\/code> storage engine.<\/p>\n\n\n\n<p>First, note that the same table (the structure is the same, too) <em>will occupy space in memory the same way it occupies space on the disk,<\/em> meaning that, likely, you won\u2019t be able to store as much data in memory as you would be able to store on the disk. The <code>MEMORY<\/code> storage engine has some more caveats, too:<\/p>\n\n\n<div class=\"block-core-list\">\n<ol class=\"wp-block-list\">\n<li><code>MEMORY<\/code> tables are similar to MyISAM tables in that they perform full-table locks when performing <code>INSERT<\/code>, <code>UPDATE<\/code>, and <code>DELETE<\/code> operations. <em>X amount of these operations means X amount of full table locks per second.<\/em><\/li>\n\n\n\n<li><code>MEMORY<\/code> tables still incur disk I\/O: the MySQL daemon will contact the .frm file belonging to the <code>MEMORY<\/code> table to verify that the table exists before parsing an SQL query. In other words, if you run many SQL queries against a <code>MEMORY<\/code> table, you can notice some disk I\/O. <\/li>\n<\/ol>\n<\/div>\n\n\n<p><a href=\"https:\/\/dba.stackexchange.com\/questions\/10806\/mysql-memory-table-getting-many-locks\/10821&quot; \\l &quot;10821\">These caveats are discussed in a StackOverflow thread by a former Pythian employee<\/a>, and they do have an impact on how you work with your database and the operations performed within. One of those operations may be a comparison of the storage engines (<code>InnoDB<\/code> vs. <code>MEMORY<\/code>):<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"block lang:mysql\" highlight=\"true\" decode=\"true\">SELECT * \nFROM `users_innodb`\nWHERE `email` = \u2018sample@sample.com\u2019;<\/pre><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1197\" height=\"203\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-2.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\2024\\August 2024\\Hash Indexes\\Images\\InnoDB vs. Memory - InnoDB Query Part 2.png\" class=\"wp-image-106165\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-2.png 1197w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-2-300x51.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-2-1024x174.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-2-768x130.png 768w\" sizes=\"auto, (max-width: 1197px) 100vw, 1197px\" \/><\/figure>\n\n\n\n<p><br><strong>Image 9 \u2013 InnoDB Table Example<\/strong><\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"block lang:mysql\" highlight=\"true\" decode=\"true\">SELECT * \nFROM `users_memory`\nWHERE `email` = \u2018sample@sample.com\u2019;<\/pre><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1199\" height=\"198\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-3.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\2024\\August 2024\\Hash Indexes\\Images\\InnoDB vs. Memory - MEMORY Query Part 2.png\" class=\"wp-image-106166\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-3.png 1199w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-3-300x50.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-3-1024x169.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-2024-august-2024-hash-indexe-3-768x127.png 768w\" sizes=\"auto, (max-width: 1199px) 100vw, 1199px\" \/><\/figure>\n\n\n\n<p><br><strong>Image 10 \u2013 MEMORY Table Example<\/strong><\/p>\n\n\n\n<p>Here, the difference may be negligible because we only have 5,000 rows we run our tests on; once the row count in our tables increases, the results will become more and more visible. However, at the same time, the amount of space taken on the memory will increase accordingly \u2013 chances are that you don\u2019t have more than 64GB of RAM available, so tread that line carefully.<\/p>\n\n\n\n<p>To understand these results, consider this illustration of hash indexes:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"333\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/05\/image-1024x333.png\" alt=\"\" class=\"wp-image-106175\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/05\/image-1024x333.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/05\/image-300x97.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/05\/image-768x250.png 768w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/05\/image.png 1305w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><strong>Image 11 \u2013 Basic Illustration of Hash Indexes in MySQL<\/strong><\/p>\n\n\n\n<p>Since hash indexes use a hash function to map keys to an index position (see illustration above), such an algorithm becomes very useful when searching for specific data. Let\u2019s try hash indexes out \u2013 we will do that with 7 queries, which will search for different things using the equality, less than, more than, or all of those operators. We will:<\/p>\n\n\n<div class=\"block-core-list\">\n<ol class=\"wp-block-list\">\n<li> Search for an <code>ID<\/code> value using the \u201cmore than\u201d operator. (<code>&gt;<\/code>) <\/li>\n\n\n\n<li> Search for an <code>ID<\/code> value using the \u201cequal or more than\u201d operator. (<code>&gt;=<\/code>) <\/li>\n\n\n\n<li> Search for a string value with an exact match. (<code>=<\/code>) <\/li>\n\n\n\n<li> Search for two exact string values using an OR operator. <\/li>\n\n\n\n<li> Utilize a <code>LIKE<\/code> query that uses an index (we won\u2019t have a wildcard in front of the query.) <\/li>\n\n\n\n<li> Search for an <code>ID<\/code> value using the <code>NULL<\/code> safe equality operator. (<code>&lt;=&gt;<\/code>) <\/li>\n\n\n\n<li> Run a search using the \u201cless than or equal to\u201d operator. (<code>&lt;=<\/code>) <\/li>\n<\/ol>\n<\/div>\n\n\n<p>Our results will look like so:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1300\" height=\"943\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-6.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\August 2024\\Hash Indexes\\Images\\EXPLAIN SELECT.png\" class=\"wp-image-106167\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-6.png 1300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-6-300x218.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-6-1024x743.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-6-768x557.png 768w\" sizes=\"auto, (max-width: 1300px) 100vw, 1300px\" \/><\/figure>\n\n\n\n<p><br><strong>Image 12 \u2013 EXPLAINing HASH Indexes in MariaDB<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1193\" height=\"379\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-7.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\August 2024\\Hash Indexes\\Images\\EXPLAIN SELECT 2.png\" class=\"wp-image-106168\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-7.png 1193w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-7-300x95.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-7-1024x325.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-7-768x244.png 768w\" sizes=\"auto, (max-width: 1193px) 100vw, 1193px\" \/><\/figure>\n\n\n\n<p><br><strong>Image 13 \u2013 EXPLAINing HASH Indexes in MariaDB Part 2<\/strong><\/p>\n\n\n\n<p>Attentive readers will notice that the internal functionality of hash indexes is somewhat different than that of B-trees. From the examples above, we can notice that, unlike B-tree indexes that can be used together with the <code>=<\/code>, <code>&lt;<\/code>, <code>&gt;=<\/code>, <code>&lt;<\/code>, <code>&lt;=<\/code> or <code>BETWEEN<\/code> operators, hash-based indexes can only be considered for use <em>if we search for an exact match together with the <\/em><code>=<\/code><em> or <\/em><code>&lt;=&gt;<\/code><em> operators.<\/em><\/p>\n\n\n\n<p>In other words, hash indexes are useless if we are searching for more than a single value because our database won\u2019t even consider such an index for use in the first place. That happens because hash indexes are used to improve data access speed when employing them in a single-value lookup operation and not anything else \u2013 as the core premise of such indexes is data residing in memory, they don\u2019t have the luxury of assisting searches for a range of values or anything in that realm, and that\u2019s why queries using clauses like <code>BETWEEN<\/code>, <code>LIKE<\/code>, <code>><\/code>, or <code>&lt;<\/code> are out of the question. They won\u2019t make use of a hash index, no matter how your query is written.<\/p>\n\n\n\n<p>To find a row using a hash index, your database will calculate the hash value of the value you\u2019re searching for, then perform a search of the buckets of values that hashed to that same value. If it finds a result in the bucket, the result is returned.<\/p>\n\n\n\n<p>The examples above support all <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.4\/en\/index-btree-hash.html#hash-index-characteristics\">hash index characteristics defined in the documentation of MySQL<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-mysql-s-adaptive-hash-index\">MySQL\u2019s Adaptive Hash Index<\/h2>\n\n\n\n<p>Many users of MySQL will also be aware of an index called the adaptive hash index. The InnoDB adaptive hash index acts as a feature assisting in-memory hash lookups for indexing. InnoDB builds the adaptive hash index at runtime, and such an index adapts to your workload in the sense that if it \u201csenses\u201d that a particular value is being looked up more often, it creates an entry in itself either with the full value or its prefix, depending on your use case. <em>Then, if such a value is searched for once again, MySQL will then use the Adaptive Hash Index instead of a B-tree index.<\/em><\/p>\n\n\n\n<p>Such an index can be enabled by fiddling with <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.4\/en\/innodb-parameters.html#sysvar_innodb_adaptive_hash_index\">the innodb_adaptive_hash_index variable<\/a> (available values include 0 and 1), and it can be used to \u201cadd a layer\u201d between MySQL and the InnoDB buffer pool. Then, the entire structure of your database would look like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"174\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/05\/image-1-1024x174.png\" alt=\"\" class=\"wp-image-106178\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/05\/image-1-1024x174.png 1024w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/05\/image-1-300x51.png 300w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/05\/image-1-768x130.png 768w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/05\/image-1.png 1351w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><strong>Image 14 \u2013 The Adaptive Hash Index in Between the InnoDB Buffer Pool and the Disk<\/strong><\/p>\n\n\n\n<p>In MySQL, MariaDB, and Percona Server, the adaptive hash index is enabled by default and will be built automatically if InnoDB deems it to be necessary:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"649\" height=\"313\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-8.png\" alt=\"D:\\Misc\\Redgate\\Blogs\\August 2024\\Hash Indexes\\Images\\SHOW VARIABLES innodb adaptive.png\" class=\"wp-image-106169\" srcset=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-8.png 649w, https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2025\/04\/d-misc-redgate-blogs-august-2024-hash-indexes-ima-8-300x145.png 300w\" sizes=\"auto, (max-width: 649px) 100vw, 649px\" \/><\/figure>\n\n\n\n<p><br><strong>Image 15 &#8211; An Adaptive Hash Index is Enabled by Default<\/strong><\/p>\n\n\n\n<p>Coming back to hash indexes, though, understand that a hash index works by taking the keys of the indexed values and \u201chashing them\u201d into output values. As such, and as I\u2019ve already mentioned numerous times in this blog post alone, hash indexes should only be used if we\u2019re 100% certain that we will only ever need a column to be looked up with equality operators and not with anything else.<\/p>\n\n\n\n<p>To recap, consider a hash index when:<\/p>\n\n\n<div class=\"block-core-list\">\n<ul class=\"wp-block-list\">\n<li> The primary types of SQL queries you find yourself running are related to equality comparisons. If you\u2019re building an app on a key-value store, hash indexes can help you. <\/li>\n\n\n\n<li> You <em>don\u2019t<\/em> have a necessity to search for a range of values. In other words, if your query is using the \u201cless than\u201d or \u201cmore than\u201d operators to assist your use case, hash indexes won\u2019t work. <\/li>\n\n\n\n<li> Your use case involves storing data in memory (for that, consider using the <code>MEMORY<\/code> storage engine or InnoDB with the Adaptive Hash Index if your use case necessitates InnoDB.) <\/li>\n\n\n\n<li>You carefully consider the upsides and downsides of the <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.4\/en\/optimization-indexes.html\">multiple types of indexes available in MySQL<\/a>, and the usage of a B-tree index doesn\u2019t satisfy your use case.<\/li>\n\n\n\n<li> Your queries don\u2019t include an ORDER BY clause (if they do, a hash index wouldn\u2019t even be considered for use.) <\/li>\n<\/ul>\n<\/div>\n\n\n<p>Finally, remember that hash indexes are only as useful as the memory within your server\u2014exceed that limit, and your data is cactus. Don\u2019t store too much data in memory: such indexes have drawbacks, and those drawbacks can burn your database to ashes if not considered in full.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-summary\">Summary<\/h2>\n\n\n\n<p>Hash indexes are an exclusive type of index that makes searching through in-memory values as quick as possible by avoiding disk I\/O. The downside of this kind of index type in MySQL is that it can only be directly with the <code>MEMORY<\/code> storage engine (or in InnoDB with an <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/8.4\/en\/innodb-adaptive-hash.html\">Adaptive Hash Index<\/a> in some cases), that the only kinds of data that can be indexed is in-memory data due to the storage engine in use, and that SQL queries will only make use of a hash index when an exact equality operator is in use.<\/p>\n\n\n\n<p>Nonetheless, these kinds of indexes have their use case: it\u2019s up to you to decide whether the use case is something that should be used in your specific scenario or not. No matter which kind of index type you elect to use, keep in mind that each one of them has upsides and downsides unique to themselves: choose the one most useful for your specific use case and until next time.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-appendix-table-structure-amp-data\">Appendix \u2013 Table Structure &amp; Data<\/h2>\n\n\n\n<p>The table structure and data in this blog were generated by a <a href=\"https:\/\/www.mockaroo.com\/\">demo data generator Mockaroo<\/a>. The data used in examples can be found at <a href=\"https:\/\/ghostbin.cloud\/v6svw\">https:\/\/ghostbin.cloud\/v6svw<\/a>.<\/p>\n\n\n\n<p>The table structure in question is as follows:<\/p>\n\n\n\n<div class=\"wp-block-urvanov-syntax-highlighter-code-block\"><pre class=\"block lang:mysql\" highlight=\"true\" decode=\"true\">CREATE TABLE `demo_table_mem` (\n  `id` INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,\n  `first_name` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019,\n  `last_name` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019,\n  `email` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019,\n  `gender` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019,\n  `ip` VARCHAR(50) NOT NULL DEFAULT \u2018\u2019\n) [ENGINE = InnoDB|MEMORY|\u2026];<\/pre><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Welcome back to the MySQL indexing series! We\u2019ve already covered the nuances surrounding indexes in MySQL as well as walked you through B-tree indexes. MySQL, however, has many other index types it can offer for your use case, and one of those indexes are hash indexes, too. What are Hash Indexes? In MySQL, hash indexes&#8230;&hellip;<\/p>\n","protected":false},"author":339547,"featured_media":106170,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[53,145792],"tags":[5854],"coauthors":[146040],"class_list":["post-106156","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-featured","category-mysql","tag-mysql"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/106156","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/339547"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=106156"}],"version-history":[{"count":4,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/106156\/revisions"}],"predecessor-version":[{"id":106179,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/106156\/revisions\/106179"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/106170"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=106156"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=106156"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=106156"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=106156"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}