Thêm cột nullable vào bảng tốn hơn 10 phút


11

Tôi có vấn đề để thêm một cột mới trên bảng.
Tôi đã thử chạy nó một vài lần, nhưng sau hơn 10 phút chạy, tôi quyết định hủy truy vấn vì thời gian khóa.

ALTER TABLE mytable ADD mycolumn VARCHAR(50);

Thông tin hữu ích:

  • Phiên bản PostgreSQL: 9.1
  • Số lượng hàng: ~ 250K
  • Số cột: 38
  • Số cột không có giá trị: 32
  • Số lượng ràng buộc: 5 (1 PK, 3 FK, 1 ĐỘC ĐÁO)
  • Số lượng chỉ mục: 1
  • Kiểu hệ điều hành: Debian Bóp 64

Tôi đã tìm thấy thông tin thú vị về cách PostgreSQL quản lý các cột không thể (thông qua HeapTupleHeader).

Dự đoán đầu tiên của tôi là bởi vì bảng này đã có 32 cột không thể có 8 bit MAXALIGN, HeapTupleHeader có độ dài 4 byte (không được xác minh và tôi không biết làm thế nào để làm như vậy).

Vì vậy, việc thêm một cột nullable mới có thể cần cập nhật HeapTupleHeader trên mỗi hàng để thêm 8 bit mới MAXALIGN, điều này có thể gây ra vấn đề về hiệu suất.

Vì vậy, tôi đã cố gắng thay đổi một trong những cột không thể thực hiện được (thực tế không phải là không thể thực hiện được) để giảm xuống còn 31 số cột không thể kiểm tra được, để kiểm tra xem dự đoán của tôi có đúng không.

ALTER TABLE mytable ALTER myothercolumn SET NOT NULL;

Thật không may, sự thay đổi này cũng mất nhiều thời gian, hơn 5 phút, vì vậy tôi cũng đã hủy bỏ nó.

Bạn có ý tưởng về những gì có thể gây ra chi phí hiệu suất này?


1
Chà, tôi có thể nói với bạn một phần của nó: Thay đổi một loại cột thành một loại khác không tương thích nhị phân thực sự tạo ra một cột mới, sao chép dữ liệu và đặt cột cũ bị bỏ. Tuy nhiên, SET NOT NULLkhông làm thay đổi chủng loại, nó chỉ bổ sung thêm một hạn chế - nhưng hạn chế phải được kiểm tra vào bàn, và điều đó đòi hỏi phải có một bảng đầy đủ quét. 9,4 cải thiện một số trường hợp này bằng cách sử dụng khóa yếu hơn, nhưng nó vẫn khá nặng.
Craig Ringer

1
Trước khi nghi ngờ rằng nó hoạt động chậm, bạn cần chắc chắn rằng ALTER TABLE không chỉ chờ khóa. Đề cập đến nó trong câu hỏi nếu bạn đã kiểm tra.
Daniel Vérité

Cảm ơn Craig và Daniel. Khi tôi chạy lệnh thay đổi, nó xuất hiện trong pg_stat_activity với chờ "true", tôi cho rằng điều đó có nghĩa là nó đang chờ khóa!? Đó là cách tốt để kiểm tra? Nhân tiện, trước khi chạy thay đổi này, mọi thứ đều ổn, nhưng vài giây sau khi bắt đầu, số lượng khóa tăng lên

Hãy thử truy vấn tại wiki.postgresql.org/wiki/Lock_dependency_inif để xem tốt hơn. Hoặc bạn có các giao dịch kéo dài mà quên cam kết hoặc hoạt động nặng nề với bảng này khiến nó luôn bận rộn.
Daniel Vérité

Có thể là một phù hợp tốt hơn tại dba.SE.
Erwin Brandstetter

Câu trả lời:


8

Có một vài hiểu lầm ở đây:

Các rỗng bitmapkhông một phần của tiêu đề đống tuple. Mỗi tài liệu:

Có một tiêu đề kích thước cố định (chiếm 23 byte trên hầu hết các máy), theo sau là một bitmap null tùy chọn ...

32 cột không thể bỏ qua của bạn là không đáng tin cậy vì hai lý do:

  • Bitmap null được thêm vào mỗi hàng và chỉ khi có ít nhất một NULLgiá trị thực trong hàng. Các cột không thể không có tác động trực tiếp, chỉ có các NULLgiá trị thực tế mới làm được. Nếu bitmap null được phân bổ, nó luôn được phân bổ hoàn toàn (tất cả hoặc không có gì). Kích thước thực tế của bitmap null là 1 bit trên mỗi cột, được làm tròn đến byte kế tiếp . Theo mã souce hiện tại:

    #define BITMAPLEN(NATTS) (((int)(NATTS) + 7) / 8)
  • Bitmap null được phân bổ sau tiêu đề heap tuple và theo sau là một OID tùy chọn và sau đó là dữ liệu hàng. Bắt đầu một dữ liệu OID hoặc hàng được chỉ định bởi t_hofftrong tiêu đề. Mỗi mã nguồn bình luận :

    Lưu ý rằng t_hoff phải là bội số của MAXALIGN.

  • Có một byte miễn phí sau tiêu đề heap tuple, chiếm 23 byte. Vì vậy, bitmap null cho các hàng lên đến 8 cột có hiệu quả mà không mất thêm chi phí. Với cột thứ 9 trong bảng, t_hoffđược nâng cao một MAXALIGNbyte khác (thường là 8) để cung cấp cho 64 cột khác. Vì vậy, đường viền tiếp theo sẽ là 72 cột.

