Làm thế nào để phân vùng bảng hiện có trong postgres?


19

Tôi muốn phân vùng một bảng có 1M + hàng theo phạm vi ngày. Làm thế nào điều này thường được thực hiện mà không đòi hỏi nhiều thời gian chết hoặc có nguy cơ mất dữ liệu? Dưới đây là các chiến lược tôi đang xem xét, nhưng mở ra cho các đề xuất:

  1. Bảng hiện có là chủ và trẻ em thừa hưởng từ nó. Theo thời gian di chuyển dữ liệu từ chủ sang con, nhưng sẽ có một khoảng thời gian trong đó một số dữ liệu nằm trong bảng chính và một số ở trẻ em.

  2. Tạo một bảng chủ và trẻ em mới. Tạo bản sao dữ liệu trong bảng hiện có trong các bảng con (vì vậy dữ liệu sẽ nằm ở hai nơi). Khi các bảng con có dữ liệu gần đây nhất, hãy thay đổi tất cả các phần chèn về phía trước để trỏ đến bảng chính mới và xóa bảng hiện có.


1
Ở đây, ý tưởng của tôi: nếu các bảng có cột datetime -> tạo chủ mới + con mới -> chèn dữ liệu mới vào NEW + OLD (ví dụ: datetime = 2015-07-06 00:00:00) -> sao chép từ OLD sang cơ sở MỚI trên cột thời gian (trong đó: datetime <2015-07-06 00:00:00) -> đổi tên bảng -> thay đổi chèn thành MỚI khác -> tạo "kích hoạt phân vùng" để chèn / cập nhật trên bản gốc (chèn / cập nhật dữ liệu mới - > di chuyển đến trẻ em, do đó dữ liệu mới sẽ được chèn vào trẻ em) -> cập nhật chủ, trình kích hoạt sẽ di chuyển dữ liệu sang trẻ em.
Luân Huỳnh

@Innnh, vì vậy bạn đang đề xuất tùy chọn thứ hai, nhưng sau đó khi dữ liệu được sao chép, hãy xóa bảng cũ và đổi tên bảng mới để có cùng tên với bảng cũ. Có đúng không?
Evan Appleby

đổi tên bảng mới thành bảng cũ, nhưng bạn nên giữ bảng cũ cho đến khi bảng phân vùng luồng mới hoàn toàn ổn.
Luân Huỳnh

2
Đối với chỉ vài triệu hàng tôi không nghĩ rằng phân vùng là thực sự cần thiết. Tại sao bạn nghĩ rằng bạn cần nó? Vấn đề gì bạn đang cố gắng giải quyết?
a_horse_with_no_name

1
@EvanAppleby DELETE FROM ONLY master_tablelà giải pháp.
dezso

Câu trả lời:


21

