Los valores superiores a 1/3 de una página de búfer no se pueden indexar

9

No soy muy bueno con DB, así que tengan paciencia conmigo.

Estoy tratando de poner datos JSON muy largos en una tabla, esta tabla fue creada por el marco Django.

Estoy usando Postgres en Heroku. Entonces, cuando trato de poner los datos me sale el siguiente error:

File "/app/.heroku/python/lib/python3.6/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
psycopg2.OperationalError: index row size 3496 exceeds maximum 2712 for index "editor_contentmodel_content_2192f49c_uniq"
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.

Mi DB y tabla se ve así:

gollahalli-me-django-test::DATABASE=> \dt
                      List of relations
 Schema |            Name            | Type  |     Owner
--------+----------------------------+-------+----------------
 public | auth_group                 | table | ffnyjettujyfck
 public | auth_group_permissions     | table | ffnyjettujyfck
 public | auth_permission            | table | ffnyjettujyfck
 public | auth_user                  | table | ffnyjettujyfck
 public | auth_user_groups           | table | ffnyjettujyfck
 public | auth_user_user_permissions | table | ffnyjettujyfck
 public | django_admin_log           | table | ffnyjettujyfck
 public | django_content_type        | table | ffnyjettujyfck
 public | django_migrations          | table | ffnyjettujyfck
 public | django_session             | table | ffnyjettujyfck
 public | editor_contentmodel        | table | ffnyjettujyfck
(11 rows)


gollahalli-me-django-test::DATABASE=> \d+ editor_contentmodel
                            Table "public.editor_contentmodel"
  Column   |           Type           | Modifiers | Storage  | Stats target | Description
-----------+--------------------------+-----------+----------+--------------+-------------
 ref_id    | character varying(120)   | not null  | extended |              |
 content   | text                     | not null  | extended |              |
 timestamp | timestamp with time zone | not null  | plain    |              |
Indexes:
    "editor_contentmodel_pkey" PRIMARY KEY, btree (ref_id)
    "editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id)
    "editor_contentmodel_ref_id_8f74b4f3_like" btree (ref_id varchar_pattern_ops)

Parece que tengo que cambiar "editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id)para tomarmd5(content)

Puede alguien ayudarme con esto? No tengo idea de cómo hacerlo.

Actualizar:

JSONcontenido - https://gist.github.com/akshaybabloo/0b3dc1fb4d964b10d09ccd6884fe3a40

Actualización 2:

He creado el siguiente UNIQUEíndice, ¿qué debo eliminar en esto?

gollahalli_me_django=> create unique index on editor_contentmodel (ref_id, md5(content::text));
CREATE INDEX
gollahalli_me_django=> \d editor_contentmodel;
        Table "public.editor_contentmodel"
  Column   |           Type           | Modifiers
-----------+--------------------------+-----------
 ref_id    | character varying(120)   | not null
 content   | jsonb                    | not null
 timestamp | timestamp with time zone | not null
Indexes:
    "editor_contentmodel_pkey" PRIMARY KEY, btree (ref_id)
    "editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id) <---- 1
    "editor_contentmodel_ref_id_md5_idx" UNIQUE, btree (ref_id, md5(content::text))
    "editor_contentmodel_ref_id_8f74b4f3_like" btree (ref_id varchar_pattern_ops) <----2

¿Debo eliminar 1o 2(ver las flechas)?

akshay
fuente
Intenta columna de índice de texto, y PostgreSQL (como todos los demás) tienen límites, por lo indexará 2713, por lo que sí - Usted puede tratar de cambiarlo por hash MD5 para hacerlo más pequeño
a_vlad
@a_vlad ¿Cómo debo hacer eso? No tengo idea de cómo hacerlo.
akshay
¿Qué es el contenido? ¿Es TEXTO o JSON?
Evan Carroll
Además, ¿alguna vez tiene dos contenidos, para el mismo ref_id? Si es así, ¿cuál es el propósito de eso?
Evan Carroll
de acuerdo con @EvanCarroll - puede ser ¿No necesita este índice en absoluto?
a_vlad

Respuestas:

7

Tiene un índice ÚNICO (content, ref_id), llamadoeditor_contentmodel_content_2192f49c_uniq

"editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id)

