我正在使用这样的表在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)我应该使用FULLTEXT还是and和INDEX我的VARCHAR(150)的密钥吗?2)如果我将字符数从150增加到220或250 ...会产生很大的不同吗? (是否有任何计算方法?)3)正如我说的那样,它们将是唯一的,因此myField应该是PRIMARY KEY。在已经是VARCHAR INDEX / FULLTEXT的字段中添加PRIMARY KEY难道不是很常见吗?

评论

您无需将PRIMARY用于唯一性。已经存在唯一性。

#1 楼

建议#1:标准索引编制
CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    key (myfield)
);

如果这样索引,则可以查找整个字符串,也可以进行左向搜索,例如
建议#2:全文索引编制
CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    fulltext (myfield)
);

您可以有效地使用搜索单个关键字以及整个短语。您将需要定义一个自定义停用词列表,因为MySQL不会索引543个单词。
这是我过去两年中关于FULLTEXT索引的其他帖子


May 23, 2011:优化mysql全文搜索(StackOverflow)


Oct 25, 2011:在带有“字数”的条件下的布尔语言模式中忽略FULLTEXT索引


Jan 26, 2012:Mysql全文搜索.cnf优化


May 07, 2012:MySQL EXPLAIN不显示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)作为关键字进行搜索]

–马克塔
13年3月3日在12:54

此处的哈希选项仍然是文本和32字节,实际上是16字节。您可以将bigint字段与conv(left(md5('whatever'),16),16,-10)一起使用。没有一个16字节的数字,但是您可能会发现md5的一半足够了,那么索引中只有8个字节

– atxdba
2014年1月15日23:47

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

– M先生
15年11月28日在14:10

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

–迈克尔
17年5月18日在15:51

是像“ a%”这样的向左搜索吗?

–会计م
18年4月3日在12:09

#2 楼

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)


如您所见,姓氏是最常用的姓氏。现在,我们要查找最常见的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,并想象一下,如果我们的表包含一百万行,该数字将如何增长。

您可以阅读其他方法来计算N中的N我的文章在MySQL中使用前缀索引。

使用MD5和SHA1函数生成应索引的值也不是一个好方法。为什么?在帖子中阅读如何在MySQL数据库中为主键选择正确的数据类型

评论


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

–必须
15年11月28日在15:31

你在跟我开玩笑吗?

– M先生
15年11月28日在15:34

您能解释什么是错误的,或什么不能应用于该问题吗?

– M先生
15年11月28日在15:38

嘿MrD。我真的很喜欢你的答案。为什么呢在我的旧答案中,我在建议#1中说过:如果您像这样进行索引,则可以查找整个字符串或进行面向左的LIKE搜索。我在建议#3中也说过:如果您要查找一个特定值,并且这些值的长度可能超过32个字符,则可以存储哈希值:。您的答案充分说明了为什么不应该使用大按键,而应该在最左边的字符处建立索引,这可能会影响性能。您的答案属于这里。 +1作为您的答案,欢迎使用DBA StackExchange。

– RolandoMySQLDBA
15年11月28日在20:05

@ Mr.M,您不应该在哈希表上使用索引吗?这将为您提供最大的索引选择性,并且作为奖励,您可以选择简单地通过增加/减少N来接近100%。

–起搏器
20年6月11日15:37