Werte größer als 1/3 einer Pufferseite können nicht indiziert werden

9

Ich bin nicht sehr gut mit DB, also bitte ertrage es mit mir.

Ich versuche, sehr lange JSON-Daten in eine Tabelle einzufügen. Diese Tabelle wurde vom Django-Framework erstellt.

Ich benutze Postgres auf Heroku. Wenn ich also versuche, die Daten zu speichern, wird der folgende Fehler angezeigt:

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.

Meine Datenbank und Tabelle sehen ungefähr so ​​aus:

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)

Es sieht so aus, als müsste ich mich ändern "editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id), um zu nehmenmd5(content)

Kann mir jemand dabei helfen? Ich habe keine Ahnung, wie es geht.

Aktualisieren:

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

Update 2:

Ich habe den folgenden UNIQUEIndex erstellt. Was soll ich dabei entfernen?

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

Soll ich entfernen 1oder 2(siehe die Pfeile)?

Akshay
quelle
Sie versuchen, die TEXT-Spalte zu indizieren, und PostgreSQL hat (wie alle anderen) Grenzwerte für den Index 2713, also ja - Sie können versuchen, ihn für MD5-Hash zu ändern, um ihn kleiner zu machen
a_vlad
@a_vlad Wie soll ich das machen? Keine Ahnung, wie es geht.
Akshay
Was ist Inhalt? Ist das TEXT oder JSON?
Evan Carroll
Haben Sie jemals zwei Inhalte für dieselbe ref_id? Wenn ja, wozu dient das?
Evan Carroll
stimme @EvanCarroll zu - vielleicht brauchst du diesen Index überhaupt nicht?
a_vlad

Antworten:

7

Sie haben einen EINZIGARTIGEN Index mit dem (content, ref_id)Nameneditor_contentmodel_content_2192f49c_uniq

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

Ich bin mir nicht sicher, warum das hier anfängt. Lassen Sie uns also einen Schritt zurücktreten und uns mit dem befassen, was dies bewirkt. Dies stellt sicher, dass contentund ref_idsind einzigartig. In PostgreSQL wird die UNIQUEEinschränkung jedoch mit einem btree implementiert, was dies zu einer schlechten Lösung macht. Mit dieser Methode erstellen Sie einen Baum mit Inhalten, der im Wesentlichen die Größe dieser kleinen Tabelle dupliziert und einen gigantischen Index ergibt. Ein gigantischer Index, der jedoch immer noch durch die Größe des Inhalts begrenzt ist - wie Sie festgestellt haben. Es wirft einige Fragen auf

  • Interessiert es Sie, dass Inhalte einzigartig sind? Wenn Sie sich darum kümmern, dass der Inhalt für ref_id eindeutig ist, möchten Sie wahrscheinlich den Hash des Inhalts speichern . Etwas wie..

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

    Dadurch wird stattdessen die md5sum des Inhalts auf dem btree gespeichert. Solange ref_id über diese ref_id Inhalte mit einem eindeutigen md5 enthält, sind Sie gut.

  • Wenn es Ihnen egal ist, dass dies contenteinzigartig ist, sollten Sie es vollständig entfernen.

Es kann nichts wert sein, dass Sie beim Implementieren einer UNIQUEEinschränkung mit einem btree (wie bei PostgreSQL) kostenlos einen zusätzlichen Index erhalten. Unter normalen Umständen hat dies einen Nebeneffekt.

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

Beschleunigt die Abfrage

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

Wenn Sie jedoch die Möglichkeit haben, die Funktionsvariante zu verwenden, md5()gibt es keinen Index mehr für den Inhalt. Um diesen Index jetzt zu verwenden, müssen Sie dies tun

  1. Nur Abfrage auf ref_id,
  2. Fügen Sie zu ref_id eine Klausel hinzu, die md5(content) = md5('This content')

Das Ganze text = textist überbewertet. Das ist fast nie was du willst. Wenn Sie die Abfragezeit über Text beschleunigen möchten, ist der btree ziemlich nutzlos. Sie möchten wahrscheinlich untersuchen

  1. pgtrgm
  2. text_pattern_ops
  3. Volltextsuche (FTS)

UPDATE 1

Basierend auf Ihrem JSON würde ich vorschlagen, es als zu speichern jsonbund dann den Index für zu erstellen md5(content). also vielleicht eher als die oben genannten stattdessen ausführen.

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

UPDATE 2

Sie fragen, welche Indizes Sie entfernen sollen

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

Hier ist die überraschende Antwort: Sie sollten alle entfernen, außer : editor_contentmodel_pkeywas besagt, dass alle ref_ideinzigartig sein müssen.

  1. editor_contentmodel_content_2192f49c_uniqDieser Index stellt sicher, dass Sie UNIQUEauf ref_idAND contentstehen. Wenn Sie jedoch kein Duplikat ref_idhaben können, können Sie niemals einen doppelten Inhalt dafür haben ref_id. Sie können diesen Index also niemals verletzen, ohne auch zu verletzen editor_contentmodel_pkey. Das macht es sinnlos.
  2. editor_contentmodel_ref_id_md5_idxDieser Index ist aus dem gleichen Grund auch sinnlos. Man kann nie ein Duplikat hat md5(content::text)über , ref_idweil unabhängig davon , was der Wert md5(content::text)ist , dass Sie nie ein Duplikat haben können ref_id.
  3. editor_contentmodel_ref_id_8f74b4f3_likeist auch eine schlechte Idee, weil Sie den Index duplizieren ref_id. Das ist nicht nutzlos, es ist einfach nicht optimal. Wenn Sie eine benötigen, varchar_pattern_opsverwenden Sie sie stattdessen nur über dem contentFeld.

