我有一个 PostgreSQL 表。select *
非常慢,但又select id
好又快。我认为可能是行的大小非常大并且需要一段时间才能传输,或者可能是其他一些因素。
我需要所有字段(或几乎所有字段),因此仅选择一个子集并不是快速解决方法。选择我想要的字段仍然很慢。
这是我的表架构减去名称:
integer | not null default nextval('core_page_id_seq'::regclass)
character varying(255) | not null
character varying(64) | not null
text | default '{}'::text
character varying(255) |
integer | not null default 0
text | default '{}'::text
text |
timestamp with time zone |
integer |
timestamp with time zone |
integer |
文本字段的大小可以是任意大小。但是,在最坏的情况下,不超过几千字节。
问题
- 这有什么叫“疯狂低效”的吗?
- 有没有办法在 Postgres 命令行中测量页面大小来帮助我调试它?
Q2:
way to measure page size
PostgreSQL 提供了许多数据库对象大小函数。我在这个查询中打包了最有趣的,并在底部添加了一些统计访问函数。(附加模块pgstattuple提供了更多有用的功能。)
这将表明测量“行大小”的不同方法会导致非常不同的结果。这完全取决于您想要测量的内容。
此查询需要Postgres 9.3 或更高版本。对于旧版本,请参见下文。
在子查询中使用
VALUES
表达式LATERAL
,以避免拼写每一行的计算。替换
public.tbl
为可选的模式限定表名,以获得收集的行大小统计信息的紧凑视图。您可以将其包装到 plpgsql 函数中以重复使用,将表名作为参数提交并使用EXECUTE
...结果:
对于旧版本(Postgres 9.2 或更早版本):
结果相同。
Q1:
anything inefficient?
您可以优化列顺序以节省每行的一些字节,目前浪费在对齐填充中:
这样每行可以节省 8 到 18 个字节。我称之为列俄罗斯方块。看:
还要考虑:
通过查询整行的 TEXT 表示的长度,很容易获得行大小的近似值,包括TOAST的内容:
这是执行时将在客户端检索的字节数的近似值:
...假设查询的调用者以文本格式请求结果,这是大多数程序所做的(二进制格式是可能的,但在大多数情况下不值得麻烦)。
可以应用相同的技术来定位
N
“文本中最大”的行tablename
:有几件事可能会发生。一般来说,我怀疑长度是近端问题。我怀疑你有一个与长度相关的问题。
你说文本字段最多可以达到几k。主存储中的一行不能超过 8k,并且您的较大文本字段可能已被TOASTed或从主存储移出到单独文件中的扩展存储中。这使您的主存储更快(因此 select id 实际上更快,因为要访问的磁盘页面更少)但 select * 变得更慢,因为有更多的随机 I/O。
如果您的总行大小仍然低于 8k,您可以尝试更改存储设置。但是,我会警告说,在将超大属性插入主存储时可能会发生不好的事情,因此如果您不必这样做,最好不要碰它,如果这样做,请通过检查约束设置适当的限制。所以交通可能不是唯一的事情。它可能正在整理许多需要随机读取的字段。大量随机读取也可能导致缓存未命中,并且所需的大量内存可能需要在磁盘上实现事物和大量宽行,如果存在连接(如果涉及 TOAST,则存在连接)可能需要更昂贵连接模式等
我要做的第一件事是选择更少的行,看看是否有帮助。如果可行,您也可以尝试向服务器添加更多 RAM,但我会先开始查看由于计划更改和缓存未命中而导致性能开始下降的位置。
使用上面提到的数据库对象大小函数:
如果需要当前行大小的平均值,您可以使用
pg_column_size
:每列使用它: