I valori maggiori di 1/3 di una pagina buffer non possono essere indicizzati


9

Non sono molto bravo con DB, quindi per favore abbi pazienza con me.

Sto provando a mettere una tabella JSON molto lunga su un tavolo, questa tabella è stata creata dal framework Django.

Sto usando Postgres su Heroku. Quindi, quando provo a mettere i dati ottengo il seguente errore:

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.

Il mio DB e la tabella hanno un aspetto simile al seguente:

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)

Sembra che devo cambiare "editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id)per prenderemd5(content)

Qualcuno mi può aiutare con questo? Non ho idea di come farlo.

Aggiornare:

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

Aggiornamento 2:

Ho creato il seguente UNIQUEindice, cosa devo rimuovere in questo?

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 rimuovere 1o 2(vedere le frecce)?


Cerchi di indicizzare la colonna TEXT, e PostgreSQL (come tutti gli altri) ha dei limiti, per indicizzarlo 2713, quindi sì - Puoi provare a cambiarlo per l'hash MD5 per renderlo più piccolo
a_vlad

@a_vlad Come dovrei farlo? Nessuna idea su come farlo.
akshay,

Che cos'è il contenuto? È TEXT o JSON?
Evan Carroll,

Inoltre, hai mai avuto due contenuti, per lo stesso ref_id? In tal caso, qual è lo scopo?
Evan Carroll,

d'accordo con @EvanCarroll - potrebbe essere Non hai bisogno di questo indice?
a_vlad

Risposte:


7

Hai un indice UNICO attivato (content, ref_id), chiamatoeditor_contentmodel_content_2192f49c_uniq

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

Non sono sicuro del perché questo lì per cominciare. Quindi facciamo un passo indietro e affrontiamo ciò che fa. Questo lo rende sicuro contente ref_idunico. Tuttavia, in PostgreSQL il UNIQUEvincolo è implementato con un btree che lo rende una soluzione scadente. Usando questo metodo, stai creando un btree con contenuto che essenzialmente duplica le dimensioni di questa piccola tabella e crea un indice gigantesco. Un indice gigantesco che è comunque limitato dalle dimensioni del contenuto, come hai scoperto. Solleva alcune domande

  • Ti interessa che il contenuto sia unico? Se ti interessa che il contenuto sia univoco per ref_id, ciò che probabilmente desideri è archiviare l' hash del contenuto. Qualcosa di simile a..

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

    Questo invece memorizzerà il contenuto md5sum sul btree. Finché ref_id ha contenuti con un unico md5 su quel ref_id, sei bravo.

  • Se non ti interessa che contentsia unico, considera di rimuoverlo del tutto.

Potrebbe non valere nulla che quando si implementa un UNIQUEvincolo con un btree (come fa PostgreSQL), si ottiene un indice aggiunto gratuitamente. In circostanze normali ciò ha un vantaggio marginale.

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

Accelererà la query

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

Tuttavia, quando hai la possibilità di utilizzare la md5()variante funzionale non c'è più un indice sul contenuto, quindi ora per usare quell'indice dovrai

  1. Interrogazione solo su ref_id,
  2. Aggiungi a ref_id una clausola che md5(content) = md5('This content')

Il tutto text = textè sopravvalutato. Non è quasi mai quello che vuoi. Se stai cercando di velocizzare il tempo delle query sul testo, btree è piuttosto inutile. Probabilmente vuoi esaminare

  1. pgtrgm
  2. text_pattern_ops
  3. Ricerca a testo integrale (FTS)

AGGIORNAMENTO 1

Basandomi sul tuo JSON , suggerirei di memorizzarlo come un jsonbe quindi creare l'indice su md5(content); quindi, piuttosto che sopra, esegui invece questo.

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

AGGIORNAMENTO 2

Chiedete quali indici dovreste rimuovere

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

Ecco la risposta sorprendente: dovresti rimuoverli tutti tranne : editor_contentmodel_pkeyche dice che tutto ref_iddeve essere unico.

  1. editor_contentmodel_content_2192f49c_uniqquesto indice si assicura che tu sia UNIQUEsu ref_idAND content, ma se non puoi avere un duplicato ref_idnon puoi mai avere un contenuto duplicato per quello ref_id. Quindi non puoi mai violare questo indice senza violare editor_contentmodel_pkey. Questo lo rende inutile.
  2. editor_contentmodel_ref_id_md5_idxquesto indice è anche inutile per lo stesso motivo. Non si può mai avere un duplicato md5(content::text)sopra ref_id, perché indipendentemente da ciò che il valore di md5(content::text)è si può mai avere un duplicato ref_id.
  3. editor_contentmodel_ref_id_8f74b4f3_likeè anche una cattiva idea perché stai duplicando l'indice ref_id. Questo non è inutile, non è semplicemente ottimale. Invece, se hai bisogno di varchar_pattern_opsusarlo invece solo sul contentcampo.

