Câu trả lời:
Giải thích_EXPLAIN.pdf cũng có thể hữu ích.
Phần tôi luôn cảm thấy khó hiểu là chi phí khởi động và tổng chi phí. Tôi Google điều này mỗi khi tôi quên nó, nó đưa tôi trở lại đây, điều này không giải thích sự khác biệt, đó là lý do tại sao tôi viết câu trả lời này. Đây là những gì tôi thu thập được từ tài liệu PostgresEXPLAIN
, được giải thích khi tôi hiểu nó.
Đây là một ví dụ từ một ứng dụng quản lý diễn đàn:
EXPLAIN SELECT * FROM post LIMIT 50;
Limit (cost=0.00..3.39 rows=50 width=422)
-> Seq Scan on post (cost=0.00..15629.12 rows=230412 width=422)
Đây là giải thích bằng hình ảnh từ PgAdmin:
(Khi bạn đang sử dụng PgAdmin, bạn có thể trỏ chuột vào một thành phần để đọc chi tiết chi phí.)
Chi phí được biểu diễn dưới dạng một bộ giá trị, ví dụ như chi phí LIMIT
là cost=0.00..3.39
và chi phí quét tuần tự post
là cost=0.00..15629.12
. Số đầu tiên trong bộ giá trị là chi phí khởi động và số thứ hai là tổng chi phí . Bởi vì tôi đã sử dụng EXPLAIN
và không sử dụng EXPLAIN ANALYZE
, những chi phí này là ước tính, không phải là thước đo thực tế.
Một điều phức tạp là chi phí của mỗi nút "cha" bao gồm chi phí của các nút con của nó. Trong biểu diễn văn bản, cây được biểu diễn bằng cách thụt lề, ví dụ như LIMIT
là một nút cha và Seq Scan
là nút con của nó. Trong biểu diễn PgAdmin, các mũi tên trỏ từ con đến cha - hướng của luồng dữ liệu - có thể phản trực giác nếu bạn đã quen thuộc với lý thuyết đồ thị.
Tài liệu nói rằng chi phí bao gồm tất cả các nút con, nhưng lưu ý rằng tổng chi phí của nút cha 3.39
nhỏ hơn nhiều so với tổng chi phí của nó 15629.12
. Tổng chi phí không bao gồm bởi vì một thành phần như LIMIT
không cần phải xử lý toàn bộ đầu vào của nó. Xem EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;
ví dụ trong tài liệu PostgresEXPLAIN
.
Trong ví dụ trên, thời gian khởi động bằng 0 đối với cả hai thành phần, vì cả hai thành phần đều không cần thực hiện bất kỳ quá trình xử lý nào trước khi bắt đầu ghi các hàng: quét tuần tự đọc hàng đầu tiên của bảng và phát ra. Các LIMIT
lần đọc dòng đầu tiên của nó và sau đó phát ra nó.
Khi nào một thành phần cần thực hiện nhiều xử lý trước khi nó có thể bắt đầu xuất ra bất kỳ hàng nào? Có rất nhiều lý do có thể xảy ra, nhưng hãy xem một ví dụ rõ ràng. Đây là cùng một truy vấn từ trước nhưng bây giờ chứa một ORDER BY
mệnh đề:
EXPLAIN SELECT * FROM post ORDER BY body LIMIT 50;
Limit (cost=23283.24..23283.37 rows=50 width=422)
-> Sort (cost=23283.24..23859.27 rows=230412 width=422)
Sort Key: body
-> Seq Scan on post (cost=0.00..15629.12 rows=230412 width=422)
Và về mặt đồ họa:
Một lần nữa, quá trình quét tuần tự được bật post
không có chi phí khởi động: nó bắt đầu xuất các hàng ngay lập tức. Nhưng việc sắp xếp có một chi phí khởi động đáng kể 23283.24
vì nó phải sắp xếp toàn bộ bảng trước khi nó có thể xuất ra dù chỉ một hàng . Tổng chi phí sắp xếp 23859.27
chỉ cao hơn một chút so với chi phí khởi động, phản ánh thực tế là một khi toàn bộ tập dữ liệu đã được sắp xếp, dữ liệu được sắp xếp có thể được phát ra rất nhanh.
Lưu ý rằng thời gian khởi động của LIMIT
23283.24
chính xác bằng thời gian khởi động của loại. Điều này không phải vì LIMIT
bản thân nó có thời gian khởi động cao. Nó thực sự không có thời gian khởi động tự nó, nhưng tổng EXPLAIN
hợp tất cả các chi phí con cho mỗi phụ huynh, vì vậy LIMIT
thời gian khởi động bao gồm tổng thời gian khởi động của các con của nó.
Việc tổng hợp chi phí này có thể gây khó khăn cho việc hiểu chi phí thực hiện của từng thành phần riêng lẻ. Ví dụ: LIMIT
thời gian khởi động của chúng tôi là 0, nhưng điều đó không rõ ràng ở cái nhìn đầu tiên. Vì lý do này, một số người khác đã liên kết với giải thích.depesz.com , một công cụ do Hubert Lubaczewski (hay còn gọi là depesz) tạo ra để giúp hiểu EXPLAIN
- trong số những thứ khác - trừ chi phí con khỏi chi phí cha mẹ. Anh ấy đề cập đến một số phức tạp khác trong một bài đăng blog ngắn về công cụ của mình.
Nó thực hiện từ thụt lề nhiều nhất đến thụt lề ít nhất và tôi tin rằng từ cuối kế hoạch đến đầu. (Vì vậy, nếu có hai phần được thụt lề, phần nằm ở phía dưới trang sẽ thực thi đầu tiên, sau đó khi chúng gặp các phần khác thực thi, thì quy tắc kết hợp chúng sẽ thực thi.)
Ý tưởng là ở mỗi bước có 1 hoặc 2 bộ dữ liệu đến và được xử lý theo quy tắc nào đó. Nếu chỉ một tập dữ liệu, thì thao tác đó được thực hiện với tập dữ liệu đó. (Ví dụ: quét chỉ mục để tìm ra những hàng bạn muốn, lọc tập dữ liệu hoặc sắp xếp nó.) Nếu hai, hai tập dữ liệu là hai thứ được thụt vào sâu hơn và chúng được nối với nhau theo quy tắc bạn thấy. Ý nghĩa của hầu hết các quy tắc có thể dễ dàng đoán ra một cách hợp lý (đặc biệt nếu bạn đã đọc một loạt các kế hoạch giải thích trước đó), tuy nhiên, bạn có thể thử xác minh các mục riêng lẻ bằng cách xem trong tài liệu hoặc (dễ dàng hơn) bằng cách chỉ cần ném cụm từ vào Google cùng với một vài từ khóa như EXPLAIN
.
Đây rõ ràng không phải là một lời giải thích đầy đủ, nhưng nó cung cấp đủ ngữ cảnh mà bạn thường có thể tìm ra bất cứ điều gì bạn muốn. Ví dụ: hãy xem xét kế hoạch này từ một cơ sở dữ liệu thực tế:
explain analyze
select a.attributeid, a.attributevalue, b.productid
from orderitemattribute a, orderitem b
where a.orderid = b.orderid
and a.attributeid = 'display-album'
and b.productid = 'ModernBook';
------------------------------------------------------------------------------------------------------------------------------------------------------------
Merge Join (cost=125379.14..125775.12 rows=3311 width=29) (actual time=841.478..841.478 rows=0 loops=1)
Merge Cond: (a.orderid = b.orderid)
-> Sort (cost=109737.32..109881.89 rows=57828 width=23) (actual time=736.163..774.475 rows=16815 loops=1)
Sort Key: a.orderid
Sort Method: quicksort Memory: 1695kB
-> Bitmap Heap Scan on orderitemattribute a (cost=1286.88..105163.27 rows=57828 width=23) (actual time=41.536..612.731 rows=16815 loops=1)
Recheck Cond: ((attributeid)::text = 'display-album'::text)
-> Bitmap Index Scan on (cost=0.00..1272.43 rows=57828 width=0) (actual time=25.033..25.033 rows=16815 loops=1)
Index Cond: ((attributeid)::text = 'display-album'::text)
-> Sort (cost=15641.81..15678.73 rows=14769 width=14) (actual time=14.471..16.898 rows=1109 loops=1)
Sort Key: b.orderid
Sort Method: quicksort Memory: 76kB
-> Bitmap Heap Scan on orderitem b (cost=310.96..14619.03 rows=14769 width=14) (actual time=1.865..8.480 rows=1114 loops=1)
Recheck Cond: ((productid)::text = 'ModernBook'::text)
-> Bitmap Index Scan on id_orderitem_productid (cost=0.00..307.27 rows=14769 width=0) (actual time=1.431..1.431 rows=1114 loops=1)
Index Cond: ((productid)::text = 'ModernBook'::text)
Total runtime: 842.134 ms
(17 rows)
Hãy thử đọc nó cho chính mình và xem nó có ý nghĩa không.
Những gì tôi đọc được là cơ sở dữ liệu đầu tiên quét id_orderitem_productid
chỉ mục, sử dụng chỉ mục đó để tìm các hàng mà nó muốn orderitem
, sau đó sắp xếp tập dữ liệu đó bằng cách sử dụng nhanh chóng (loại được sử dụng sẽ thay đổi nếu dữ liệu không vừa với RAM), sau đó đặt điều đó sang một bên.
Tiếp theo, nó quét orditematt_attributeid_idx
để tìm các hàng mà nó muốn orderitemattribute
và sau đó sắp xếp tập dữ liệu đó bằng cách sử dụng nhanh chóng.
Sau đó, nó lấy hai tập dữ liệu và hợp nhất chúng. (Phép nối hợp nhất là một loại hoạt động "nén" trong đó nó sẽ chuyển hai tập dữ liệu được sắp xếp song song, tạo ra hàng đã nối khi chúng khớp với nhau.)
Như tôi đã nói, bạn làm việc thông qua kế hoạch từ phần bên trong đến phần bên ngoài, từ dưới lên trên.
Depesz cũng có một công cụ trợ giúp trực tuyến, công cụ này sẽ làm nổi bật những phần đắt giá của kết quả phân tích.
cũng có một, đây là kết quả tương tự , tôi làm rõ hơn vấn đề nằm ở đâu.
PgAdmin sẽ hiển thị cho bạn một biểu diễn đồ họa của kế hoạch giải thích. Chuyển đổi qua lại giữa hai kiểu thực sự có thể giúp bạn hiểu ý nghĩa của biểu diễn văn bản. Tuy nhiên, nếu bạn chỉ muốn biết nó sẽ làm gì, bạn có thể luôn sử dụng GUI.
Tài liệu chính thức của PostgreSQL cung cấp một lời giải thích thú vị, thấu đáo về cách hiểu đầu ra của giải thích.