Vì # 1 yêu cầu sao chép dữ liệu từ chủ sang con trong khi nó ở trong môi trường sản xuất đang hoạt động, cá nhân tôi đã đi với số 2 (tạo chủ mới). Điều này ngăn chặn sự gián đoạn đối với bảng gốc trong khi nó đang được sử dụng tích cực và nếu có bất kỳ vấn đề nào, tôi có thể dễ dàng xóa chủ mới mà không gặp sự cố và tiếp tục sử dụng bảng gốc. Dưới đây là các bước để làm điều đó:

  1. Tạo bảng tổng thể mới.

    CREATE TABLE new_master (
        id          serial,
        counter     integer,
        dt_created  DATE DEFAULT CURRENT_DATE NOT NULL
    );
    
  2. Tạo con kế thừa từ chủ.

    CREATE TABLE child_2014 (
        CONSTRAINT pk_2014 PRIMARY KEY (id),
        CONSTRAINT ck_2014 CHECK ( dt_created < DATE '2015-01-01' )
    ) INHERITS (new_master);
    CREATE INDEX idx_2014 ON child_2014 (dt_created);
    
    CREATE TABLE child_2015 (
        CONSTRAINT pk_2015 PRIMARY KEY (id),
        CONSTRAINT ck_2015 CHECK ( dt_created >= DATE '2015-01-01' AND dt_created < DATE '2016-01-01' )
    ) INHERITS (new_master);
    CREATE INDEX idx_2015 ON child_2015 (dt_created);
    
    ...
    
  3. Sao chép tất cả dữ liệu lịch sử vào bảng chính mới

    INSERT INTO child_2014 (id,counter,dt_created)
    SELECT id,counter,dt_created
    from old_master
    where dt_created < '01/01/2015'::date;
    
  4. Tạm thời tạm dừng chèn / cập nhật mới vào cơ sở dữ liệu sản xuất

  5. Sao chép dữ liệu gần đây nhất vào bảng chính mới

    INSERT INTO child_2015 (id,counter,dt_created)
    SELECT id,counter,dt_created
    from old_master
    where dt_created >= '01/01/2015'::date AND dt_created < '01/01/2016'::date;
    
  6. Đổi tên bảng để new_master trở thành cơ sở dữ liệu sản xuất.

    ALTER TABLE old_master RENAME TO old_master_backup;
    ALTER TABLE new_master RENAME TO old_master;
    
  7. Thêm chức năng cho các câu lệnh INSERT vào old_master để dữ liệu được chuyển đến phân vùng chính xác.

    CREATE OR REPLACE FUNCTION fn_insert() RETURNS TRIGGER AS $$
    BEGIN
        IF ( NEW.dt_created >= DATE '2015-01-01' AND
             NEW.dt_created < DATE '2016-01-01' ) THEN
            INSERT INTO child_2015 VALUES (NEW.*);
        ELSIF ( NEW.dt_created < DATE '2015-01-01' ) THEN
            INSERT INTO child_2014 VALUES (NEW.*);
        ELSE
            RAISE EXCEPTION 'Date out of range';
        END IF;
        RETURN NULL;
    END;
    $$
    LANGUAGE plpgsql;
    
  8. Thêm kích hoạt để chức năng đó được gọi trên INSERTS

    CREATE TRIGGER tr_insert BEFORE INSERT ON old_master
    FOR EACH ROW EXECUTE PROCEDURE fn_insert();
    
  9. Đặt loại trừ ràng buộc thành BẬT

    SET constraint_exclusion = on;
  10. Kích hoạt lại CẬP NHẬT và BẢO HIỂM trên cơ sở dữ liệu sản xuất

  11. Thiết lập kích hoạt hoặc cron để các phân vùng mới được tạo và chức năng được cập nhật để gán dữ liệu mới cho phân vùng chính xác. Tham khảo bài viết này cho các ví dụ mã

  12. Xóa old_master_backup


1
Đẹp viết. Sẽ rất thú vị nếu điều đó thực sự làm cho các truy vấn của bạn nhanh hơn. 10 triệu vẫn chưa có nhiều hàng mà tôi nghĩ về việc phân vùng. Tôi tự hỏi nếu hiệu suất xuống cấp của bạn có thể gây ra do vacuumkhông theo kịp hoặc bị ngăn chặn do các phiên "nhàn rỗi trong giao dịch".
a_horse_with_no_name 7/07/2015

@a_horse_with_no_name, cho đến nay nó vẫn chưa giúp truy vấn tốt hơn đáng kể :( Tôi sử dụng Heroku có cài đặt tự động hút bụi và nó dường như xảy ra hàng ngày cho bảng lớn này. Sẽ xem xét thêm về điều đó
Evan Appleby

Không nên chèn các bước trong bước 3 và 5 vào bảng new_master và để postgresql chọn đúng bảng / phân vùng con?
pakman

@pakman chức năng gán đúng đứa trẻ không được thêm vào cho đến bước 7
Evan Appleby

4

Có một công cụ mới gọi là pg_pathman ( https://github.com/postgrespro/pg_pathman ) sẽ tự động làm điều này cho bạn.

Vì vậy, một cái gì đó như sau sẽ làm điều đó.

SELECT create_range_partitions('master', 'dt_created', 
   '2015-01-01'::date, '1 day'::interval);
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.