Tăng work_mem và shared_buffers trên Postgres 9.2 làm chậm đáng kể các truy vấn


39

Tôi có một phiên bản PostgreSQL 9.2 chạy trên RHEL 6.3, máy 8 lõi với 16GB RAM. Máy chủ được dành riêng cho cơ sở dữ liệu này. Cho rằng postgresql.conf mặc định khá bảo thủ về cài đặt bộ nhớ, tôi nghĩ có thể nên cho phép Postgres sử dụng nhiều bộ nhớ hơn. Thật ngạc nhiên, theo lời khuyên trên wiki.postgresql.org/wiki/Tuning_Your_PostgreQuery_Server đã làm chậm đáng kể thực tế mọi truy vấn tôi chạy nhưng rõ ràng đáng chú ý hơn đối với các truy vấn phức tạp hơn.

Tôi cũng đã thử chạy pgtune, đưa ra khuyến nghị sau với nhiều tham số được điều chỉnh hơn, nhưng điều đó không thay đổi gì cả. Nó gợi ý shared_buffers bằng 1/4 kích thước RAM có vẻ phù hợp với lời khuyên ở nơi khác (và đặc biệt là trên PG wiki).

default_statistics_target = 50
maintenance_work_mem = 960MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 11GB
work_mem = 96MB
wal_buffers = 8MB
checkpoint_segments = 16
shared_buffers = 3840MB
max_connections = 80

Tôi đã thử lập lại toàn bộ cơ sở dữ liệu sau khi thay đổi cài đặt (sử dụng reindex database), nhưng điều đó cũng không giúp được gì. Tôi đã chơi xung quanh với shared_buffers và work_mem. Dần dần thay đổi chúng từ các giá trị mặc định rất bảo thủ (128k / 1MB) giảm dần hiệu suất.

Tôi đã chạy EXPLAIN (ANALYZE,BUFFERS)trên một vài truy vấn và thủ phạm dường như là Hash Join chậm hơn đáng kể. Tôi không rõ tại sao.

Để đưa ra một số ví dụ cụ thể, tôi có truy vấn sau đây. Nó chạy trong ~ 2100ms trên cấu hình mặc định và ~ 3300ms trên cấu hình với kích thước bộ đệm tăng:

select count(*) from contest c
left outer join contestparticipant cp on c.id=cp.contestId
left outer join teammember tm on tm.contestparticipantid=cp.id
left outer join staffmember sm on cp.id=sm.contestparticipantid
left outer join person p on p.id=cp.personid
left outer join personinfo pi on pi.id=cp.personinfoid
where pi.lastname like '%b%' or pi.firstname like '%a%';

EXPLAIN (ANALYZE,BUFFERS) cho truy vấn trên:

Câu hỏi là tại sao tôi quan sát hiệu suất giảm khi tôi tăng kích thước bộ đệm? Máy chắc chắn không hết bộ nhớ. Phân bổ nếu bộ nhớ dùng chung trong HĐH là ( shmmaxshmall) được đặt thành các giá trị rất lớn, đó không phải là vấn đề. Tôi cũng không nhận được bất kỳ lỗi nào trong nhật ký Postgres. Tôi đang chạy autovacuum trong cấu hình mặc định nhưng tôi không hy vọng điều đó có liên quan đến nó. Tất cả các truy vấn được chạy trên cùng một máy cách nhau vài giây, chỉ với cấu hình đã thay đổi (và PG được khởi động lại).

Chỉnh sửa: Tôi chỉ tìm thấy một sự thật đặc biệt thú vị: khi tôi thực hiện thử nghiệm tương tự trên iMac giữa năm 2010 (OSX 10.7.5) của tôi với Postgres 9.2.1 và RAM 16GB, tôi không gặp phải tình trạng chậm. Đặc biệt:

set work_mem='1MB';
select ...; // running time is ~1800 ms
set work_mem='96MB';
select ...' // running time is ~1500 ms

Khi tôi thực hiện chính xác cùng một truy vấn (ở trên) với cùng một dữ liệu trên máy chủ, tôi nhận được 2100 ms với work_mem = 1MB và 3200 ms với 96 MB.

Mac có SSD nên nhanh hơn một cách dễ hiểu, nhưng nó thể hiện một hành vi mà tôi mong đợi.

Xem thêm các cuộc thảo luận tiếp theo về hiệu suất pssql .


1
Có vẻ như trong trường hợp chậm hơn, mọi bước đều chậm hơn. Các cài đặt khác có giữ nguyên không?
dezso

1
Có lẽ bạn nên dành thời gian để hỏi điều này trên một diễn đàn chuyên biệt hơn là diễn đàn chung chung. Trong trường hợp này tôi đề nghị các pgsql Tổng mailing list archives.postgresql.org/pgsql-general
Colin 't Hart

