Cuộc chiến nảy lửa của tuần này trên danh sách hiệu suất pgsql một lần nữa xoay quanh thực tế là PostgreSQL không có sẵn cú pháp gợi ý truyền thống trong các cơ sở dữ liệu khác. Có sự kết hợp của các lý do kỹ thuật và thực tế đằng sau lý do tại sao đó là:
- Giới thiệu gợi ý là nguồn gốc phổ biến của các vấn đề sau này, vì việc sửa một vị trí truy vấn một lần trong một trường hợp đặc biệt không phải là một cách tiếp cận hiệu quả. Khi tập dữ liệu của bạn phát triển và có thể thay đổi cả phân phối, ý tưởng mà bạn đã gợi ý khi còn nhỏ có thể ngày càng trở thành một ý tưởng tồi.
- Việc thêm một giao diện gợi ý hữu ích sẽ làm phức tạp mã trình tối ưu hóa, điều này đủ khó để duy trì như hiện tại. Một phần lý do khiến PostgreSQL hoạt động tốt như khi chạy các truy vấn là vì mã cảm thấy tốt (“chúng tôi có thể kiểm tra gợi ý trên danh sách tính năng so sánh nhà cung cấp của chúng tôi!”) Không thực sự tự trả tiền, về mặt tạo cơ sở dữ liệu đủ tốt hơn để biện minh cho việc duy trì liên tục của nó, đã bị chính sách từ chối. Nếu nó không hoạt động, nó sẽ không được thêm vào. Và khi được đánh giá một cách khách quan, trung bình các gợi ý là một vấn đề hơn là một giải pháp.
- Loại vấn đề mà gợi ý hoạt động có thể là lỗi của trình tối ưu hóa. Cộng đồng PostgreSQL phản hồi các lỗi thực sự trong trình tối ưu hóa nhanh hơn bất kỳ ai khác trong ngành. Hãy hỏi xung quanh và bạn không cần phải gặp nhiều người dùng PostgreSQL trước khi tìm thấy một người đã báo cáo lỗi và xem nó được khắc phục vào ngày hôm sau.
Bây giờ, câu trả lời hoàn toàn hợp lệ chính để tìm ra các gợi ý bị thiếu, thường là từ các DBA đã quen với chúng, là "tôi làm cách nào để xử lý lỗi trình tối ưu hóa khi tôi gặp phải nó?" Giống như tất cả các công việc công nghệ hiện nay, thường có áp lực rất lớn để có được giải pháp khắc phục nhanh nhất có thể khi một vấn đề truy vấn xấu xuất hiện.
Nếu PostgreSQL không có một số cách để đối phó với tình huống đó, thì sẽ không có cơ sở dữ liệu PostgreSQL nào được sản xuất nghiêm túc. . Sự khác biệt là những thứ bạn điều chỉnh trong cơ sở dữ liệu này bắt nguồn từ việc ảnh hưởng đến các quyết định mà trình tối ưu hóa đã đưa ra theo một cách khá tinh tế, thay vì chỉ bạn chỉ cho nó biết phải làm gì. Đây là những gợi ý theo nghĩa đen của từ này, chúng chỉ không có một số giao diện người dùng để gợi ý rằng những người dùng cơ sở dữ liệu khác mới sử dụng PostgreSQL đang tìm kiếm.
Với ý nghĩ đó, chúng ta hãy xem những gì bạn có thể làm trong PostgreSQL để giải quyết các kế hoạch truy vấn xấu và lỗi trình tối ưu hóa, đặc biệt là những điều mà nhiều người nghĩ rằng chỉ có thể được giải quyết bằng các gợi ý:
- join_collapse_limit:Điều này điều chỉnh mức độ linh hoạt mà trình tối ưu hóa có để sắp xếp lại các phép kết hợp của nhiều bảng. Thông thường, nó sẽ thử mọi kết hợp có thể có khi các phép nối có thể được sắp xếp lại (hầu hết thời gian, trừ khi bạn đang sử dụng một phép nối bên ngoài). Việc hạ thấp join_collapse_limit, thậm chí có thể xuống 1, loại bỏ một số hoặc tất cả tính linh hoạt này. Khi nó được đặt thành 1, bạn sẽ nhận được các liên kết theo thứ tự mà bạn đã viết chúng trong khoảng thời gian. Lập kế hoạch cho số lượng lớn các phép nối là một trong những điều khó nhất đối với trình tối ưu hóa; mỗi phép nối sẽ phóng đại các lỗi trong ước tính và tăng thời gian lập kế hoạch truy vấn. Nếu bản chất cơ bản của dữ liệu của bạn cho thấy rõ ràng thứ tự kết hợp nào sẽ xảy ra và bạn không mong đợi điều đó sẽ thay đổi, khi bạn tìm ra thứ tự phù hợp, bạn có thể khóa nó bằng cách sử dụng thông số này.
- random_page_cost:Mặc định là 4,0, tham số này đặt việc tìm kiếm đĩa để tìm một trang ngẫu nhiên trên đĩa đắt như thế nào, so với giá trị tham chiếu là 1,0. Bây giờ, trong thực tế, nếu bạn đo tỷ lệ I / O ngẫu nhiên so với tuần tự trên ổ cứng thông thường, bạn sẽ thấy con số này gần với 50. Vậy tại sao lại là 4.0? Đầu tiên, vì nó hoạt động tốt hơn các giá trị lớn hơn trong thử nghiệm cộng đồng. Thứ hai, trong nhiều trường hợp, dữ liệu chỉ mục nói riêng sẽ được lưu trong bộ nhớ, làm cho chi phí hiệu quả của việc đọc các giá trị đó thấp hơn. Ví dụ:nếu chỉ mục của bạn được lưu vào bộ nhớ đệm 90% trong RAM, điều đó có nghĩa là 10% thời gian bạn sẽ thực hiện thao tác đắt gấp 50 lần; điều đó sẽ làm cho random_page_cost hiệu quả của bạn khoảng 5. Loại tình huống thực tế này là lý do tại sao mặc định có ý nghĩa ở vị trí của nó. Tôi thường thấy các chỉ mục phổ biến nhận được> 95% bộ nhớ đệm trong bộ nhớ. Nếu chỉ mục của bạn thực sự có nhiều khả năng hơn nhiều so với tất cả trong RAM, thì việc giảm random_page_cost xuống chỉ trên 1,0 có thể là một lựa chọn hợp lý, để phản ánh rằng nó không đắt hơn bất kỳ lần đọc nào khác. Đồng thời, các tìm kiếm ngẫu nhiên trên một hệ thống thực sự bận rộn có thể đắt hơn nhiều so với kỳ vọng của bạn khi chỉ xem xét các mô phỏng của một người dùng. Tôi đã phải đặt random_page_cost cao tới 60 để cơ sở dữ liệu ngừng sử dụng các chỉ mục khi người lập kế hoạch ước tính sai chúng sẽ đắt như thế nào. Thông thường, tình huống đó xuất phát từ lỗi ước tính độ nhạy trên phần của người lập kế hoạch – nếu bạn đang quét hơn khoảng 20% bảng, người lập kế hoạch biết rằng việc sử dụng Quét tuần tự sẽ hiệu quả hơn nhiều so với Quét chỉ mục. Tình huống tồi tệ mà tôi buộc hành vi đó xảy ra sớm hơn nhiều so với khi người lập kế hoạch dự kiến 1% số hàng được trả lại, nhưng thực tế con số này gần như là 15%.
- work_mem:Điều chỉnh lượng bộ nhớ khả dụng cho các truy vấn đang thực hiện các thao tác sắp xếp, băm và tương tự dựa trên bộ nhớ. Đây chỉ là hướng dẫn sơ bộ cho các truy vấn, không phải là giới hạn cứng và một ứng dụng khách có thể sử dụng nhiều work_mem khi chạy một truy vấn. Theo đó, bạn cần lưu ý không đặt giá trị này quá cao trong tệp postgresql.conf. Tuy nhiên, thay vào đó, bạn có thể làm gì trước khi chạy một truy vấn thực sự có lợi từ việc có thêm bộ nhớ để chứa dữ liệu sắp xếp hoặc băm. Đôi khi bạn có thể tìm thấy những truy vấn này từ việc ghi nhật ký những truy vấn chậm bằng log_min_duration_statement. Bạn cũng có thể tìm thấy chúng bằng cách bật log_temp_files, tính năng này sẽ ghi nhật ký mỗi khi work_mem quá nhỏ và do đó, các thao tác sắp xếp sẽ tràn vào đĩa thay vì diễn ra trong bộ nhớ.
- OFFSET 0:PostgreSQL sẽ sắp xếp lại các truy vấn con thành dạng nối, do đó, nó có thể sử dụng logic thứ tự nối thông thường để tối ưu hóa nó. Trong một số trường hợp, quyết định đó có thể là một quyết định thực sự tồi tệ, vì loại thứ mà mọi người có xu hướng viết như các truy vấn con dường như khó ước tính hơn một chút vì một số lý do (tôi nói rằng dựa trên số lượng các truy vấn rắc rối như vậy mà tôi thấy). Một thủ thuật lén lút bạn có thể làm để ngăn chặn logic này là đặt OFFSET 0 vào cuối truy vấn con. Điều này không làm thay đổi bất kỳ kết quả nào, nhưng việc chèn loại nút truy vấn Giới hạn được sử dụng để thực thi OFFSET sẽ ngăn việc sắp xếp lại. Sau đó, truy vấn con sẽ luôn thực thi theo cách mà hầu hết mọi người mong đợi – như một nút truy vấn biệt lập của riêng nó.
- enable_seqscan, enable_indexscan, enable_bitmapscan:Việc tắt một trong các tính năng này để tra cứu các hàng trong bảng là một yếu tố khá lớn để khuyên bạn nên tránh kiểu quét đó (không phải lúc nào cũng ngăn chặn được – nếu không có cách nào để thực hiện kế hoạch của bạn nhưng một seqscan, bạn sẽ nhận được một seqscan ngay cả khi các thông số bị tắt). Điều chính mà tôi khuyên bạn nên làm là không phải để sửa các truy vấn, mà là thử nghiệm với EXPLAIN và xem lý do tại sao kiểu quét khác lại được ưa thích.
- enable_nestloop, enable_hashjoin, enable_mergejoin:Nếu bạn nghi ngờ vấn đề của mình là kiểu kết hợp đang được sử dụng chứ không phải cách các bảng đang được đọc, hãy thử tắt kiểu bạn đang thấy trong kế hoạch của mình bằng một trong các tham số này, sau đó chạy GIẢI THÍCH lần nữa. Sai sót trong ước tính độ nhạy có thể dễ dàng làm cho một phép liên kết có vẻ hiệu quả hơn hoặc kém hơn so với thực tế. Và, một lần nữa, việc xem kế hoạch thay đổi như thế nào với phương thức tham gia hiện tại bị vô hiệu hóa có thể rất nhiều thông tin về lý do tại sao nó quyết định lựa chọn đó ngay từ đầu.
- enable_hashagg, enable_material:Các tính năng này tương đối mới đối với PostgreSQL. Sử dụng Hash Aggregation tích cực đã được giới thiệu trong phiên bản 8.4 và hiện thực hóa tích cực hơn trong phiên bản 9.0. Nếu bạn thấy các loại nút đó trong đầu ra EXPLAIN
của mình và chúng dường như đang làm sai điều gì đó, bởi vì mã này mới hơn rất nhiều, thì có nhiều khả năng bị hạn chế hoặc lỗi hơn một số tính năng cũ hơn. Nếu bạn đã có một kế hoạch hoạt động tốt trong các phiên bản cũ hơn của PostgreSQL, nhưng sử dụng một trong các loại nút này và có vẻ như hoạt động kém hơn nhiều, thì việc tắt các tính năng này đôi khi có thể đưa bạn trở lại hành vi trước đó – cũng như làm sáng tỏ tại sao trình tối ưu hóa đã làm sai điều đó là phản hồi hữu ích. Lưu ý rằng đây thường là cách mà các tính năng nâng cao hơn có xu hướng được đưa vào PostgreSQL:với tùy chọn tắt nó cho mục đích khắc phục sự cố, nếu có chứng minh là có một hồi quy kế hoạch liên quan đến cách các phiên bản trước đó thực thi mọi thứ. - cursor_tuple_fraction:Nếu bạn không có ý định đọc lại tất cả các hàng từ một truy vấn, bạn nên sử dụng con trỏ để triển khai điều đó. Trong trường hợp đó, trình tối ưu hóa sẽ cố gắng ưu tiên xem nó có đưa bạn trở lại hàng đầu tiên một cách nhanh chóng hay không hay nó thích tối ưu hóa toàn bộ truy vấn, dựa trên tham số này. Theo mặc định, cơ sở dữ liệu giả định rằng bạn sẽ đọc lại 10% truy vấn khi bạn sử dụng con trỏ. Việc điều chỉnh thông số này khiến bạn có xu hướng mong đợi bạn đọc ít hơn hoặc nhiều hơn thông số đó.
Tất cả các tham số này và các điều chỉnh truy vấn nên được xem xét điều chỉnh phân đoạn. Bạn không muốn chạy với những thứ này tại chỗ mãi mãi (có lẽ ngoại trừ join_collapse_limit). Bạn sử dụng chúng để thoát khỏi bế tắc và sau đó hy vọng bạn sẽ tìm ra nguyên nhân cơ bản thực sự của kế hoạch xấu - thống kê xấu, hạn chế / lỗi của trình tối ưu hóa hoặc điều gì khác – và sau đó giải quyết vấn đề từ hướng đó. Bạn càng thúc đẩy hành vi của trình tối ưu hóa theo một hướng, thì bạn càng tiếp xúc nhiều hơn với những thay đổi trong tương lai trong việc làm cho dữ liệu của bạn không còn là một hướng chính xác. Nếu bạn sử dụng chúng đúng, như một cách để nghiên cứu lý do tại sao bạn chọn sai kế hoạch (cách tiếp cận mà tôi đã sử dụng trong chương tối ưu hóa truy vấn của Hiệu suất cao PostgreSQL 9.0), cách bạn gợi ý về những thứ trong PostgreSQL sẽ dẫn đến việc bạn rời khỏi mỗi lần chạy- với hành vi của trình tối ưu hóa không tốt hơn một chút về cách tránh loại vấn đề đó trong tương lai