Tại sao Postgres CẬP NHẬT mất 39 giờ?


17

Tôi có một bảng Postgres với ~ 2,1 triệu hàng. Tôi đã chạy bản cập nhật dưới đây về nó:

WITH stops AS (
    SELECT id,
           rank() OVER (ORDER BY offense_timestamp,
                     defendant_dl,
                     offense_street_number,
                     offense_street_name) AS stop
    FROM   consistent.master
    WHERE  citing_jurisdiction=1
)

UPDATE consistent.master
SET arrest_id=stops.stop
FROM stops
WHERE master.id = stops.id;

Truy vấn này mất 39 giờ để chạy. Tôi đang chạy cái này trên bộ vi xử lý laptop i7 Q720 lõi 4 (vật lý), nhiều RAM, không có gì khác chạy trong phần lớn thời gian. Không hạn chế dung lượng ổ cứng. Bảng gần đây đã được hút bụi, phân tích và reindexed.

Toàn bộ thời gian truy vấn đang chạy, ít nhất là sau khi WITHhoàn thành ban đầu , mức sử dụng CPU thường thấp và ổ cứng được sử dụng 100%. Ổ cứng đã được sử dụng quá mạnh đến nỗi bất kỳ ứng dụng nào khác chạy chậm hơn đáng kể so với bình thường.

Cài đặt nguồn của máy tính xách tay là hiệu năng cao (Windows 7 x64).

Đây là GIẢI THÍCH:

Update on master  (cost=822243.22..1021456.89 rows=2060910 width=312)
  CTE stops
    ->  WindowAgg  (cost=529826.95..581349.70 rows=2060910 width=33)
          ->  Sort  (cost=529826.95..534979.23 rows=2060910 width=33)
                Sort Key: consistent.master.offense_timestamp, consistent.master.defendant_dl, consistent.master.offense_street_number, consistent.master.offense_street_name
                ->  Seq Scan on master  (cost=0.00..144630.06 rows=2060910 width=33)
                      Filter: (citing_jurisdiction = 1)
  ->  Hash Join  (cost=240893.51..440107.19 rows=2060910 width=312)
        Hash Cond: (stops.id = consistent.master.id)
        ->  CTE Scan on stops  (cost=0.00..41218.20 rows=2060910 width=48)
        ->  Hash  (cost=139413.45..139413.45 rows=2086645 width=268)
              ->  Seq Scan on master  (cost=0.00..139413.45 rows=2086645 width=268)

citing_jurisdiction=1chỉ loại trừ một vài chục ngàn hàng. Ngay cả với WHEREđiều khoản đó , tôi vẫn hoạt động trên hơn 2 triệu hàng.

Ổ cứng được mã hóa toàn bộ ổ đĩa bằng TrueCrypt 7.1a. Làm chậm điều xuống một chút, nhưng không đủ để gây ra một truy vấn để lấy đó nhiều giờ.

Phần WITHchỉ mất khoảng 3 phút để chạy.

Trường arrest_idkhông có chỉ số cho khóa ngoại. Có 8 chỉ mục và 2 khóa ngoại trên bảng này. Tất cả các trường khác trong truy vấn được lập chỉ mục.

Trường arrest_idkhông có ràng buộc ngoại trừ NOT NULL.

Bảng có tổng số 32 cột.

arrest_idlà loại ký tự khác nhau (20) . Tôi nhận ra việc rank()tạo ra một giá trị số, nhưng tôi phải sử dụng thay đổi ký tự (20) vì tôi có các hàng khác citing_jurisdiction<>1sử dụng dữ liệu không phải là số cho trường này.

Trường arrest_idtrống cho tất cả các hàng với citing_jurisdiction=1.

Đây là một máy tính xách tay cá nhân, cao cấp (tính đến 1 năm trước). Tôi là người dùng duy nhất. Không có truy vấn hoặc hoạt động khác đang chạy. Khóa dường như không thể.

Không có kích hoạt bất cứ nơi nào trong bảng này hoặc bất cứ nơi nào khác trong cơ sở dữ liệu.

