Cấu hình PostgreSQL để đọc hiệu suất


39

Hệ thống của chúng tôi ghi rất nhiều dữ liệu (loại hệ thống Dữ liệu lớn). Hiệu suất viết là đủ tốt cho nhu cầu của chúng tôi nhưng hiệu suất đọc thực sự quá chậm.

Cấu trúc khóa chính (ràng buộc) tương tự cho tất cả các bảng của chúng tôi:

timestamp(Timestamp) ; index(smallint) ; key(integer).

Một bảng có thể có hàng triệu hàng, thậm chí hàng tỷ hàng và yêu cầu đọc thường dành cho một khoảng thời gian cụ thể (dấu thời gian / chỉ mục) và thẻ. Thông thường có một truy vấn trả về khoảng 200 nghìn dòng. Hiện tại, chúng tôi có thể đọc khoảng 15k dòng mỗi giây nhưng chúng tôi cần phải nhanh hơn 10 lần. Điều này có thể và nếu vậy, làm thế nào?

Lưu ý: PostgreSQL được đóng gói với phần mềm của chúng tôi, vì vậy phần cứng khác nhau từ máy khách này sang máy khách khác.

Nó là một VM được sử dụng để thử nghiệm. Máy chủ của VM là Windows Server 2008 R2 x64 với 24.0 GB RAM.

Thông số máy chủ (VMWare máy ảo)

Server 2008 R2 x64
2.00 GB of memory
Intel Xeon W3520 @ 2.67GHz (2 cores)

postgresql.conf tối ưu hóa

shared_buffers = 512MB (default: 32MB)
effective_cache_size = 1024MB (default: 128MB)
checkpoint_segment = 32 (default: 3)
checkpoint_completion_target = 0.9 (default: 0.5)
default_statistics_target = 1000 (default: 100)
work_mem = 100MB (default: 1MB)
maintainance_work_mem = 256MB (default: 16MB)

Bảng định nghĩa

CREATE TABLE "AnalogTransition"
(
  "KeyTag" integer NOT NULL,
  "Timestamp" timestamp with time zone NOT NULL,
  "TimestampQuality" smallint,
  "TimestampIndex" smallint NOT NULL,
  "Value" numeric,
  "Quality" boolean,
  "QualityFlags" smallint,
  "UpdateTimestamp" timestamp without time zone, -- (UTC)
  CONSTRAINT "PK_AnalogTransition" PRIMARY KEY ("Timestamp" , "TimestampIndex" , "KeyTag" ),
  CONSTRAINT "FK_AnalogTransition_Tag" FOREIGN KEY ("KeyTag")
      REFERENCES "Tag" ("Key") MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
  OIDS=FALSE,
  autovacuum_enabled=true
);

Truy vấn

Truy vấn mất khoảng 30 giây để thực thi trong pgAdmin3, nhưng chúng tôi muốn có kết quả tương tự dưới 5 giây nếu có thể.

SELECT 
    "AnalogTransition"."KeyTag", 
    "AnalogTransition"."Timestamp" AT TIME ZONE 'UTC', 
    "AnalogTransition"."TimestampQuality", 
    "AnalogTransition"."TimestampIndex", 
    "AnalogTransition"."Value", 
    "AnalogTransition"."Quality", 
    "AnalogTransition"."QualityFlags", 
    "AnalogTransition"."UpdateTimestamp"
FROM "AnalogTransition"
WHERE "AnalogTransition"."Timestamp" >= '2013-05-16 00:00:00.000' AND "AnalogTransition"."Timestamp" <= '2013-05-17 00:00:00.00' AND ("AnalogTransition"."KeyTag" = 56 OR "AnalogTransition"."KeyTag" = 57 OR "AnalogTransition"."KeyTag" = 58 OR "AnalogTransition"."KeyTag" = 59 OR "AnalogTransition"."KeyTag" = 60)
ORDER BY "AnalogTransition"."Timestamp" DESC, "AnalogTransition"."TimestampIndex" DESC
LIMIT 500000;

Giải thích 1

"Limit  (cost=0.00..125668.31 rows=500000 width=33) (actual time=2.193..3241.319 rows=500000 loops=1)"
"  Buffers: shared hit=190147"
"  ->  Index Scan Backward using "PK_AnalogTransition" on "AnalogTransition"  (cost=0.00..389244.53 rows=1548698 width=33) (actual time=2.187..1893.283 rows=500000 loops=1)"
"        Index Cond: (("Timestamp" >= '2013-05-16 01:00:00-04'::timestamp with time zone) AND ("Timestamp" <= '2013-05-16 15:00:00-04'::timestamp with time zone))"
"        Filter: (("KeyTag" = 56) OR ("KeyTag" = 57) OR ("KeyTag" = 58) OR ("KeyTag" = 59) OR ("KeyTag" = 60))"
"        Buffers: shared hit=190147"
"Total runtime: 3863.028 ms"