1
Oh, và báo cáo lại và xin vui lòng trả lời câu hỏi của riêng bạn nếu bạn tìm thấy câu trả lời! (Điều này được cho phép, khuyến khích thậm chí).
Colin 't Hart

1
Tôi tự hỏi Postgres tương tự như thế nào với Oracle về vấn đề này: Tôi nhớ một khóa học của Jonathan Lewis (giáo sư Oracle) trong đó ông đã chứng minh rằng việc phân bổ nhiều bộ nhớ hơn cho các loại đôi khi khiến chúng chậm hơn. Tôi quên các chi tiết cụ thể nhưng đó là điều cần làm với việc Oracle sắp xếp một phần và sau đó ghi chúng ra bộ lưu trữ tạm thời và sau đó kết hợp chúng lại sau. Bằng cách nào đó nhiều bộ nhớ làm cho quá trình này chậm hơn.
Colin 't Hart

2
Câu hỏi hiện được đăng trên pssql-Performance: archives.postgresql.org/pgsql-performance/2012-11/msg00004.php
Petr Praus

Câu trả lời:


28

Trước hết, hãy nhớ rằng work_mem là trên mỗi hoạt động và vì vậy nó có thể trở nên quá nhanh chóng. Nói chung nếu bạn không gặp rắc rối với việc bị chậm thì tôi sẽ để work_mem một mình cho đến khi bạn cần.

Nhìn vào các kế hoạch truy vấn của bạn, một điều gây ấn tượng với tôi là các lần truy cập bộ đệm rất khác nhau khi xem xét hai kế hoạch và thậm chí các lần quét liên tiếp cũng chậm hơn. Tôi nghi ngờ rằng vấn đề có liên quan đến bộ nhớ đệm đọc trước và có ít không gian hơn cho việc đó. Điều này có nghĩa là bạn đang thiên vị bộ nhớ để sử dụng lại các chỉ mục và chống lại việc đọc các bảng trên đĩa.


Tôi hiểu rằng PostgreSQL sẽ tìm đến bộ đệm cho một trang trước khi đọc nó từ đĩa vì nó thực sự không biết liệu bộ đệm của hệ điều hành có chứa trang đó hay không. Vì các trang sau đó nằm trong bộ đệm và vì bộ đệm đó chậm hơn bộ đệm của hệ điều hành, nên điều này thay đổi các loại truy vấn nhanh so với các loại chậm. Trong thực tế, đọc các kế hoạch, ngoài các vấn đề về work_mem, có vẻ như tất cả thông tin truy vấn của bạn đến từ bộ đệm nhưng đó là câu hỏi về bộ đệm nào.

work_mem : chúng ta có thể phân bổ bao nhiêu bộ nhớ cho một hoạt động sắp xếp hoặc tham gia liên quan. Đây là cho mỗi hoạt động, không phải cho mỗi câu lệnh hoặc mỗi back-end, vì vậy một truy vấn phức tạp có thể sử dụng nhiều lần số lượng bộ nhớ này. Không rõ bạn đang đạt đến giới hạn này nhưng điều đáng chú ý và nhận thức được. nếu bạn tăng quá xa, bạn sẽ mất bộ nhớ có thể có sẵn cho bộ đệm đọc và bộ đệm chia sẻ.

shared_buffers : phân bổ bao nhiêu bộ nhớ cho hàng đợi trang PostgreSQL thực tế. Bây giờ, lý tưởng là bộ cơ sở dữ liệu thú vị của bạn sẽ nằm trong bộ nhớ được lưu trong bộ nhớ cache ở đây và trong bộ đệm đọc. Tuy nhiên, điều này đảm bảo rằng thông tin được sử dụng thường xuyên nhất trên tất cả các phụ trợ sẽ được lưu vào bộ nhớ cache và không bị xóa vào đĩa. Trên Linux, bộ đệm này chậm hơn đáng kể so với bộ đệm đĩa hệ điều hành, nhưng nó đảm bảo rằng bộ đệm của đĩa hệ điều hành không phải là trong suốt đối với PostgreQuery. Điều này là khá rõ ràng vấn đề của bạn là ở đâu.

