测量 PostgreSQL 表行的大小

Joe*_*Joe 119 postgresql performance size disk-space postgresql-performance

我有一个 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                  | 
Run Code Online (Sandbox Code Playgroud)

文本字段的大小可以是任意大小。但是,在最坏的情况下,不会超过几千字节。

问题

  1. 有什么关于这叫“疯狂低效”的吗?
  2. 有没有办法在 Postgres 命令行中测量页面大小来帮助我调试?

Erw*_*ter 128

问题 2: way to measure page size

PostgreSQL 提供了许多数据库对象大小函数。我在这个查询中打包了最有趣的,并在底部添加了一些统计访问功能。(附加模块pgstattuple提供了更多有用的功能。)

这将表明测量“行大小”的不同方法会导致非常不同的结果。这完全取决于您想要测量的内容。

此查询需要Postgres 9.3 或更高版本。对于旧版本,请参见下文。

VALUESLATERAL子查询中使用表达式,以避免为每一行拼写计算。

替换public.tbl为您可选的模式限定表名,以获得收集的行大小统计信息的紧凑视图。您可以将其包装到 plpgsql 函数中以供重复使用,将表名作为参数提交并使用EXECUTE...

SELECT l.metric, l.nr AS bytes
     , CASE WHEN is_size THEN pg_size_pretty(nr) END AS bytes_pretty
     , CASE WHEN is_size THEN nr / NULLIF(x.ct, 0) END AS bytes_per_row
FROM  (
   SELECT min(tableoid)        AS tbl      -- = 'public.tbl'::regclass::oid
        , count(*)             AS ct
        , sum(length(t::text)) AS txt_len  -- length in characters
   FROM   public.tbl t                     -- provide table name *once*
   ) x
CROSS  JOIN LATERAL (
   VALUES
     (true , 'core_relation_size'               , pg_relation_size(tbl))
   , (true , 'visibility_map'                   , pg_relation_size(tbl, 'vm'))
   , (true , 'free_space_map'                   , pg_relation_size(tbl, 'fsm'))
   , (true , 'table_size_incl_toast'            , pg_table_size(tbl))
   , (true , 'indexes_size'                     , pg_indexes_size(tbl))
   , (true , 'total_size_incl_toast_and_indexes', pg_total_relation_size(tbl))
   , (true , 'live_rows_in_text_representation' , txt_len)
   , (false, '------------------------------'   , NULL)
   , (false, 'row_count'                        , ct)
   , (false, 'live_tuples'                      , pg_stat_get_live_tuples(tbl))
   , (false, 'dead_tuples'                      , pg_stat_get_dead_tuples(tbl))
   ) l(is_size, metric, nr);
Run Code Online (Sandbox Code Playgroud)

结果:

              公制| 字节 | bytes_pretty | bytes_per_row
-----------------------------------+-----------+--- -----------+--------------
 core_relation_size | 44138496 | 42 MB | 91
 可见性_map | 0 | 0 字节 | 0
 free_space_map | 32768 | 32 KB | 0
 table_size_incl_toast | 44179456 | 42 MB | 91
 index_size | 33128448 | 32 MB | 68
 total_size_incl_toast_and_indexes | 77307904 | 74 MB | 159
 live_rows_in_text_representation | 29987360 | 29 MB | 62
 ------------------------------ | | |
 row_count | 483424 | |
 live_tuples | 483424 | |
 死元组| 2677 | |

对于旧版本(Postgres 9.2 或更早版本):

WITH x AS (
   SELECT count(*)               AS ct
        , sum(length(t::text))   AS txt_len  -- length in characters
        , 'public.tbl'::regclass AS tbl      -- provide table name as string
   FROM   public.tbl t                       -- provide table name as name
   ), y AS (
   SELECT ARRAY [pg_relation_size(tbl)
               , pg_relation_size(tbl, 'vm')
               , pg_relation_size(tbl, 'fsm')
               , pg_table_size(tbl)
               , pg_indexes_size(tbl)
               , pg_total_relation_size(tbl)
               , txt_len
             ] AS val
        , ARRAY ['core_relation_size'
               , 'visibility_map'
               , 'free_space_map'
               , 'table_size_incl_toast'
               , 'indexes_size'
               , 'total_size_incl_toast_and_indexes'
               , 'live_rows_in_text_representation'
             ] AS name
   FROM   x
   )
SELECT unnest(name)                AS metric
     , unnest(val)                 AS bytes
     , pg_size_pretty(unnest(val)) AS bytes_pretty
     , unnest(val) / NULLIF(ct, 0) AS bytes_per_row