Giải thích 2

Trong thử nghiệm mới nhất của tôi, phải mất 7 phút để chọn dữ liệu của tôi! Xem bên dưới:

"Limit  (cost=0.00..313554.08 rows=250001 width=35) (actual time=0.040..410721.033 rows=250001 loops=1)"
"  ->  Index Scan using "PK_AnalogTransition" on "AnalogTransition"  (cost=0.00..971400.46 rows=774511 width=35) (actual time=0.037..410088.960 rows=250001 loops=1)"
"        Index Cond: (("Timestamp" >= '2013-05-22 20:00:00-04'::timestamp with time zone) AND ("Timestamp" <= '2013-05-24 20:00:00-04'::timestamp with time zone) AND ("KeyTag" = 16))"
"Total runtime: 411044.175 ms"

Câu trả lời:


52

Căn chỉnh dữ liệu và kích thước lưu trữ

Trên thực tế, tổng phí trên mỗi tuple là 24 byte cho tiêu đề tuple cộng với 4 byte cho con trỏ mục.
Thêm chi tiết trong tính toán trong câu trả lời liên quan này:

Khái niệm cơ bản về căn chỉnh dữ liệu và phần đệm trong câu trả lời liên quan này trên SO:

Chúng tôi có ba cột cho khóa chính:

PRIMARY KEY ("Timestamp" , "TimestampIndex" , "KeyTag")

"Timestamp"      timestamp (8 bytes)
"TimestampIndex" smallint  (2 bytes)
"KeyTag"         integer   (4 bytes)

Kết quả trong:

 Con trỏ mục 4 byte trong tiêu đề trang (không tính vào bội số của 8 byte)
---
23 byte cho tiêu đề bộ
 Đệm 1 byte để căn chỉnh dữ liệu (hoặc bitmap NULL)
 8 byte "Dấu thời gian"
 2 byte "Dấu thời gian"
 Đệm 2 byte để căn chỉnh dữ liệu
 4 byte "KeyTag" 
 0 đệm vào bội số gần nhất của 8 byte
-----
44 byte mỗi bộ

Thông tin thêm về đo kích thước đối tượng trong câu trả lời liên quan này:

Thứ tự các cột trong một chỉ mục nhiều màu

Đọc hai câu hỏi và câu trả lời để hiểu:

Cách bạn có chỉ mục của mình (khóa chính), bạn có thể truy xuất các hàng mà không cần một bước sắp xếp, điều đó thật hấp dẫn, đặc biệt là với LIMIT. Nhưng lấy các hàng có vẻ vô cùng đắt đỏ.

Nói chung, trong một chỉ mục nhiều cột, các cột "bình đẳng" sẽ đi trước và các cột "phạm vi" cuối cùng:

Do đó, hãy thử một chỉ mục bổ sung với thứ tự cột đảo ngược :

CREATE INDEX analogransition_mult_idx1
   ON "AnalogTransition" ("KeyTag", "TimestampIndex", "Timestamp");

Nó phụ thuộc vào phân phối dữ liệu. Nhưng với millions of row, even billion of rowsđiều này có thể nhanh hơn đáng kể.

Kích thước bộ dữ liệu lớn hơn 8 byte, do căn chỉnh dữ liệu & đệm. Nếu bạn đang sử dụng chỉ mục này làm chỉ mục đơn giản, bạn có thể thử thả cột thứ ba "Timestamp". Có thể nhanh hơn một chút hoặc không (vì nó có thể giúp sắp xếp).

Bạn có thể muốn giữ cả hai chỉ mục. Tùy thuộc vào một số yếu tố, chỉ số ban đầu của bạn có thể thích hợp hơn - đặc biệt là với một số nhỏ LIMIT.

thống kê tự động và bảng thống kê

Số liệu thống kê bảng của bạn cần được cập nhật. Tôi chắc chắn bạn có autovacuum chạy.

Vì bảng của bạn có vẻ rất lớn và số liệu thống kê quan trọng cho kế hoạch truy vấn phù hợp, tôi sẽ tăng đáng kể mục tiêu thống kê cho các cột có liên quan:

ALTER TABLE "AnalogTransition" ALTER "Timestamp" SET STATISTICS 1000;

