2015-11-30 121 views
0

我的第一個attempt在一個問題被證明是令人困惑,我收到了一些混合的答案(可能是由於我的混淆問題)。這裏有一個不同的,更好的問題...使用尾隨通配符搜索first_name和last_name的最佳索引?

假設我的表看起來像這樣在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)是2個獨立的指標似乎比使用SQL_NO_CACHE的last_name(15),first_name(15)複合索引快?但也許我正在測試這個錯誤。我也在考慮,也許綜合索引和單個名稱上的索引組合會很好(如果這不會混淆優化器)?

獎勵題:
考慮到我在尋找局部的話,不完全的話,會像ElasticSearch爲此查詢的更好嗎?

+0

我想象一下,在搜索名字和姓氏時,組合索引會更快。但請注意,姓氏搜索只能使用(第一個,最後一個)索引 – Strawberry

+0

但我被告知在複合索引中對last_name使用通配符(尾隨)會使複合索引的其餘部分無用(右邊的列)。 – prograhammer

+0

該查詢將只使用一個索引。優化器將選擇最具選擇性的索引。 –

回答

1

你是對的,單獨的first_name和last_name索引會更好地工作。

根據我的經驗,複合索引最好在非變量字段(如2個數字)上。我會在每個姓名字段上使用一個索引。

如果你還沒有調整你的my.cnf設置,那麼調整可用於MySQL的內存可以在排序/索引搜索方面產生巨大的差異。國際海事組織關於my.cnf,這是一個完整的其他問題。你可以從這裏開始:https://dev.mysql.com/doc/refman/5.6/en/server-default-configuration-file.html。 Mysql與my-large.cnf,my-huge.cnf一起提供,所以這些應該給你一個好的開始。

+0

+1。真棒邁克!但讓我讓這個問題坐在我接受它的前一天。而對於my.cnf設置,你主要是指'innodb_buffer_pool'嗎? – prograhammer

+0

聽起來不錯,看我編輯my.cnf的變化。 – mikeb

+0

還有一件事Mike,如果表單字段都是必需的(first_name和last_name)是否有任何指向兩個索引?我應該去last_name索引吧?由於優化器可能不會執行索引合併,誰知道索引合併是否最好? – prograhammer

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 | 
+----+-------------+----------+-------+---------------+------+---------+------+------+----------+--------------------------+ 
+0

這是一個部分索引查找。它使用'first_name'部分。你看到你得到檢查rows = 6(所有行爲「ja」前綴爲first_name)。它沒有使用完整的組合,因爲它沒有使用'last_name'部分。 – prograhammer

+0

@prograhammer我看到了 - 我們知道它不使用整個索引,因爲它說'使用where'? – Strawberry

+1

這是一個很好的問題。我還沒有成爲EXPLAIN的主人。但我認爲'使用何處'並不能幫助你知道綜合指數是否被充分利用。它確實告訴你在索引f1上有6個被檢查行使用了一個範圍。但老實說,我甚至不確定被檢查的行是否是最好的知道的方法,因爲這並不總是準確的。但我剛剛發現這個http://dev.mysql.com/doc/refman/5.7/en/range-optimization.html#range-access-multi-part – prograhammer

1

添加到從@mikeb和@RickJames上述問題的答案,

MySQL的文檔說here

對於BTREE索引,間隔可能是合併 與條件可用AND,其中每個條件將關鍵部分與使用=,< =>,IS NULL,>,<,> =,< =,<>,BETWEEN或LIKE 'pattern'(其中'pattern'不以通配符開頭)。只要可以確定包含與條件匹配的所有行的單個密鑰元組(或者如果使用<>或!=,則兩個 區間),可以使用 區間。

只要比較運算符是=,< =>或IS NULL,優化程序就會嘗試使用其他關鍵部分來確定間隔 。如果 運算符>,<,> =,< =,!=,<>,BETWEEN或LIKE,優化程序 使用它,但不考慮更多關鍵部分。對於以下表達式, 優化程序使用第一個比較中的=。它還使用> =從 第二比較但認爲沒有進一步的關鍵部件和不 使用第三比較區間施工

key_part1 = '富' AND key_part2> = 10且key_part3> 10

單間隔爲:

( '富',10,-INF)<(key_part1,key_part2,key_part3)<( '富',+ INF,+ INF)

創建的區間可能包含比 初始條件更多的行。例如,前面的間隔包含 值('foo',11,0),它不符合原始條件。

在複合材料的關鍵部分使用LIKE時,不使用右側的關鍵部分。因此,這證實了@mikeb所說的兩個單一索引會更好地工作,因爲MySQL可以判斷哪一個具有更好的基數並使用它。 但是,我最終使用了Rick Jameslast_name,first_name,person_id(前綴/大小刪除)的答案,因爲我只是選擇了person_id。這起到了覆蓋索引的作用,並且在測試中的工作速度(可能更快)比單獨的索引工作得更快,並且可以使用last_name和first_name進行良好的排序。無論如何,複合鍵通常是更好的方法。

+1

好的參考 - 它涵蓋了大部分問題。 –

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%。

進一步討論Building an index from a SELECTCompound indexes

+0

太棒了!看起來像'INDEX(last_name,first_name,person_id)'是要走的路(沒有前綴,並且我還將varchar(255)縮減爲名稱字段的varchar(70))。 – prograhammer