【问题标题】:Postgresql: How do I ensure that indexes are in memoryPostgresql:如何确保索引在内存中
【发布时间】:2014-01-07 15:42:08
【问题描述】:

我一直在尝试在 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

其他较小的表上还有其他索引,但它们很小(

使用 url 查询 test_table 时,特别是在搜索中使用通配符时,问题在于速度(或缺少通配符)。例如

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...

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

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 中的全文索引功能是否会显着提高性能?

感谢阅读。

【问题讨论】:

  • 需要考虑的几件事:创建全文搜索索引并匹配字符串几乎总是比使用“喜欢”更好。当查询不好时,无论您添加多少内存,它都不会改变性能。 PG的全文搜索就可以了,除非你有数百万条记录那么你应该考虑弹性搜索或类似的东西。
  • @SamD 我一般同意,但 FTS 并不适合所有工作。我想你不能有用地 lex 它你不能 FTS 它。除非您喜欢编写自定义 FTS 解析器,否则如果它的结构很奇怪,则同上。在这些情况下,pg_tgrm 索引 + LIKE 可能很有用。

标签: sql postgresql configuration indexing


【解决方案1】:

这些 seq 扫描使您在导入数据后看起来好像没有在表上运行 analyze

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

在正常操作期间,调度运行 vacuum analyze 没有用,因为自动清理会定期启动。但在执行大量写入时很重要,例如在导入期间。

在一个稍微相关的说明中,如果您需要在最后而不是在开始时运行锚定查询,请参阅 Pavel 的 PostgreSQL Tricks 网站上的反向索引提示,例如like '%.com'

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


关于您的实际问题,请注意您喜欢的帖子中的一些建议充其量是可疑的。更改索引使用的成本经常令人怀疑,禁用 seq 扫描是彻头彻尾的愚蠢。 (有时,seq 扫描表比使用索引便宜。)

话虽如此:

  1. Postgres 主要根据索引的使用频率缓存索引,如果统计数据表明它不应该使用索引,它不会使用索引——因此在导入后需要analyze。当然,为 Postgres 提供大量内存也会增加它在内存中的可能性,但请记住后一点。
  2. 和 3. 全文搜索工作正常。

有关微调的进一步阅读,请参阅手册和:

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

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

  1. 上次我检查过,bigint(在您的情况下是 bigserial)比普通 int 慢。 (这是不久前的事了,所以现在在现代 64 位服务器上差异可能可以忽略不计。)除非您预见到实际需要超过 23 亿个条目,否则 int 足够多并且占用的空间更少。
  2. 从实现的角度来看,varchar(300) 和没有指定长度的varchar(或text,就此而言)之间的唯一区别是对长度的额外检查约束。如果您实际上 需要 数据来适应该大小并且只是出于习惯以外的原因这样做,那么通过摆脱该限制,您的数据库插入和更新将运行得更快。

【讨论】:

  • 所有这些都非常有帮助,非常感谢!关键是在桌子上运行分析。那花了几分钟。我也不再像您建议的那样在我的任何 id 字段上使用 bigint(我不需要 23 亿,但肯定可以达到 10 亿)。此外,我会听取您关于 varchar 长度的建议。我定义了一个长度,因为我来自 SQL Server 和 Oracle 背景,您需要在这些字段(文本除外)上定义长度。
【解决方案2】:

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

【讨论】:

  • 您能否提供此声明的一些参考或文档的链接?
【解决方案3】:

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

全文搜索非常适合更自然的文本搜索,例如书面文档,但可能更难让它在 URL 搜索中正常工作。几个月前的邮件列表中还有this thread,其中可能包含更多特定于域的信息,说明您正在尝试做什么。

【讨论】:

  • tsearch 具有特定的 url 令牌类型,因此可能值得考虑。
【解决方案4】:

解释分析 select * from test_table where url like 'orange%' limit 20;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-23
    • 1970-01-01
    • 2016-07-18
    • 2012-09-14
    • 1970-01-01
    • 2020-10-23
    • 1970-01-01
    • 2020-11-12
    相关资源
    最近更新 更多