web-dev-qa-db-ja.com

オートコンプリートが遅すぎる:可能な最適化?

私のWebサイトのオートコンプリート検索機能は、販売アイテムのモデル番号を含むvarcharフィールドを検索します。

このフィールドには1〜75文字の文字列を含めることができ、テーブルには400 000行が含まれます。文字列の先頭からのみ検索し、実行に約150〜250ミリ秒かかるクエリを考え出しましたが、許容範囲内ですが、上司がクエリに部分文字列を検索させ、クエリを3〜10倍遅くします(およそ1000-2000ms)。

私はJSフィドルを作成して、データがどのように見え、2つのクエリがどのように見えるかの例を示します。

http://sqlfiddle.com/#!6/9efa3/2/

テーブルにはすでにいくつかのインデックスがあります。このオートコンプリート検索フィールドを高速化するためのベストプラクティスは何ですか? (データベースバージョンはSQLSERVER 2008R2です)

これが私が扱っているデータの簡単な例です:

CREATE TABLE [Products](
    [productid] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [model] [nvarchar](75) NOT NULL 
 CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED 
(
    [productid] ASC
));

insert into products values ('UMPX1AA0011 danish e-315 woot');
insert into products values ('P27y719VC');
insert into products values ('VG2y439m-LED');
insert into products values ('UMUyX165AAB01');
insert into products values ('U28y79VF');
insert into products values ('U52417HJ');
insert into products values ('VA25746M-LED WITH FLYING CORNERS');
insert into products values ('S19F350HNN 1pc california storage');
insert into products values ('VA211917A');
insert into products values ('PM2500X2');
insert into products values ('E22470SWHE');
insert into products values ('V22465WLYDP');
insert into products values ('I129LMH1HKC');
insert into products values ('OM5EN X 35 the new version');
insert into products values ('DLS3060WDB');
insert into products values ('PVW');
insert into products values ('LI23721S');
insert into products values ('V173516LBM');
insert into products values ('VX2376-SMHD-A');
insert into products values ('GUM5FX1AA1001');
insert into products values ('GPM300X11');
insert into products values ('GUM-WH6AA002');
insert into products values ('2435V5LSB');
insert into products values ('P2418HZ');
insert into products values ('Stylish sectional one of a kind y-5151');

これらは私が比較している2つのクエリです

--runs acceptably fast, about 100-250ms
select * from products where model like 'y-5151'+'%';
--takes too long, around 1000-2500ms
select * from products where model like '%' + 'y-5151' +'%'
2
A_V

私が行った解決策は、彼の以下の2つのブログ投稿で Aaron Bertrand によって提案されているように、すべてのモデル#サブストリングの前処理バージョンを含む「ハーフトリアングラム」テーブルを作成することです。

https://sqlperformance.com/2017/02/sql-indexes/seek-leading-wildcard-sql-server

https://sqlperformance.com/2017/02/sql-performance/follow-up-1-leading-wildcard-seeks

私にとっての解決策は、検索文字列で始まるモデルを探す新しいテーブルを作成することでした。各モデルはそのようにリストされています。たとえば、product_IDが51、モデルは7500 Twin Bed

ID|Model
----------------
51|7500 Twin Bed
51|500 Twin Bed
51|00 Twin Bed
51|0 Twin Bed
51| Twin Bed
51|Twin Bed
51|win Bed
51|in Bed
51|n Bed
51| Bed
51|Bed
51|ed
51|d

これにより、完全なワイルドカード検索を行う必要がなくなり、単純なselect distinct id_product from products_dictionary where model like 'Twin%'は必要な結果を返します。クエリの所要時間が100ミリ秒未満になりました。

これが、テーブルを作成してデータを設定するために使用したコードです。繰り返しますが、これはすべてAaronのブログ投稿で適切に説明されています。

CREATE TABLE [dbo].[products_dictionary](
    [Id_product] [int],
    [model] [nvarchar](75) NOT NULL
)
insert into [products_dictionary] 
select p.id_product,f.fragment from products p 
cross apply dbo.CreateStringFragments(p.model) AS f;

create clustered index index_idprod_substrmodel on [Products_dictionary]([model],[Id_product])
4
A_V

フルテキストインデックスは、テーブル全体を毎回スキャンするのではなく、文字列値から作成されたディクショナリに基づいているため、複雑で集中的な文字列検索を実行する必要がある場合、通常は「移動」オプションです(これは、 '%一部のテキスト% 'タイプの検索)、ディクショナリを参照するクエリオプティマイザーが判断すると、代わりにインデックスシークを使用します。

テーブルを作成し、100,000レコードを入力しました。

次のような通常の検索パターンを使用するだけです:

_select * from products where model like '%' + 'kind' +'%'_

私はこの結果を得ました: enter image description here

同じクエリを使用して、1行_select * from products where model like '%' + 'human' +'%'_を返す他のWordと同じクエリを使用して、1102レコードが返されたことに注意してください。

enter image description here

現在、フルテキストインデックスを使用しています。

クエリの使用:select * from products where freetext(model,'kind')

enter image description here

スキャンの代わりにインデックスシークを使用するため、返される行が少ない場合は、実際に最適に機能します。

_select  * from products where freetext(model,'Human') 
_

enter image description hereenter image description here

注意する別のこと:

なんらかのブロッキングが発生している場合、クエリが予想よりも「遅くなる」ことがあります。これは、誰かがレコードを更新または挿入しようとして、その行を含むページで(X)ロックを保持しているときに発生する可能性があります。全テーブルスキャンのため、 '%some text%'形式を使用してクエリを検索するときに要求するのと同じページです。

フルテキストインデックスを使用すると、その時点で更新されたまったく同じレコードを検索しない限り、インデックスシークを使用してこれらの状況を回避できます。

多くの検索バリエーション、ファジーマッチング、「スペルチェック」バリエーションもあり、これらは非常に役立ちます。

適切な検索クエリの作成に役立つ本SQL Server 2008のプロフルテキスト検索があります。

2
S4V1N