繁体   English   中英

无法通过索引提高 SQL 连接速度

[英]Can't improve SQL join speed with indexes

我对 SQL 完全陌生,我正在尝试加快对非常大数据的连接查询。 我开始添加索引(但老实说,我对它们没有深入的了解)并且没有看到太大的变化,我决定在一个更简单的模拟示例上进行基准测试。 我在 MacOS 10.14.6 上使用 PostgreSQL 11.5 的 psql 接口。 数据服务器本地托管在我的计算机上。 对于缺乏相关信息,我深表歉意,第一次发布有关 SQL 的信息。

数据库的结构

我创建了两个最初相同的数据库,db 和 db_idx。 我从来没有在 db 中的表上放置任何索引或键,而我尝试在 db_idx 中的表上放置索引和键。 然后我分别在 db 和 db_idx 中运行简单的连接查询并比较性能。 具体来说,db_idx 由两个表组成:

  • 具有 100,000 行和以下结构的客户端表:
                   Table "public.client"
       Column    |  Type   | Collation | Nullable | Default
    -------------+---------+-----------+----------+---------
     client_id   | integer |           | not null |
     client_name | text    |           |          |
    Indexes:
        "pkey_c" PRIMARY KEY, btree (client_id)
  • 具有 70,000 行和以下结构的 client_additional 表:
             Table "public.client_additional"
       Column   |  Type   | Collation | Nullable | Default
    ------------+---------+-----------+----------+---------
     client_id  | integer |           | not null |
     client_age | integer |           |          |
    Indexes:
        "pkey_ca" PRIMARY KEY, btree (client_id)
        "cov_idx" btree (client_id, client_age)

client_additional 表中的 client_id 列包含客户端的 client_id 值的子集。 请注意主键和我在 client_additional 上创建的其他索引。 我认为这些会提高基准查询速度(见下文),但事实并非如此。

重要的是 db 数据库完全相同(相同的结构,相同的值),只是它没有 index 或 key

旁注: client 和 client_additional 表可能应该是一个表,因为它们在完全相同的级别(客户端级别)提供信息。 然而,我在现实生活中使用的数据库的结构是这样的:一些表被“主题”分成几个表,尽管它们提供相同级别的信息。 我不知道这对我的问题是否重要。

基准查询

我正在使用以下查询,它模仿了很多我需要对真实数据执行的操作:

    SELECT 
      client_additional.client_id, 
      client_additional.client_age,
      client.client_name
    FROM client
    INNER JOIN client_additional 
    ON client.client_id = client_additional.client_id;

基准测试结果

在这两个数据库上,基准查询大约需要 630 毫秒。 删除 db_idx 中的键和/或索引不会改变任何内容。 这些基准测试结果适用于更大的数据量:索引和非索引情况下的速度相同。

这就是我所在的地方。 我如何解释这些结果? 我可以提高加入速度吗?如何提高?

使用EXPLAIN动词查看 SQL 引擎打算如何解析查询。 (不同的 SQL 引擎以不同的方式呈现这一点。)您可以最终确定是否会使用该索引。

此外,您首先需要加载包含大量测试数据的表,因为EXPLAIN会告诉您 SQL 引擎现在打算做什么,这个决定部分基于表的大小和其他各种统计数据. 如果表实际上是空的,则 SQL 引擎可能会认为索引现在没有用处

SQL 引擎使用各种非常巧妙的技巧来优化性能,因此实际上很难获得有用的时序测试。 但是,如果EXPLAIN告诉您正在使用该索引,那么这几乎就是您正在寻找的答案。

您在两个表上有一个主键,将用于join 如果您想真正看到查询变慢,请删除主键。

怎么了? 好吧,我的猜测是,无论有没有二级索引,执行计划都是一样的。 您需要查看计划本身。

与大多数其他数据库不同,Postgres 并没有从覆盖索引中受益,因为锁信息只存储在数据页中。 因此,总是需要访问数据页。

设置一个小型测试数据库,添加一些行并运行您的查询:

CREATE TABLE client
(
   client_id integer PRIMARY KEY,
   client_name text
);

CREATE TABLE client_additional
(
   client_id integer PRIMARY KEY,
   client_age integer
);

INSERT INTO client (client_id, client_name) VALUES (generate_series(1,100000),'Phil');
INSERT INTO client_additional (client_id, client_age) VALUES (generate_series(1,70000),21);

ANALYZE;

EXPLAIN ANALYZE SELECT 
   client_additional.client_id, 
   client_additional.client_age,
   client.client_name
FROM
   client
INNER JOIN
   client_additional 
ON
   client.client_id = client_additional.client_id;

给了我这个计划:

 Hash Join  (cost=1885.00..3590.51 rows=70000 width=11) (actual time=158.958..44 1.222 rows=70000 loops=1)
   Hash Cond: (client.client_id = client_additional.client_id)
   ->  Seq Scan on client  (cost=0.00..1443.00 rows=100000 width=7) (actual time =0.019..100.318 rows=100000 loops=1)
   ->  Hash  (cost=1010.00..1010.00 rows=70000 width=8) (actual time=158.785..15 8.786 rows=70000 loops=1)
         Buckets: 131072  Batches: 1  Memory Usage: 3759kB
         ->  Seq Scan on client_additional  (cost=0.00..1010.00 rows=70000 width =8) (actual time=0.016..76.507 rows=70000 loops=1)
 Planning Time: 0.357 ms
 Execution Time: 506.739 ms

从中可以看出,两个表都被顺序扫描,每个表中的值都经过哈希处理,并完成了 hash 连接。 Postgres 确定这是执行此查询的最佳方式。

如果您要重新创建没有主键的表(并因此删除每个表的 PK 列上的隐式索引),您将获得完全相同的计划,因为 Postgres 已确定执行此查询的最快方法是忽略索引并通过散列表的值,然后对两组散列值进行 hash 连接以获得结果。

像这样更改客户端表中的行数后:

TRUNCATE Client;

INSERT INTO client (client_id, client_name) VALUES (generate_series(1,200000),'phil');

ANALYZE;

然后我重新运行了相同的查询,我看到了这个计划:

Merge Join  (cost=1.04..5388.45 rows=70000 width=13) (actual time=0.050..415.50
3 rows=70000 loops=1)
   Merge Cond: (client.client_id = client_additional.client_id)
   ->  Index Scan using client_pkey on client  (cost=0.42..6289.42 rows=200000 width=9) (actual time=0.022..86.897 rows=70001 loops=1)
   ->  Index Scan using client_additional_pkey on client_additional  (cost=0.29..2139.29 rows=70000 width=8) (actual time=0.016..86.818 rows=70000 loops=1)
 Planning Time: 0.517 ms
 Execution Time: 484.264 ms

在这里你可以看到索引扫描已经完成,因为 Postgres 根据表中的当前行数确定这个计划是一个更好的计划。

关键是 Postgres 会在感觉索引会产生更快的结果时使用索引,但是使用它们之前的阈值比您预期的要高一些。

万事如意,

菲尔

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM