Les valeurs supérieures à 1/3 d'une page tampon ne peuvent pas être indexées

9

Je ne suis pas très bon avec DB, alors soyez indulgent avec moi.

J'essaie de mettre une très longue donnée JSON dans une table, cette table a été créée par le framework Django.

J'utilise Postgres sur Heroku. Ainsi, lorsque j'essaie de mettre les données, j'obtiens l'erreur suivante:

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.

Ma base de données et ma table ressemblent à ceci:

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)

On dirait que je dois changer "editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id)pour prendremd5(content)

Est-ce que quelqu'un peut m'aider avec ça? Je n'ai aucune idée de comment le faire.

Mise à jour:

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

Mise à jour 2:

J'ai créé l' UNIQUEindex suivant , que dois-je supprimer ici?

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

Dois-je retirer 1ou 2(Voir les flèches)?

akshay
la source
Vous essayez d'indexer la colonne TEXT, et PostgreSQL (comme toutes les autres) a des limites, pour l'indexer 2713, alors oui - Vous pouvez essayer de le changer pour le hachage MD5 pour le rendre plus petit
a_vlad
@a_vlad Comment dois-je faire cela? Aucune idée sur la façon de le faire.
akshay
Qu'est-ce que le contenu? Est-ce TEXT ou JSON?
Evan Carroll
De plus, avez-vous déjà deux contenus, pour le même ref_id? Si oui, quel est le but de cela?
Evan Carroll
d'accord avec @EvanCarroll - peut-être Vous n'avez pas du tout besoin de cet index?
a_vlad

Réponses:

7

Vous avez un index UNIQUE (content, ref_id), appeléeditor_contentmodel_content_2192f49c_uniq

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

Je ne sais pas pourquoi cela là pour commencer. Revenons donc en arrière et examinons ce que cela fait. Cela garantit cela contentet ref_idest unique. Cependant, dans PostgreSQL, la UNIQUEcontrainte est implémentée avec un btree, ce qui en fait une mauvaise solution. En utilisant cette méthode, vous créez un btree avec un contenu qui duplique essentiellement la taille de cette petite table et crée un index gigantesque. Un index gigantesque qui est toujours limité par la taille du contenu, comme vous l'avez trouvé. Cela soulève quelques questions

  • Vous souciez-vous que le contenu soit unique? Si vous vous souciez du fait que le contenu est unique pour ref_id, alors ce que vous voulez probablement est de stocker le hachage du contenu. Quelque chose comme..

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

    Cela stockera à la place la somme md5 du contenu sur le btree. Tant que ref_id a du contenu avec un md5 unique sur ce ref_id, vous êtes bon.

  • Si vous ne vous souciez pas de cela, contentpensez à le supprimer entièrement.

Cela ne vaut peut-être rien que lorsque vous implémentez une UNIQUEcontrainte avec un btree (comme le fait PostgreSQL), vous obtenez un index supplémentaire gratuitement. Dans des circonstances normales, cela présente un avantage marginal.

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

Accélérera la requête

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

Cependant, lorsque vous avez la possibilité d'utiliser la md5()variante fonctionnelle, il n'y a plus d'index sur le contenu, alors maintenant, pour utiliser cet index, vous devrez

  1. Requête uniquement sur ref_id,
  2. Ajoutez à ref_id une clause md5(content) = md5('This content')

L'ensemble text = textest surévalué. Ce n'est presque jamais ce que tu veux. Si vous cherchez à accélérer le temps de requête sur le texte, le btree est assez inutile. Vous voudrez probablement examiner

  1. pgtrgm
  2. text_pattern_ops
  3. Recherche plein texte (FTS)

MISE À JOUR 1

Sur la base de votre JSON, je suggère de le stocker en tant que jsonb, puis de créer l'index md5(content); alors peut-être que plutôt que ci-dessus exécutez cela.

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

MISE À JOUR 2

Vous demandez quels index vous devez supprimer

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

Voici la réponse surprenante: vous devez tous les supprimer sauf : editor_contentmodel_pkeyqui dit que tout ref_iddoit être unique.

  1. editor_contentmodel_content_2192f49c_uniqcet index garantit que vous êtes UNIQUEsur ref_idAND content, mais si vous ne pouvez pas avoir de doublon, ref_idvous ne pouvez jamais avoir de contenu en double pour cela ref_id. Vous ne pouvez donc jamais violer cet index sans violer également editor_contentmodel_pkey. Cela le rend inutile.
  2. editor_contentmodel_ref_id_md5_idxcet indice est également inutile pour la même raison. Vous ne pouvez jamais avoir un double md5(content::text)sur ref_idparce que quelle que soit la valeur de md5(content::text)c'est que vous ne pouvez jamais avoir un double ref_id.
  3. editor_contentmodel_ref_id_8f74b4f3_likeest également une mauvaise idée car vous dupliquez l'index ref_id. Ce n'est pas inutile, c'est tout simplement pas optimal. Au lieu de cela, si vous en avez besoin, varchar_pattern_opsutilisez-le plutôt que sur le contentterrain.

