MySql中的VARCHAR字段上可能的INDEX


40

我正在使用这样的表在MySql数据库中工作:

+--------------+
|  table_name  |
+--------------+
|    myField   |
+--------------+

...并且我需要进行很多这样的查询(列表中有5-10个字符串)

SELECT myField FROM table_name
WHERE myField IN ('something', 'other stuff', 'some other a bit longer'...)

大约有24.000.000个唯一行

1)我应该使用FULLTEXTor或and INDEXVARCHAR(150)吗?
2)如果我将字符数从150增加到220或250 ...会产生很大的不同吗?(有什么方法可以计算出来吗?)
3)正如我所说,它们将是唯一的,因此myField应该是PRIMARY KEY。在已经是VARCHAR INDEX / FULLTEXT的字段中添加PRIMARY KEY难道不是很罕见吗?


您无需将PRIMARY用于唯一性。已经有唯一的。
kommradHomer 2014年

Answers:


62

建议1:标准索引

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    key (myfield)
);

如果您这样索引,则可以查找整个字符串,也可以进行向左的LIKE搜索

建议2:全文索引

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    fulltext (myfield)
);

您可以有效地使用搜索单个关键字以及整个短语。您将需要定义一个自定义停用词列表,因为MySQL不会索引543个词

这是我过去两年中有关FULLTEXT索引的其他帖子

建议3:哈希索引

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    hashmyfield char(32) not null,
    primary key (id),
    key (hashmyfield)
);

如果要查找一个特定值,并且这些值的长度可能超过32个字符,则可以存储哈希值:

INSERT INTO mytable (myfield,hashmyfield)
VALUES ('whatever',MD5('whatever'));

这样,您只需搜索哈希值即可检索结果

SELECT * FROM mytable WHERE hashmyfield = MD5('whatever');

试试看 !!!


我没有足够的声誉来投票赞成你的答案,但我必须说这是很棒的。感谢您的解释和示例。我认为哈希索引最适合我的情况,这是一个很棒的解决方案。但是仍然有一个问题:您认为表中快速搜索的行数限制是多少?[使用VARCHAR(32)作为关键字进行搜索]
马克·塔

2
这里的hash选项仍然是一个文本和32个字节,实际上是16个字节。您可以将bigint字段与conv(left(md5('whatever'),16),16,-10)一起使用。没有16字节的数字,但是您可能会发现一半的md5就足够了,那么索引中的字节数仅为8个字节
atxdba 2014年

1
使用MD5或SHA1生成将被索引的字符串不是很好。由MD5或SHA1之类的哈希函数产生的字符串的分布在很大的空间内是随机的,这会降低索引的效率,从而降低INSERT和SELECT语句的速度。下面是文章,解释它:code-epicenter.com/...
Mr.M

我很抱歉,因为这是一个老话题,但是我的问题与此直接相关,但是通过阅读以上内容和其他类似文章,我无法明确满足我的需求。我的情况是:我正在开发一个非常基本的库存系统,目前仅由一个表组成。它可以通过API从外部访问,因此所有配置都保存在其他位置-这就是为什么我们只需要一个表的原因。我正在考虑建立索引的两列,每列大约有200个唯一的条目,长度小于20个字符。我应该考虑添加索引吗?
迈克(Mike)

这是向左搜索like 'a%'吗?
会计师م18年

18

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数据库中为主键选择正确的数据类型


这是对另一个问题的非常冗长的答案。
mustaccio

1
你在跟我开玩笑吗?
M.M先生2015年

您能解释什么是错误的,或什么不能应用于该问题吗?
M.M先生2015年

2
嘿MrD。我真的很喜欢你的答案。为什么呢 在我的旧答案中,我在建议1中说:If you index like this, you can either look for the whole string or do left-oriented LIKE searches。我在建议#3中也说过:If you are looking for one specific value and those values could be lengths well beyond 32 characters, you could store the hash value:。您的答案充分说明了为什么不应该使用巨大的键,而应该在最左边的字符上建立索引,这可能会影响性能。您的答案属于这里。+1作为您的答案,欢迎使用DBA StackExchange。
RolandoMySQLDBA 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.