Các giá trị lớn hơn 1/3 của trang đệm không thể được lập chỉ mục


9

Tôi không tốt lắm với DB nên xin hãy đồng ý.

Tôi đang cố gắng đặt một dữ liệu JSON rất dài vào một bảng, bảng này được tạo bởi khung Django.

Tôi đang sử dụng Postgres trên Heroku. Vì vậy, khi tôi cố gắng đặt dữ liệu, tôi gặp lỗi sau:

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 và bảng của tôi trông giống như thế này:

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)

Có vẻ như tôi phải thay đổi "editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (content, ref_id)để lấymd5(content)

Bất cứ ai có thể giúp tôi với điều này? Tôi không có manh mối về cách làm điều đó.

Cập nhật:

JSONnội dung - https://gist.github.com/akshaybabloo/0b3dc1fb4d964b10d09ccd6884fe3a40

Cập nhật 2:

Tôi đã tạo UNIQUEchỉ mục sau đây , tôi nên xóa cái gì trong cái này?

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

Tôi nên loại bỏ 1hoặc 2(Xem các mũi tên)?


Bạn cố gắng lập chỉ mục cột văn bản và PostgreSQL (như tất cả các mục khác) có giới hạn, đối với chỉ mục đó là 2713, vì vậy có - Bạn có thể thử thay đổi nó cho hàm băm MD5 để làm cho nó nhỏ hơn
a_vlad

@a_vlad Tôi nên làm thế nào? Không có ý tưởng về cách làm điều đó.
akshay

Nội dung là gì? Đó là văn bản hay JSON?
Evan Carroll

Ngoài ra, bạn có bao giờ có hai nội dung, cho cùng một ref_id không? Nếu vậy, mục đích của điều đó là gì?
Evan Carroll

đồng ý với @EvanCarroll - có thể bạn không cần chỉ số này?
a_vlad

Câu trả lời:


7

Bạn có một chỉ số ĐỘC ĐÁO trên (content, ref_id), được gọi làeditor_contentmodel_content_2192f49c_uniq

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

Tôi không chắc tại sao điều này lại bắt đầu. Vì vậy, hãy lùi lại và giải quyết những gì nó làm. Điều này đảm bảo rằng content, và ref_idlà duy nhất. Tuy nhiên, trong PostgreSQL, UNIQUEràng buộc được triển khai với btree khiến cho đây là một giải pháp kém. Sử dụng phương pháp này, bạn đang tạo một btree với nội dung về cơ bản nhân đôi kích thước của bảng nhỏ này và tạo ra một chỉ mục khổng lồ. Một chỉ số khổng lồ vẫn bị giới hạn bởi kích thước nội dung - như bạn đã tìm thấy. Nó đặt ra một vài câu hỏi

  • Bạn có quan tâm rằng nội dung là duy nhất? Nếu bạn quan tâm rằng nội dung là duy nhất cho ref_id, thì điều bạn có thể muốn là lưu trữ hàm băm của nội dung. Cái gì đó như..

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

    Điều này thay vào đó sẽ lưu trữ md5sum nội dung trên btree. Miễn là ref_id có nội dung với md5 duy nhất so với ref_id đó, bạn vẫn ổn.

  • Nếu bạn không quan tâm đó contentlà duy nhất xem xét loại bỏ nó hoàn toàn.

Có thể không có giá trị gì khi bạn thực hiện một UNIQUEràng buộc với btree (như PostgreQuery làm), bạn sẽ nhận được một chỉ mục được thêm miễn phí. Trong một trường hợp bình thường, điều này có lợi ích bên lề.

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

Sẽ tăng tốc truy vấn

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

Tuy nhiên, khi bạn có cơ hội sử dụng md5()biến thể chức năng thì không còn chỉ mục trên nội dung, vì vậy bây giờ để sử dụng chỉ mục đó, bạn sẽ phải

  1. Chỉ truy vấn trên ref_id,
  2. Thêm vào ref_id một mệnh đề md5(content) = md5('This content')

Toàn bộ text = textđược đánh giá cao hơn. Đó gần như không bao giờ là những gì bạn muốn. Nếu bạn đang tìm cách tăng tốc thời gian truy vấn qua văn bản, btree khá vô dụng. Bạn có thể muốn xem xét

  1. pgtrgm
  2. text_potype_ops
  3. Tìm kiếm toàn văn (FTS)

CẬP NHẬT 1

Dựa trên JSON của bạn, tôi khuyên bạn nên lưu trữ nó dưới dạng jsonb, và sau đó tạo chỉ mục trên md5(content); Vì vậy, có lẽ thay vì ở trên thay vì chạy này.

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

CẬP NHẬT 2

Bạn hỏi bạn nên xóa chỉ mục nào

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

Đây là câu trả lời đáng ngạc nhiên: bạn nên loại bỏ tất cả chúng ngoại trừ : editor_contentmodel_pkeyđiều này nói rằng tất cả đều ref_idcần phải là duy nhất.

  1. editor_contentmodel_content_2192f49c_uniqchỉ mục này đảm bảo rằng bạn đang UNIQUEref_idcontent, nhưng nếu bạn không có bản sao, ref_idbạn không bao giờ có thể có nội dung trùng lặp cho điều đó ref_id. Vì vậy, bạn không bao giờ có thể vi phạm chỉ số này mà không vi phạm editor_contentmodel_pkey. Điều đó làm cho nó vô nghĩa.
  2. editor_contentmodel_ref_id_md5_idxchỉ số này cũng vô nghĩa vì lý do tương tự. Bạn không bao giờ có thể có một bản sao md5(content::text)trên ref_idvì bất kể những gì giá trị của md5(content::text)là bạn không bao giờ có thể có một bản sao ref_id.
  3. editor_contentmodel_ref_id_8f74b4f3_likecũng là một ý tưởng tồi vì bạn đang sao chép chỉ mục ref_id. Điều này không vô dụng, nó chỉ không tối ưu. Thay vào đó, nếu bạn cần varchar_pattern_opssử dụng nó thay vì chỉ trên contenttrường.