No estoy seguro de por qué está ahí para empezar. Entonces, demos un paso atrás y abordemos lo que esto hace. Esto asegura que content, y ref_idson únicos. Sin embargo, en PostgreSQL la UNIQUErestricción se implementa con un btree que hace que esta sea una solución pobre. Con este método, está creando un btree con contenido que esencialmente duplica el tamaño de esta pequeña tabla y crea un índice gigantesco. Sin embargo, como ha encontrado, un índice gigantesco que todavía está limitado por el tamaño del contenido. Plantea algunas preguntas

  • ¿Te importa que el contenido sea único? Si te importa que el contenido sea único para ref_id, entonces lo que probablemente quieras es almacenar el hash del contenido. Algo como..

    CREATE TABLE foo ( ref_id int, content text );
    CREATE UNIQUE INDEX ON foo (ref_id,md5(content));

    En su lugar, esto almacenará el md5sum de contenido en el btree. Siempre que ref_id tenga contenido con un md5 único sobre ese ref_id, eres bueno.

  • Si no le importa eso, contentconsidere eliminarlo por completo.

Puede no valer nada que cuando implemente una UNIQUErestricción con un btree (como lo hace PostgreSQL), obtenga un índice agregado de forma gratuita. En circunstancias normales, esto tiene un beneficio adicional.

CREATE TABLE foo ( ref_id int, content text );
CREATE UNIQUE INDEX ON foo (ref_id,content);

Acelerará la consulta

SELECT *
FROM foo
WHERE ref_id = 5
  AND content = 'This content'

Sin embargo, cuando tiene la posibilidad de usar la md5()variante funcional, ya no hay un índice en el contenido, por lo que ahora para usar ese índice tendrá que

  1. Solo consulta en ref_id,
  2. Agregue a ref_id una cláusula que md5(content) = md5('This content')

El conjunto text = textestá sobrevalorado. Eso casi nunca es lo que quieres. Si está buscando acelerar el tiempo de consulta sobre el texto, el btree es bastante inútil. Probablemente quieras investigar

  1. pgtrgm
  2. text_pattern_ops
  3. Búsqueda de texto completo (FTS)

ACTUALIZACIÓN 1

Base en su JSON . Sugeriría almacenarlo como a jsonby luego crear el índice md5(content); así que quizás en lugar de lo anterior ejecute esto.

ALTER TABLE public.editor_contentmodel
  ALTER COLUMN content
  SET DATA TYPE jsonb
  USING content::jsonb;

CREATE UNIQUE INDEX ON foo (ref_id,md5(content::text));

ACTUALIZACIÓN 2

Pregunta qué índices debe eliminar

gollahalli_me_django=> create unique index on editor_contentmodel (ref_id, md5(content::text));
CREATE INDEX
gollahalli_me_django=> \d editor_contentmodel;
        Table "public.editor_contentmodel"
  Column   |           Type           | Modifiers
-----------+--------------------------+-----------
 ref_id    | character varying(120)   | not null
 content   | jsonb                    | not null
 timestamp | timestamp with time zone | not null
Indexes:
    "editor_contentmodel_pkey" PRIMARY KEY, btree (ref_id)
    "editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id) <---- 1
    "editor_contentmodel_ref_id_md5_idx" UNIQUE, btree (ref_id, md5(content::text))
    "editor_contentmodel_ref_id_8f74b4f3_like" btree (ref_id varchar_pattern_ops) <----2

Aquí está la respuesta sorprendente: debe eliminarlos todos, excepto : lo editor_contentmodel_pkeyque dice que todos ref_iddeben ser únicos.

  1. editor_contentmodel_content_2192f49c_uniqeste índice asegura que esté UNIQUEen ref_idAND content, pero si no puede tener un duplicado ref_id, nunca puede tener un contenido duplicado para eso ref_id. Por lo tanto, nunca puede violar este índice sin violarlo también editor_contentmodel_pkey. Eso lo hace inútil.
  2. editor_contentmodel_ref_id_md5_idxeste índice tampoco tiene sentido por la misma razón. Nunca se puede tener un duplicado md5(content::text)más ref_idporque independientemente de lo que el valor de md5(content::text)es que nunca puede tener un duplicado ref_id.
  3. editor_contentmodel_ref_id_8f74b4f3_likeTambién es una mala idea porque estás duplicando el índice ref_id. Esto no es inútil, simplemente no es óptimo. En cambio, si necesita varchar_pattern_opsusarlo en lugar de solo el contentcampo.