... hoặc thậm chí cao hơn với hàng tỷ hàng. Tối đa là 10000, mặc định là 100.

Làm điều đó cho tất cả các cột liên quan đến WHEREhoặc ORDER BYmệnh đề. Sau đó chạy ANALYZE.

Bố trí bảng

Trong khi ở đó, nếu bạn áp dụng những gì bạn đã học về căn chỉnh và đệm dữ liệu, cách bố trí bảng được tối ưu hóa này sẽ tiết kiệm một số dung lượng đĩa và giúp thực hiện một chút (bỏ qua pk & fk):

CREATE TABLE "AnalogTransition"(
  "Timestamp" timestamp with time zone NOT NULL,
  "KeyTag" integer NOT NULL,
  "TimestampIndex" smallint NOT NULL,
  "TimestampQuality" smallint,
  "UpdateTimestamp" timestamp without time zone, -- (UTC)
  "QualityFlags" smallint,
  "Quality" boolean,
  "Value" numeric
);

CLUSTER / pg numpack

Để tối ưu hóa hiệu suất đọc cho các truy vấn sử dụng một chỉ mục nhất định (có thể là chỉ mục ban đầu của bạn hoặc thay thế được đề xuất của tôi), bạn có thể viết lại bảng theo thứ tự vật lý của chỉ mục. CLUSTERlàm điều đó, nhưng nó khá xâm lấn và yêu cầu một khóa độc quyền trong suốt thời gian hoạt động. pg_repacklà một sự thay thế tinh vi hơn có thể làm tương tự mà không cần khóa độc quyền trên bàn.
Điều này có thể giúp đáng kể với các bảng lớn, vì phải đọc ít khối hơn của bảng.

RAM

Nói chung, 2GB RAM vật lý là không đủ để xử lý hàng tỷ hàng một cách nhanh chóng. Nhiều RAM hơn có thể đi một chặng đường dài - kèm theo cài đặt thích nghi: rõ ràng là effective_cache_sizebắt đầu lớn hơn .


2
Tôi đã thêm một chỉ mục đơn giản trên KeyTag và dường như nó khá nhanh. Tôi cũng sẽ áp dụng các khuyến nghị của bạn về căn chỉnh dữ liệu. Cảm ơn rất nhiều!
JPelletier

9

Vì vậy, từ các kế hoạch tôi thấy một điều: chỉ mục của bạn là cồng kềnh (sau đó cùng với bảng bên dưới) hoặc đơn giản là không thực sự tốt cho loại truy vấn này (tôi đã cố gắng giải quyết vấn đề này trong nhận xét mới nhất của tôi ở trên).

Một hàng của chỉ mục chứa 14 byte dữ liệu (và một số cho tiêu đề). Bây giờ, tính toán từ các số được đưa ra trong kế hoạch: bạn có 500.000 hàng từ 190147 trang - có nghĩa là, trung bình, có ít hơn 3 hàng hữu ích trên mỗi trang, nghĩa là khoảng 37 byte trên mỗi trang 8 kb. Đây là một tỷ lệ rất xấu phải không? Vì cột đầu tiên của chỉ mục là Timestamptrường và nó được sử dụng trong truy vấn dưới dạng phạm vi, nên trình hoạch định có thể - và thực hiện - chọn chỉ mục để tìm các hàng khớp. Nhưng không có TimestampIndexđề cập nào trong các WHEREđiều kiện, vì vậy việc lọc trên KeyTagkhông hiệu quả lắm vì những giá trị được cho là xuất hiện ngẫu nhiên trong các trang chỉ mục.

Vì vậy, một khả năng là thay đổi định nghĩa chỉ mục thành

CONSTRAINT "PK_AnalogTransition" PRIMARY KEY ("Timestamp", "KeyTag", "TimestampIndex")

(hoặc, với tải của hệ thống của bạn, tạo chỉ mục này dưới dạng mới:

CREATE INDEX CONCURRENTLY "idx_AnalogTransition" 
    ON "AnalogTransition" ("Timestamp", "KeyTag", "TimestampIndex");
  • điều này chắc chắn sẽ mất một lúc nhưng bạn vẫn có thể làm việc trong thời gian này.)

Khả năng khác là một tỷ lệ lớn các trang chỉ mục bị chiếm bởi các hàng chết, có thể được loại bỏ bằng cách hút bụi. Bạn đã tạo bảng với cài đặt autovacuum_enabled=true- nhưng bạn đã bao giờ bắt đầu tự động tìm kiếm chưa? Hay chạy VACUUMthủ công?

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.