完全一致ファジークエリがインデックスを使用できないのと同じように、常にsqlクエリの厄介な問題なので、mysqlの全文検索で本当にこの問題を解決できますか?
バックグラウンド
最近、作業中にクエリの最適化の問題が発生しました。簡略化したSQLは次のとおりです。
SELECT
*
FROM
wxswj_nsrxx
WHERE
nsrmc LIKE '%东鹏%'
OR nsrsbh LIKE '%东鹏%'
OR shxydm LIKE '%东鹏%';
質問:
1.完全一致ファジークエリが使用されている
2. ORキーワードが使用されている
明らかに、このようなクエリにはインデックスを付けることができません。また、テーブルのデータ量は非常に大きく、500万以上のデータがあるため、クエリ全体の応答速度は非常に不十分です。
実際の中国語全文検索
ngramセグメンテーション挿入の手順:
https ://dev.mysql.com/doc/refman/5.7/en/fulltext-search-ngram.html リンクの説明を追加
1.最適化のアイデア:
中国のあいまい一致クエリには、主に単語のセグメンテーションと全文検索が含まれ、mysqlには全文索引FULLTEXTの索引タイプがあります。ですから、フルテキストインデックスを使用して、mysqlの完全一致ファジークエリの問題を解決したいと思います。
2.説明:
MySQL 5.7.6より前のフルテキストインデックスは、英語のフルテキストインデックスのみをサポートしており、中国語のフルテキストインデックスはサポートしていません。中国語の段落を単語に前処理してデータベースに保存するには、単語セグメンターを使用する必要があります。
MySQL 5.7.6以降、MySQLには中国語の単語分割をサポートする組み込みのngramフルテキストパーサーがあります。
3.現在のデータベースバージョンを表示します。
select version() from dual;
結果は5.7.28で、中国語の全文検索をサポートしています
4.フルテキスト検索の制限:
FULLTEXTインデックスはテキストベースの列(CHAR、VARCHAR、またはTEXTカラム)
に作成されますフルテキストインデックスは、CHAR、VARCHAR、またはTEXTカラムにのみ作成できます。
各テーブルには1つの全文検索インデックスのみを含めることができます
。複数の列で構成される全文検索インデックスは、同じ文字セットと照合順序を使用する必要があります。
5.クエリキャッシュの
sql最適化を閉じる前に、クエリキャッシュは通常閉じられています:
SHOW VARIABLES LIKE'query_cache% ';
set global query_cache_size = 0;
set global query_cache_type = 0;
SHOW VARIABLES LIKE 'query_cache%';
6.フルテキストインデックスを作成する
ALTER TABLE `wxswj`.`wxswj_nsrxx` ADD FULLTEXT INDEX `ft_index`(`nsrmc`,`nsrsbh`,`shxydm`) WITH PARSER ngram;
7.フルテキストインデックスを使用
する** MATCH(col1、col2、...)AGAINST(expr [search_modifier])**ステートメントでフルテキストインデックスを使用します。
SELECT
*
FROM
wxswj_nsrxx MATCH ( `nsrmc`, `nsrsbh`, `shxydm` ) against ( '东鹏' IN boolean MODE )
ここでは东鹏
、ファジーマッチングの3つのフィールドが使用されnsrmc
、いずれかのフィールドにクエリキーが含まれている場合nsrsbh
、対応するレコードが返さshxydm
れます东鹏
。
8.クエリ実行プラン
は新しく結合された全文検索を使用し、refはconstレベルに達します
9.最適化効果
クエリのパフォーマンスが100倍以上向上しました。
ピット
これまでのところ、すべてが非常に良いようですが、すぐにピットが現れました。
クエリキーワードが長すぎると、例外が発生しますか?
質問1:
比較的長いクエリ条件を使用してクエリを照合したり、クエリプランを実行したりすると、FTSクエリが結果キャッシュの制限を超え、例外が発生します。
188 - FTS query exceeds result cache limit
mysql公式Webサイトの例外の説明:https :
//bugs.mysql.com/bug.php?id=86036
各スレッドの全文検索クエリまたはInnoDB全文検索には、クエリ結果に対するキャッシュ制限があり、バイト単位で定義されます。中間および最終のInnoDB全文検索クエリ結果はメモリで処理されます。innodb_ft_result_cache_limitを使用してサイズ制限を設定できます。フルテキスト検索クエリ結果のキャッシュにより、InnoDBフルテキスト検索クエリ結果が非常に大きい場合(たとえば、数百万または数億行)、過度のメモリ消費を回避できます。結果のキャッシュサイズの制限に達すると、エラーが返され、クエリが最大許容メモリを超えたことを示します。
推奨される解決策:
1. innodb_ft_result_cache_limitの値を増やして、4Gより大きくします。
SHOW VARIABLES LIKE 'innodb_ft_result_cache_limit%';
set global innodb_ft_result_cache_limit=4000000000;
2.クエリステートメントを最適化し、クエリによって返されるレコードの数を制限し、中間結果からの巨大なキャッシュを減らします。通常、指定された制限を表示することで制限されます。
問題2:クエリ速度が非常に不安定
であるinnodb_ft_result_cache_limitの値を変更することで、キャッシュ制限の異常な問題を解決しました。
当時、クエリの条件を変更しようとすると、クエリのパフォーマンスが非常に不安定であることがわかりました。
クエリの速度は非常に速い場合もあれば、完全一致モジュールのクエリほどよくない場合もあります。
特にクエリ条件が非常に長い場合、問題は非常に明白であり、クエリのパフォーマンスはまったく保証されません。
SELECT
*
FROM
wxswj_nsrxx MATCH ( `nsrmc`, `nsrsbh`, `shxydm` ) against ( '中国航天工业科学技术咨询有限公司' IN boolean MODE )
あきらめる
いろいろな資料を調べた結果、もっと良い解決策が見つからず、やっとあきらめて諦めました。
テストステートメント
create table test(
id int(11) not null primary key auto_increment,
name varchar(100) not null comment '工商名',
brand varchar(100) default null comment '品牌名',
en varchar(100) default 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 MODE) LIMIT 100;
作成されるテストデータの合計量は次のとおりです。655360
テストから選択カウント(*)。
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 MODE) LIMIT 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 MODE) LIMIT 100;
耗时:5.626
二重引用符を追加することにより、完全なフレーズ検索が実現され、検索条件が単語のセグメンテーションと一致しなくなります。テストしてみましょう。
SELECT * from test where match (name,brand,en) against ('"芜湖美的厨卫电气制造有限公司"' IN BOOLEAN MODE) LIMIT 100;
耗时:5.626
クエリのパフォーマンスへの影響はありません。
実験により、クエリ条件が長くなるほど、クエリのパフォーマンスが低下することがわかっています。
自分でテストして感じることができます。
mysqlの全文検索の使用に関する提案を共有していただけます。
結論として
この実験は、MySQLの全文検索のサポートが制限されており、制限が比較的大きく、クエリのパフォーマンスが保証されていないことを証明しています。多くの場合、同様のクエリを直接使用するほど良くない場合があります。
数十万のデータを含む小さなテーブルを再生することを検討してください。
一部の大きなテーブルで完全一致ファジークエリが必要な場合は、まずビジネス側と事前一致ファジークエリしかサポートできないかどうかを話し合い、次に他のクエリ条件を可能な限り増やし、一致するレコードの数を制限で制限します。
複雑なクエリでは、完全一致のファジークエリサポートとクエリパフォーマンスに関する厳しい要件が必要な場合は、Elasticsearchをお勧めします。
プライベートチャットでフォローして、ビデオチュートリアルを無料で受け取ってください。
もっとわくわくしてください。