Các hoạt động khác trên cơ sở dữ liệu này không bao giờ mất nhiều thời gian. Với việc lập chỉ mục thích hợp, SELECTcác truy vấn thường khá nhanh.


Đó Seq Scanlà một chút đáng sợ ...
rogerdpack

Câu trả lời:


18

Tôi đã có một cái gì đó tương tự xảy ra gần đây với một bảng 3,5 triệu hàng. Cập nhật của tôi sẽ không bao giờ kết thúc. Sau rất nhiều thử nghiệm và thất vọng, cuối cùng tôi đã tìm ra thủ phạm. Hóa ra là các chỉ mục trên bảng đang được cập nhật.

Giải pháp là bỏ tất cả các chỉ mục trên bảng đang được cập nhật trước khi chạy câu lệnh cập nhật. Khi tôi đã làm điều đó, bản cập nhật kết thúc sau vài phút. Sau khi cập nhật hoàn tất, tôi tạo lại các chỉ mục và hoạt động trở lại. Điều này có thể sẽ không giúp bạn vào thời điểm này nhưng nó có thể là người khác đang tìm kiếm câu trả lời.

Tôi sẽ giữ các chỉ mục trên bảng mà bạn đang lấy dữ liệu từ đó. Người ta sẽ không phải tiếp tục cập nhật bất kỳ chỉ mục nào và sẽ giúp tìm kiếm dữ liệu bạn muốn cập nhật. Nó chạy tốt trên một máy tính xách tay chậm.


3
Tôi đang chuyển câu trả lời tốt nhất cho bạn. Kể từ khi tôi đăng bài này, tôi đã gặp phải các tình huống khác trong đó các chỉ mục là vấn đề, ngay cả khi cột được cập nhật đã có giá trị và không có chỉ mục (!). Có vẻ như Postgres có vấn đề với cách nó quản lý các chỉ mục trên các cột khác. Không có lý do gì để các chỉ mục khác này tạo ra thời gian truy vấn của bản cập nhật khi thay đổi duy nhất cho bảng là cập nhật một cột không được lập trình và bạn không tăng không gian được phân bổ cho bất kỳ hàng nào của cột đó.
Aren Camename

1
Cảm ơn! Hy vọng nó sẽ giúp người khác. Nó sẽ giúp tôi tiết kiệm hàng giờ đau đầu vì một thứ dường như rất đơn giản.
JC Avena

5
@ArenCambre - có một lý do: PostgreSQL sao chép toàn bộ hàng sang một vị trí khác và đánh dấu phiên bản cũ là đã xóa. Đây là cách PostgreSQL thực hiện Điều khiển đồng thời nhiều phiên bản (MVCC).
Piotr Findeisen

Câu hỏi của tôi là ... tại sao nó là thủ phạm? Xem thêm stackoverflow.com/a/35660593/32453
rogerdpack

15

Vấn đề lớn nhất của bạn là thực hiện một số lượng lớn công việc nặng nhọc, nặng nhọc trên ổ cứng máy tính xách tay. Điều đó sẽ không bao giờ trở nên nhanh chóng cho dù bạn có làm gì đi nữa, đặc biệt nếu đó là loại ổ 5400RPM chậm hơn được vận chuyển trong rất nhiều máy tính xách tay.

TrueCrypt làm mọi thứ chậm lại hơn "một chút" cho việc ghi. Đọc sẽ nhanh chóng hợp lý, nhưng viết làm cho RAID 5 trông nhanh. Chạy một DB trên một khối lượng TrueCrypt sẽ là cực hình cho việc ghi, đặc biệt là ghi ngẫu nhiên.

Trong trường hợp này, tôi nghĩ bạn sẽ lãng phí thời gian để cố gắng tối ưu hóa truy vấn. Dù sao bạn cũng đang viết lại hầu hết các hàng và sẽ chậm với tình huống viết kinh hoàng của bạn. Những gì tôi muốn giới thiệu là:

BEGIN;
SELECT ... INTO TEMPORARY TABLE master_tmp ;
TRUNCATE TABLE consistent.master;
-- Now DROP all constraints on consistent.master, then:
INSERT INTO consistent.master SELECT * FROM master_tmp;
-- ... and re-create any constraints.

