web-dev-qa-db-ja.com

なぜ*ではない*エラー:インデックス行サイズxxxxがインデックス "foo"の最大2712を超えていますか?

最大サイズを超える値を持つ列にインデックスを付ける試みが失敗することを繰り返し見ました。 Postgres 10には次のようなエラーメッセージがあります。

ERROR:  index row size xxxx exceeds maximum 2712 for index "foo_idx"
HINT:  Values larger than 1/3 of a buffer page cannot be indexed.
       Consider a function index of an MD5 hash of the value, or use full text indexing.

例:

等。

現在、 a_horse_with_no_nameはケースを示しています より大きなtext値(10000文字)があり、Postgres 9.6のUNIQUEインデックスでまだ機能しているようです。彼のテストケースを引用する:

create table tbl (col text);
create unique index on tbl (col);

insert into tbl
values (rpad(md5(random()::text), 10000, md5(random()::text)));

select length(val) from x;  -- 10000

エラーはなく、列の値は実際に10000文字の長さでテストされました。

最近の変更はありますか、またはこれはどのようにして可能ですか?

8

短い答え:圧縮。

データ型textは、デフォルトで(ロスレス!)圧縮と行外ストレージを許可します。

_SELECT typstorage FROM pg_type WHERE typname = 'text';  -- 'x'
_

_pg_type.typstorage_: に関するマニュアル

_p: Value must always be stored plain.
e: Value can be stored in a “secondary” relation (if relation has one, see pg_class.reltoastrelid).
m: Value can be stored compressed inline.
x: Value can be stored compressed inline or stored in “secondary” storage.
_

M列をセカンダリストレージに移動することもできますが、これは最後の手段としてのみ可能です(e列とx列が最初に移動されます)。

pg_column_size()の代わりに length() を使用してテストします。入力値だけでなく、(圧縮が適用された)実際のテーブル列を必ずテストしてください。見る:

_CREATE TABLE tbl (id int, col text);
INSERT INTO tbl(id, col) VALUES 
   (1, rpad(md5('non_random'::text),     100, md5('non_random'::text)))
 , (2, rpad(md5('non_random'::text),    1000, md5('non_random'::text)))
 , (3, rpad(md5('non_random'::text),   10000, md5('non_random'::text)))
 , (4, rpad(md5('non_random'::text),  100000, md5('non_random'::text)))
 , (5, rpad(md5('non_random'::text),  500000, md5('non_random'::text)))
 , (6, rpad(md5('non_random'::text), 1000000, md5('non_random'::text)));
_
_SELECT id, left(col, 10) || ' ...' AS col
     , length(col) AS char_length
     , pg_column_size(col) AS compressed
     , pg_column_size(col || '') AS uncompressed
FROM   tbl ORDER BY id;
_
 id | col | char_length |圧縮|非圧縮
-:| :---------- ----------:| ---------:| -----------:
 1 | 67ad0f29fa ... | 100 | 101 | 104 
 2 | 67ad0f29fa ... | 1000 | 1004 | 1004 
 3 | 67ad0f29fa ... | 10000 | 160 | 10004 
 4 | 67ad0f29fa ... | 100000 | 1191 | 100004 
 5 | 67ad0f29fa ... | 500000 | 5765 | 500004 
 6 | 67ad0f29fa ... | 1000000 | 11487 | 1000004 
_SELECT pg_column_size(rpad(md5('non_random'::text), 1000000, md5('non_random'::text)));
_
 | pg_column_size | 
 | -------------:| 
 | 1000004 | 

db <> fiddle ---(ここ

Noop式pg_column_size(col || '')を使用して、ストレージフォーマットから値を強制的にアンパックする方法に注意してください。

5行目は大きすぎて、タプルのインデックスに収まらず(圧縮されていても)、タイトルにエラーメッセージが表示されます。

6番目の行は、インデックスページにも適合し、関連するエラーメッセージをトリガーするために大きくなります。

エラー:インデックス行には11504バイトが必要です。最大サイズは8191です

rpad()で生成されたテスト値には、大規模な圧縮を可能にする繰り返しパターンがあります。非常に長いストリングでも、簡単に最大に適合します。この方法で圧縮後のサイズ。

関連:

長い答え

私はより広範なテストを実行し、ストレージの内部を改ざんして私の理解を確認しました。テスト目的のみ!

dbfiddleでは、システムカタログへの書き込みアクセスは許可されていません。しかし、クエリは「自宅で」試すためのものです。

10