PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Làm cách nào để thực hiện cập nhật không chặn lớn trong PostgreSQL?

Cột / Hàng

... Tôi không cần tính toàn vẹn của giao dịch được duy trì trong toàn bộ hoạt động, bởi vì tôi biết rằng cột tôi đang thay đổi sẽ không được ghi hoặc đọc trong quá trình cập nhật.

Bất kỳ UPDATE nào trong mô hình MVCC của PostgreSQL viết một phiên bản mới của toàn bộ hàng . Nếu các giao dịch đồng thời thay đổi bất kỳ cột của cùng một hàng, các vấn đề đồng thời tốn thời gian phát sinh. Chi tiết trong sách hướng dẫn. Biết cùng cột sẽ không bị ảnh hưởng bởi các giao dịch đồng thời tránh một số các biến chứng có thể xảy ra, nhưng không phải những biến chứng khác.

Chỉ mục

Để tránh bị chuyển hướng sang một cuộc thảo luận ngoài lề, hãy giả sử rằng tất cả các giá trị của trạng thái cho 35 triệu cột hiện được đặt thành cùng một giá trị (không rỗng), do đó làm cho một chỉ mục trở nên vô dụng.

Khi cập nhật toàn bộ bảng (hoặc các phần chính của nó) Postgres không bao giờ sử dụng chỉ mục . Quét tuần tự nhanh hơn khi tất cả hoặc hầu hết các hàng phải được đọc. Ngược lại:Duy trì chỉ mục có nghĩa là chi phí bổ sung cho UPDATE .

Hiệu suất

Ví dụ:giả sử tôi có một bảng có tên "đơn đặt hàng" với 35 triệu đơn đặt hàng và tôi muốn thực hiện điều này:

UPDATE orders SET status = null;

Tôi hiểu rằng bạn đang hướng tới một giải pháp tổng quát hơn (xem bên dưới). Nhưng để giải quyết câu hỏi thực tế đã hỏi:Điều này có thể được giải quyết sau một phần nghìn giây , bất kể kích thước bảng:

ALTER TABLE orders DROP column status
                 , ADD  column status text;

Hướng dẫn sử dụng (lên đến Postgres 10):

Khi một cột được thêm với ADD COLUMN , tất cả các hàng hiện có trong bảng được khởi tạo bằng giá trị mặc định của cột (NULL nếu không có DEFAULT mệnh đề được chỉ định). Nếu không có DEFAULT , đây chỉ là một thay đổi siêu dữ liệu [...]

Hướng dẫn sử dụng (kể từ Postgres 11):

Khi một cột được thêm với ADD COLUMNDEFAULT không bay hơi được chỉ định, giá trị mặc định được đánh giá tại thời điểm của câu lệnh và kết quả được lưu trữ trong siêu dữ liệu của bảng. Giá trị đó sẽ được sử dụng cho cột cho tất cả các hàng hiện có. Nếu không có DEFAULT được chỉ định, NULL được sử dụng. Trong cả hai trường hợp đều không bắt buộc phải viết lại bảng.

Thêm một cột có DEFAULT dễ bay hơi hoặc thay đổi kiểu cột mới hiện hành sẽ yêu cầu viết toàn bộ bảng và các chỉ mục của nó. [...]

Và:

DROP COLUMN biểu mẫu không loại bỏ cột về mặt vật lý, nhưng chỉ làm cho nó ẩn đối với các hoạt động SQL. Các thao tác chèn và cập nhật tiếp theo trong bảng sẽ lưu trữ giá trị null cho cột. Do đó, việc giảm một cột diễn ra nhanh chóng nhưng sẽ không làm giảm ngay kích thước trên đĩa của bảng của bạn, vì không gian bị chiếm bởi cột bị loại bỏ không được lấy lại. Khoảng trống sẽ được lấy lại theo thời gian các hàng hủy bỏ danh sách được cập nhật.

Đảm bảo rằng bạn không có các đối tượng phụ thuộc vào cột (ràng buộc khóa ngoại, chỉ mục, chế độ xem, ...). Bạn sẽ cần phải bỏ / tạo lại chúng. Bỏ qua điều đó, các thao tác nhỏ trên bảng danh mục hệ thống pg_attribute thực hiện công việc. Yêu cầu khóa độc quyền trên bàn có thể là một vấn đề đối với tải nặng đồng thời. (Giống như Buurman nhấn mạnh trong nhận xét của mình.) Nói rằng, hoạt động chỉ là một vấn đề của mili giây.

Nếu bạn muốn giữ mặc định cột, hãy thêm lại nó trong một lệnh riêng . Thực hiện nó trong cùng một lệnh sẽ áp dụng nó cho tất cả các hàng ngay lập tức. Xem:

  • Thêm cột mới mà không cần khóa bảng?

Để thực sự áp dụng mặc định, hãy xem xét thực hiện nó theo lô:

  • PostgreSQL có tối ưu hóa việc thêm các cột có ĐỊNH NGHĨA KHÔNG ĐẦY ĐỦ không?

