Đo kích thước của một hàng của bảng PostgreSQL


83

Tôi có một bảng PostgreSQL. select *là rất chậm trong khi đó select idlà tốt đẹp và nhanh chóng. Tôi nghĩ rằng nó có thể là kích thước của hàng là rất lớn và phải mất một thời gian để vận chuyển, hoặc nó có thể là một số yếu tố khác.

Tôi cần tất cả các trường (hoặc gần như tất cả các trường), vì vậy chỉ chọn một tập hợp con không phải là cách khắc phục nhanh. Chọn các lĩnh vực mà tôi muốn vẫn còn chậm.

Đây là lược đồ bảng của tôi trừ đi các tên:

integer                  | not null default nextval('core_page_id_seq'::regclass)
character varying(255)   | not null
character varying(64)    | not null
text                     | default '{}'::text
character varying(255)   | 
integer                  | not null default 0
text                     | default '{}'::text
text                     | 
timestamp with time zone | 
integer                  | 
timestamp with time zone | 
integer                  | 

Kích thước của trường văn bản có thể là bất kỳ kích thước. Nhưng vẫn không quá vài kilobyte trong trường hợp xấu nhất.

Câu hỏi

  1. Có bất cứ điều gì về điều này mà hét lên 'điên không hiệu quả'?
  2. Có cách nào để đo kích thước trang tại dòng lệnh Postgres để giúp tôi gỡ lỗi này không?

Trên thực tế ... một trong các cột là 11 MB. Điều đó sẽ giải thích nó tôi nghĩ. Vì vậy, có cách nào để làm length(*)hơn là chỉ length(field)? Tôi biết đó là ký tự không phải byte nhưng tôi chỉ cần một giá trị xấp xỉ.
Joe

Câu trả lời:


101

Quý 2: way to measure page size

PostgreSQL cung cấp một số Hàm Kích thước Đối tượng Cơ sở dữ liệu . Tôi đã đóng gói những cái thú vị nhất trong truy vấn này và thêm một số Hàm truy cập thống kê ở phía dưới. (Các pgstattuple mô-đun bổ sung cung cấp nhiều chức năng hữu ích hơn.)

Điều này sẽ cho thấy rằng các phương pháp khác nhau để đo "kích thước của một hàng" dẫn đến kết quả rất khác nhau. Tất cả phụ thuộc vào những gì bạn muốn đo, chính xác.

Truy vấn này yêu cầu Postgres 9.3 trở lên . Đối với các phiên bản cũ hơn xem bên dưới.

Sử dụng một VALUESbiểu thức trong LATERALtruy vấn con , để tránh đánh vần các phép tính cho mỗi hàng.

Thay thế public.tbl(hai lần) bằng tên bảng tùy chọn lược đồ tùy chọn của bạn để có chế độ xem nhỏ gọn các thống kê được thu thập về kích thước của các hàng của bạn. Bạn có thể gói nó thành một hàm plpgsql để sử dụng nhiều lần, nhập tên bảng làm tham số và sử dụng EXECUTE...

SELECT l.metric, l.nr AS "bytes/ct"
     , CASE WHEN is_size THEN pg_size_pretty(nr) END AS bytes_pretty
     , CASE WHEN is_size THEN nr / NULLIF(x.ct, 0) END AS bytes_per_row
FROM  (
   SELECT min(tableoid)        AS tbl      -- = 'public.tbl'::regclass::oid
        , count(*)             AS ct
        , sum(length(t::text)) AS txt_len  -- length in characters
   FROM   public.tbl t                     -- provide table name *once*
   ) x
 , LATERAL (
   VALUES
      (true , 'core_relation_size'               , pg_relation_size(tbl))
    , (true , 'visibility_map'                   , pg_relation_size(tbl, 'vm'))
    , (true , 'free_space_map'                   , pg_relation_size(tbl, 'fsm'))
    , (true , 'table_size_incl_toast'            , pg_table_size(tbl))
    , (true , 'indexes_size'                     , pg_indexes_size(tbl))
    , (true , 'total_size_incl_toast_and_indexes', pg_total_relation_size(tbl))
    , (true , 'live_rows_in_text_representation' , txt_len)
    , (false, '------------------------------'   , NULL)
    , (false, 'row_count'                        , ct)
    , (false, 'live_tuples'                      , pg_stat_get_live_tuples(tbl))
    , (false, 'dead_tuples'                      , pg_stat_get_dead_tuples(tbl))
   ) l(is_size, metric, nr);

