Sql 优化postgres搜索查询的几个问题
我对下面的PostgreSQL查询有一个问题,它需要10秒以上的时间才能运行。是否有任何方法可以将此查询提高到合理的速度,我只是在一个非常大的数据库上查找与视频相关的最相关搜索词Sql 优化postgres搜索查询的几个问题,sql,postgresql,query-optimization,top-n,Sql,Postgresql,Query Optimization,Top N,我对下面的PostgreSQL查询有一个问题,它需要10秒以上的时间才能运行。是否有任何方法可以将此查询提高到合理的速度,我只是在一个非常大的数据库上查找与视频相关的最相关搜索词 SELECT count(*), videoid FROM term_search where word = 'tester' OR word = 'question' OR word = 'one' group by videoid order by count(*
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运行查询时,结果查询计划如下():
该表的架构如下所示:
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
我已经设法用索引扫描将它降低到7秒,但它仍然不够低。我在Ubuntu14.04上的aws r3.xlarge实例上运行PostgreSQL 9.3,表中大约有5000万行。非常感谢您的建议
编辑:
附件是选择schemaname、tablename、attname、null_frac、avg_width、n_与pgu stats的结果,其中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
如果我有机会与用户断开连接一晚,我会:
- 从
使用term\u 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);
测试:
革命后,我必须负责
term\u search
上的插入和更新。我可能会创建一个包含插入和更新规则的视图。您可以优化postgresql设置以减少查询执行时间。例如,您可以使用pgtune实用程序:
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%是以下值之一:
- 数据用于大数据量、大查询、低频呼叫
- WEB适用于WEB应用程序,最适合Django应用程序和其他WEB应用程序
#!/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无法启动。让我们从重新表述查询开始,解释它真正想要做什么 查询:
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名最佳匹配”
对吗
如果是这样,你为什么不使用?每个视频的索引tsquery
与tsvector
匹配可能是一场胜利。全文搜索具有模糊匹配、排名和几乎所有您想要的内容,并且与当前的方法不同,它不需要对整个数据集进行具体化和排序,只需要丢弃大部分数据即可
您还没有提供示例数据,所以我无法真正进行演示
PostgreSQL当前如何执行查询可以这样解释:
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);
- 为表中的每个磁盘页(8kb)创建一个具有一位的映射,其中true表示该页可能包含一个或多个匹配行
- 对于每个搜索项,扫描索引
,并更新位图以设置找到匹配项的位terms\u word\u idx
- 扫描表格,跳过位图显示不存在任何匹配项的页面,查找包含任何单词的行。这就像是一个快速的,跳过空白扫描。如果匹配的百分比很高,它实际上不会比普通的seqscan快很多
- 对于每个匹配的行,根据视频id将其排序为一系列“bucket”。然后在最后,计算每个bucket中有多少行,并返回计数+该bucket的视频id。(虽然没那么简单,但已经够近了)
- 在计算每个存储桶时,将结果放在计数次高和次低的结果之间
- 拿着1800个最好的成绩,扔掉剩下的辛苦工作
因此:为了提高效率,您需要从根本上改变解决问题的方式。添加索引或调整某些参数不会突然使这花费0.5秒。其他人提供了一些关于如何重构数据库的建议,但您可能会使查询运行得更好。解释中的以下行表示位图溢出:
Rows Removed by Index Recheck: 25512434
如果重新检查是在消耗时间(与IO消耗时间相反——如果运行
EXPLAIN(ANALYZE,BUFFERS)
,这将有助于澄清这一点,特别是如果您跟踪IO定时处于开启状态),那么增加工时会有很大帮助,假设您可以在不耗尽RAM的情况下这样做。如果您在('tester'、'question'、'one')中使用单词有什么区别吗?
?我尝试了使用and Any,而不是or,差别不大。总运行时间:10931.711 MSC您可以更改表term\u search
结构吗?列word
有多少个不同的值?@user3354369 PostgreSQL在内部将中的转换为=ANY
,因此您不会看到任何差异。请使用仅索引扫描显示计划,并解释您是如何得到它的。请添加SELECT schemaname、tablename、attname、null_frac、avg_width、n_的输出,以区别于pg_stats,其中schemaname='public'和tablename='term_search'代码>我认为这是一个好主意:让术语搜索
成为视频和文字之间的桥梁。下一步是使{video\u id,word\u id}
成为主键(强制两个字段也不为空);删除代理键id
。它充其量也是一个相当粗糙的工具,远不是“让我的数据库更快”的魔法。当面对一个特定的查询时
Rows Removed by Index Recheck: 25512434