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:
JSON
contenido - 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 1
o 2
(ver las flechas)?
postgresql
akshay
fuente
fuente
Respuestas:
Tiene un índice ÚNICO
(content, ref_id)
, llamadoeditor_contentmodel_content_2192f49c_uniq
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
, yref_id
son únicos. Sin embargo, en PostgreSQL laUNIQUE
restricció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..
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,
content
considere eliminarlo por completo.Puede no valer nada que cuando implemente una
UNIQUE
restricción con un btree (como lo hace PostgreSQL), obtenga un índice agregado de forma gratuita. En circunstancias normales, esto tiene un beneficio adicional.Acelerará la consulta
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á quemd5(content) = md5('This content')
El conjunto
text = text
está 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 investigarACTUALIZACIÓN 1
Base en su JSON . Sugeriría almacenarlo como a
jsonb
y luego crear el índicemd5(content)
; así que quizás en lugar de lo anterior ejecute esto.ACTUALIZACIÓN 2
Pregunta qué índices debe eliminar
Aquí está la respuesta sorprendente: debe eliminarlos todos, excepto : lo
editor_contentmodel_pkey
que dice que todosref_id
deben ser únicos.editor_contentmodel_content_2192f49c_uniq
este índice asegura que estéUNIQUE
enref_id
ANDcontent
, pero si no puede tener un duplicadoref_id
, nunca puede tener un contenido duplicado para esoref_id
. Por lo tanto, nunca puede violar este índice sin violarlo tambiéneditor_contentmodel_pkey
. Eso lo hace inútil.editor_contentmodel_ref_id_md5_idx
este índice tampoco tiene sentido por la misma razón. Nunca se puede tener un duplicadomd5(content::text)
másref_id
porque independientemente de lo que el valor demd5(content::text)
es que nunca puede tener un duplicadoref_id
.editor_contentmodel_ref_id_8f74b4f3_like
También es una mala idea porque estás duplicando el índiceref_id
. Esto no es inútil, simplemente no es óptimo. En cambio, si necesitavarchar_pattern_ops
usarlo en lugar de solo elcontent
campo.Como última nota, no utilizamos mucho
varchar
en 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 usatext
. Entonces, a menos que haya una razón concreta por la queref_id
puede haber 120 caracteres pero puede ser 119 caracteres, entonces simplemente usaría eltext
tipo.ACTUALIZACIÓN 3
Volvamos a tu problema anterior ...
Esto le dice que el problema es específicamente con el índice
"editor_contentmodel_content_2192f49c_uniq"
. Lo has definido comoEntonces, 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 jsoncontent
, 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 lugareditor_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.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.UNIQUE
ref_id
ref_id
ref_id
PRIMARY 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ífuente
editor_contentmodel
tablascolumn
y agregarle unicidad md5? o no podemos simplemente alterarCONSTRAINT editor_contentmodel_content_2192f49c_uniq UNIQUE (content, ref_id)
? ¿Por qué tengo que crear una nueva tabla para ello?CREATE TABLE
comando y emita elCREATE UNIQUE INDEX
derecho debajo de él. EntoncesDROP
tu antiguo índice.Update 2
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.
fuente
create unique index on editor_contentmodel (ref_id, md5(content::text))
? o podría volver a crear la tabla y eliminar la clave primaria.