Valores maiores que 1/3 de uma página de buffer não podem ser indexados


9

Eu não sou muito bom com DB, por favor, tenha paciência comigo.

Estou tentando colocar dados JSON muito longos em uma tabela, esta tabela foi criada pelo framework Django.

Estou usando o Postgres no Heroku. Então, quando tento colocar os dados, recebo o seguinte erro:

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.

Meu banco de dados e tabela são mais ou menos assim:

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 eu tenho que mudar "editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id)para tirarmd5(content)

Alguém pode me ajudar com isso? Não tenho idéia de como fazê-lo.

Atualizar:

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

Atualização 2:

Eu criei o seguinte UNIQUEíndice, o que devo remover neste?

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

Devo remover 1ou 2(veja as setas)?


Você tenta coluna índice de texto, e PostgreSQL (como todos os outros) têm limites, para o índice que 2713, então sim - você pode tentar mudá-lo para hash MD5 para torná-la menor
a_vlad

@a_vlad Como devo fazer isso? Não faço ideia de como fazê-lo.
akshay

O que é conteúdo? Isso é TEXT ou JSON?
Evan Carroll

Além disso, você já tem dois conteúdos, para o mesmo ref_id? Se sim, qual é o propósito disso?
Evan Carroll

concorda com @EvanCarroll - pode ser Você não precisa deste índice?
precisa saber é o seguinte

Respostas:


7

Você tem um índice UNIQUE (content, ref_id), chamadoeditor_contentmodel_content_2192f49c_uniq

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

Não sei por que isso está aí para começar. Então, vamos voltar e abordar o que isso faz. Isso garante contente ref_idé único. No entanto, no PostgreSQL, a UNIQUErestrição é implementada com uma btree, o que a torna uma solução ruim. Usando esse método, você está criando uma btree com conteúdo que duplica essencialmente o tamanho dessa pequena tabela e cria um índice gigantesco. Um índice gigantesco que ainda é limitado pelo tamanho do conteúdo - como você encontrou. Isso levanta algumas questões

  • Você se importa que o conteúdo seja único? Se você se importa que o conteúdo seja exclusivo para ref_id, o que você provavelmente quer é armazenar o hash do conteúdo. Algo como..

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

    Em vez disso, ele armazenará o md5sum de conteúdo na btree. Desde que ref_id tenha conteúdo com um md5 exclusivo sobre esse ref_id, você estará bem.

  • Se você não se importa com isso, contentconsidere removê-lo completamente.

Pode não valer nada que, ao implementar uma UNIQUErestrição com uma btree (como o PostgreSQL faz), você obtém um índice adicionado gratuitamente. Sob uma circunstância normal, isso tem um benefício adicional.

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

Acelerará a consulta

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

No entanto, quando você tiver a chance de usar a md5()variante funcional, não haverá mais um índice no conteúdo; portanto, agora, para usar esse índice, você precisará

  1. Consulta apenas em ref_id,
  2. Adicione à ref_id uma cláusula que md5(content) = md5('This content')

O todo text = textestá superestimado. Isso quase nunca é o que você deseja. Se você deseja acelerar o tempo de consulta sobre o texto, o btree é bastante inútil. Você provavelmente quer investigar

  1. pgtrgm
  2. text_pattern_ops
  3. Pesquisa em texto completo (STF)

ATUALIZAÇÃO 1

Baseando-se no seu JSON , sugiro armazená-lo como um jsonbe criar o índice md5(content); então, talvez, ao invés do acima, execute isso.

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));

ATUALIZAÇÃO 2

Você pergunta quais índices você deve remover

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

Aqui está a resposta surpreendente: você deve remover todos eles, exceto : o editor_contentmodel_pkeyque diz que todos ref_idprecisam ser únicos.

  1. editor_contentmodel_content_2192f49c_uniqesse índice garante que você esteja UNIQUEno ref_idANDcontent , mas se não puder ter uma duplicata, ref_idnunca poderá ter um conteúdo duplicado para isso ref_id. Portanto, você nunca pode violar esse índice sem violar também editor_contentmodel_pkey. Isso torna inútil.
  2. editor_contentmodel_ref_id_md5_idxesse índice também não faz sentido pelo mesmo motivo. Você nunca pode ter uma duplicatamd5(content::text) mais ref_idporque, independentemente do que o valor de md5(content::text)é que você nunca pode ter uma duplicata ref_id.
  3. editor_contentmodel_ref_id_8f74b4f3_like também é uma má ideia, porque você está duplicando o índice ref_id . Isso não é inútil, apenas não é o ideal. Em vez disso, se você precisar, varchar_pattern_opsuse apenas o contentcampo.