Enfin, nous n'utilisons pas beaucoup varchardans PostgreSQL car il est implémenté comme une varlena avec une contrainte de vérification. Il n'y a aucun gain à cela, et il n'y a rien de perdu lorsque vous utilisez simplement text. Donc, à moins qu'il n'y ait une raison concrète pour laquelle il ref_idpeut jamais y avoir 120 caractères mais il peut être de 119 caractères, alors j'utiliserais simplement le texttype.

MISE À JOUR 3

Revenons à votre problème précédent ..

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

Cela vous indique que le problème concerne spécifiquement l' index"editor_contentmodel_content_2192f49c_uniq" . Vous l'avez défini comme

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

Le problème ici est que vous essayez de créer un index content. Mais, encore une fois, l'index lui-même stocke le contenu json réel de content, et c'est ce qui dépasse la limite. Ce n'est pas vraiment un problème, car même si cette limite n'était pas en place, elle editor_contentmodel_content_2192f49c_uniqserait totalement inutile. Pourquoi? encore une fois, vous ne pouvez pas ajouter plus d'unicité à une ligne qui est déjà garantie à 100% unique. Vous ne semblez pas comprendre cela. Restons simples.

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

Dans ce qui précède, un seul index / contrainte unique (sans aucun autre index) (ref_id, content)est logique car cela arrêterait la duplication de (1,1). Un index au-dessus (ref_id, md5(content))aurait également un sens car il arrêterait la duplication de (1,1)par proxy d'arrêter la duplication de (1, md5(1)). Cependant, tout cela fonctionne parce que dans l'exemple que j'ai donné, ce ref_idn'est PAS garanti UNIQUE. Ce ref_idn'est pas ça ref_id. Votre ref_idest un PRIMARY KEY. Cela signifie qu'il est garanti d'être UNIQUE.

Cela signifie que le doublon (1,1)et la ligne de (1,2)ne pourraient JAMAIS être insérés. Cela signifie également que les index sur n'importe quoi en plus de ref_id ne peuvent pas garantir plus d'unicité. Ils devraient être moins stricts que l'indice que vous avez actuellement. Donc, votre table ne pourrait ressembler à ceci

ref_id | content
1      | 1
2      | 1
Evan Carroll
la source
Ne puis-je pas modifier les editor_contentmodeltables columnet y ajouter l'unicité md5? ou ne pouvons-nous pas simplement modifier CONSTRAINT editor_contentmodel_content_2192f49c_uniq UNIQUE (content, ref_id)? Pourquoi dois-je créer une nouvelle table pour cela?
akshay
Vous n'avez pas besoin de créer un nouveau tableau, je vous montrais juste à quoi il ressemblerait avec une version simplifiée du tableau que vous avez. Ignorez simplement la CREATE TABLEcommande et lancez le CREATE UNIQUE INDEXdroit en dessous. Ensuite, DROPvotre ancien index.
Evan Carroll
Dernière question, pourriez-vous voir monUpdate 2
akshay
@akshay mis à jour.
Evan Carroll
1
Merci beaucoup, Evan, cela m'a beaucoup aidé. Le concept est encore un peu fragile (pas du tout mon domaine). Je vais essayer de l'apprendre.
akshay
2

"editor_contentmodel_pkey" CLÉ PRIMAIRE, btree (ref_id) "editor_contentmodel_content_2192f49c_uniq" CONTRAINTE UNIQUE, btree (content, ref_id)

Puisque ref_id est la clé primaire, vous ne pouvez pas en avoir de valeurs en double. Cela signifie que la contrainte unique sur la combinaison (contenu, ref_id) est inutile, car tout ce qui viole violerait également la contrainte de clé primaire. Débarrassez-vous-en.

jjanes
la source
Tu veux dire te débarrasser de ça et mettre quelque chose comme ça create unique index on editor_contentmodel (ref_id, md5(content::text))? ou je pourrais recréer la table et supprimer la clé primaire.
akshay
Je ne sais pas ce que tu veux. Si vous voulez la clé primaire sur ref_id, conservez-la. Mais si vous le gardez, alors editor_contentmodel_content_2192f49c_uniq est inutile, et le supprimer résoudra votre problème de titre. De plus, si vous conservez la clé primaire, le nouvel index que vous proposez est également inutile (inutile comme contrainte, il pourrait être utile comme index, mais c'est très peu probable).
jjanes