MySQL使您能够定义前缀索引,这意味着您要从要索引的原始字符串中定义前N个字符,诀窍是选择一个数字N,该数字足够长以提供良好的选择性,但又足够短以节省空间。前缀应该足够长,以使索引几乎与为整个列建立索引一样有用。
在继续之前,让我们定义一些重要的术语。索引选择性是总的不同索引值与总行数之比。这是测试表的一个示例:
+-----+-----------+
| id | value |
+-----+-----------+
| 1 | abc |
| 2 | abd |
| 3 | adg |
+-----+-----------+
如果我们仅索引第一个字符(N = 1),那么索引表将如下表所示:
+---------------+-----------+
| indexedValue | rows |
+---------------+-----------+
| a | 1,2,3 |
+---------------+-----------+
在这种情况下,指数选择性等于IS = 1/3 = 0.33。
现在让我们看看如果将索引字符的数量增加到两个(N = 2)会发生什么。
+---------------+-----------+
| indexedValue | rows |
+---------------+-----------+
| ab | 1,2 |
| ad | 3 |
+---------------+-----------+
在这种情况下,IS = 2/3 = 0.66,这意味着我们增加了索引的选择性,但同时也增加了索引的大小。关键是要找到的最小数N,这将导致以最大的索引选择性。
您可以通过两种方法对数据库表进行计算。我将在此数据库转储上进行演示。
假设我们要在表employees中添加列last_name到索引,并且要定义最小的数字N,它将产生最佳的索引选择性。
首先,让我们确定最常用的姓氏:
select count(*) as cnt, last_name from employees group by employees.last_name order by cnt
+-----+-------------+
| cnt | last_name |
+-----+-------------+
| 226 | Baba |
| 223 | Coorg |
| 223 | Gelosh |
| 222 | Farris |
| 222 | Sudbeck |
| 221 | Adachi |
| 220 | Osgood |
| 218 | Neiman |
| 218 | Mandell |
| 218 | Masada |
| 217 | Boudaillier |
| 217 | Wendorf |
| 216 | Pettis |
| 216 | Solares |
| 216 | Mahnke |
+-----+-------------+
15 rows in set (0.64 sec)
如您所见,姓Baba是最常用的。现在,我们将查找最常见的last_name前缀,从五个字母的前缀开始。
+-----+--------+
| cnt | prefix |
+-----+--------+
| 794 | Schaa |
| 758 | Mande |
| 711 | Schwa |
| 562 | Angel |
| 561 | Gecse |
| 555 | Delgr |
| 550 | Berna |
| 547 | Peter |
| 543 | Cappe |
| 539 | Stran |
| 534 | Canna |
| 485 | Georg |
| 417 | Neima |
| 398 | Petti |
| 398 | Duclo |
+-----+--------+
15 rows in set (0.55 sec)
每个前缀的出现次数更多,这意味着我们必须增加数字N,直到值几乎与前面的示例相同为止。
这是N = 9的结果
select count(*) as cnt, left(last_name,9) as prefix from employees group by prefix order by cnt desc limit 0,15;
+-----+-----------+
| cnt | prefix |
+-----+-----------+
| 336 | Schwartzb |
| 226 | Baba |
| 223 | Coorg |
| 223 | Gelosh |
| 222 | Sudbeck |
| 222 | Farris |
| 221 | Adachi |
| 220 | Osgood |
| 218 | Mandell |
| 218 | Neiman |
| 218 | Masada |
| 217 | Wendorf |
| 217 | Boudailli |
| 216 | Cummings |
| 216 | Pettis |
+-----+-----------+
这是N = 10的结果。
+-----+------------+
| cnt | prefix |
+-----+------------+
| 226 | Baba |
| 223 | Coorg |
| 223 | Gelosh |
| 222 | Sudbeck |
| 222 | Farris |
| 221 | Adachi |
| 220 | Osgood |
| 218 | Mandell |
| 218 | Neiman |
| 218 | Masada |
| 217 | Wendorf |
| 217 | Boudaillie |
| 216 | Cummings |
| 216 | Pettis |
| 216 | Solares |
+-----+------------+
15 rows in set (0.56 sec)
这是非常好的结果。这意味着我们可以在last_name列上建立索引,仅索引前10个字符。在表定义列中,last_name定义为VARCHAR(16)
,这意味着每个条目我们已保存6个字节(如果姓氏中包含UTF8字符,则更多)。在此表中,有1637个不同的值乘以6个字节,大约为9KB,并想象如果我们的表包含一百万行,这个数字将如何增长。
您可以阅读计算多种其他方式ñ在我的岗位在MySQL前缀索引。
使用MD5和SHA1函数生成应被索引的值也不是一个好方法。为什么?在帖子中阅读如何在MySQL数据库中为主键选择正确的数据类型