Come ultima nota, non usiamo molto varcharPostgreSQL perché è implementato come varlena con un vincolo di controllo. Non c'è guadagno e non c'è nulla di perso quando si usa semplicemente text. Quindi, a meno che non ci sia una ragione concreta per cui ref_idpossano mai essere 120 caratteri ma può essere 119 caratteri, allora semplicemente userei il texttipo.

AGGIORNAMENTO 3

Torniamo al tuo problema precedente.

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

Questo ti sta dicendo che il problema riguarda specificamente l' indice"editor_contentmodel_content_2192f49c_uniq" . L'hai definito come

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

Quindi il problema qui è che stai cercando di creare un indice content. Ma, di nuovo, l'indice stesso memorizza l'effettivo contenuto json di content, ed è ciò che supera il limite. Questo non è in realtà un problema, perché anche se quel limite non fosse stato fissato editor_contentmodel_content_2192f49c_uniqsarebbe totalmente inutile. Perché? di nuovo non puoi aggiungere più unicità a una riga che è già garantita come unica al 100%. Sembra che tu non stia ottenendo questo. Manteniamolo semplice.

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

Nel precedente un unico indice / vincolo unico (senza altri indici) (ref_id, content)ha senso perché fermerebbe la duplicazione di (1,1). Un indice oltre (ref_id, md5(content))avrebbe anche senso perché fermerebbe la duplicazione (1,1)per procura di interrompere la duplicazione di (1, md5(1)). Comunque tutto questo funziona perché nell'esempio che ho dato NONref_id è garantito . Il tuo non è questo . Il tuo è un . Ciò significa che è garantito per essere UNICO.UNIQUEref_idref_idref_idPRIMARY KEY

Ciò significa che il duplicato (1,1)e la riga di (1,2)non possono MAI essere inseriti. Ciò significa anche che gli indici su qualsiasi cosa oltre a ref_id non possono garantire una maggiore unicità. Dovrebbero essere meno rigidi dell'indice che hai attualmente. Quindi il tuo tavolo potrebbe solo apparire così

ref_id | content
1      | 1
2      | 1

Non posso modificare le editor_contentmodeltabelle columne aggiungere l'unicità md5 ad essa? o non possiamo semplicemente cambiare CONSTRAINT editor_contentmodel_content_2192f49c_uniq UNIQUE (content, ref_id)? Perché devo creare una nuova tabella per esso?
akshay,

Non devi creare una nuova tabella, ti stavo solo mostrando come sarebbe con una versione semplificata della tabella che hai. Basta ignorare il CREATE TABLEcomando ed emettere il CREATE UNIQUE INDEXdiritto sotto di esso. Quindi il DROPtuo vecchio indice.
Evan Carroll,

Ultima domanda, potresti vedere il mioUpdate 2
akshay il

@akshay aggiornato.
Evan Carroll,

1
Grazie mille, Evan, questo mi ha aiutato molto. Il concetto è ancora un po 'traballante (non è affatto il mio campo). Proverò ad impararlo.
akshay,

2

"editor_contentmodel_pkey" PRIMARY KEY, btree (ref_id) "editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id)

Poiché ref_id è la chiave primaria, non puoi avere valori duplicati di essa. Ciò significa che il vincolo univoco sulla combinazione (content, ref_id) è inutile, poiché qualsiasi cosa violi la violazione del vincolo chiave principale. Liberatene.


Intendi liberartene e mettere qualcosa del genere create unique index on editor_contentmodel (ref_id, md5(content::text))? oppure potrei ricreare la tabella e rimuovere la chiave primaria.
akshay,

Non so cosa vuoi. Se vuoi la chiave primaria su ref_id, conservala. Ma se lo tieni, allora editor_contentmodel_content_2192f49c_uniq è inutile e lasciarlo cadere risolverà il problema del titolo. Inoltre, se mantieni la chiave primaria, anche il nuovo indice che proponi è inutile (inutile come vincolo, potrebbe essere utile come indice, ma è molto improbabile).
jjanes,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.