pg_column_size报告表。*的大小不同于特定列

时间:2017-05-23 20:05:58

标签: postgresql

我有一个简单的例子,其中pg_column_size报告的值非常不同。我认为这与它是否正在考虑TOASTed值有关,但我不确定。这是设置:

CREATE TABLE foo (bar TEXT);
INSERT INTO foo (bar) VALUES (repeat('foo', 100000));
SELECT pg_column_size(bar) as col, pg_column_size(foo.*) as table FROM foo;

我在Postgres 9.6中看到的是,

col     table
3442    300028

这里存在一个数量级的差异。思考?我计算行的大小的正确方法是什么?我有一个想法是,

SELECT pg_column_size(bar), pg_column_size(foo.*) - octet_length(bar) + pg_column_size(bar) FROM foo;

哪个应该减去TOAST后尺寸并添加TOAST尺寸。

修改:我建议的解决方法仅适用于字符列,例如不适用于JSONB。

1 个答案:

答案 0 :(得分:1)

第一个值是TOASTed值的压缩大小,而第二个值是整行的未压缩大小。

SELECT 'foo'::regclass::oid;
┌───────┐
│  oid  │
├───────┤
│ 36344 │
└───────┘
(1 row)

SELECT sum(length(chunk_data)) FROM pg_toast.pg_toast_36344;
┌──────┐
│ sum  │
├──────┤
│ 3442 │
└──────┘
(1 row)

foo.*(或者foo)是PostgreSQL中的“wholerow引用”,其数据类型为foo(在创建表时创建)。

PostgreSQL知道foo.bar存储在外部,所以它返回它在TOAST表中的大小,但foo(复合类型)不是,所以你得到总数大小

请参阅src/backend/access/heap/tuptoaster.c中的相关代码:

Size
toast_datum_size(Datum value)
{
    struct varlena *attr = (struct varlena *) DatumGetPointer(value);
    Size        result;

    if (VARATT_IS_EXTERNAL_ONDISK(attr))
    {
        /*
         * Attribute is stored externally - return the extsize whether
         * compressed or not.  We do not count the size of the toast pointer
         * ... should we?
         */
        struct varatt_external toast_pointer;

        VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
        result = toast_pointer.va_extsize;
    }
    [...]
    else
    {
        /*
         * Attribute is stored inline either compressed or not, just calculate
         * the size of the datum in either case.
         */
        result = VARSIZE(attr);
    }
    return result;
}