Como última nota, não usamos muito varchar no PostgreSQL porque ele é implementado como uma varlena com uma restrição de verificação. Não há ganho para isso e não há nada perdido quando você simplesmente usa text. Portanto, a menos que haja uma razão concreta para que ref_idpossa haver 120 caracteres, mas 119 caracteres, eu simplesmente usaria o texttipo

ATUALIZAÇÃO 3

Vamos voltar ao seu problema anterior.

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

Isso está dizendo que o problema está especificamente no índice"editor_contentmodel_content_2192f49c_uniq" . Você definiu isso como

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

Portanto, o problema aqui é que você está tentando criar um índice novamente content. Mas, novamente, o próprio índice armazena o conteúdo json real de contente é isso que excede o limite. Isso não é realmente um problema, porque mesmo se esse limite não estivesse em vigor, editor_contentmodel_content_2192f49c_uniqseria totalmente inútil. Por quê? novamente, você não pode adicionar mais exclusividade a uma linha que já é garantida como 100% exclusiva. Você não parece estar entendendo isso. Vamos simplificar.

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

No exemplo acima, um único índice / restrição único (sem outros índices) (ref_id, content)faz sentido porque interromperia a duplicação de (1,1). Um excesso de índice (ref_id, md5(content))também faria sentido, pois interromperia a duplicação de (1,1)por proxy de interromper a duplicação de (1, md5(1)). No entanto, tudo isso funciona, porque no exemplo que eu dei NÃOref_id é garantido . Seu não é isso . SeuUNIQUEref_idref_idref_id é um PRIMARY KEY. Isso significa que é garantido que seja ÚNICO.

Isso significa que a duplicata (1,1)e a linha (1,2)NUNCA poderão ser inseridas. Isso também significa que índices além de ref_id não podem garantir mais exclusividade. Eles teriam que ser menos rigorosos que o índice que você possui atualmente. Portanto, sua mesa só poderia ficar assim

ref_id | content
1      | 1
2      | 1

Não posso alterar editor_contentmodeltabelas columne adicionar exclusividade md5 a ela? ou não podemos simplesmente alterar CONSTRAINT editor_contentmodel_content_2192f49c_uniq UNIQUE (content, ref_id)? Por que eu tenho que criar uma nova tabela para isso?
akshay

Você não precisa criar uma nova tabela, eu estava apenas mostrando a você como ficaria com uma versão simplificada da tabela que você possui. Apenas ignore o CREATE TABLEcomando e emita CREATE UNIQUE INDEXlogo abaixo. Então DROPseu índice antigo.
Evan Carroll

Última pergunta, você poderia ver o meuUpdate 2
akshay 02/02

@akshay updated.
Evan Carroll

11
Muito obrigado, Evan isso me ajudou muito. O conceito ainda é um pouco instável (não é o meu campo). Vou tentar aprender.
akshay

2

"editor_contentmodel_pkey" CHAVE PRIMÁRIA, btree (ref_id) "editor_contentmodel_content_2192f49c_uniq" CONSTRAINT ÚNICA, btree (content, ref_id)

Como ref_id é a chave primária, você não pode ter valores duplicados. Isso significa que a restrição exclusiva na combinação (content, ref_id) é inútil, pois qualquer coisa que viole isso também violaria a restrição da chave primária. apenas livre-se disso.


Você quer se livrar disso e colocar algo assim create unique index on editor_contentmodel (ref_id, md5(content::text))? ou eu poderia recriar a tabela e remover a chave primária.
akshay

Eu não sei o que você quer. Se você deseja a chave primária em ref_id, mantenha-a. Mas, se você o mantiver, o editor_contentmodel_content_2192f49c_uniq é inútil, e largar o arquivo resolverá o seu problema de título. Além disso, se você mantiver a chave primária, o novo índice que você propõe também será inútil (inútil como restrição, pode ser útil como índice, mas isso é muito improvável).
jjanes
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.