Я не очень хорошо разбираюсь в БД, поэтому, пожалуйста, потерпите меня.
Я пытаюсь поместить очень длинные данные JSON в таблицу, эта таблица была создана фреймворком Django.
Я использую Postgres на Heroku. Итак, когда я пытаюсь поместить данные, я получаю следующую ошибку:
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.
Моя БД и таблица выглядят примерно так:
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)
Похоже, я должен изменить, "editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id)
чтобы взятьmd5(content)
Кто-нибудь может мне с этим помочь? Я понятия не имею, как это сделать.
Обновить:
JSON
содержание - https://gist.github.com/akshaybabloo/0b3dc1fb4d964b10d09ccd6884fe3a40
Обновление 2:
Я создал следующий UNIQUE
индекс, что я должен удалить в этом?
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
Должен ли я удалить 1
или 2
(см. Стрелки)?
postgresql
Акшай
источник
источник
Ответы:
У вас есть уникальный индекс
(content, ref_id)
, называетсяeditor_contentmodel_content_2192f49c_uniq
Я не уверен, почему это там для начала. Итак, давайте вернемся назад и рассмотрим, что это делает. Это гарантирует, что
content
иref_id
являются уникальными. Однако в PostgreSQLUNIQUE
ограничение реализовано с помощью btree, что делает его плохим решением. Используя этот метод, вы создаете btree с содержимым, которое по сути дублирует размер этой маленькой таблицы и создает гигантский индекс. Гигантский индекс, который все еще ограничен размером контента, как вы уже нашли. Возникает несколько вопросовВас волнует, что контент уникален? Если вас волнует, что контент уникален для ref_id, то вам, вероятно, нужно сохранить хэш контента. Что-то вроде..
Вместо этого он будет хранить md5sum контента на btree. Пока у ref_id есть контент с уникальным md5 над этим ref_id, у вас все хорошо.
Если вам все равно, что
content
является уникальным, рассмотрите возможность его полного удаления.Может быть ничего не стоит, что когда вы реализуете
UNIQUE
ограничение с помощью btree (как это делает PostgreSQL), вы получаете дополнительный индекс бесплатно. При нормальных обстоятельствах это имеет дополнительное преимущество.Ускорит запрос
Однако, когда вы можете использовать функциональный
md5()
вариант, индекс контента больше не существует, поэтому теперь для использования этого индекса вам придетсяmd5(content) = md5('This content')
Весь
text = text
переоценен. Это почти никогда не то, что вы хотите. Если вы хотите сократить время запроса по тексту, btree довольно бесполезен. Вы, вероятно, хотите посмотреть наОБНОВЛЕНИЕ 1
Основываясь на вашем JSON, я бы предложил сохранить его как
jsonb
, а затем создать индекс дляmd5(content)
; поэтому, возможно, вместо того, чтобы выше, вместо этого запустите это.ОБНОВЛЕНИЕ 2
Вы спрашиваете, какие индексы вы должны удалить
Вот неожиданный ответ: вы должны удалить их все, кроме :
editor_contentmodel_pkey
который говорит, что всеref_id
должно быть уникальным.editor_contentmodel_content_2192f49c_uniq
этот индекс гарантирует, что вы находитесьUNIQUE
наref_id
ANDcontent
, но если у вас не может быть дубликата, уref_id
вас никогда не будет дублированного контента для этогоref_id
. Таким образом, вы никогда не сможете нарушить этот индекс без нарушенияeditor_contentmodel_pkey
. Это делает это бессмысленным.editor_contentmodel_ref_id_md5_idx
этот индекс также бессмыслен по той же причине. Вы никогда не можете иметь дубликат заmd5(content::text)
кадр ,ref_id
потому что независимо от того, что значенияmd5(content::text)
есть вы никогда не можете иметь дубликатаref_id
.editor_contentmodel_ref_id_8f74b4f3_like
Это также плохая идея, потому что вы дублируете индексref_id
. Это не бесполезно, просто неоптимально. Вместо этого, если вам нужноvarchar_pattern_ops
использовать его вместо всегоcontent
поля.В заключение отметим, что мы не очень часто используем
varchar
PostgreSQL, потому что он реализован как varlena с проверочным ограничением. В этом нет никакой выгоды, и ничего не теряется, когда вы просто используетеtext
. Так что, если нет конкретной причины, почемуref_id
когда-либо может быть 120 символов, но это может быть 119 символов, тогда я просто использовал быtext
тип.ОБНОВЛЕНИЕ 3
Давайте вернемся к вашей предыдущей проблеме ..
Это говорит вам, что проблема именно с индексом
"editor_contentmodel_content_2192f49c_uniq"
. Вы определили это какПроблема в том, что вы пытаетесь создать индекс
content
. Но, опять же, сам индекс хранит фактическое содержание jsoncontent
, и это то, что превышает предел. На самом деле это не проблема, потому что даже если бы этого ограничения неeditor_contentmodel_content_2192f49c_uniq
было, оно было бы совершенно бесполезным. Почему? Опять же, вы не можете добавить больше уникальности в строку, которая уже гарантированно будет на 100% уникальной. Вы, кажется, не получаете это. Давайте будем простыми.Выше
(ref_id, content)
имеет смысл использовать единственный уникальный индекс / ограничение (без других индексов), поскольку он остановит дублирование(1,1)
. Индекс over(ref_id, md5(content))
также имеет смысл, потому что он остановит дублирование с(1,1)
помощью прокси-сервера и остановит дублирование(1, md5(1))
. Однако все это работает , потому что в этом примере я далref_id
это НЕ гарантируетсяUNIQUE
. Выref_id
не этоref_id
. Выref_id
этоPRIMARY KEY
. Это означает, что он гарантированно будет УНИКАЛЬНЫМ.Это означает, что копия
(1,1)
и строка(1,2)
никогда не могут быть вставлены. Это также означает, что индексы над чем-либо, кроме ref_id, не могут гарантировать большую уникальность. Они должны быть менее строгими, чем индекс, который у вас есть в настоящее время. Таким образом, ваш стол может выглядеть только такисточник
editor_contentmodel
таблицыcolumn
и добавить уникальность md5? или мы не можем просто изменитьCONSTRAINT editor_contentmodel_content_2192f49c_uniq UNIQUE (content, ref_id)
? Почему я должен создать для него новую таблицу?CREATE TABLE
команду и введитеCREATE UNIQUE INDEX
право под ней. ТогдаDROP
твой старый индекс.Update 2
Поскольку ref_id является первичным ключом, вы не можете иметь его дублирующиеся значения. Это означает, что уникальное ограничение на комбинацию (content, ref_id) бесполезно, так как все, что нарушает его, также нарушает ограничение первичного ключа. Просто избавься от этого.
источник
create unique index on editor_contentmodel (ref_id, md5(content::text))
? или я мог бы воссоздать таблицу и удалить первичный ключ.