pir*_*pir 5 postgresql indexing storage
我试图更好地了解创建 Postgres 索引所涉及的权衡。作为其中的一部分,我很想了解通常使用多少空间索引。我已经通读了文档,但找不到任何关于此的信息。我一直在做我自己的小实验来创建表和索引,但是如果有人能解释为什么大小是这样的,那就太棒了。假设一个像这样有 1M 行的公共表,其中每一行都有一个唯一的id和唯一的outstanding.
CREATE TABLE account (
id integer,
active boolean NOT NULL,
outstanding double precision NOT NULL,
);
Run Code Online (Sandbox Code Playgroud)
以及由创建的索引
CREATE INDEX id_idx ON account(id)CREATE INDEX outstanding_idx ON account(outstanding)CREATE INDEX id_outstanding_idx ON account(id, outstanding)CREATE INDEX active_idx ON account(active)CREATE INDEX partial_id_idx ON account(id) WHERE active你估计索引大小是多少字节,更重要的是,为什么?
Lau*_*lbe 10
你可以自己计算一下。每个索引条目有 8 个字节的开销。添加索引数据的平均大小(采用内部二进制格式)。
还有一些额外的开销,例如页眉和页脚以及内部索引页,但这并不占太多,除非您的索引行非常宽。
Erw*_*ter 10
由于您没有指定索引类型,我将假设默认的B 树索引。其他类型可能有很大不同。
这是一个简单的函数,用于计算给定表上具有给定列的索引的估计最小大小(以字节为单位):
CREATE OR REPLACE FUNCTION f_index_minimum_size(_tbl regclass, _cols VARIADIC text[], OUT estimated_minimum_size bigint)
LANGUAGE plpgsql AS
$func$
DECLARE
_missing_column text;
BEGIN
-- assert
SELECT i.attname
FROM unnest(_cols) AS i(attname)
LEFT JOIN pg_catalog.pg_attribute a ON a.attname = i.attname
AND a.attrelid = _tbl
WHERE a.attname IS NULL
INTO _missing_column;
IF FOUND THEN
RAISE EXCEPTION 'Table % has no column named %', _tbl, quote_ident(_missing_column);
END IF;
SELECT INTO estimated_minimum_size
COALESCE(1 + ceil(reltuples/trunc((blocksize-page_overhead)/(4+tuple_size)))::int, 0) * blocksize -- AS estimated_minimum_size
FROM (
SELECT maxalign, blocksize, reltuples, fillfactor, page_overhead
, (maxalign -- up to 16 columns, else nullbitmap may force another maxalign step
+ CASE WHEN datawidth <= maxalign THEN maxalign
WHEN datawidth%maxalign = 0 THEN datawidth
ELSE (datawidth + maxalign) - datawidth%maxalign END -- add padding to the data to align on MAXALIGN
) AS tuple_size
FROM (
SELECT c.reltuples, count(*)
, 90 AS fillfactor
, current_setting('block_size')::bigint AS blocksize
, CASE WHEN version() ~ '64-bit|x86_64|ppc64|ia64|amd64|mingw32' -- MAXALIGN: 4 on 32bits, 8 on 64bits
THEN 8 ELSE 4 END AS maxalign
, 40 AS page_overhead -- 24 bytes page header + 16 bytes "special space"
-- avg data width without null values
, sum(ceil((1-COALESCE(s.null_frac, 0)) * COALESCE(s.avg_width, 1024))::int) AS datawidth -- ceil() because avg width has a low bias
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
JOIN pg_catalog.pg_stats s ON s.schemaname = c.relnamespace::regnamespace::text
AND s.tablename = c.relname
AND s.attname = a.attname
WHERE c.oid = _tbl
AND a.attname = ANY(_cols) -- all exist, verified above
GROUP BY 1
) sub1
) sub2;
END
$func$;
Run Code Online (Sandbox Code Playgroud)
调用示例:
SELECT f_index_minimum_size('my_table', 'col1', 'col2', 'col3');
SELECT f_index_minimum_size('public.my_table', VARIADIC '{col1, col2, col3}');
Run Code Online (Sandbox Code Playgroud)
db<>在这里摆弄
关于VARIADIC参数:
基本上,所有索引都使用通常为 8 kb 块大小(很少为 4 kb)的数据页。B 树索引一开始就有一个数据页开销每个附加数据页的固定开销为 40 字节(当前)。每个页面都存储元组,如此处手册中所述。每个元组都有一个元组标头(通常为 8 字节,包括对齐填充)、可能为空位图、数据(可能包括多列索引的列之间的对齐填充)以及可能到下一个倍数的对齐填充(通常为 8 字节)。另外,每个元组有4 个字节。最初可能会保留一些空间以供以后添加,默认情况下,B 树索引的空间为 - 90 %。MAXALIGNItemIdfillfactor
报告的尺寸是估计的最小尺寸。由于页面拆分造成的自然膨胀,实际索引通常会大 25% 左右。另外,计算不考虑多列之间可能的对齐填充。可以再增加几个百分点(或者在极端情况下更多)。看:
估计基于视图中的列统计信息,pg_stats该视图基于系统表pg_statistics。(直接使用后者会更快,但只允许超级用户使用。)特别是,计算基于null_frac,“为空的列条目的分数”和avg_width,“列条目的平均宽度(以字节为单位)”来计算平均数据宽度 - 忽略多列索引可能的额外对齐填充。
fillfactor考虑默认的 90% 。(人们可能会指定不同的一个。)
对于 B 树索引来说,高达 50% 的膨胀通常是正常的,无需担心。
不适用于表达式索引。
没有提供部分索引。
如果传递除现有普通列名之外的任何内容,函数将引发异常。区分大小写!
如果表是新的(或者在任何情况下统计信息可能已过时),请确保ANALYZE在调用函数更新(甚至启动!)统计信息之前在表上运行。
由于重大优化,Postgres 12中的 B 树索引浪费的空间更少,并且通常更接近报告的最小大小。
不考虑Postgres 13引入的重复数据删除,它可以压缩具有重复值的索引。
部分代码取自 ioguix 的膨胀估计查询:
更多血淋淋的细节参见 Postgres 源代码:
| 归档时间: |
|
| 查看次数: |
1158 次 |
| 最近记录: |