簡體   English   中英

搜索帶有尾隨通配符的first_name和last_name的最佳索引?

[英]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
...

假設將josmi輸入到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%。

進一步的討論從SELECTComposite索引建立索引

好像用了鍵?!?

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM