有一个具有这种结构的表:
Table "public.all_emails"
│ Column | Type | Modifiers
│ ----------- + -------- + -----------
│ email | text |
│ frequency | bigint |
│Indexes:
│ "all_emails_email_idx" UNIQUE, btree (email)我希望通过对这些记录进行更多的操作,将所有记录从这个表移到另一个数据库中。为了加快速度,我编写了多进程应用程序,该应用程序需要几次特定的表。为了知道以下哪个过程开始,我对表进行了如下排序:
Select email from all_emails order by email limit # {PULL_SIZE} offset # {offset}由于表中有大量的记录,这个操作非常昂贵,而且不是最优的。我怎样才能让它变得更好?
发布于 2016-09-09 07:56:38
为此,您可以对您的表进行CLUSTER:
CLUSTER all_emails USING all_emails_email_idx;
ANALYZE all_emails;根据指定的索引对表中的行进行物理重新排序。因此,电子邮件地址是根据电子邮件地址排序的,然后查询--就像处理任何其他查询一样--将在有限数量的磁盘页面上找到请求子集中的所有行,因此I/O减少,以及任何排序(因为查询计划器识别表聚集在特定索引上)。ANALYZE命令在集群之后更新表统计信息,以帮助查询规划者做出最佳选择。
这实际上只适用于只读或不经常更新或插入新行的表,因为群集不被维护:这是一个一次性的过程。集群也是一个相当“昂贵”的过程,因为整个表是重写的,并且需要一个排他的表锁。您可以使用缩写形式CLUSTER all_emails的相同索引定期重新对表进行重新聚类。
发布于 2016-09-09 07:56:45
没有什么比读取整个表的单次顺序扫描速度更快的了,至少在PostgreSQL 9.6之前是这样,在此之前,并行顺序扫描将被引入。
将表拆分为ctid (表中元组的物理位置)是很有诱惑力的,但是PostgreSQL并没有为不同于=的运营商优化ctid的访问
test=> EXPLAIN SELECT * FROM large WHERE ctid BETWEEN '(390, 0)' AND '(400,0)';
┌───────────────────────────────────────────────────────────────────┐
│ QUERY PLAN │
├───────────────────────────────────────────────────────────────────┤
│ Seq Scan on large (cost=0.00..1943.00 rows=500 width=8) │
│ Filter: ((ctid >= '(390,0)'::tid) AND (ctid <= '(400,0)'::tid)) │
└───────────────────────────────────────────────────────────────────┘
(2 rows)插入的情况也是如此:如果不能显示数字,我非常肯定,将一个进程INSERTing或COPYing放到一个表中不会比将数据加载到同一个表中的几个进程慢。
由于瓶颈似乎是处理原点的SELECT和目的地的INSERT之间的行,我建议如下:
SELECT * FROM all_emails的线程。COPY tablename FROM STDIN语句的输入中。https://stackoverflow.com/questions/39405912
复制相似问题