Como última nota, no utilizamos mucho varcharen PostgreSQL porque está implementado como una varlena con una restricción de verificación. No hay ganancia, y no hay nada perdido cuando simplemente lo usa text. Entonces, a menos que haya una razón concreta por la que ref_idpuede haber 120 caracteres pero puede ser 119 caracteres, entonces simplemente usaría el texttipo.

ACTUALIZACIÓN 3

Volvamos a tu problema anterior ...

psycopg2.OperationalError: index row size 3496 exceeds maximum 2712 for index "editor_contentmodel_content_2192f49c_uniq"

Esto le dice que el problema es específicamente con el índice"editor_contentmodel_content_2192f49c_uniq" . Lo has definido como

"editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id)

Entonces, el problema aquí es que estás tratando de crear un índice content. Pero, nuevamente, el índice en sí mismo almacena el contenido real de json content, y eso es lo que excede el límite. Esto no es realmente un problema, porque incluso si ese límite no estuviera en su lugar editor_contentmodel_content_2192f49c_uniq, sería totalmente inútil. ¿Por qué? de nuevo, no puede agregar más unicidad a una fila que ya está garantizada como 100% única. Parece que no estás entendiendo esto. Hagámoslo simple.

ref_id | content
1      | 1
1      | 1
1      | 2
2      | 1

En lo anterior, un único índice / restricción único (sin otros índices) (ref_id, content)tiene sentido porque detendría la duplicación de (1,1). Un índice sobre (ref_id, md5(content))también tendría sentido porque detendría la duplicación (1,1)por proxy de detener la duplicación de (1, md5(1)). Sin embargo, todo esto funciona porque en el ejemplo que he dado NOref_id está garantizado . Tu no es esto . Tu es un . Eso significa que está garantizado para ser ÚNICO.UNIQUEref_idref_idref_idPRIMARY KEY

Eso significa que el duplicado (1,1)y la fila de (1,2)NUNCA podrían insertarse. Eso también significa que los índices sobre cualquier cosa además de ref_id no pueden garantizar más exclusividad. Tendrían que ser menos estrictos que el índice que tiene actualmente. Entonces tu mesa solo podría verse así

ref_id | content
1      | 1
2      | 1
Evan Carroll
fuente
¿No puedo alterar las editor_contentmodeltablas columny agregarle unicidad md5? o no podemos simplemente alterar CONSTRAINT editor_contentmodel_content_2192f49c_uniq UNIQUE (content, ref_id)? ¿Por qué tengo que crear una nueva tabla para ello?
akshay
No tiene que crear una nueva tabla, solo le estaba mostrando cómo se vería con una versión simplificada de la tabla que tiene. Simplemente ignore el CREATE TABLEcomando y emita el CREATE UNIQUE INDEXderecho debajo de él. Entonces DROPtu antiguo índice.
Evan Carroll
Última pregunta, ¿podría ver miUpdate 2
akshay
@akshay actualizado.
Evan Carroll
1
Muchas gracias, Evan, esto me ayudó mucho. El concepto sigue siendo un poco inestable (no es mi campo en absoluto). Trataré de aprenderlo.
akshay
2

"editor_contentmodel_pkey" CLAVE PRIMARIA, btree (ref_id) "editor_contentmodel_content_2192f49c_uniq" RESTRICCIÓN ÚNICA, btree (content, ref_id)

Como ref_id es la clave principal, no puede tener valores duplicados. Eso significa que la restricción única en la combinación (contenido, ref_id) es inútil, ya que cualquier cosa que viole eso también violaría la restricción de clave principal. Solo deshazte de eso.

jjanes
fuente
¿Te refieres a deshacerte de eso y poner algo como create unique index on editor_contentmodel (ref_id, md5(content::text))? o podría volver a crear la tabla y eliminar la clave primaria.
akshay
No se lo que quieres. Si desea la clave principal en ref_id, consérvela. Pero si lo conserva, entonces editor_contentmodel_content_2192f49c_uniq es inútil, y soltarlo resolverá su problema de título. Además, si mantiene la clave primaria, entonces el nuevo índice que propone también es inútil (inútil como restricción, podría ser útil como índice, pero eso es muy poco probable).
jjanes