Нет преимуществ индексов для вашей структуры данных / запроса. Просто представьте, как здесь можно использовать индексы. Мне не повезло.
Я предлагаю преобразовать домены / суффиксы в массивы, например
alter table "companyDomain" add column adomain text[];
update "companyDomain" set adomain = string_to_array(domain, '.');
create index idx_adom on "companyDomain" using gin (adomain array_ops);
alter table "publicSuffix" add column asuffix text[];
update "publicSuffix" set asuffix = string_to_array(ltrim(suffix, '.'), '.');
create index idx_asuffix on "publicSuffix" using gin (asuffix array_ops);
Давайте сравним эти запросы:
ostgres=# explain (analyze, verbose, buffers)
SELECT DISTINCT ON ("companyDomain".id)
"companyDomain".domain,
"publicSuffix".suffix
FROM
"companyDomain"
INNER JOIN "publicSuffix" ON REVERSE("companyDomain".domain) LIKE REVERSE("publicSuffix".suffix) || '%'
ORDER BY "companyDomain".id, LENGTH("publicSuffix".suffix) DESC;
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ QUERY PLAN │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Unique (cost=185738.35..185940.72 rows=908 width=31) (actual time=2364.720..2364.890 rows=908 loops=1) │
│ Output: "companyDomain".domain, "publicSuffix".suffix, "companyDomain".id, (length(("publicSuffix".suffix)::text)) │
│ Buffers: shared hit=306 │
│ -> Sort (cost=185738.35..185839.53 rows=40474 width=31) (actual time=2364.719..2364.764 rows=1006 loops=1) │
│ Output: "companyDomain".domain, "publicSuffix".suffix, "companyDomain".id, (length(("publicSuffix".suffix)::text)) │
│ Sort Key: "companyDomain".id, (length(("publicSuffix".suffix)::text)) DESC │
│ Sort Method: quicksort Memory: 103kB │
│ Buffers: shared hit=306 │
│ -> Nested Loop (cost=0.00..182641.13 rows=40474 width=31) (actual time=22.735..2364.484 rows=1006 loops=1) │
│ Output: "companyDomain".domain, "publicSuffix".suffix, "companyDomain".id, length(("publicSuffix".suffix)::text) │
│ Join Filter: (reverse(("companyDomain".domain)::text) ~~ (reverse(("publicSuffix".suffix)::text) || '%'::text)) │
│ Rows Removed by Join Filter: 8093814 │
│ Buffers: shared hit=306 │
│ -> Seq Scan on public."publicSuffix" (cost=0.00..377.15 rows=8915 width=12) (actual time=0.081..0.794 rows=8915 loops=1) │
│ Output: "publicSuffix".id, "publicSuffix".suffix, "publicSuffix".created_at, "publicSuffix".asuffix │
│ Buffers: shared hit=288 │
│ -> Materialize (cost=0.00..31.62 rows=908 width=15) (actual time=0.001..0.036 rows=908 loops=8915) │
│ Output: "companyDomain".domain, "companyDomain".id │
│ Buffers: shared hit=18 │
│ -> Seq Scan on public."companyDomain" (cost=0.00..27.08 rows=908 width=15) (actual time=11.576..11.799 rows=908 loops=1) │
│ Output: "companyDomain".domain, "companyDomain".id │
│ Buffers: shared hit=18 │
│ Planning Time: 0.167 ms │
│ JIT: │
│ Functions: 9 │
│ Options: Inlining false, Optimization false, Expressions true, Deforming true │
│ Timing: Generation 1.956 ms, Inlining 0.000 ms, Optimization 0.507 ms, Emission 10.878 ms, Total 13.341 ms │
│ Execution Time: 2366.971 ms │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Узкое место здесь, поскольку Я понимаю, Rows Removed by Join Filter: 8093814
Кажется, что PostgreSQL создает декартово соединение таблиц, а затем фильтрует его, используя условие ON
:
select count(*) from "companyDomain", "publicSuffix";
---
8094820
Для обходного пути попробуйте использовать оператор массива :
postgres=# explain (analyze, verbose, buffers)
SELECT DISTINCT ON ("companyDomain".id)
"companyDomain".domain,
"publicSuffix".suffix
FROM
"companyDomain"
INNER JOIN "publicSuffix" ON adomain @> asuffix
ORDER BY "companyDomain".id, LENGTH("publicSuffix".suffix) DESC;
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ QUERY PLAN │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Unique (cost=8310.60..8512.97 rows=908 width=31) (actual time=180.149..180.335 rows=908 loops=1) │
│ Output: "companyDomain".domain, "publicSuffix".suffix, "companyDomain".id, (length(("publicSuffix".suffix)::text)) │
│ Buffers: shared hit=48986 │
│ -> Sort (cost=8310.60..8411.78 rows=40474 width=31) (actual time=180.148..180.200 rows=1239 loops=1) │
│ Output: "companyDomain".domain, "publicSuffix".suffix, "companyDomain".id, (length(("publicSuffix".suffix)::text)) │
│ Sort Key: "companyDomain".id, (length(("publicSuffix".suffix)::text)) DESC │
│ Sort Method: quicksort Memory: 145kB │
│ Buffers: shared hit=48986 │
│ -> Nested Loop (cost=0.59..5213.39 rows=40474 width=31) (actual time=0.190..179.693 rows=1239 loops=1) │
│ Output: "companyDomain".domain, "publicSuffix".suffix, "companyDomain".id, length(("publicSuffix".suffix)::text) │
│ Buffers: shared hit=48986 │
│ -> Seq Scan on public."companyDomain" (cost=0.00..27.08 rows=908 width=57) (actual time=0.015..0.098 rows=908 loops=1) │
│ Output: "companyDomain".id, "companyDomain".domain, "companyDomain".created_at, "companyDomain".adomain │
│ Buffers: shared hit=18 │
│ -> Bitmap Heap Scan on public."publicSuffix" (cost=0.59..5.15 rows=45 width=54) (actual time=0.052..0.197 rows=1 loops=908) │
│ Output: "publicSuffix".id, "publicSuffix".suffix, "publicSuffix".created_at, "publicSuffix".asuffix │
│ Recheck Cond: ("companyDomain".adomain @> "publicSuffix".asuffix) │
│ Rows Removed by Index Recheck: 572 │
│ Heap Blocks: exact=41510 │
│ Buffers: shared hit=48968 │
│ -> Bitmap Index Scan on idx_asuffix (cost=0.00..0.58 rows=45 width=0) (actual time=0.039..0.039 rows=573 loops=908) │
│ Index Cond: ("publicSuffix".asuffix <@ "companyDomain".adomain) │
│ Buffers: shared hit=7458 │
│ Planning Time: 0.189 ms │
│ Execution Time: 180.434 ms │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Вероятно, это не слишком точно (например, aaa.bbb
здесь равно bbb.aaa
), но вы можете исправить это в предложении WHERE
. В любом случае это будет быстрее.
А пока старые столбцы domain
и suffix
избыточны, потому что вы можете восстановить их из adomain/asuffix
, используя array_to_string(anyarray, text [, text])
функцию .
В качестве альтернативы, чтобы избежать изменений в структуре таблиц, вы можете создать функциональные индексы на string_to_array()
, а затем использовать его в фильтрах / объединениях.