Để hiển thị thông tin điều khiển của cụm cơ sở dữ liệu PostgreSQL (bao gồm MAXALIGN), ví dụ cho cài đặt điển hình của Postgres 9.3 trên máy Debian:

    sudo /usr/lib/postgresql/9.3/bin/pg_controldata /var/lib/postgresql/9.3/main

Tôi cập nhật hướng dẫn trong câu trả lời liên quan mà bạn trích dẫn .

Bỏ qua một bên, ngay cả khi ALTER TABLEcâu lệnh của bạn kích hoạt viết lại toàn bộ bảng (có thể là như vậy, thay đổi loại dữ liệu), 250K thực sự không nhiều và sẽ là vấn đề của vài giây trên bất kỳ máy tính nửa chừng nào (trừ khi các hàng lớn bất thường) . 10 phút trở lên cho thấy một vấn đề hoàn toàn khác. Tuyên bố của bạn đang chờ để có được một khóa trên bàn, rất có thể.

Số lượng mục nhập ngày càng tăng pg_stat_activityđồng nghĩa với việc giao dịch mở nhiều hơn - biểu thị quyền truy cập đồng thời trên bảng (rất có thể) phải chờ hoạt động kết thúc.

Một vài bức ảnh trong bóng tối

Kiểm tra sự phình to của bảng có thể, thử một cách nhẹ nhàng VACUUM mytablehoặc tích cực hơn VACUUM FULL mytable- có thể gặp phải các vấn đề tương tranh tương tự, vì hình thức này cũng có được một khóa độc quyền. Thay vào đó, bạn có thể thử pg numpack ...

Tôi sẽ bắt đầu bằng cách kiểm tra các vấn đề có thể xảy ra với các chỉ mục, trình kích hoạt, khóa ngoại hoặc các ràng buộc khác, đặc biệt là các vấn đề liên quan đến cột. Đặc biệt là một chỉ số bị hỏng có thể được tham gia? Hãy thử REINDEX TABLE mytable;hoặc DROPtất cả chúng và thêm lại chúng sau ALTER TABLE trong cùng một giao dịch .

Hãy thử chạy lệnh trong đêm hoặc bất cứ khi nào không có nhiều tải.

Phương pháp brute-force sẽ là dừng truy cập vào máy chủ, sau đó thử lại:

Không thể ghim nó xuống, việc nâng cấp lên phiên bản hiện tại hoặc cụ thể là phiên bản 9,4 sắp tới có thể giúp ích. Đã có một số cải tiến cho các bảng lớn và cho các chi tiết khóa. Nhưng nếu có một cái gì đó bị hỏng trong DB của bạn, có lẽ bạn nên tìm ra nó trước.


2
Nó gần như chắc chắn là khóa. Nhưng, như một bài kiểm tra, bạn luôn có thể tạo một bản sao của bảng và thử thay đổi điều đó. Nếu việc đó không mất nhiều thời gian thì bạn biết đó không phải là vấn đề sửa đổi thực sự.

Cảm ơn đã giải thích Erwin. Tôi nghĩ bạn đã đúng, nó dường như là một vấn đề khóa. Khi tôi kiểm tra pg_stat_activity, tôi có thể thấy rằng ALTER của tôi có "chờ" đúng. Điều tôi không thể hiểu là tại sao ALTER không thể có được khóa trên bàn, vì ngay cả khi tôi không thể tìm thấy bất kỳ truy vấn nào đang chạy, có vẻ như nó không thể lấy được. Nhưng ngay khi ALTER của tôi bắt đầu chạy, tất cả các truy vấn khác đang chờ nó kết thúc. Vì vậy, hoạt động dường như chỉ ra rằng ALTER khóa tất cả các truy vấn khác, nhưng cũng chỉ ra rằng ALTER không nhận được khóa. Tôi nghĩ có điều gì đó tôi không hiểu rõ!?

@MatthieuVerrecchia: Bạn đã thử bài kiểm tra mà Richard gợi ý chưa?
Erwin Brandstetter

1
Tôi vừa nhân bản bảng của mình sang một bảng mới (với pg_dump -> pg_sql). Cột mới được thêm chính xác trong 50ms, xác nhận sự cố khóa. Nhân tiện, vẫn không hiểu tại sao ALTER không thể khóa với hoạt động db thực sự tiêu chuẩn.

1
@ErwinBrandstetter Tôi đã làm theo gợi ý của bạn và đã thử VACUUM, sau đó là REINDEX. REINDEX cũng bị chặn, vì nó cũng không thể có được khóa .. Sau một số điều tra, vấn đề đơn giản hơn chúng tôi rất khó khăn .. Có một tuần còn lại <IDLE> với một giao dịch mở. Vấn đề đã được giải quyết, cảm ơn Đối với tất cả mọi thứ, thông tin là rất hữu ích.
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.