Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/78.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Can';t使用索引提高SQL连接速度_Sql_Postgresql_Performance_Join - Fatal编程技术网

Can';t使用索引提高SQL连接速度

Can';t使用索引提高SQL连接速度,sql,postgresql,performance,join,Sql,Postgresql,Performance,Join,我对SQL完全陌生,我正在尝试加快超大数据的联接查询。我开始添加索引(但老实说,我对它们没有深入的了解),但没有看到太多的变化,我决定在一个更简单的模拟示例上进行基准测试。我正在MacOS 10.14.6上使用PostgreSQL 11.5的psql接口。数据服务器在我的计算机上本地托管。我很抱歉没有相关信息,第一次发布关于SQL的帖子 数据库结构 我创建了两个最初完全相同的数据库,db和db_idx。我从不在db中的表上放置任何索引或键,而尝试在db_idx中的表上放置索引和键。然后,我分别在

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

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

  • 具有100000行和以下结构的客户端表:
  • 一个包含70000行和以下结构的附加表:
client_附加表中的client_id列包含客户端的client_id值的子集。请注意主键和我在客户机上创建的其他索引。我原以为这会提高基准查询速度(见下文),但事实并非如此

重要的是,db数据库完全相同(相同的结构,相同的值),只是它没有索引或键。

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

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

    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中的键和/或索引不会改变任何事情。这些基准测试结果适用于更大的数据量:在索引和非索引情况下,速度是相同的


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

您在两个表上有一个主键,用于
联接。如果您真的想看到查询速度变慢,请删除主键

发生了什么事?嗯,我的猜测是,执行计划是相同的,无论有没有二级索引。你需要看看这些计划本身


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

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

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


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

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

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
从中可以看到,两个表都被顺序扫描,每个表的值都被散列,并进行了散列联接。Postgres确定这是执行此查询的最佳方式

如果要在不使用主键的情况下重新创建表(并因此删除每个表的PK列上的隐式索引),则会得到完全相同的计划,因为Postgres已经确定执行此查询的最快方法是忽略索引,对表的值进行散列,然后对两组散列值进行散列联接以获得结果

按如下方式更改客户端表中的行数后:

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会在感觉索引会产生更快的结果时使用索引,但使用之前的阈值可能比您预期的要高

非常好


菲尔

谢谢。但是db数据库中绝对没有主键,连接所用的时间与db_idx中的时间相同,db_idx中有主键。谢谢。当你说大量数据时,多行就足够了(1000万行?)?或者它也取决于表的宽度?我检查了查询计划,实际上,查询只使用顺序扫描。通过在client_id上添加WHERE子句,我成功地使它使用了索引(主键)。总的来说,我只想确保我给了SQL尽可能快的速度。我真正的问题非常相似:我需要在主键列上连接表(两侧相同)。有什么结论性建议吗?
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