Tôi nghi ngờ rằng sẽ nhanh hơn việc bỏ và tạo lại các ràng buộc một mình, bởi vì một CẬP NHẬT sẽ có các mẫu ghi khá ngẫu nhiên sẽ giết chết bộ nhớ của bạn. Hai chèn số lượng lớn, một vào bảng chưa được gắn và một vào bảng đã ghi nhật ký WAL mà không bị ràng buộc, có thể sẽ nhanh hơn.

Nếu bạn có các bản sao lưu hoàn toàn cập nhật và không cần phải khôi phục cơ sở dữ liệu của mình từ các bản sao lưu, bạn cũng có thể khởi động lại PostgreQuery với fsync=offtham số và full_page_writes=off tạm thời cho hoạt động hàng loạt này. Bất kỳ sự cố không mong muốn nào như mất điện hoặc sự cố hệ điều hành sẽ khiến cơ sở dữ liệu của bạn không thể phục hồi được fsync=off.

POSTGreSQL tương đương với "không đăng nhập" là sử dụng các bảng chưa được đăng. Các bảng không được gắn này bị cắt bớt nếu DB tắt không sạch trong khi chúng bẩn. Sử dụng các bảng chưa được đăng ký sẽ ít nhất giảm một nửa tải ghi của bạn và giảm số lượng tìm kiếm, vì vậy chúng có thể là RẤT NHIỀU nhanh hơn.

Giống như trong Oracle, có thể là một ý tưởng tốt để loại bỏ một chỉ mục sau đó tạo lại nó sau khi cập nhật hàng loạt lớn. Công cụ lập kế hoạch của PostgreQuery không thể biết rằng một bản cập nhật lớn đang diễn ra, tạm dừng cập nhật chỉ mục, sau đó xây dựng lại chỉ mục ở cuối; ngay cả nếu có thể, nó sẽ rất khó để tìm ra điểm nào đáng làm, đặc biệt là trước.


Câu trả lời này được phát hiện trên số lượng lớn ghi và sự hoàn hảo khủng khiếp của mã hóa cộng với ổ đĩa laptop chậm. Tôi cũng sẽ lưu ý rằng sự hiện diện của 8 chỉ mục tạo ra nhiều lần ghi thêm và đánh bại khả năng áp dụng các cập nhật hàng trong khối HOT , do đó việc bỏ chỉ mục và sử dụng công cụ điền vào thấp hơn trên bảng có thể ngăn chặn hàng tấn di chuyển hàng
ngay từ

1
Lời kêu gọi tốt về việc tăng cơ hội HOT với một fillfactor - mặc dù với TrueCrypt buộc các chu kỳ đọc lại khối trong các khối lớn tôi không chắc nó sẽ giúp ích nhiều; di chuyển hàng thậm chí có thể nhanh hơn bởi vì việc phát triển bảng ít nhất là thực hiện các khối ghi tuyến tính.
Craig Ringer

2,5 năm sau tôi đang làm một cái gì đó tương tự nhưng trên một cái bàn lớn hơn. Để chắc chắn, có nên bỏ tất cả các chỉ mục hay không, ngay cả khi cột duy nhất tôi đang cập nhật không được lập chỉ mục?
Aren Camename

1
@ArenCambre Trong trường hợp đó ... tốt, nó phức tạp. Nếu hầu hết các cập nhật của bạn sẽ đủ điều kiện HOTthì tốt hơn hết là giữ nguyên các chỉ mục. Nếu không, thì có khả năng bạn sẽ muốn thả và tạo lại. Cột không được lập chỉ mục, nhưng để có thể thực hiện cập nhật NÓNG, cũng cần có không gian trống trên cùng một trang, do đó, nó phụ thuộc một chút vào số lượng không gian chết trong bảng. Nếu nó viết - chủ yếu, tôi sẽ nói giảm tất cả các chỉ mục. Nếu nó được cập nhật rất nhiều, nó có thể có lỗ hổng và bạn có thể ổn. Các công cụ thích pageinspectpg_freespacemapcó thể giúp xác định điều này.
Craig Ringer

