如果执行以下数据库(postgres)查询,则第二次调用要快得多。
我猜第一个查询很慢,因为操作系统(linux)需要从磁盘获取数据。第二个查询受益于文件系统级别和postgres中的缓存。
有没有办法优化数据库,以便在第一次电话中快速获得结果?
先致电(慢)
foo3_bar_p@BAR-FOO3-Test:~$ psql
foo3_bar_p=# explain analyze SELECT "foo3_beleg"."id", ... FROM "foo3_beleg" WHERE
foo3_bar_p-# (("foo3_beleg"."id" IN (SELECT beleg_id FROM foo3_text where
foo3_bar_p(# content @@ 'footown'::tsquery)) AND "foo3_beleg"."belegart_id" IN
foo3_bar_p(# ('...', ...));
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=75314.58..121963.20 rows=152 width=135) (actual time=27253.451..88462.165 rows=11 loops=1)
-> HashAggregate (cost=75314.58..75366.87 rows=5229 width=4) (actual time=16087.345..16113.988 rows=17671 loops=1)
-> Bitmap Heap Scan on foo3_text (cost=273.72..75254.67 rows=23964 width=4) (actual time=327.653..16026.787 rows=27405 loops=1)
Recheck Cond: (content @@ '''footown'''::tsquery)
-> Bitmap Index Scan on foo3_text_content_idx (cost=0.00..267.73 rows=23964 width=0) (actual time=281.909..281.909 rows=27405 loops=1)
Index Cond: (content @@ '''footown'''::tsquery)
-> Index Scan using foo3_beleg_pkey on foo3_beleg (cost=0.00..8.90 rows=1 width=135) (actual time=4.092..4.092 rows=0 loops=17671)
Index Cond: (id = foo3_text.beleg_id)
Filter: ((belegart_id)::text = ANY ('{...
Rows Removed by Filter: 1
Total runtime: 88462.809 ms
(11 rows)
第二次通话(快速)
Nested Loop (cost=75314.58..121963.20 rows=152 width=135) (actual time=127.569..348.705 rows=11 loops=1)
-> HashAggregate (cost=75314.58..75366.87 rows=5229 width=4) (actual time=114.390..133.131 rows=17671 loops=1)
-> Bitmap Heap Scan on foo3_text (cost=273.72..75254.67 rows=23964 width=4) (actual time=11.961..97.943 rows=27405 loops=1)
Recheck Cond: (content @@ '''footown'''::tsquery)
-> Bitmap Index Scan on foo3_text_content_idx (cost=0.00..267.73 rows=23964 width=0) (actual time=9.226..9.226 rows=27405 loops=1)
Index Cond: (content @@ '''footown'''::tsquery)
-> Index Scan using foo3_beleg_pkey on foo3_beleg (cost=0.00..8.90 rows=1 width=135) (actual time=0.012..0.012 rows=0 loops=17671)
Index Cond: (id = foo3_text.beleg_id)
Filter: ((belegart_id)::text = ANY ('...
Rows Removed by Filter: 1
Total runtime: 348.833 ms
(11 rows)
foo3_text表的表格布局(28M行)
foo3_egs_p=# \d foo3_text
Table "public.foo3_text"
Column | Type | Modifiers
----------+-----------------------+------------------------------------------------------------
id | integer | not null default nextval('foo3_text_id_seq'::regclass)
beleg_id | integer | not null
index_id | character varying(32) | not null
value | text | not null
content | tsvector |
Indexes:
"foo3_text_pkey" PRIMARY KEY, btree (id)
"foo3_text_index_id_2685e3637668d5e7_uniq" UNIQUE CONSTRAINT, btree (index_id, beleg_id)
"foo3_text_beleg_id" btree (beleg_id)
"foo3_text_content_idx" gin (content)
"foo3_text_index_id" btree (index_id)
"foo3_text_index_id_like" btree (index_id varchar_pattern_ops)
Foreign-key constraints:
"beleg_id_refs_id_6e6d40770e71292" FOREIGN KEY (beleg_id) REFERENCES foo3_beleg(id) DEFERRABLE INITIALLY DEFERRED
"index_id_refs_name_341600137465c2f9" FOREIGN KEY (index_id) REFERENCES foo3_index(name) DEFERRABLE INITIALLY DEFERRED
可以进行硬件更改(SSD而不是传统磁盘)或RAM磁盘。但也许当前的硬件也可以做得更快。
版本:x86_64-unknown-linux-gnu上的PostgreSQL 9.1.2
如果您需要更多详细信息,请发表评论。
答案 0 :(得分:3)
Postgres为您提供了在执行运行时查询时进行一些配置的机会,以确定您的I / O操作优先级。
random_page_cost(floating point)
-(reference)可能会对您有所帮助。它基本上会设置你的IO / CPU操作比。
值越大意味着I / O很重要,我有顺序磁盘;较低的值意味着I / O并不重要,我有随机访问磁盘。
默认值为4.0
,您可能希望增加并测试查询是否缩短时间。
不要忘记,您的I / O优先级取决于您的列数,行数。
一个大的但是;由于你的指标是btree,你的CPU优先级比I / O优先级上升要快得多。您基本上可以将复杂性映射到优先级。
CPU Priority = O(log(x))
I/O Priority = O(x)
总而言之,这意味着,如果Postgre的值4.0
适用于100k
条目,则应将其设置为(4.0 * log(100k) * 10M)/(log(10M) * 100k)
10M
进入。
答案 1 :(得分:1)
同意Julius但是,如果你只需要来自foo3_beleg的东西,请尝试使用EXISTS(如果你也粘贴了你的sql,那将会有所帮助,而不仅仅是你的解释计划)。
select ...
from foo3_beleg b
where exists
(select 1 from foo_text s where t.beleg_id = b.id)
....
然而,我怀疑你的醒来"在第一遍是你的数据库将IN子查询行加载到内存中。无论如何,这可能会发生,尽管EXISTS通常比IN快得多(如果不包含硬编码列表则很少需要IN,如果我查看sql则需要黄色标记)。
答案 2 :(得分:1)
第一次执行查询时,postgres将从磁盘加载数据,即使硬盘驱动器也很慢。第二次运行查询时,它将从RAM加载先前加载的数据,这显然更快。
此问题的解决方案是将关系数据加载到操作系统缓冲区缓存或PostgreSQL缓冲区缓存中:
int8 pg_prewarm(regclass, mode text default 'buffer', fork text default 'main', first_block int8 default null, last_block int8 default null)
:
第一个论点是要预热的关系。第二个参数是要使用的预热方法,如下面进一步讨论的;第三个是要预热的关系叉,通常是主要的。第四个参数是第一个预热的块编号(NULL被接受为零的同义词)。第五个参数是预警的最后一个块编号(NULL表示通过关系中的最后一个块预热)。返回值是预热的块数。
有三种可用的预热方法。 prefetch向操作系统发出异步预取请求(如果支持),否则抛出错误。 read读取请求的块范围;与prefetch不同,这是同步的,并且在所有平台和构建上都受支持,但可能会更慢。 buffer将请求的块范围读入数据库缓冲区缓存。
请注意,使用这些方法中的任何一种方法,尝试预热更多的块而不是缓存 - 操作系统在使用预取或读取时,或者使用PostgreSQL时使用缓冲区 - 可能会导致编号较低的块被驱逐为更高编号读入块。预先加热的数据也不受高速缓存驱逐的特殊保护,因此其他系统活动可能会在读取后立即驱逐新预热的块;相反,预热也可能从缓存中驱逐其他数据。出于这些原因,预热通常在启动时最有用,因为缓存很大程度上是空的。
希望这有帮助!
答案 3 :(得分:0)
有时将“WHERE x IN”移动到JOIN中可以显着提高性能。试试这个:
SELECT
foo3_beleg.id, ...
FROM
foo3_beleg b INNER JOIN
foo3_text t ON (t.beleg_id = b.id AND t.content @@ 'footown'::tsquery)
WHERE
foo3_beleg.belegart_id IN ('...', ...);
这是一个可重复的实验来支持我的主张。
我碰巧有一个很大的Postgres数据库(3000万行)(http://juliusdavies.ca/2013/j.emse/bertillonage/),所以我把它加载到postgres 9.4beta3。
结果令人印象深刻。子选择方法大约慢20倍:
time psql myDb < using-in.sql
real 0m17.212s
time psql myDb < using-join.sql
real 0m0.807s
对于那些对复制感兴趣的人,以下是我用来测试我的理论的原始SQL查询。
此查询使用“SELECT IN”子查询,速度慢20倍(第一次执行时笔记本电脑上的时间为17秒):
-- using-in.sql
SELECT
COUNT(DISTINCT sigsha1re) AS a_intersect_b, infilesha1
FROM
files INNER JOIN sigs ON (files.filesha1 = sigs.filesha1)
WHERE
sigs.sigsha1re IN (
SELECT sigsha1re FROM sigs WHERE sigs.sigsha1re like '0347%'
)
GROUP BY
infilesha1
此查询将条件移出子查询并进入加入条件,速度提高了20倍(第一次执行时笔记本电脑上的时间为0.8秒)。
-- using-join.sql
SELECT
COUNT(DISTINCT sigsha1re) AS a_intersect_b, infilesha1
FROM
files INNER JOIN sigs ON (
files.filesha1 = sigs.filesha1 AND sigs.sigsha1re like '0347%'
)
GROUP BY
infilesha1
P.S。如果您对该数据库的用途感到好奇,可以用它来计算任意jar文件与大约2011年maven存储库中所有jar文件的相似程度。
./query.sh lib/commons-codec-1.5.jar | psql myDb
similarity | a = 39 = commons-codec-1.5.jar (bin2bin)
------------+--------------------------------------------------------------------------------------
1.000 | commons-codec-1.5.jar
0.447 | commons-codec-1.4.jar
0.174 | org.apache.sling.auth.form-1.0.2.jar
0.170 | org.apache.sling.auth.form-1.0.0.jar
0.142 | jbehave-core-3.0-beta-3.jar
0.142 | jbehave-core-3.0-beta-4.jar
0.141 | jbehave-core-3.0-beta-5.jar
0.141 | jbehave-core-3.0-beta-6.jar
0.140 | commons-codec-1.2.jar