我遇到以下PostgreSQL查询的问题,运行需要10秒以上是否有任何方法可以将此查询加速到合理的速度,我只是在寻找与视频相关的最相关的搜索字词非常大的数据库。
SELECT count(*), videoid
FROM term_search
where word = 'tester'
OR word = 'question'
OR word = 'one'
group by videoid
order by count(*) desc
limit 1800;
使用analyze运行查询时,生成的查询计划如下(http://explain.depesz.com/s/yDJ):
Limit (cost=389625.50..389630.00 rows=1800 width=4) (actual time=11766.693..11770.001 rows=1800 loops=1)
Output: (count(*)), videoid
-> Sort (cost=389625.50..389692.68 rows=26873 width=4) (actual time=11766.689..11767.818 rows=1800 loops=1)
Output: (count(*)), videoid
Sort Key: (count(*))
Sort Method: top-N heapsort Memory: 181kB
-> HashAggregate (cost=387769.41..388038.14 rows=26873 width=4) (actual time=9215.653..10641.993 rows=1632578 loops=1)
Output: count(*), videoid
-> Bitmap Heap Scan on public.term_search (cost=44915.83..378163.38 rows=1921207 width=4) (actual time=312.449..7026.036 rows=2047691 loops=1)
Output: id, videoid, word, termindex, weight
Recheck Cond: (((term_search.word)::text = 'tester'::text) OR ((term_search.word)::text = 'question'::text) OR ((term_search.word)::text = 'one'::text))
Rows Removed by Index Recheck: 25512434
-> BitmapOr (cost=44915.83..44915.83 rows=1950031 width=0) (actual time=288.937..288.937 rows=0 loops=1)
-> Bitmap Index Scan on terms_word_idx (cost=0.00..8552.83 rows=383502 width=0) (actual time=89.266..89.266 rows=419750 loops=1)
Index Cond: ((term_search.word)::text = 'tester'::text)
-> Bitmap Index Scan on terms_word_idx (cost=0.00..13171.84 rows=590836 width=0) (actual time=89.700..89.700 rows=604348 loops=1)
Index Cond: ((term_search.word)::text = 'question'::text)
-> Bitmap Index Scan on terms_word_idx (cost=0.00..21750.26 rows=975693 width=0) (actual time=109.964..109.964 rows=1023593 loops=1)
Index Cond: ((term_search.word)::text = 'one'::text)
该表的架构如下:
Column | Type | Modifiers | Storage | Description
-----------+------------------------+----------------------------------------------------------+----------+-------------
id | integer | not null default nextval('term_search_id_seq'::regclass) | plain |
videoid | integer | | plain |
word | character varying(100) | | extended |
termindex | character varying(15) | | extended |
weight | smallint | | plain |
Indexes:
"term_search_pkey" PRIMARY KEY, btree (id)
"search_term_exists_idx" btree (videoid, word)
"terms_caverphone_idx" btree (termindex)
"terms_video_idx" btree (videoid)
"terms_word_idx" btree (word, videoid)
Foreign-key constraints:
"term_search_videoid_fkey" FOREIGN KEY (videoid) REFERENCES videos(id) ON DELETE CASCADE
Has OIDs: no
我已经设法通过Index Only扫描将其降低到7秒,但仍然不够低。我在一个aws r3.xlarge实例上在Ubuntu 14.04上运行PostgreSQL 9.3,表中有大约5000万行。任何意见是极大的赞赏!
编辑:
附加是SELECT schemaname,tablename,attname,null_frac,avg_width,n_distinct FROM pg_stats WHERE schemaname =' public'和tablename =' term_search';
schemaname | tablename | attname | null_frac | avg_width | n_distinct
------------+-------------+-----------+-----------+-----------+------------
public | term_search | id | 0 | 4 | -1
public | term_search | videoid | 0 | 4 | 568632
public | term_search | word | 0 | 6 | 5054
public | term_search | termindex | 0 | 11 | 2485
public | term_search | weight | 0 | 2 | 3
答案 0 :(得分:1)
如果我有机会连接用户一晚,我会:
words
term_search
的新表格
word
,类似的东西:
create table words (
word_id serial primary key,
word text);
insert into words (word)
select distinct word
from term_search;
alter table term_search add column word_id integer;
update term_search t
set word_id = w.word_id
from words w
where t.word = w.word;
alter table term_search add constraint term_search_word_fkey
foreign key (word_id) references words (word_id);
测试:
SELECT count(*), videoid
FROM term_search t
JOIN words w on t.word_id = w.word_id
WHERE w.word = 'tester'
OR w.word = 'question'
OR w.word = 'one'
GROUP BY videoid
ORDER BY count(*) desc
LIMIT 1800;
-- if was faster then
alter table term_search drop column word;
-- and on the fly...
alter table term_search alter termindex type text;
革命后,我必须处理term_search
上的插入和更新。我可能会创建一个包含插入和更新规则的视图。
答案 1 :(得分:1)
让我们从重新描述查询开始,解释它真正想做的事情。
查询:
SELECT count(*), videoid
FROM term_search
where word = 'tester'
OR word = 'question'
OR word = 'one'
group by videoid
order by count(*) desc
limit 1800;
似乎意味着:
“在搜索字词表中,找到包含搜索字词tester
,question
或one
的视频。计算每个视频的匹配项并最多返回1800个视频匹配”。
或者更一般地说:
“找到最适合我搜索字词的视频,并向我展示前n个最佳匹配”。
正确?
如果是这样,为什么不使用PostgreSQL's built-in full-text search and full-text indexing?与每个视频tsquery
进行索引tsvector
匹配可能会在此获胜。全文搜索具有模糊匹配,排名以及几乎所有其他您想要的东西 - 与您当前的方法不同,它不需要实现整个数据集并仅排序以丢弃大部分数据集。
您尚未提供样本数据,因此我无法真正进行演示。
PostgreSQL当前如何执行您的查询可以这样解释:
为表中的每个磁盘页(8kb)创建一个位,其中true表示页面可能包含一个或多个匹配的行
对于每个搜索字词,扫描索引terms_word_idx
并更新位图以设置找到匹配项的位
扫描表格,跳过位图显示无法匹配的页面,查找包含任何单词的行。这就像一个快速,跳过空白的seqscan。如果比赛的百分比很高,它实际上并不比普通的seqscan快。
对于每个匹配的行,根据视频ID将其排序为一系列“存储桶”。然后在最后,计算每个存储桶中的行数并返回该存储桶的计数+视频ID。 (这不是那么简单,但足够接近)。
在计算每个存储桶时,将结果放在结果中,包括次高和次最低计数。
这听起来不是很有趣,但它没有任何选择。 b树索引不能同时搜索多个术语,因此必须进行多个索引扫描。剩下的就是那个。
所以:为了提高效率,你需要从根本上改变解决问题的方法。添加索引或调整一些参数不会突然使这需要0.5秒。
答案 2 :(得分:0)
您可以优化postgresql设置以减少查询执行的时间。例如,您可以使用 pgtune utilite:
apt-get install pgtune
cd /etc/postgresql/*.*/main/
cp postgresql.conf postgresql.conf.default
pgtune -i postgresql.conf.default -o postgresql.conf --type=%TYPE%
此处%TYPE%是值之一:
有关pgtune的其他信息,请访问Google并提供帮助。
对于PostgreSQL< 9.3你必须使用这个脚本:
#!/bin/bash
# simple shmsetup script
page_size=`getconf PAGE_SIZE`
phys_pages=`getconf _PHYS_PAGES`
shmall=`expr $phys_pages / 2`
shmmax=`expr $shmall \* $page_size`
echo kernel.shmmax = $shmmax
echo kernel.shmall = $shmall
白色结果进入文件/etc/sysctl.conf并重启系统。否则Postgres无法启动。
答案 3 :(得分:0)
其他人已就如何重构数据库提供了一些建议,但您可以使查询运行得更好。 EXPLAIN中的以下行表明您的位图溢出:
Rows Removed by Index Recheck: 25512434
如果重新检查耗费时间(而不是IO消耗时间 - 如果你运行EXPLAIN (ANALYZE, BUFFERS)
它将有助于澄清这一点,特别是如果你启用了track_io_timing)那么增加work_mem可能会有很大帮助,假设您可以负担得起,而不会耗尽RAM。