Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/9.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
Sql 优化postgres搜索查询的几个问题_Sql_Postgresql_Query Optimization_Top N - Fatal编程技术网

Sql 优化postgres搜索查询的几个问题

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(*

我对下面的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运行查询时,结果查询计划如下():

该表的架构如下所示:

    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应用程序
关于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无法启动。

让我们从重新表述查询开始,解释它真正想要做什么

查询:

  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个最好的成绩,扔掉剩下的辛苦工作
这听起来不太有趣,但它没有任何选择。b树索引不能向下搜索多个词,因此它必须进行多个索引扫描。剩下的就是这样


因此:为了提高效率,您需要从根本上改变解决问题的方式。添加索引或调整某些参数不会突然使这花费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