Als letzte Anmerkung verwenden wir varcharin PostgreSQL nicht viel, da es als Varlena mit einer Prüfbedingung implementiert ist. Es gibt keinen Gewinn, und es geht nichts verloren, wenn Sie es einfach verwenden text. Wenn es also keinen konkreten Grund gibt, warum ref_ides jemals 120 Zeichen geben kann, aber 119 Zeichen, dann würde ich einfach den textTyp verwenden.

UPDATE 3

Kehren wir zu Ihrem früheren Problem zurück.

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

Dies sagt Ihnen, dass das Problem speziell mit dem Index zusammenhängt"editor_contentmodel_content_2192f49c_uniq" . Sie haben das als definiert

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

Das Problem hier ist also, dass Sie versuchen, einen Index zu erstellen content. Aber auch hier speichert der Index selbst den tatsächlichen JSON-Inhalt von content, und das ist es, was das Limit überschreitet. Dies ist eigentlich kein Problem, denn selbst wenn diese Grenze nicht vorhanden editor_contentmodel_content_2192f49c_uniqwäre, wäre dies völlig nutzlos. Warum? Auch hier können Sie einer Zeile, die bereits zu 100% eindeutig ist, keine weitere Eindeutigkeit hinzufügen. Sie scheinen das nicht zu verstehen. Lassen Sie es uns einfach halten.

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

Oben ist ein einzelner eindeutiger Index / eine einzelne Einschränkung (ohne andere Indizes) (ref_id, content)sinnvoll, da dies die Duplizierung von stoppen würde (1,1). Ein Index-Over (ref_id, md5(content))wäre auch deshalb sinnvoll, weil es die Duplizierung von (1,1)durch Proxy stoppen würde, um die Duplizierung von zu stoppen (1, md5(1)). All diese Arbeiten jedoch , weil in diesem Beispiel habe ich gegeben ref_idist nicht garantiert werden kann UNIQUE. Dein ref_idist das nicht ref_id. Dein ref_idist ein PRIMARY KEY. Das heißt, es ist garantiert EINZIGARTIG.

Das bedeutet, dass das Duplikat (1,1)und die Zeile von (1,2)NIEMALS eingefügt werden können. Das bedeutet auch, dass Indizes über alles zusätzlich zu ref_id keine größere Eindeutigkeit garantieren können. Sie müssten weniger streng sein als der Index, den Sie derzeit haben. Ihr Tisch könnte also nur so aussehen

ref_id | content
1      | 1
2      | 1
Evan Carroll
quelle
Kann ich editor_contentmodelTabellen nicht ändern columnund MD5-Eindeutigkeit hinzufügen? oder können wir nicht einfach ändern CONSTRAINT editor_contentmodel_content_2192f49c_uniq UNIQUE (content, ref_id)? Warum muss ich dafür eine neue Tabelle erstellen?
Akshay
Sie müssen keine neue Tabelle erstellen. Ich habe Ihnen nur gezeigt, wie es mit einer vereinfachten Version der Tabelle aussehen würde. Ignorieren Sie einfach den CREATE TABLEBefehl und geben Sie das CREATE UNIQUE INDEXRecht darunter ein. Dann DROPdein alter Index.
Evan Carroll
Letzte Frage, könnten Sie meineUpdate 2
akshay
@akshay aktualisiert.
Evan Carroll
1
Vielen Dank, Evan, das hat mir sehr geholfen. Das Konzept ist immer noch etwas wackelig (überhaupt nicht mein Fachgebiet). Ich werde versuchen, es zu lernen.
Akshay
2

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

Da ref_id der Primärschlüssel ist, können Sie keine doppelten Werte davon haben. Das bedeutet, dass die eindeutige Einschränkung für die Kombination (Inhalt, ref_id) unbrauchbar ist, da alles, was verletzt wird, auch die Primärschlüsseleinschränkung verletzt. Lass es einfach los.

jjanes
quelle
Du meinst das loswerden und so etwas setzen create unique index on editor_contentmodel (ref_id, md5(content::text))? oder ich könnte die Tabelle neu erstellen und den Primärschlüssel entfernen.
Akshay
Ich weiß nicht was du willst. Wenn Sie den Primärschlüssel für ref_id möchten, behalten Sie ihn bei. Wenn Sie es jedoch behalten, ist editor_contentmodel_content_2192f49c_uniq nutzlos, und das Löschen löst Ihr Titelproblem. Wenn Sie den Primärschlüssel behalten, ist der von Ihnen vorgeschlagene neue Index ebenfalls unbrauchbar (als Einschränkung nutzlos, kann er als Index nützlich sein, aber das ist sehr unwahrscheinlich).
jjanes