web-dev-qa-db-ja.com

類似性関数の最適なインデックス

したがって、このテーブルには620万件のレコードがあり、列の類似性を使用して検索クエリを実行する必要があります。クエリは次のとおりです。

 SELECT  "lca_test".* FROM "lca_test"
 WHERE (similarity(job_title, 'sales executive') > 0.6)
 AND worksite_city = 'los angeles' 
 ORDER BY salary ASC LIMIT 50 OFFSET 0

Where(year = X、worksite_state = N、status = 'certified'、visa_class = Z)にさらに条件を追加できます。

これらのクエリの一部を実行すると、30秒を超える非常に長い時間がかかる場合があります。時々1分以上。

EXPLAIN ANALYZE前述のクエリの結果は次のとおりです。

Limit  (cost=0.43..42523.04 rows=50 width=254) (actual time=9070.268..33487.734 rows=2 loops=1)
->  Index Scan using index_lca_test_on_salary on lca_test  (cost=0.43..23922368.16 rows=28129 width=254) (actual time=9070.265..33487.727 rows=2 loops=1)
>>>> Filter: (((worksite_city)::text = 'los angeles'::text) AND (similarity((job_title)::text, 'sales executive'::text) > 0.6::double precision))
>>>> Rows Removed by Filter: 6330130 Total runtime: 33487.802 ms
Total runtime: 33487.802 ms

列にインデックスを付けて高速に実行する方法を理解できません。

編集:これはpostgresバージョンです:

X86_64-unknown-linux-gnu上のPostgreSQL 9.3.5、gccでコンパイル(Debian 4.7.2-5)4.7.2、64ビット

以下はテーブルの定義です。

                                                         Table "public.lca_test"
         Column         |       Type        |                       Modifiers                       | Storage  | Stats target | Description
------------------------+-------------------+-------------------------------------------------------+----------+--------------+-------------
 id                     | integer           | not null default nextval('lca_test_id_seq'::regclass) | plain    |              |
 raw_id                 | integer           |                                                       | plain    |              |
 year                   | integer           |                                                       | plain    |              |
 company_id             | integer           |                                                       | plain    |              |
 visa_class             | character varying |                                                       | extended |              |
 employement_start_date | character varying |                                                       | extended |              |
 employement_end_date   | character varying |                                                       | extended |              |
 employer_name          | character varying |                                                       | extended |              |
 employer_address1      | character varying |                                                       | extended |              |
 employer_address2      | character varying |                                                       | extended |              |
 employer_city          | character varying |                                                       | extended |              |
 employer_state         | character varying |                                                       | extended |              |
 employer_postal_code   | character varying |                                                       | extended |              |
 employer_phone         | character varying |                                                       | extended |              |
 employer_phone_ext     | character varying |                                                       | extended |              |
 job_title              | character varying |                                                       | extended |              |
 soc_code               | character varying |                                                       | extended |              |
 naic_code              | character varying |                                                       | extended |              |
 prevailing_wage        | character varying |                                                       | extended |              |
 pw_unit_of_pay         | character varying |                                                       | extended |              |
 wage_unit_of_pay       | character varying |                                                       | extended |              |
 worksite_city          | character varying |                                                       | extended |              |
 worksite_state         | character varying |                                                       | extended |              |
 worksite_postal_code   | character varying |                                                       | extended |              |
 total_workers          | integer           |                                                       | plain    |              |
 case_status            | character varying |                                                       | extended |              |
 case_no                | character varying |                                                       | extended |              |
 salary                 | real              |                                                       | plain    |              |
 salary_max             | real              |                                                       | plain    |              |
 prevailing_wage_second | real              |                                                       | plain    |              |
 lawyer_id              | integer           |                                                       | plain    |              |
 citizenship            | character varying |                                                       | extended |              |
 class_of_admission     | character varying |                                                       | extended |              |