Như một lưu ý cuối cùng, chúng tôi không sử dụng nhiều varchartrong PostgreSQL vì nó được triển khai như một varlena với một ràng buộc kiểm tra. Không có lợi cho nó, và không có gì mất khi bạn chỉ cần sử dụng text. Vì vậy, trừ khi có một lý do cụ thể tại sao ref_idcó thể là 120 ký tự nhưng nó có thể là 119 ký tự, thì tôi chỉ đơn giản là sử dụng textloại.

CẬP NHẬT 3

Hãy quay trở lại vấn đề trước đó của bạn ..

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

Điều này cho bạn biết vấn đề cụ thể là với chỉ số"editor_contentmodel_content_2192f49c_uniq" . Bạn đã định nghĩa rằng

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

Vì vậy, vấn đề ở đây là bạn đang cố gắng tạo ra một chỉ mục content. Nhưng, một lần nữa, chính chỉ mục lưu trữ nội dung json thực tế contentvà đó là những gì vượt quá giới hạn. Đây thực sự không phải là một vấn đề, bởi vì ngay cả khi giới hạn đó không editor_contentmodel_content_2192f49c_uniqđược áp dụng sẽ hoàn toàn vô dụng. Tại sao? một lần nữa, bạn không thể thêm tính độc đáo vào một hàng đã được đảm bảo là duy nhất 100%. Bạn dường như không nhận được điều này. Hãy giữ nó đơn giản.

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

Trong các chỉ mục / ràng buộc duy nhất ở trên (không có chỉ mục nào khác) (ref_id, content)có ý nghĩa bởi vì nó sẽ ngăn chặn sự trùng lặp (1,1). Một chỉ mục trên (ref_id, md5(content))cũng sẽ có ý nghĩa bởi vì nó sẽ ngăn chặn sự trùng lặp của (1,1)proxy bằng cách dừng việc sao chép (1, md5(1)). Tuy nhiên tất cả các công trình này vì trong ví dụ tôi đã đưa ref_idđược không đảm bảo được UNIQUE. Bạn ref_idkhông phải là này ref_id. Bạn ref_idlà một PRIMARY KEY. Điều đó có nghĩa là nó được đảm bảo là ĐỘC ĐÁO.

Điều đó có nghĩa là trùng lặp (1,1)và hàng (1,2)KHÔNG BAO GIỜ được chèn vào. Điều đó cũng có nghĩa là các chỉ mục trên bất kỳ thứ gì ngoài ref_id không thể đảm bảo tính độc đáo hơn. Họ sẽ phải ít nghiêm ngặt hơn chỉ số bạn hiện có. Vì vậy, bảng của bạn chỉ có thể trông như thế này

ref_id | content
1      | 1
2      | 1

Tôi không thể thay đổi editor_contentmodelbảng columnvà thêm tính độc đáo md5 vào nó? hoặc chúng ta không thể thay đổi CONSTRAINT editor_contentmodel_content_2192f49c_uniq UNIQUE (content, ref_id)? Tại sao tôi phải tạo một bảng mới cho nó?
akshay

Bạn không cần phải tạo một bảng mới, tôi chỉ cho bạn thấy nó trông như thế nào với phiên bản đơn giản của bảng bạn có. Chỉ cần bỏ qua CREATE TABLElệnh và đưa ra CREATE UNIQUE INDEXquyền bên dưới nó. Sau đó, DROPchỉ số cũ của bạn.
Evan Carroll

Câu hỏi cuối cùng, bạn có thể thấy tôiUpdate 2
akshay

@akshay cập nhật.
Evan Carroll

1
Cảm ơn bạn rất nhiều, Evan điều này đã giúp tôi rất nhiều. Khái niệm này vẫn còn một chút run rẩy (không phải lĩnh vực của tôi). Tôi sẽ cố gắng học nó.
akshay

2

"Editor_contentmodel_pkey" PRIMARY KEY, btree (ref_id) "Editor_contentmodel_content_2192f49c_uniq" UNIQUE CONSTRAINT, btree (nội dung, ref_id)

Vì ref_id là khóa chính, bạn không thể có các giá trị trùng lặp của nó. Điều đó có nghĩa là ràng buộc duy nhất đối với sự kết hợp (nội dung, ref_id) là vô ích, vì bất kỳ điều gì vi phạm rằng nó cũng sẽ vi phạm ràng buộc khóa chính. Chỉ cần có được thoát khỏi nó.


Bạn có nghĩa là thoát khỏi điều đó và đặt một cái gì đó như thế create unique index on editor_contentmodel (ref_id, md5(content::text))nào? hoặc tôi có thể tạo lại bảng và loại bỏ khóa chính.
akshay

Tôi không biết bạn muốn gì. Nếu bạn muốn khóa chính trên ref_id, thì hãy giữ nó. Nhưng nếu bạn giữ nó, thì Editor_contentmodel_content_2192f49c_uniq là vô dụng, và bỏ nó sẽ giải quyết vấn đề tiêu đề của bạn. Ngoài ra, nếu bạn giữ khóa chính, thì chỉ mục mới mà bạn đề xuất cũng vô dụng (vô dụng như một ràng buộc, nó có thể hữu ích như một chỉ mục, nhưng điều đó rất khó xảy ra).
jjanes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.