Error de tamaño máximo de fila de índice

12

¿Hay un límite superior para una arraycolumna?

Recibo este error al insertar en el campo de matriz:

PG::Error: ERROR:  index row size 3480 exceeds maximum 2712 for index "ix_data"

Aquí está mi definición de tabla:

create table test_array(id varchar(50), data text[]);

ALTER TABLE test_array ADD PRIMARY KEY (id);

CREATE INDEX ix_data ON test_array USING GIN (data);

Necesito un índice en el campo de matriz, ya que estoy haciendo algunas búsquedas en él.


fuente
¿Podría ser que datacontiene una lista de etiquetas como las demostradas en esta publicación de blog relacionada de Scott Snyder ? Si ese es el caso, podría tener una mejor solución para usted.
Erwin Brandstetter
user310525, me gustaría respaldar la sugerencia de Erwin de que esto sería mejor en dba.se, si está dispuesto a crear una cuenta allí e indicar que migre un moderador.
Jack dice que intente topanswers.xyz

Respuestas:

14

El problema

Aquí hay un caso muy similar discutido en pgsql.general . Se trata de la limitación en un índice b-tree, pero es todo lo mismo porque un índice GIN usa un índice b-tree para las claves internamente y, por lo tanto, se encuentra con la misma limitación para el tamaño de la clave (en lugar del tamaño del elemento en un b-tree simple índice).

Cito el manual sobre la implementación del índice GIN :

Internamente, un índice GIN contiene un índice de árbol B construido sobre claves, donde cada clave es un elemento de uno o más elementos indexados

De cualquier manera, al menos un elemento de matriz en su columna dataes demasiado grande para ser indexado. Si esto es solo un valor anormal singular o algún tipo de accidente, es posible que pueda truncar el valor y acabar de una vez.

Para el propósito de la siguiente demostración, supondré lo contrario: muchos valores de texto largos en la matriz.

Solución simple

Puede reemplazar elementos en su matriz datacon los valores hash correspondientes . Y envíe valores de búsqueda a través de la misma función hash. Por supuesto, es probable que desee almacenar sus originales además en algún lugar. Con eso, casi llegamos a mi segunda variante ...

Solución avanzada

Podría crear una tabla de búsqueda para elementos de matriz con una serialcolumna como clave primaria sustituta (efectivamente, un tipo radical de valor hash), que es aún más interesante si los valores de los elementos involucrados no son únicos:

CREATE TABLE elem (
  elem_id serial NOT NULL PRIMARY KEY
, elem    text UNIQUE NOT NULL
);

Como deseamos buscar elem, agregamos un índice, pero esta vez un índice en una expresión , con solo los primeros 10 caracteres del texto largo. Eso debería ser suficiente en la mayoría de los casos para limitar una búsqueda a uno o unos pocos resultados. Adapte el tamaño a su distribución de datos. O use una función hash más sofisticada.

CREATE INDEX elem_elem_left10_idx ON elem(left(elem,10));

Su columna datasería entonces de tipo int[]. Cambié el nombre de la tabla datay me deshice de lo ominoso varchar(50)que tenía en su ejemplo:

CREATE TEMP TABLE data(
  data_id serial PRIMARY KEY
, data int[]
);

Cada elemento de matriz se datarefiere a a elem.elem_id. En este punto, puede considerar reemplazar la columna de matriz con una tabla n: m, normalizando así su esquema y permitiendo que Postgres imponga integridad referencial. La indexación y el manejo general se vuelven más fáciles ...

Sin embargo, por razones de rendimiento, la int[]columna en combinación con un índice GIN puede ser superior. El tamaño de almacenamiento es mucho más pequeño. En este caso, necesitamos el índice GIN:

CREATE INDEX data_data_gin_idx ON data USING GIN (data);

Ahora, cada clave del índice GIN (= elemento de matriz) es un en integerlugar de un largo text. El índice será más pequeño en varios órdenes de magnitud, por lo tanto, las búsquedas serán mucho más rápidas.

La desventaja: antes de poder realizar una búsqueda, debe buscar en elem_idla tabla elem. Usando mi índice funcional recién introducido elem_elem_left10_idx, esto también será mucho más rápido.

Puede hacerlo todo en una consulta simple :

SELECT d.*, e.*
FROM   elem e
JOIN   data d ON ARRAY[e.elem_id] <@ d.data
WHERE  left(e.elem, 10) = left('word1234word', 10) -- match index condition
AND    e.elem = 'word1234word';  -- need to recheck, functional index is lossy

Puede interesarle la extensión intarray, que proporciona operadores adicionales y clases de operadores.

Demostración en vivo totalmente funcional en sqlfiddle.

Erwin Brandstetter
fuente
2

El error está en el índice ix_data, no en el text[]campo. El tamaño máximo para una fila en ese tipo de índice en particular está limitado a 2712bytes. Si suelta su índice e intenta insertarlo nuevamente, debería funcionar para usted. Si necesita indexar un campo más grande, es posible que desee ver las características de indexación de texto completo de postgres.

jcern
fuente
2

Estaba obteniendo esto en una columna de geografía PostGIS. Fue porque accidentalmente creé el índice incorrectamente. Debe incluir el parámetro USING GIST al crear dichos índices.

Brad Mathews
fuente
Gracias, eso fue todo. Wow, hasta ahora traído. Puede que me haya ahorrado horas. Especialmente porque pensé que GiST se usaba por defecto, pero me equivoqué y trata de usar b-tree.
Jonas