Cảm ơn. Trong trường hợp này, đó là một cột boolean đã có một mục vào mỗi hàng. Tôi đã thay đổi mục trên một số hàng. Tôi chỉ xác nhận: bản cập nhật chỉ mất 2 giờ sau khi bỏ tất cả các chỉ mục. Trước đó, tôi đã phải dừng cập nhật sau 18 giờ vì nó chỉ mất quá nhiều thời gian. Điều này bất chấp thực tế là cột được cập nhật chắc chắn không được lập chỉ mục.
Aren Camename

2

Ai đó sẽ đưa ra câu trả lời tốt hơn cho Postgres, nhưng đây là một vài quan sát từ quan điểm của Oracle có thể áp dụng (và các bình luận quá dài cho lĩnh vực bình luận).

Mối quan tâm đầu tiên của tôi sẽ là cố gắng cập nhật 2 triệu hàng trong một giao dịch. Trong Oracle, bạn sẽ viết một hình ảnh trước khi mỗi khối được cập nhật để phiên khác vẫn có kết quả đọc ổn định mà không cần đọc các khối đã sửa đổi của bạn và bạn có khả năng quay ngược lại. Đó là một rollback dài đang được xây dựng. Bạn thường tốt hơn để thực hiện các giao dịch trong khối nhỏ. Nói 1.000 hồ sơ tại một thời điểm.

Nếu bạn có các chỉ mục trên bàn và bảng sẽ bị coi là không hoạt động trong quá trình bảo trì, bạn nên loại bỏ các chỉ mục trước một thao tác lớn và sau đó tạo lại nó sau đó. Rẻ hơn sau đó liên tục cố gắng duy trì các chỉ số với mỗi bản ghi được cập nhật.

Oracle cho phép gợi ý "không ghi nhật ký" trên các báo cáo để ngăn chặn việc ghi nhật ký. Nó tăng tốc các câu lệnh rất nhiều, nhưng để db của bạn trong tình huống "không thể phục hồi". Vì vậy, bạn sẽ muốn sao lưu trước và sao lưu lại ngay sau đó. Tôi không biết nếu Postgres có các tùy chọn tương tự.


PostgreSQL không có vấn đề với một rollback dài, không tồn tại. ROLBACK rất nhanh trong PostgreSQL, bất kể giao dịch của bạn lớn như thế nào. Oracle! = PostgreSQL
Frank Heikens

@FrankHeikens Cảm ơn, điều đó thật thú vị. Tôi sẽ phải đọc về cách hoạt động của tạp chí trên Postgres. Để làm cho toàn bộ khái niệm giao dịch hoạt động, bằng cách nào đó, hai phiên bản dữ liệu khác nhau cần được duy trì trong quá trình giao dịch, hình ảnh trước và hình ảnh sau và đó là cơ chế mà tôi đang đề cập. Bằng cách này hay cách khác, tôi đoán có một ngưỡng vượt quá các nguồn lực để duy trì giao dịch sẽ quá đắt.
Glenn

2
@Glenn postgres giữ các phiên bản của một hàng trong bảng - xem tại đây để được giải thích. Sự thỏa hiệp là bạn nhận được các bộ dữ liệu 'chết' treo xung quanh, được dọn sạch không đồng bộ với cái gọi là 'chân không' trong postgres (Oracle không cần chân không vì nó không bao giờ có hàng 'chết' trong bảng)
Jack Douglas

Bạn được chào đón và khá muộn màng: chào mừng bạn đến với trang web :-)
Jack Douglas

@Glenn Tài liệu chính tắc cho kiểm soát đồng thời phiên bản hàng của PostgreSQL là postgresql.org/docs/civerse/static/mvcc-intro.html và nó cũng đáng để đọc. Xem thêm wiki.postgresql.org/wiki/MVCC . Lưu ý rằng MVCC với các hàng chết và VACUUMchỉ là một nửa câu trả lời; PostgreSQL cũng sử dụng cái gọi là "ghi nhật ký trước" (hiệu quả là một tạp chí) để cung cấp các cam kết nguyên tử và bảo vệ chống lại việc ghi một phần, v.v. Xem postgresql.org/docs/cản/static/wal-intro.html
Craig Ringer
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.