FROM   x, y

UNION ALL SELECT '------------------------------', NULL, NULL, NULL
UNION ALL SELECT 'row_count', ct, NULL, NULL FROM x
UNION ALL SELECT 'live_tuples', pg_stat_get_live_tuples(tbl), NULL, NULL FROM x
UNION ALL SELECT 'dead_tuples', pg_stat_get_dead_tuples(tbl), NULL, NULL FROM x;
Run Code Online (Sandbox Code Playgroud)

结果一样。

问题 1: anything inefficient?

您可以优化列顺序以每行节省一些字节,目前浪费在对齐填充上:

integer                  | not null default nextval('core_page_id_seq'::regclass)
integer                  | not null default 0
character varying(255)   | not null
character varying(64)    | not null
text                     | default '{}'::text
character varying(255)   | 
text                     | default '{}'::text
text                     |
timestamp with time zone |
timestamp with time zone |
integer                  |
integer                  |
Run Code Online (Sandbox Code Playgroud)

这可以每行节省 8 到 18 个字节。我称之为Column Tetris。看:

还要考虑:


Dan*_*ité 55

通过查询整行的 TEXT 表示的长度,可以很容易地获得行大小的近似值,包括TOAST 'ed 的内容:

SELECT octet_length(t.*::text) FROM tablename AS t WHERE primary_key=:value;
Run Code Online (Sandbox Code Playgroud)

这是执行时将在客户端检索的字节数的近似值:

SELECT * FROM tablename WHERE primary_key=:value;
Run Code Online (Sandbox Code Playgroud)

...假设查询的调用者正在请求文本格式的结果,这是大多数程序所做的(二进制格式是可能的,但在大多数情况下不值得麻烦)。

可以应用相同的技术来定位 的N“最大文本”行tablename

SELECT primary_key, octet_length(t.*::text) FROM tablename AS t
   ORDER BY 2 DESC LIMIT :N;
Run Code Online (Sandbox Code Playgroud)

  • @AkmalSalikhov是的,刚刚在[文档](https://www.postgresql.org/docs/9.0/functions-binarystring.html)中检查了它。我还有一个问题 - 我是否正确 - octet_length 给出未压缩的大小,而不是它所需的磁盘上的实际大小?我发现[这个答案](/sf/ask/931320071/)向我建议了这一点。 (2认同)

Chr*_*ers 15

有一些事情可能会发生。一般来说,我怀疑长度是最近的问题。我怀疑你有一个与长度相关的问题。

你说文本字段可以达到几千。主存储中的一行不能超过 8k,并且您的较大文本字段可能已被TOAST 处理,或者从主存储移出到单独文件中的扩展存储中。这使您的主存储更快(因此 select id 实际上更快,因为要访问的磁盘页面更少)但 select * 变得更慢,因为有更多的随机 I/O。

如果您的总行大小仍然低于 8k,您可以尝试更改存储设置。但是,我会警告说,将过大的属性插入主存储时可能会发生不好的事情,因此如果您不需要,最好不要触摸它,如果您这样做,请通过检查约束设置适当的限制。所以交通可能不是唯一的事情。它可能正在整理许多需要随机读取的字段。大量随机读取也可能导致缓存未命中,并且需要大量内存可能需要在磁盘上实现事物和大量宽行,如果存在连接(如果涉及 TOAST 则有一个连接)可能需要更高的成本连接模式等。

我会考虑做的第一件事是选择更少的行,看看是否有帮助。如果可行,您也可以尝试向服务器添加更多 RAM,但我会首先查看由于计划更改和缓存未命中而导致性能开始下降的地方。


小智 13

使用上面提到的数据库对象大小函数

SELECT primary_key, pg_column_size(tablename.*) FROM tablename;
Run Code Online (Sandbox Code Playgroud)


小智 8

如果需要当前行大小的平均值,您可以使用 pg_column_size

SELECT SUM(pg_column_size(table_name.*))/COUNT(*) FROM tablename;
Run Code Online (Sandbox Code Playgroud)

每列使用它:

SELECT SUM(pg_column_size(table_name.column_name))/COUNT(*) FROM tablename;
Run Code Online (Sandbox Code Playgroud)

  • 或者`SELECT AVG(pg_column_size(table_name.*)) FROM tablename;` (3认同)

归档时间:

查看次数:

97578 次

最近记录:

5 年,2 月 前