mysql中文全文檢索從入門到放棄

like全匹配模糊查詢不能使用索引一直是sql查詢的一個棘手的問題,那麼mysql的全文檢索真的能解決這個問題嗎?php

背景

最近,在工做中遇到一個查詢優化的問題,簡化的sql以下:java

SELECT
    * 
FROM
    wxswj_nsrxx 
WHERE
    nsrmc LIKE '%東鵬%' 
    OR nsrsbh LIKE '%東鵬%' 
    OR shxydm LIKE '%東鵬%';

問題:
一、採用了全匹配模糊查詢
二、使用了OR關鍵字mysql

很明顯這樣的查詢是不能走索引,而因爲表的數據量很是大,有500多萬數據,致使整個查詢的響應速度很是不理想。web

中文全文檢索實戰

ngram分詞插使用說明:
https://dev.mysql.com/doc/refman/5.7/en/fulltext-search-ngram.htmsql

一、優化思路:
中文模糊匹配查詢,主要涉及到分詞和全文檢索,而mysql裏面有一種索引類型就是全文索引FULLTEXT。因此想經過全文索引來解決mysql中全匹配模糊查詢的問題。數據庫

二、說明:
在MySQL 5.7.6以前,全文索引只支持英文全文索引,不支持中文全文索引,須要利用分詞器把中文段落預處理拆分紅單詞,而後存入數據庫。
從MySQL 5.7.6開始,MySQL內置了ngram全文解析器,用來支持中文分詞。緩存

三、查看當前數據庫版本:微信

select version() from dual;

結果爲5.7.28,支持中文全文檢索性能

四、全文檢索限制:
FULLTEXT indexes are created on text-based columns (CHAR, VARCHAR, or TEXT columns)
全文索引只能被建立在CHAR, VARCHAR, or TEXT的字段上。
每張表只能有一個全文檢索的索引
由多列組合而成的全文檢索的索引必須使用相同的字符集與排序規則測試

五、關閉查詢緩存
sql優化前,通常會關閉查詢緩存:
SHOW VARIABLES LIKE 'query_cache%';
set global query_cache_size=0;
set global query_cache_type=0;

SHOW VARIABLES LIKE 'query_cache%';

六、創建全文索引

ALTER TABLE `wxswj`.`wxswj_nsrxx`  ADD FULLTEXT INDEX `ft_index`(`nsrmc`,`nsrsbh`,`shxydm`) WITH PARSER ngram;

七、使用全文索引
經過MATCH (col1,col2,…) AGAINST (expr [search_modifier])語句,使用全文索引。

SELECT
    * 
FROM
    wxswj_nsrxx MATCH ( `nsrmc``nsrsbh``shxydm` ) against ( '東鵬' IN boolean MODE )

這裏使用東鵬去模糊匹配nsrmc, nsrsbh, shxydm這三個字段,任意一個字段中包含查詢關鍵字東鵬就返回對應記錄。

八、查詢執行計劃

使用了新建的組合全文檢索,ref達到const級別

九、優化效果
查詢性能提高了100多倍。

目前爲止,好像一切都很是美好,但很快坑就出現了。
當查詢的關鍵詞太長,就出現了異常?

問題一:FTS query exceeds result cache limit
當採用比較長的查詢條件去匹配執行查詢或甚至執行查詢計劃時,出現異常:

188 - FTS query exceeds result cache limit

mysql官網中對該異常的解釋說明:
https://bugs.mysql.com/bug.php?id=86036

每一個全文搜索查詢或每一個線程的InnoDB全文搜索都對查詢結果進行了緩存限制,以字節爲單位定義。中間和最終的InnoDB全文搜索查詢結果在內存中處理。可使用innodb_ft_result_cache_limit設置大小限制。全文搜索查詢結果緩存可避免InnoDB全文搜索查詢結果很是大(例如,數百萬或數億行)時過多的內存消耗。若是達到結果緩存大小限制,則返回錯誤,指示查詢超出了最大容許的內存。

推薦解決辦法:


一、增長innodb_ft_result_cache_limit的值,使其大於4G
SHOW VARIABLES LIKE 'innodb_ft_result_cache_limit%';
set global innodb_ft_result_cache_limit=4000000000;

二、優化查詢語句,限制查詢返回的記錄條數,減小來自中間結果的巨大緩存。通常經過顯示指定limit來限制。

