Postgres'te mevcut tablo nasıl bölümlenir?


19

Bir tabloyu tarih aralığına göre 1 milyondan fazla satırla bölümlemek istiyorum. Bu, fazla kesinti gerektirmeden veya veri kaybı riski olmadan yaygın olarak nasıl yapılır? İşte düşündüğüm, ancak önerilere açık olan stratejiler:

  1. Varolan tablo efendi ve çocuklar ondan miras alır. Zaman içinde verileri master'dan çocuğa taşıyın, ancak verilerin bir kısmının master tablosunda ve bazılarının çocuklarda olduğu bir süre olacaktır.

  2. Yeni bir ana ve alt tablolar oluşturun. Alt tablolardaki mevcut tabloda verilerin bir kopyasını oluşturun (böylece veriler iki yerde bulunur). Alt tablolar en son verilere sahip olduktan sonra, yeni ana tabloya yönlendirmek için mevcut tüm ekleri değiştirin ve mevcut tabloyu silin.


1
İşte fikirlerim: tabloların datetime sütunu varsa -> yeni ana + yeni çocuk oluştur -> YENİ + ESKİ yeni veri ekle (ör: datetime = 2015-07-06 00:00:00) -> ESKİDEN YENİ tabana kopyala zaman sütununda (burada: datetime <2015-07-06 00:00:00) -> tabloyu yeniden adlandır -> eki YENİ olarak değiştir -> master'a ekleme / güncelleme için "bölüm tetikleyici" oluştur (yeni verileri ekle / güncelle - > Childs'a taşı, böylece yeni veriler childs'a eklenecek) -> Master'ı güncelleyin, tetikleyici verileri childs'a taşıyacaktır.
Luan Huynh

@Innnh, bu nedenle ikinci seçeneği öneriyorsunuz, ancak veriler kopyalandıktan sonra eski tabloyu silin ve yeni tabloyu eski tabloyla aynı ada sahip olacak şekilde yeniden adlandırın. Bu doğru mu?
Evan Appleby

yeni tabloyu eski tablo olarak yeniden adlandırın, ancak yeni akış bölüm tabloları tamamen tamam olana kadar eski tabloyu tutmalısınız.
Luan Huynh

2
Sadece birkaç milyon satır için bölümlemenin gerçekten gerekli olduğunu düşünmüyorum. Neden ihtiyacın olduğunu düşünüyorsun? Ne problemi çözmeye çalışıyorsun?
a_horse_with_no_name

1
@EvanAppleby DELETE FROM ONLY master_tableçözümdür.
dezso

Yanıtlar:


21

# 1, aktif bir üretim ortamındayken master'dan çocuğa veri kopyalamayı gerektirdiğinden, kişisel olarak # 2 ile (yeni bir master oluşturarak) gittim. Bu, etkin olarak kullanımdayken orijinal tablonun bozulmasını önler ve herhangi bir sorun varsa, yeni master'ı sorunsuz bir şekilde silebilir ve orijinal tabloyu kullanmaya devam edebilirim. İşte yapmanız gereken adımlar:

  1. Yeni ana tablo oluşturun.

    CREATE TABLE new_master (
        id          serial,
        counter     integer,
        dt_created  DATE DEFAULT CURRENT_DATE NOT NULL
    );
  2. Ustadan miras alan çocuklar oluşturun.

    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. Tüm geçmiş verileri yeni ana tabloya kopyala

    INSERT INTO child_2014 (id,counter,dt_created)
    SELECT id,counter,dt_created
    from old_master
    where dt_created < '01/01/2015'::date;
  4. Üretim veritabanındaki yeni eklemeleri / güncellemeleri geçici olarak duraklat

  5. En son verileri yeni ana tabloya kopyala

    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. New_master'ın üretim veritabanı olması için tabloları yeniden adlandırın.

    ALTER TABLE old_master RENAME TO old_master_backup;
    ALTER TABLE new_master RENAME TO old_master;
  7. Verilerin doğru bölüme aktarılması için INSERT deyimleri için işlevi old_master öğesine ekleyin.

    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. INSERTS üzerinde işlevin çağrılması için tetikleyici ekle

    CREATE TRIGGER tr_insert BEFORE INSERT ON old_master
    FOR EACH ROW EXECUTE PROCEDURE fn_insert();
  9. Sınırlama hariç tutma özelliğini AÇIK olarak ayarlayın

    SET constraint_exclusion = on;
  10. Üretim veritabanında GÜNCELLEMELERİ ve EKLER'i yeniden etkinleştirin

  11. Tetikleyici veya cron'u, yeni bölümlerin oluşturulması ve işlevin, bölüme doğru yeni veriler atayacak şekilde güncellenmesi için ayarlayın. Kod örnekleri için bu makaleye bakın

  12. Old_master_backup öğesini sil


1
Güzel yazma. Bu gerçekten sorgularınızı daha hızlı hale getirirse ilginç olurdu. 10 milyon hala bölümleme hakkında düşünebileceğim pek fazla satır değil. Düşme performansınızın vacuum"işlemde boşta" oturumları nedeniyle yakalanmama veya önlenmeme neden olup olmadığını merak ediyorum .
a_horse_with_no_name

@a_horse_with_no_name, şimdiye kadar sorguları önemli ölçüde daha iyi :( Ben otomatik vakum ayarları vardır Heroku kullanmak ve büyük tablo için günlük şey görünmüyor kaydetmediği yönünde Tho içine daha bakacağız..
Evan Appleby

3. ve 5. adımdaki kesici uçlar new_master'a eklenmemeli ve postgresql'nin doğru alt tablo / bölümü seçmesine izin vermemeli mi?
pakman

@pakman doğru çocuğu atama işlevi adım 7'ye kadar eklenmez
Evan Appleby

4

Pg_pathman ( https://github.com/postgrespro/pg_pathman ) adında , bunu sizin için otomatik olarak yapacak yeni bir araç var .

Yani aşağıdaki gibi bir şey bunu yapardı.

SELECT create_range_partitions('master', 'dt_created', 
   '2015-01-01'::date, '1 day'::interval);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.