MySql 中 VARCHAR 字段的可能索引

Mar*_*wer 51 mysql index full-text-search varchar

我在MySql 数据库中工作,有一个这样的表:

+--------------+
|  table_name  |
+--------------+
|    myField   |
+--------------+
Run Code Online (Sandbox Code Playgroud)

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

SELECT myField FROM table_name
WHERE myField IN ('something', 'other stuff', 'some other a bit longer'...)
Run Code Online (Sandbox Code Playgroud)

将有大约 24.000.000 个唯一行

1)我应该使用FULLTEXTor 和INDEXkeyVARCHAR(150)吗?
2)如果我将字符从 150 增加到 220 或 250 ......会产生很大的不同吗?(有没有办法计算它?)
3)正如我所说,它们将是唯一的,所以myField应该是一个PRIMARY KEY。向已经是 VARCHAR INDEX/FULLTEXT 的字段添加 PRIMARY KEY 是不是很少见?

Rol*_*DBA 81

建议 #1:标准索引

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    key (myfield)
);
Run Code Online (Sandbox Code Playgroud)

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

建议#2:全文索引

CREATE TABLE mytable
(
    id int not null auto_increment,
    myfield varchar(255) not null,
    primary key (id),
    fulltext (myfield)
);
Run Code Online (Sandbox Code Playgroud)

您可以有效地搜索单个关键字以及整个短语。您将需要定义一个自定义停用词列表,因为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)
);
Run Code Online (Sandbox Code Playgroud)

如果您正在寻找一个特定值并且这些值的长度可能远远超过 32 个字符,您可以存储散列值:

INSERT INTO mytable (myfield,hashmyfield)
VALUES ('whatever',MD5('whatever'));
Run Code Online (Sandbox Code Playgroud)

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

SELECT * FROM mytable WHERE hashmyfield = MD5('whatever');
Run Code Online (Sandbox Code Playgroud)

试一试 !!!

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

Mr.*_*r.M 20

MySQL 使您能够定义前缀索引,这意味着您可以从要索引的原始字符串中定义前 N 个字符,诀窍是选择一个足够长的数字 N 以提供良好的选择性,但又足够短以节省空间。前缀应该足够长,以使索引几乎与索引整个列时一样有用。

在我们进一步讨论之前,让我们定义一些重要的术语。索引选择性总不同索引值与总行数的比率。这是测试表的一个示例:

+-----+-----------+
| id  | value     |
+-----+-----------+
| 1   | abc       |
| 2   | abd       |
| 3   | adg       |
+-----+-----------+
Run Code Online (Sandbox Code Playgroud)

如果我们只索引第一个字符(N=1),那么索引表将如下表所示:

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| a             | 1,2,3     |
+---------------+-----------+
Run Code Online (Sandbox Code Playgroud)

在这种情况下,索引选择性等于 IS=1/3 = 0.33。

现在让我们看看如果我们将索引字符的数量增加到两个(N=2)会发生什么。

+---------------+-----------+
| indexedValue  | rows      |
+---------------+-----------+
| ab             | 1,2      |
| ad             | 3        |
+---------------+-----------+
Run Code Online (Sandbox Code Playgroud)

在这种情况下,IS=2/3=0.66 这意味着我们增加了索引选择性,但我们也增加了索引的大小。关键是要找到的最小数N,这将导致以最大的索引选择性

有两种方法可以对数据库表进行计算。我将对此数据库转储进行演示。

假设我们要将表雇员中的列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)
Run Code Online (Sandbox Code Playgroud)

如您所见,姓氏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)
Run Code Online (Sandbox Code Playgroud)

每个前缀的出现次数要多得多,这意味着我们必须增加 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    |
+-----+-----------+
Run Code Online (Sandbox Code Playgroud)

这是 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)
Run Code Online (Sandbox Code Playgroud)

这是非常好的结果。这意味着我们可以在列last_name上创建索引,只索引前 10 个字符。在表定义中,last_name列被定义为VARCHAR(16),这意味着我们为每个条目节省了 6 个字节(如果姓氏中有 UTF8 字符则更多)。在这个表中有 1637 个不同的值乘以 6 个字节大约是 9KB,想象一下如果我们的表包含数百万行,这个数字会如何增长。

您可以在我的帖子Prefixed index in MySQL 中阅读其他计算N数的方法。

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

  • 你在开玩笑吧? (3认同)
  • 嘿,D先生。我其实很喜欢你的回答。为什么 ?在我的旧答案中,我在 SUGGESTION #1 中说:`如果你这样索引,你可以查找整个字符串或进行向左的 LIKE 搜索`。我还在 SUGGESTION #3 中说过:`如果你正在寻找一个特定的值并且这些值的长度可能远远超过 32 个字符,你可以存储散列值:`。您的回答充分说明了为什么不应该使用大键而应该对最左边的字符进行索引,这会对性能产生影响。你的答案属于这里。+1 为您解答,欢迎使用 DBA StackExchange。 (3认同)