[英]Best index for searching both first_name and last_name with trailing wildcards?
我對一個問題的第一次嘗試被證明是令人困惑的,並且我得到了一些混雜的答案(可能是由於我令人困惑的問題)。 這是另一個更好的問題...
假設我的表在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)
兩個單獨索引比使用last_name(15),first_name(15)
的復合索引快SQL_NO_CACHE? 但是也許我正在測試這個錯誤。 我還考慮將復合索引和單個名稱上的索引結合起來會很好(如果這不會使優化程序感到困惑)?
獎勵問題:
考慮到我要搜索部分單詞而不是完整單詞,像ElasticSearch這樣的查詢會更好嗎?
沒錯,單獨的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,因此這些應該為您提供一個良好的開端。
除了上述@mikeb和@RickJames的答案之外,
MySQL文檔在這里說:
對於BTREE索引,區間可用於與AND組合的條件,其中每個條件使用=,<=>,IS NULL,>,<,> =,<=,!=, <>,BETWEEN或類似“模式”(其中“模式”不以通配符開頭)。 只要可以確定包含所有與條件匹配的行的單個鍵元組,就可以使用一個間隔(如果使用<>或!=,則可以使用兩個間隔)。
只要比較運算符為=,<=>或IS NULL,優化器就會嘗試使用其他關鍵部分來確定間隔。 如果運算符是>,<,> =,<=,!=,<>,BETWEEN或LIKE,則優化器將使用它,但不再考慮其他關鍵部分。 對於以下表達式,優化器使用第一個比較中的=。 它還從第二次比較中使用> =,但是不考慮其他關鍵部分, 並且不將第三次比較用於區間構造 :
key_part1 ='foo'AND key_part2> = 10 AND key_part3> 10
單個間隔為:
('foo',10,-inf)<(key_part1,key_part2,key_part3)<('foo',+ inf,+ inf)
創建的間隔可能包含比初始條件更多的行。 例如,前面的間隔包含不滿足原始條件的值('foo',11,0)。
在組合的關鍵部分上使用LIKE時,不使用右側的關鍵部分。 因此,這證實了@mikeb所說的兩個單一索引會更好,因為MySQL可以判斷哪個具有更好的基數並使用它。 但是,由於我只選擇person_id ,因此我最終使用了Rick James的答案,其中last_name,first_name,person_id
(刪除了前綴/大小)。 這可以作為覆蓋索引,並且在我的測試中可以比單獨的單個索引更快(可能更快)運行,而且還可以按last_name然后first_name進行排序。 無論如何,組合鍵通常是更好的選擇。
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%。
進一步的討論 : 從SELECT和Composite索引建立索引 。
好像用了鍵?!?
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 |
+----+-------------+----------+-------+---------------+------+---------+------+------+----------+--------------------------+
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.