Giải pháp chung

dblink đã được đề cập trong một câu trả lời khác. Nó cho phép truy cập cơ sở dữ liệu Postgres "từ xa" trong các kết nối riêng biệt ngầm định. Cơ sở dữ liệu "từ xa" có thể là cơ sở dữ liệu hiện tại, do đó đạt được "giao dịch tự trị" :những gì hàm ghi trong db "từ xa" được cam kết và không thể khôi phục.

Điều này cho phép chạy một chức năng duy nhất cập nhật một bảng lớn trong các phần nhỏ hơn và mỗi phần được cam kết riêng biệt. Tránh tăng tổng chi phí giao dịch cho số lượng hàng rất lớn và quan trọng hơn là giải phóng khóa sau mỗi phần. Điều này cho phép các hoạt động đồng thời tiến hành mà không bị chậm trễ nhiều và ít xảy ra tình trạng bế tắc hơn.

Nếu bạn không có quyền truy cập đồng thời, điều này hầu như không hữu ích - ngoại trừ việc tránh ROLLBACK sau một ngoại lệ. Cũng xem xét SAVEPOINT cho trường hợp đó.

Tuyên bố từ chối trách nhiệm

Trước hết, rất nhiều giao dịch nhỏ thực sự đắt hơn. Điều này chỉ có ý nghĩa đối với các bảng lớn . Điểm ngọt ngào phụ thuộc vào nhiều yếu tố.

Nếu bạn không chắc mình đang làm gì: một giao dịch duy nhất là phương pháp an toàn . Để điều này hoạt động bình thường, các hoạt động đồng thời trên bàn phải thực hiện theo. Ví dụ:đồng thời ghi có thể di chuyển một hàng đến một phân vùng được cho là đã được xử lý. Hoặc đọc đồng thời có thể thấy các trạng thái trung gian không nhất quán. Bạn đã được cảnh báo.

Hướng dẫn từng bước

Trước tiên cần cài đặt dblink mô-đun bổ sung:

  • Cách sử dụng (cài đặt) dblink trong PostgreSQL?

Việc thiết lập kết nối với dblink phụ thuộc rất nhiều vào việc thiết lập cụm DB của bạn và các chính sách bảo mật tại chỗ. Nó có thể phức tạp. Câu trả lời sau có liên quan với thêm cách kết nối với dblink :

  • Chèn liên tục trong một UDF ngay cả khi chức năng này ngừng hoạt động

Tạo FOREIGN SERVER USER MAPPING như được hướng dẫn ở đó để đơn giản hóa và hợp lý hóa kết nối (trừ khi bạn đã có).
Giả sử serial PRIMARY KEY có hoặc không có một số khoảng trống.

CREATE OR REPLACE FUNCTION f_update_in_steps()
  RETURNS void AS
$func$
DECLARE
   _step int;   -- size of step
   _cur  int;   -- current ID (starting with minimum)
   _max  int;   -- maximum ID
BEGIN
   SELECT INTO _cur, _max  min(order_id), max(order_id) FROM orders;
                                        -- 100 slices (steps) hard coded
   _step := ((_max - _cur) / 100) + 1;  -- rounded, possibly a bit too small
                                        -- +1 to avoid endless loop for 0
   PERFORM dblink_connect('myserver');  -- your foreign server as instructed above

   FOR i IN 0..200 LOOP                 -- 200 >> 100 to make sure we exceed _max
      PERFORM dblink_exec(
       $$UPDATE public.orders
         SET    status = 'foo'
         WHERE  order_id >= $$ || _cur || $$
         AND    order_id <  $$ || _cur + _step || $$
         AND    status IS DISTINCT FROM 'foo'$$);  -- avoid empty update

      _cur := _cur + _step;

      EXIT WHEN _cur > _max;            -- stop when done (never loop till 200)
   END LOOP;

   PERFORM dblink_disconnect();
END
$func$  LANGUAGE plpgsql;

Gọi:

SELECT f_update_in_steps();

Bạn có thể tham số hóa bất kỳ phần nào theo nhu cầu của mình:tên bảng, tên cột, giá trị, ... chỉ cần đảm bảo làm sạch các số nhận dạng để tránh chèn SQL:

  • Tên bảng dưới dạng tham số hàm PostgreSQL

Tránh các CẬP NHẬT trống:

  • Làm cách nào để (hoặc tôi có thể) CHỌN DISTINCT trên nhiều cột?


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cách thanh lịch nhất để lưu trữ dấu thời gian với nanosec trong postgresql là gì?

  2. Không tìm thấy trình điều khiển phù hợp cho jdbc:postgresql://192.168.1.8:5432 / NexentaSearch

  3. Không có hàm nào phù hợp với các loại đối số và tên đã cho

  4. Tìm n Hàng xóm Gần nhất cho Điểm đã cho bằng PostGIS?

  5. Thứ tự mảng_agg PostgreSQL