Nilai yang lebih besar dari 1/3 halaman buffer tidak dapat diindeks

9

Saya tidak terlalu baik dengan DB, jadi mohon bersabar.

Saya mencoba untuk menempatkan data JSON yang sangat panjang ke tabel, tabel ini dibuat oleh Django framework.

Saya menggunakan Postgres di Heroku. Jadi, ketika saya mencoba memasukkan data, saya mendapatkan kesalahan berikut:

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.

DB dan tabel saya terlihat seperti ini:

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)

Sepertinya saya harus berubah "editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id)untuk mengambilmd5(content)

Adakah yang bisa membantu saya dengan ini? Saya tidak tahu bagaimana melakukannya.

Memperbarui:

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

Pembaruan 2:

Saya telah membuat UNIQUEindeks berikut , apa yang harus saya hapus di ini?

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

Haruskah saya menghapus 1atau 2(Lihat panah)?

akshay
sumber
Anda mencoba untuk mengindeks kolom TEXT, dan PostgreSQL (seperti yang lainnya) memiliki batas, untuk mengindeksnya 2713, jadi ya - Anda dapat mencoba mengubahnya untuk hash MD5 untuk membuatnya lebih kecil
a_vlad
@ a_vlad Bagaimana saya harus melakukan itu? Tidak tahu bagaimana melakukannya.
akshay
Apa itu konten? Apakah itu TEKS atau JSON?
Evan Carroll
Juga, apakah Anda pernah memiliki dua konten, untuk ref_id yang sama? Jika demikian, apa tujuan dari itu?
Evan Carroll
setuju dengan @EvanCarroll - mungkin Anda tidak membutuhkan indeks ini sama sekali?
a_vlad

Jawaban:

7

Anda memiliki indeks UNIK pada (content, ref_id), dipanggileditor_contentmodel_content_2192f49c_uniq

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

Saya tidak yakin mengapa ini ada untuk memulai. Jadi mari kita mundur dan membahas apa yang dilakukannya. Ini memastikan itu content, dan ref_idunik. Namun, dalam PostgreSQL UNIQUEkendala diimplementasikan dengan btree yang menjadikan ini solusi yang buruk. Dengan menggunakan metode ini, Anda membuat btree dengan konten yang pada dasarnya menduplikasi ukuran tabel kecil ini, dan membuat indeks raksasa. Indeks raksasa yang masih dibatasi oleh ukuran konten - seperti yang Anda temukan. Itu menimbulkan beberapa pertanyaan

  • Apakah Anda peduli bahwa konten itu unik? Jika Anda benar-benar peduli bahwa konten itu unik untuk ref_id, maka yang mungkin Anda inginkan adalah menyimpan hash dari konten tersebut. Sesuatu seperti..

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

    Ini sebagai gantinya akan menyimpan konten md5sum di btree. Selama ref_id memiliki konten dengan md5 unik di atas ref_id itu, Anda baik.

  • Jika Anda tidak peduli itu contentunik, pertimbangkan untuk menghapusnya sepenuhnya.

Mungkin tidak ada artinya sama sekali ketika Anda menerapkan UNIQUEbatasan dengan btree (seperti yang dilakukan PostgreSQL), Anda mendapatkan indeks tambahan secara gratis. Dalam keadaan normal ini memiliki manfaat pinggiran.

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

Akan mempercepat kueri

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

Namun, ketika Anda berkesempatan untuk menggunakan md5()varian fungsional tidak ada lagi indeks pada konten, jadi sekarang untuk menggunakan indeks itu Anda harus

  1. Hanya permintaan pada ref_id,
  2. Tambahkan ke ref_id klausa itu md5(content) = md5('This content')

Keseluruhan text = textdinilai lebih. Itu hampir tidak pernah seperti yang Anda inginkan. Jika Anda ingin mempercepat permintaan-waktu atas teks, btree tidak berguna. Anda mungkin ingin melihatnya

  1. pgtrgm
  2. text_pattern_ops
  3. Pencarian teks lengkap (FTS)

PEMBARUAN 1

Berdasarkan JSON Anda, saya sarankan menyimpannya sebagai jsonb, dan kemudian membuat indeks md5(content); jadi mungkin alih-alih di atas, jalankan ini.

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

PEMBARUAN 2

Anda bertanya indeks mana yang harus Anda hapus

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

Inilah jawaban yang mengejutkan: Anda harus menghapus semuanya kecuali : editor_contentmodel_pkeyyang mengatakan bahwa semua ref_idharus unik.

  1. editor_contentmodel_content_2192f49c_uniqindeks ini memastikan bahwa Anda berada UNIQUEdi ref_idAND content, tetapi jika Anda tidak dapat memiliki duplikat ref_idAnda tidak akan pernah memiliki konten duplikat untuk itu ref_id. Jadi Anda tidak akan pernah bisa melanggar indeks ini tanpa juga melanggar editor_contentmodel_pkey. Itu membuatnya sia-sia.
  2. editor_contentmodel_ref_id_md5_idxindeks ini juga tidak ada gunanya karena alasan yang sama. Anda tidak pernah memiliki duplikat md5(content::text)lebih ref_idkarena terlepas dari apa nilai md5(content::text)adalah Anda tidak pernah memiliki duplikat ref_id.
  3. editor_contentmodel_ref_id_8f74b4f3_likejuga merupakan ide yang buruk karena Anda menduplikasi indeks ref_id. Ini tidak berguna, hanya saja tidak optimal. Sebaliknya, jika Anda perlu varchar_pattern_opsmenggunakannya, alih-alih hanya contentbidang.