Kết quả:

              số liệu | byte / ct | byte_pretty | byte_per_row
----------------------------------- + ---------- + --- ----------- + ---------------
 core_relation_size | 44138496 | 42 MB | 91
 tầm nhìn_map | 0 | 0 byte | 0
 free_space_map | 32768 | 32 kB | 0
 bảng_size_incl_toast | 44179456 | 42 MB | 91
 indexes_size | 33128448 | 32 MB | 68
 Total_size_incl_toast_and_indexes | 77307904 | 74 MB | 159
 live_rows_inSphere_Vpresentation | 29987360 | 29 MB | 62
 ------------------------------ | | |
 hàng_count | 483424 | |
 live_tuples | 483424 | |
 dead_tuples | 2677 | |

Đối với phiên bản cũ hơn (Postgres 9.2 trở lên):

WITH x AS (
   SELECT count(*)               AS ct
        , sum(length(t::text))   AS txt_len  -- length in characters
        , 'public.tbl'::regclass AS tbl      -- provide table name as string
   FROM   public.tbl t                       -- provide table name as name
   ), y AS (
   SELECT ARRAY [pg_relation_size(tbl)
               , pg_relation_size(tbl, 'vm')
               , pg_relation_size(tbl, 'fsm')
               , pg_table_size(tbl)
               , pg_indexes_size(tbl)
               , pg_total_relation_size(tbl)
               , txt_len
             ] AS val
        , ARRAY ['core_relation_size'
               , 'visibility_map'
               , 'free_space_map'
               , 'table_size_incl_toast'
               , 'indexes_size'
               , 'total_size_incl_toast_and_indexes'
               , 'live_rows_in_text_representation'
             ] AS name
   FROM   x
   )
SELECT unnest(name)                AS metric
     , unnest(val)                 AS "bytes/ct"
     , pg_size_pretty(unnest(val)) AS bytes_pretty
     , unnest(val) / NULLIF(ct, 0) AS bytes_per_row
FROM   x, y

UNION ALL SELECT '------------------------------', NULL, NULL, NULL
UNION ALL SELECT 'row_count', ct, NULL, NULL FROM x
UNION ALL SELECT 'live_tuples', pg_stat_get_live_tuples(tbl), NULL, NULL FROM x
UNION ALL SELECT 'dead_tuples', pg_stat_get_dead_tuples(tbl), NULL, NULL FROM x;

Cùng một kết quả.

Q1: anything inefficient?

Bạn có thể tối ưu hóa thứ tự cột để lưu một số byte mỗi hàng, hiện đang lãng phí cho việc đệm căn chỉnh:

integer                  | not null default nextval('core_page_id_seq'::regclass)
integer                  | not null default 0
character varying(255)   | not null
character varying(64)    | not null
text                     | default '{}'::text
character varying(255)   | 
text                     | default '{}'::text
text                     |
timestamp with time zone |
timestamp with time zone |
integer                  |
integer                  |

Điều này tiết kiệm từ 8 đến 18 byte mỗi hàng. Tôi gọi nó là "cột tetris" . Chi tiết:

Cũng xem xét:


Đoạn mã trước 9.3 của bạn ném một phép chia bằng 0 nếu bảng trống. Tôi thực sự muốn sử dụng phiên bản 9.3+, nhưng đã chọn nhầm phiên bản và phải mất vài giờ để sửa nó ... Bây giờ tôi không thể để tất cả thời gian đó bị lãng phí. Thay thế , unnest(val) / ctbằng , (LEAST(unnest(val), unnest(val) * ct)) / (ct - 1 + sign(ct))và nó sẽ không ném. Lý do là, khi ctđang 0, valsẽ được thay thế bằng 0ctsẽ được thay thế bằng 1.
GuiRitter