Vì vậy, điều xảy ra là khi chúng tôi có một yêu cầu, chúng tôi sẽ kiểm tra bộ đệm được chia sẻ trước vì PostgreQuery có kiến ​​thức sâu về bộ đệm này và tìm các trang. Nếu chúng không ở đó, chúng tôi yêu cầu HĐH mở chúng từ tệp và nếu HĐH đã lưu vào bộ đệm thì kết quả sẽ trả về bản sao được lưu trong bộ nhớ cache (tốc độ này nhanh hơn bộ đệm được chia sẻ, nhưng PG không thể biết nó được lưu vào bộ nhớ cache hay bật đĩa và đĩa chậm hơn rất nhiều vì vậy PostgreSQL thường sẽ không nắm lấy cơ hội đó). Hãy nhớ rằng điều này cũng ảnh hưởng đến việc truy cập trang ngẫu nhiên và tuần tự. Vì vậy, bạn có thể có hiệu suất tốt hơn với cài đặt shared_buffers thấp hơn.

Ý thức ruột của tôi là bạn có thể trở nên tốt hơn, hoặc ít nhất là phù hợp hơn, hiệu suất trong môi trường đồng thời cao với cài đặt shared_buffer lớn hơn. Ngoài ra, hãy nhớ rằng PostgreSQL lấy bộ nhớ này và giữ nó để nếu bạn có những thứ khác đang chạy trên hệ thống, bộ đệm đọc sẽ giữ các tệp được đọc bởi các quá trình khác. Đây là một chủ đề rất lớn và phức tạp. Cài đặt bộ đệm chia sẻ lớn hơn cung cấp đảm bảo hiệu suất tốt hơn nhưng có thể cung cấp hiệu suất thấp hơn trong một số trường hợp.


10

Ngoài hiệu ứng có vẻ nghịch lý là tăng work_memhiệu suất làm giảm ( @Chris có thể có lời giải thích), bạn có thể cải thiện chức năng của mình theo ít nhất hai cách.

  • Viết lại hai cái giả LEFT JOINvới JOIN. Điều đó có thể gây nhầm lẫn cho kế hoạch truy vấn và dẫn đến các kế hoạch kém.

SELECT count(*) AS ct
FROM   contest            c
JOIN   contestparticipant cp ON cp.contestId = c.id
JOIN   personinfo         pi ON pi.id = cp.personinfoid
LEFT   JOIN teammember    tm ON tm.contestparticipantid = cp.id
LEFT   JOIN staffmember   sm ON sm.contestparticipantid = cp.id
LEFT   JOIN person        p  ON p.id = cp.personid
WHERE (pi.firstname LIKE '%a%'
OR     pi.lastname  LIKE '%b%')
  • Giả sử rằng các mẫu tìm kiếm thực tế của bạn có nhiều lựa chọn hơn, hãy sử dụng các chỉ mục trigram trên pi.firstnamepi.lastnameđể hỗ trợ các LIKEtìm kiếm không neo . (Các mẫu ngắn hơn cũng '%a%'được hỗ trợ nhưng chỉ mục không có khả năng trợ giúp cho các vị từ không chọn lọc.):

CREATE INDEX personinfo_firstname_gin_idx ON personinfo USING gin (firstname gin_trgm_ops);
CREATE INDEX personinfo_lastname_gin_idx  ON personinfo USING gin (lastname gin_trgm_ops);

Hoặc một chỉ số nhiều màu:

CREATE INDEX personinfo_name_gin_idx ON personinfo USING gin (firstname gin_trgm_ops, lastname gin_trgm_ops);

Nên làm cho truy vấn của bạn nhanh hơn một chút. Bạn cần cài đặt mô-đun pg_trgm bổ sung cho việc này. Chi tiết dưới những câu hỏi liên quan:


Ngoài ra, bạn đã thử cài đặt work_mem cục bộ - chỉ cho giao dịch hiện tại ?

SET LOCAL work_mem = '96MB';

Điều này giữ cho các giao dịch đồng thời từ việc ăn nhiều RAM hơn, có thể bỏ đói lẫn nhau.


3
Tôi muốn đề xuất công việc địa phương của Erwin thứ hai. Vì work_mem thay đổi các loại truy vấn nhanh hơn, nên bạn có thể cần thay đổi nó cho một số truy vấn. Tức là các mức work_mem thấp là tốt nhất cho các truy vấn sắp xếp / tham gia số lượng nhỏ các bản ghi theo các cách phức tạp (nghĩa là nhiều liên kết) trong khi các mức work_mem cao là tốt nhất cho các truy vấn có một vài loại nhưng sắp xếp hoặc tham gia một số lượng lớn các hàng cùng một lúc .
Chris Travers

Tôi đã cải thiện truy vấn trong thời gian này (câu hỏi là từ tháng 10 năm ngoái) nhưng cảm ơn :) Câu hỏi này liên quan nhiều đến hiệu quả bất ngờ hơn là truy vấn cụ thể. Các truy vấn phục vụ chủ yếu để chứng minh hiệu quả. Cảm ơn các mẹo về chỉ số, tôi sẽ thử nó!
Petr Praus
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.