Postgresql:如何确保索引在内存中

时间:2014-01-07 15:42:09

标签: sql postgresql configuration indexing

我一直在尝试在Windows Server 2012上的Azure VM上运行postgres 9.3。我最初在7GB服务器上运行它...我现在在14GB Azure VM上运行它。在尝试解决下面描述的问题时,我增加了一个尺寸。

顺便说一句,我对posgresql很新,所以我只是逐点了解配置选项。此外,虽然我喜欢在Linux上运行它,但我和我的同事根本没有专业知识来解决Linux中出现问题时的问题,因此Windows是我们唯一的选择。

问题描述:

我有一个名为test_table的表;它目前存储大约9000万行。它将每月增长约3-4百万行。 test_table中有2列:

id (bigserial)
url (charachter varying 300)

我在导入几个CSV文件中的数据后创建了索引。两列都被编入索引.... id是主键。 url上的索引是使用默认值pgAdmin创建的普通btree。

我跑的时候:

SELECT sum(((relpages*8)/1024)) as MB FROM pg_class WHERE reltype=0;

......总大小为5980MB

这里讨论的2个索引的个别大小如下,我通过运行得到它们:

 # SELECT relname, ((relpages*8)/1024) as MB, reltype FROM pg_class WHERE 
  reltype=0 ORDER BY relpages DESC LIMIT 10;


             relname      |  mb  | reltype
----------------------------------+------+--------
 test_url_idx             | 3684 |       0
 test_pk                  | 2161 |       0

其他较小的表上还有其他索引,但它们很小(<5MB)....所以我在这里忽略了它们

使用url查询test_table时遇到的麻烦,特别是在搜索中使用通配符时,速度(或缺少速度)。 e.g。

select * from test_table where url like 'orange%' limit 20;

...需要20-40秒才能运行。

对上面的运行说明分析给出了以下内容:

# explain analyze select * from test_table where
   url like 'orange%' limit 20;

          QUERY PLAN
-----------------------------------------------------------------    
 Limit  (cost=0.00..4787.96 rows=20 width=57) 
     (actual time=0.304..1898.583 rows=20 loops=1)
   ->  Seq Scan on test_table  (cost=0.00..2303247.60 rows=9621 width=57)
     (actual time=0.302..1898
    .542 rows=20 loops=1)
     Filter: ((url)::text ~~ 'orange%'::text)
     Rows Removed by Filter: 210286
    Total runtime: 1898.650 ms
  (5 rows)

再举一个例子......这次是美国人和.com之间的通配符....

# explain  select * from test_table where url 
   like 'american%.com' limit 50;

QUERY PLAN
-------------------------------------------------------
 Limit  (cost=0.00..11969.90 rows=50 width=57)
  ->  Seq Scan on test_table  (cost=0.00..2303247.60 rows=9621 width=57)
     Filter: ((url)::text ~~ 'american%.com'::text)
    (3 rows)


# explain analyze select * from test_table where url 
    like 'american%.com' limit 50;

QUERY PLAN
-----------------------------------------------------
 Limit  (cost=0.00..11969.90 rows=50 width=57) 
    (actual time=83.470..3035.696 rows=50      loops=1)
    ->  Seq Scan on test_table  (cost=0.00..2303247.60 rows=9621 width=57) 
            (actual time=83.467..303
  5.614 rows=50 loops=1)
     Filter: ((url)::text ~~ 'american%.com'::text)
     Rows Removed by Filter: 276142
 Total runtime: 3035.774 ms
(5 rows)

然后我从7GB服务器转到14GB服务器。查询速度并不好。

服务器上的观察

  • 我可以看到内存使用量从未真正超过2MB。
  • 使用LIKE语句运行查询时,磁盘读取将脱离图表。
  • 与id(主键)
  • 匹配时,查询速度完全正常

postgresql.conf文件只有一些默认值的更改。请注意,我从以下博文中了解了以下一些建议:http://www.gabrielweinberg.com/blog/2011/05/postgresql.html

对conf的更改

shared_buffers = 512MB  

checkpoint_segments = 10 