Indexes:
    "lca_test_pkey" PRIMARY KEY, btree (id)
    "index_lca_test_on_id_and_salary" btree (id, salary)
    "index_lca_test_on_id_and_salary_and_year" btree (id, salary, year)
    "index_lca_test_on_id_and_salary_and_year_and_wage_unit_of_pay" btree (id, salary, year, wage_unit_of_pay)
    "index_lca_test_on_id_and_visa_class" btree (id, visa_class)
    "index_lca_test_on_id_and_worksite_state" btree (id, worksite_state)
    "index_lca_test_on_lawyer_id" btree (lawyer_id)
    "index_lca_test_on_lawyer_id_and_company_id" btree (lawyer_id, company_id)
    "index_lca_test_on_raw_id_and_visa_and_pw_second" btree (raw_id, visa_class, prevailing_wage_second)
    "index_lca_test_on_raw_id_and_visa_class" btree (raw_id, visa_class)
    "index_lca_test_on_salary" btree (salary)
    "index_lca_test_on_visa_class" btree (visa_class)
    "index_lca_test_on_wage_unit_of_pay" btree (wage_unit_of_pay)
    "index_lca_test_on_worksite_state" btree (worksite_state)
    "index_lca_test_on_year_and_company_id" btree (year, company_id)
    "index_lca_test_on_year_and_company_id_and_case_status" btree (year, company_id, case_status)
    "index_lcas_job_title_trigram" gin (job_title gin_trgm_ops)
    "lca_test_company_id" btree (company_id)
    "lca_test_employer_name" btree (employer_name)
    "lca_test_id" btree (id)
    "lca_test_on_year_and_companyid_and_wage_unit_and_salary" btree (year, company_id, wage_unit_of_pay, salary)
Foreign-key constraints:
    "fk_Rails_8a90090fe0" FOREIGN KEY (lawyer_id) REFERENCES lawyers(id)
Has OIDs: no
8
bl0b

similarity()関数を提供する追加モジュール pg_trgm をインストールしたことを忘れていました。

類似度演算子%

まず、他に何をする場合でも、式%の代わりに、類似演算子(similarity(job_title, 'sales executive') > 0.6)を使用します。もっと安い。そして、インデックスのサポートは関数ではなく、Postgresのoperatorsにバインドされています。

0.6の望ましい最小類似度を取得するには、次のコマンドを実行します。

SELECT set_limit(0.6);

他の設定にリセットしない限り、設定はセッションの残りの部分に残ります。確認する:

SELECT show_limit();

これは少し不格好ですが、パフォーマンスには最適です。

シンプルなケース

文字列 'sales Executive'の列job_titleに最も一致したい場合、これは「最も近い隣人」検索の単純なケースであり、トライグラム演算子クラスGist_trgm_opsを使用してGistインデックスで解決できます(ただし、GINは使用できません)インデックス):

CREATE INDEX trgm_idx ON lcas USING Gist (job_title Gist_trgm_ops);

worksite_cityに等価条件も含めるには、追加モジュール btree_Gist が必要になります。実行(DBごとに1回):

CREATE EXTENSION btree_Gist;

次に:

CREATE INDEX lcas_trgm_Gist_idx ON lcas USING Gist (worksite_city, job_title Gist_trgm_ops);

クエリ:

SELECT set_limit(0.6);  -- once per session

SELECT *
FROM   lca_test
WHERE  job_title % 'sales executive'
AND    worksite_city = 'los angeles' 
ORDER  BY (job_title <-> 'sales executive')
LIMIT  50;

<-> 「距離」演算子:

1マイナスsimilarity()値。

Postgresは、2つの個別のインデックス、worksite_cityのプレーンなbtreeインデックス、およびjob_titleの個別のGistインデックスを組み合わせることもできますが、クエリでこのように2つのカラムを定期的に組み合わせる場合、複数カラムのインデックスが最も高速になります。

あなたの場合

ただし、クエリは距離/類似度ではなくsalaryで並べ替えられるため、ゲームの性質が完全に変わります。これでGINとGistインデックスの両方を使用できるようになり、GINはより高速になります(GINインデックスが大幅に改善されたPostgres 9.4ではさらにヒント-ヒント!)

worksite_cityの追加の等価チェックの同様のストーリー:追加のモジュールをインストールします btree_gin 。実行(DBごとに1回):

CREATE EXTENSION btree_gin;

次に:

CREATE INDEX lcas_trgm_gin_idx ON lcas USING gin (worksite_city, job_title gin_trgm_ops);

クエリ:

SELECT set_limit(0.6);  -- once per session

SELECT *
FROM   lca_test
WHERE  job_title % 'sales executive'
AND    worksite_city = 'los angeles' 
ORDER  BY salary 
LIMIT  50 -- OFFSET 0

繰り返しになりますが、これは、すでに持っているより単純なインデックス("index_lcas_job_title_trigram")と(おそらく他のインデックスと組み合わせて)機能します(効率が悪くなります)。最善の解決策は全体像に依存します。

アサイド

  • インデックスがたくさんあります。それらがすべて使用されていて、維持費を支払っていますか?

  • 疑わしいデータタイプがいくつかあります。

    employement_start_date | character varying
    employement_end_date   | character varying
    

    それらはdateである必要があるようです。等。

関連する回答:

13