我在问题上的第一个attempt被证明是混乱的,我得到了一些混合的答案(可能是由于我的疑惑问题)。这是一个不同的更好的问题......
假设我的表在MySQL中看起来像这样:
CREATE TABLE `people` (
`person_id` INT(11),
`alias_num` TINYINT(3),
`first_name` VARCHAR(255) NOT NULL,
`last_name` VARCHAR(255) NOT NULL,
PRIMARY KEY (`person_id`,`alias_num`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB;
使用这样的数据:
person_id alias_num first_name last_name
--------- --------- ---------- ---------
1 1 John Smith
2 1 Joe Smith
3 1 Bill Smith # <-- Notice this guy has 3 aliases
3 2 Billy Smith # <--
3 3 William Smith # <--
4 1 Susan Thompson
...
假设jo
和smi
已输入HTML搜索表单(需要两个字段),我的查询将始终如下:
SELECT person_id FROM people WHERE first_name LIKE 'jo%' AND last_name LIKE 'smi%';
问题:为了使上述查询最快,添加到我的表中的最佳索引是什么?
注意:
我对几乎一百万行的表做了一些快速测试,看起来first_name(15)
和last_name(15)
的两个单独索引看起来比使用SQL_NO_CACHE的last_name(15),first_name(15)
的复合索引更快?但也许我正在测试这个错误。我也在考虑,复合索引和单个名称上的索引的组合可能会很好(如果这不会混淆优化器)?
奖金问题:
考虑到我正在搜索部分单词而非完整单词,ElasticSearch会更好地进行此查询吗?
答案 0 :(得分:1)
您是正确的,单独的first_name和last_name索引将更好地工作。
根据我的经验,复合索引最适合非变量字段(如2个数字)。我在每个名字字段上都有一个索引。
如果您还没有调整my.cnf设置,调整MySQL可用的内存可能会对索引的排序/搜索产生巨大差异。
至于my.cnf,那是另外一个问题,IMO。你可以从这里开始:https://dev.mysql.com/doc/refman/5.6/en/server-default-configuration-file.html。 Mysql附带my-large.cnf,my-huge.cnf所以那些应该给你一个良好的开端。
答案 1 :(得分:1)
从@mikeb和@RickJames添加上述答案,
MySQL文档说here:
对于BTREE索引,间隔可用于组合条件 使用AND,其中每个条件将关键部分与常量进行比较 值使用=,&lt; =&gt;,IS NULL,&gt;,&lt;,&gt; =,&lt; =,!=,&lt;&gt;,BETWEEN或LIKE 'pattern'('pattern'不以通配符开头)。一个 可以使用间隔,只要可以确定单个 包含与条件匹配的所有行的键元组(或两个 间隔如果&lt;&gt;或!=使用)。
优化程序尝试使用其他关键部分来确定 间隔,只要比较运算符是=,&lt; =&gt;或IS NULL。如果 运算符是&gt;,&lt;,&gt; =,&lt; =,!=,&lt;&gt;,BETWEEN或LIKE,优化程序 使用它但不再考虑关键部分。对于以下表达式, 优化程序使用=来自第一次比较。它还使用&gt; = from 第二个比较,但没有考虑其他关键部分,没有考虑 使用间隔构造的第三个比较:
key_part1 ='foo'AND key_part2&gt; = 10 AND key_part3&gt; 10
单个间隔是:
('foo',10,-inf)&lt; (key_part1,key_part2,key_part3)&lt; ( '富',+ INF,+ INF)
创建的间隔可能包含的行数多于 初始条件。例如,前面的间隔包括 值('foo',11,0),它不满足原始条件。
在复合的关键部分使用LIKE时,不使用右侧的关键部分。因此,这证实了@mikeb所说的两个单一索引可以更好地工作,因为MySQL可以判断哪一个具有更好的基数并使用它。 然而,由于我只选择了person_id,我最终使用了Rick James的答案 last_name,first_name,person_id
(前缀/大小已删除)。这充当覆盖索引,并且在我的测试中的工作速度(可能更快)比单个单独的索引更快,并且通过last_name然后first_name给我很好的排序。复合键通常是更好的方式。
答案 2 :(得分:1)
SELECT person_id FROM people WHERE first_name LIKE 'jo%' AND last_name LIKE 'smi%';
案例1 - 覆盖(罕见):所有 整个 SELECT
的字段都包含在索引中。这些都是“覆盖”和最佳:
INDEX(first_name, last_name, person_id)
INDEX(last_name, first_name, person_id)
<覆>“覆盖”意味着它完成了索引中的所有工作,并且不需要触摸数据。注意:“数据”和PRIMARY KEY
共同生活在一个BTree中;每个二级索引都存在于另一个BTree中。
案例2 - 非覆盖:如果您不想或不能(因为TEXT
等)包含所有字段,则其中任何一个都是最佳:
INDEX(first_name)
INDEX(last_name)
创建两个索引并让优化器动态选择更好的索引。由于外卡,INDEX(first_name, last_name)
没有用;它不会越过索引的第一列。
前缀:不使用first_name(15)
。它不会节省太多空间,并且不有助于提高性能。与案例2一样,不将超过复合索引中的第一列。
(255):不要随意使用VARCHAR(255)
。 255参与了可能用于执行SELECT
的临时表的详细信息,并且您将减慢查询的速度,以及合理的最大长度。在某些情况下,您将超出限制,不允许构建索引。
辅助密钥:在InnoDB中,每个“辅助密钥”都隐式包含PRIMARY KEY
中的所有列。因此,INDEX(first_name, last_name)
实际上会包含person_id
(和alias_num
),因此与我推荐的INDEX(first_name, last_name, person_id)
相同。
INDEX(a)和INDEX(a,b):前者实际上总是多余的;只保留后者。
my.cnf :此讨论最重要的设置是将innodb_buffer_pool_size
设置为可用 RAM的约70%。
答案 3 :(得分:0)
似乎使用了密钥?!?
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,first_name VARCHAR(12) NOT NULL
,last_name VARCHAR(12) NOT NULL
,INDEX fl (first_name,last_name)
);
INSERT INTO my_table (first_name,last_name) VALUES
('John','Brown'),
('John','Smith'),
('John','Johnson'),
('John','Lewis'),
('John','Lennon'),
('John','Major'),
('James','Brown'),
('James','McIlroy'),
('James','Napier'),
('Jamie','Oliver'),
('James','May'),
('James','Martin');
SELECT * FROM my_table WHERE first_name LIKE 'Ja%' AND last_name LIKE 'Bro%';
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 7 | James | Brown |
+----+------------+-----------+
EXPLAIN
SELECT * FROM my_table WHERE first_name LIKE 'Ja%' AND last_name LIKE 'Bro%';
+----+-------------+----------+-------+---------------+------+---------+------+------+----------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------+-------+---------------+------+---------+------+------+----------+--------------------------+
| 1 | SIMPLE | my_table | range | fl | fl | 28 | NULL | 6 | 100.00 | Using where; Using index |
+----+-------------+----------+-------+---------------+------+---------+------+------+----------+--------------------------+