Sebagai catatan terakhir, kami tidak banyak menggunakan varcharPostgreSQL karena ini diimplementasikan sebagai varlena dengan batasan cek. Tidak ada untungnya, dan tidak ada yang hilang saat Anda menggunakannya text. Jadi kecuali ada alasan konkret mengapa ref_idbisa menjadi 120 karakter tetapi bisa 119 karakter, maka saya hanya akan menggunakan texttipenya.

PEMBARUAN 3

Mari kita kembali ke masalah Anda sebelumnya ..

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

Ini memberitahu Anda masalahnya secara khusus dengan indeks"editor_contentmodel_content_2192f49c_uniq" . Anda telah mendefinisikannya sebagai

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

Jadi masalahnya di sini adalah Anda mencoba membuat indeks selesai content. Tapi, sekali lagi, indeks itu sendiri menyimpan konten json yang sebenarnya content, dan itulah yang melebihi batas. Ini sebenarnya bukan masalah, karena meskipun batas itu tidak ada di tempat editor_contentmodel_content_2192f49c_uniqakan sama sekali tidak berguna. Mengapa? lagi Anda tidak dapat menambahkan lebih banyak keunikan ke baris yang sudah dijamin 100% unik. Anda sepertinya tidak mendapatkan ini. Mari kita tetap sederhana.

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

Di atas satu-satunya indeks unik / kendala (tanpa indeks lain) lebih (ref_id, content)masuk akal karena akan menghentikan duplikasi (1,1). Indeks atas (ref_id, md5(content))juga masuk akal karena akan menghentikan duplikasi (1,1)dengan proxy menghentikan duplikasi (1, md5(1)). Namun semua karya ini karena dalam contoh saya berikan ref_idadalah TIDAK dijamin akan UNIQUE. Anda ref_idbukan ini ref_id. Anda ref_idadalah seorang PRIMARY KEY. Itu artinya dijamin UNIK.

Itu berarti duplikat (1,1)dan deretan (1,2)TIDAK PERNAH bisa dimasukkan. Itu juga berarti bahwa indeks lebih dari apa pun selain ref_id tidak dapat menjamin lebih banyak keunikan. Mereka harus kurang ketat dari indeks yang Anda miliki saat ini. Jadi meja Anda hanya bisa terlihat seperti ini

ref_id | content
1      | 1
2      | 1
Evan Carroll
sumber
Tidak bisakah saya mengubah editor_contentmodeltabel columndan menambahkan keunikan md5? atau tidak bisakah kita mengubah saja CONSTRAINT editor_contentmodel_content_2192f49c_uniq UNIQUE (content, ref_id)? Mengapa saya harus membuat tabel baru untuk itu?
akshay
Anda tidak harus membuat tabel baru, saya hanya menunjukkan kepada Anda bagaimana tampilannya dengan versi sederhana dari tabel yang Anda punya. Abaikan saja CREATE TABLEperintahnya dan keluarkan CREATE UNIQUE INDEXhak di bawahnya. Lalu DROPindeks lama Anda.
Evan Carroll
Pertanyaan terakhir, bisakah Anda melihat myUpdate 2
akshay
@akshay diperbarui.
Evan Carroll
1
Terima kasih banyak, Evan ini banyak membantu saya. Konsepnya masih agak goyah (bukan bidang saya sama sekali). Saya akan mencoba mempelajarinya.
akshay
2

"editor_contentmodel_pkey" KUNCI UTAMA, btree (ref_id) "editor_contentmodel_content_2192f49c_uniq" CONSTRAINT UNIK, btree (konten, ref_id)

Karena ref_id adalah kunci utama, Anda tidak dapat memiliki nilai duplikatnya. Itu berarti batasan unik pada kombinasi (konten, ref_id) tidak berguna, karena apa pun yang melanggar itu juga akan melanggar batasan kunci utama. Singkirkan saja itu.

jjanes
sumber
Maksud Anda menyingkirkan itu dan meletakkan sesuatu seperti create unique index on editor_contentmodel (ref_id, md5(content::text))? atau saya bisa membuat ulang tabel dan menghapus kunci utama.
akshay
Saya tidak tahu apa yang Anda inginkan. Jika Anda ingin kunci utama pada ref_id, maka simpanlah. Tetapi jika Anda menyimpannya, maka editor_contentmodel_content_2192f49c_uniq tidak berguna, dan menjatuhkannya akan menyelesaikan masalah judul Anda. Juga, jika Anda menyimpan kunci utama, maka indeks baru yang Anda usulkan juga tidak berguna (tidak berguna sebagai kendala, mungkin berguna sebagai indeks, tetapi itu sangat tidak mungkin).
jjanes