問題二:查詢速度很是不穩定
咱們經過修改innodb_ft_result_cache_limit的值,解決了緩存限制的異常問題。
當時,咱們嘗試修改查詢條件時,發現查詢性能很是不穩定。
有時候查詢速度很是快,有時候甚至比不上like全匹配模塊查詢。
特別是當查詢條件很是長的時候,問題很是明顯,查詢性能徹底沒有保證。

SELECT
    * 
FROM
    wxswj_nsrxx MATCH ( `nsrmc``nsrsbh``shxydm` ) against ( '中國航天工業科學技術諮詢有限公司' IN boolean MODE )

放棄

經過調研各類資料,沒有找到比較好解決方案,最後仍是無奈選擇放棄。

測試語句

create table test(
id int(11not null primary key auto_increment,
name varchar(100not null comment '工商名',
brand varchar(100default null comment '品牌名',
en varchar(100default null comment '英文名',
fulltext key (name,brand,en) with parser ngram
)engine=innodb default charset=utf8;
insert into test (name,brand,en) values ('蕪湖美的廚衛電氣製造有限公司','aa','wh');
insert into test (name,brand,en) values ('北京凡客尚品電子商務有限公司','aa','ef');
insert into test (name,brand,en) values ('凡客誠品(北京)科技有限公司','aa','dfd');
insert into test (name,brand,en) values ('瞬聯訊通科技(北京)有限公司','aa','sdfs');
insert into test (name,brand,en) values ('北京暢捷通信有限公司','aa','wsdh');
insert into test (name,brand,en) values ('北京暢捷通支付技術有限公司','aa','df');
insert into test (name,brand,en) values ('暢捷通訊息技術股份有限公司','aa','whdfgh');
insert into test (name,brand,en) values ('北京暢捷科技有限公司','aa','dgdf');
insert into test (name,brand,en) values ('中國航天工業科學技術諮詢有限公司','aa','whffgh');
insert into test (name,brand,en) values ('北京·松下彩色顯象管有限公司','aa','wfghfgh');
insert into test(name,brand,en) select name,brand,en from test;
insert into test(name,brand,en) select name,brand,en from test;
insert into test(name,brand,en) select name,brand,en from test;
insert into test(name,brand,en) select name,brand,en from test;
insert into test(name,brand,en) select name,brand,en from test;
insert into test(name,brand,en) select name,brand,en from test;

EXPLAIN  SELECT  *  from  test  where  match  (name,brand,en)  against  ('通信錄' IN BOOLEAN MODELIMIT 100;

建立的測試數據總數據量爲:655360
select count(*) from test;

SELECT  *  from  test  where name like '%美的%' or brand like '%美的%' or en like '%美的%';
耗時:0.544

EXPLAIN  SELECT  *  from  test  where  match  (name,brand,en)  against  ('美的' IN BOOLEAN MODELIMIT 100;
耗時:0.150



SELECT  *  from  test  where name like '%蕪湖美的廚衛電氣製造有限公司%' or brand like '%蕪湖美的廚衛電氣製造有限公司%' or en like '%蕪湖美的廚衛電氣製造有限公司%';
耗時:0.679

EXPLAIN  SELECT  *  from  test  where  match  (name,brand,en)  against  ('蕪湖美的廚衛電氣製造有限公司' IN BOOLEAN MODELIMIT 100;
耗時:5.626

經過加雙引號,實現確切短語搜索,不對搜索條件進行分詞匹配,咱們測試下:


 SELECT  *  from  test  where  match  (name,brand,en)  against  ('"蕪湖美的廚衛電氣製造有限公司"' IN BOOLEAN MODELIMIT 100;
耗時:5.626

發現對查詢性能沒有影響。

經過實驗發現,當查詢條件越長,查詢性能越慢。
你們能夠本身測試感覺一下。

有關於mysql全文檢索使用好的建議,歡迎分享。

結論

本次實驗證實,mysql對全文檢索的支持有限,限制比較大,查詢性能也得不到保證,不少時候可能比不上直接使用like查詢。
幾十萬數據的小表,能夠考慮玩一下。
對一些大表須要全匹配模糊查詢時,首先是和業務方商量是否能夠只支持前匹配模糊查詢,其次儘量增長其餘查詢條件,另外經過limit限制匹配的記錄數。
複雜查詢下,而且必定要求對全匹配模糊查詢支持且對查詢性能有嚴格要求,那麼推薦使用Elasticsearch。

關注私聊我,免費領取視頻教程。




更多精彩,關注我吧。
圖注:跟着老萬學java



本文分享自微信公衆號 - 跟着老萬學java(douzhe_2019)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索