1
@GuiRitter: Cảm ơn bạn đã chỉ ra. Tôi đã áp dụng một sửa chữa đơn giản hơn, mặc dù. Ngoài ra một số cập nhật chung trong khi đang ở đó - nhưng truy vấn vẫn giữ nguyên.
Erwin Brandstetter

35

Có thể dễ dàng lấy xấp xỉ kích thước của một hàng, bao gồm các nội dung ed của TOAST , bằng cách truy vấn độ dài của biểu diễn văn bản của toàn bộ hàng:

SELECT octet_length(t.*::text) FROM tablename AS t WHERE primary_key=:value;

Đây là một xấp xỉ gần đúng với số byte sẽ được truy xuất phía máy khách khi thực hiện:

SELECT * FROM tablename WHERE primary_key=:value;

... giả sử rằng người gọi truy vấn đang yêu cầu kết quả ở định dạng văn bản, đó là điều mà hầu hết các chương trình làm (định dạng nhị phân là có thể, nhưng hầu như không đáng để gặp rắc rối trong hầu hết các trường hợp).

Kỹ thuật tương tự có thể được áp dụng để định vị các Nhàng "văn bản lớn nhất" của tablename:

SELECT primary_key, octet_length(t.*::text) FROM tablename AS t
   ORDER BY 2 DESC LIMIT :N;

Cách tuyệt vời để nhanh chóng có được một số ước tính khi làm việc với dữ liệu lớn (ví dụ: phần lớn kích thước hàng nằm trong các cột được lưu trữ bánh mì nướng có chiều dài thay đổi), ý tưởng hay!
fgblomqvist

kết quả là byte?
Akmal Salikhov

14

Có một vài điều có thể xảy ra. Nói chung, tôi nghi ngờ rằng chiều dài là vấn đề gần. Tôi nghi ngờ thay vì bạn có một vấn đề liên quan đến chiều dài.

Bạn nói rằng các trường văn bản có thể lên đến một vài k. Một hàng không thể vượt quá 8k trong bộ lưu trữ chính và có khả năng các trường văn bản lớn hơn của bạn đã bị ĐỔI hoặc chuyển ra khỏi bộ lưu trữ chính vào bộ lưu trữ mở rộng trong các tệp riêng biệt. Điều này làm cho bộ lưu trữ chính của bạn nhanh hơn (vì vậy, chọn id thực sự nhanh hơn vì có ít trang đĩa hơn để truy cập) nhưng chọn * trở nên chậm hơn vì có nhiều I / O ngẫu nhiên hơn.

Nếu tổng kích thước hàng của bạn vẫn dưới 8k, bạn có thể thử thay đổi cài đặt lưu trữ. Tuy nhiên, tôi sẽ cảnh báo rằng bạn có thể gặp phải những điều tồi tệ xảy ra khi chèn một thuộc tính quá khổ vào bộ lưu trữ chính để tốt nhất không chạm vào điều này nếu bạn không phải và nếu bạn làm vậy, hãy đặt giới hạn phù hợp thông qua các ràng buộc kiểm tra. Vì vậy, giao thông không phải là điều duy nhất. Nó có thể đối chiếu nhiều, nhiều lĩnh vực yêu cầu đọc ngẫu nhiên. Số lượng lớn các lần đọc ngẫu nhiên cũng có thể gây ra lỗi nhớ cache và số lượng lớn bộ nhớ cần thiết có thể yêu cầu mọi thứ được cụ thể hóa trên đĩa và số lượng lớn các hàng rộng, nếu có sự tham gia (và có một nếu tham gia TOAST) có thể yêu cầu chi phí cao hơn tham gia mô hình, vv

Điều đầu tiên tôi sẽ làm là chọn ít hàng hơn và xem nếu điều đó có ích. Nếu điều đó hoạt động, bạn cũng có thể thử thêm RAM vào máy chủ, nhưng tôi sẽ bắt đầu và xem hiệu suất bắt đầu giảm do thay đổi kế hoạch và bộ nhớ cache bị lỗi trước tiên.


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.