(我更改了checkpoint_segments,因为我在加载CSV文件时收到了很多警告......虽然生产数据库不会非常密集,所以如果需要可以将其更改回3 ......)

cpu_index_tuple_cost = 0.0005       
effective_cache_size = 10GB    # recommendation in the blog post was 2GB...

在服务器本身的任务管理器中 - &gt;性能选项卡,以下可能是可以提供帮助的人的相关位:

CPU:很少超过2%(无论运行什么查询...当我导入6GB CSV文件时,它达到11%)

内存:1.5 / 14.0GB(11%)

有关记忆的更多细节:

  • 使用中:1.4GB
  • 可用:12.5GB
  • 承诺1.9 / 16.1 GB
  • 缓存:835MB
  • 分页池:95.2MB
  • 非分页池:71.2 MB

问题

  1. 如何确保索引位于内存中(假设内存不会太大)?这只是我需要的配置调整吗?
  2. 在这里实施我自己的搜索索引(例如Lucene)是一个更好的选择吗?
  3. 即使我可以解决内存问题中的索引,postgres中的全文索引功能是否会显着提高性能?
  4. 感谢阅读。

4 个答案:

答案 0 :(得分:7)

这些seq扫描使得您在导入数据后看起来没有在桌面上运行analyze

http://www.postgresql.org/docs/current/static/sql-analyze.html

在正常操作期间,运行vacuum analyze的计划没有用,因为autovacuum会定期启动。但是在执行大量写入时很重要,例如在导入期间。

在一个稍微相关的说明中,如果你需要在结尾处而不是在开始时运行锚点查询,请参阅Pavel的PostgreSQL技巧网站上的这个反向索引提示,例如: like '%.com'

http://postgres.cz/wiki/PostgreSQL_SQL_Tricks_I#section_20


关于你的实际问题,要小心你喜欢的那篇文章中的一些建议充其量是可疑的。改变索引使用的成本通常是可疑的,禁用seq扫描是彻头彻尾的愚蠢。 (有时,对于seq扫描表而言 比使用索引更便宜。)

话虽如此:

  1. Postgres主要根据索引的使用频率来缓存索引,如果统计信息表明它不应该使用索引,那么在导入后需要analyze。当然,给予Postgres充足的记忆力也会增加记忆的可能性,但要记住后面的观点。
  2. 和3.全文搜索工作正常。
  3. 有关微调的详细信息,请参阅手册和:

    http://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server

    关于架构的最后两个注释:

    1. 最后我检查过,bigint(在你的情况下是bigserial)比plain int慢。 (这是前一阵子,所以现在的差异可能是微不足道的现代,64位服务器。)除非你预见到你的实际需要超过23十亿项,int是充足且占用较少的空间。
    2. 从实施的角度来看,varchar(300)varchar之间没有指定长度(或text的唯一区别)就长度而言是一个额外的检查约束。如果你实际上不需要数据来适应这个大小,并且除了习惯之外没有任何理由这样做,你的数据库插入和更新将通过摆脱该约束而更快地运行。

答案 1 :(得分:3)

除非您的编码或排序规则是C或POSIX,否则普通的btree索引无法有效地满足锚定的类似查询。您可能必须使用varchar_pattern_ops op类声明btree索引才能受益。

答案 2 :(得分:2)

问题在于,每次查找都会遇到全表扫描(“内存中的索引”并不是真正的问题)。每次运行其中一个查询时,数据库都会访问每一行,这会导致磁盘使用率过高。您可以查看here以获取更多信息(尤其是关于运算符类和索引类型的文档链接)。如果您遵循该建议,您应该能够使前缀查找工作正常,即您匹配“orange%”之类的情况。

全文搜索非常适合更自然的文本搜索,例如书面文档,但是它可能更难以使其适用于URL搜索。几个月前邮件列表中还有this thread,可能会针对您要执行的操作提供更多针对特定领域的信息。

答案 3 :(得分:0)

解释分析select * from test_table where    url喜欢'orange%'限制20;

您可能希望对类似查询使用gin / gist索引。应该给你比btree更好的结果 - 我不认为btree支持像查询一样。