

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

# Menghindari masalah kinerja dengan REPLICA IDENTITY FULL di PostgreSQL RDS untuk PostgreSQL
<a name="PostgreSQL.ReplicaIdentityFull"></a>

Replikasi logis PostgreSQL mengharuskan setiap tabel yang diterbitkan memiliki identitas *replika sehingga pelanggan dapat menemukan* baris yang benar untuk diperbarui atau dihapus. Secara default, kunci utama berfungsi sebagai identitas replika. Ketika tabel tidak memiliki kunci primer atau indeks unik yang sesuai, Anda dapat mengatur identitas replika`FULL`, yang menyebabkan PostgreSQL menggunakan seluruh baris sebagai kunci.

Sementara `REPLICA IDENTITY FULL` memecahkan masalah langsung mereplikasi tabel tanpa kunci utama, itu dapat memperkenalkan masalah kinerja yang serius pada penerbit dan pelanggan. Memahami dampak ini penting bagi siapa saja yang menggunakan replikasi logis dengan PostgreSQL RDS untuk PostgreSQL, termasuk fitur yang mengandalkan replikasi logis secara internal, seperti penerapan. blue/green 

## Mengapa REPLICA IDENTITY FULL menyebabkan masalah
<a name="PostgreSQL.ReplicaIdentityFull.WhyProblems"></a>

### Peningkatan volume WAL pada penerbit
<a name="PostgreSQL.ReplicaIdentityFull.WALVolume"></a>

`REPLICA IDENTITY`Pengaturan mengontrol informasi apa yang ditulis PostgreSQL ke log write-ahead (WAL) untuk mengidentifikasi baris yang diperbarui atau dihapus. Dengan identitas replika default (kunci utama), hanya kolom kunci yang dicatat sebagai identitas baris lama. Dengan`FULL`, PostgreSQL mencatat nilai-nilai lama *dari* setiap kolom untuk masing-masing dan. `UPDATE` `DELETE` Ini memiliki beberapa konsekuensi:
+ **Ukuran WAL meningkat secara signifikan.** Untuk pembaruan, ukuran setiap catatan WAL kira-kira dua kali lipat karena nilai lama dan baru untuk setiap kolom dicatat. Jika tabel berisi nilai besar yang disimpan menggunakan [TOAST](https://www.postgresql.org/docs/current/storage-toast.html), peningkatannya bisa jauh lebih besar karena nilai ToASted harus diambil dan ditulis ke WAL meskipun tidak dimodifikasi oleh pembaruan.
+ **Lebih tinggi I/O dan penggunaan CPU pada penerbit.** Penulisan WAL tambahan mengkonsumsi lebih banyak I/O bandwidth disk dan siklus CPU, terutama untuk beban kerja berat tulis.
+ **Lebih banyak data dikirim ke pelanggan.** Penerbit harus mengirimkan catatan WAL yang lebih besar melalui jaringan ke setiap pelanggan, meningkatkan konsumsi bandwidth.

### Pencarian baris lambat pada pelanggan
<a name="PostgreSQL.ReplicaIdentityFull.SlowLookups"></a>

Ketika pelanggan menerima catatan `UPDATE` atau `DELETE` log, ia harus menemukan baris yang cocok dalam salinan tabel lokalnya. Dengan`REPLICA IDENTITY FULL`, pelanggan mencari baris yang cocok dengan *semua* nilai kolom dari gambar baris lama.

Bagaimana PostgreSQL melakukan pencarian ini berbeda dengan versi utama PostgreSQL:
+ **Sebelum PostgreSQL 16**: Jika tabel tidak memiliki kunci utama dan tidak ada indeks identitas replika yang dikonfigurasi secara eksplisit, pelanggan melakukan pemindaian sekuensial dari seluruh tabel untuk setiap satu atau operasi. `UPDATE` `DELETE` Pada tabel besar, ini membuat kinerja penerapan sangat lambat.
+ **PostgreSQL 16 dan yang lebih baru**: Pelanggan dapat menggunakan btree atau indeks hash untuk pencarian baris, bahkan jika indeks tersebut tidak secara eksplisit ditetapkan sebagai identitas replika. Namun, pelanggan tidak mengevaluasi indeks mana yang paling efisien. [Mulai versi 16](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=89e46da5e), PostgreSQL memilih indeks [pertama yang cocok](https://www.postgresql.org/docs/18/logical-replication-publication.html#LOGICAL-REPLICATION-PUBLICATION-REPLICA-IDENTITY) ditemukannya, dan pengguna tidak memiliki kendali atas pilihan ini. Jika indeks yang dipilih memiliki selektivitas rendah (misalnya, indeks pada kolom boolean atau status), pencarian baris bisa hampir sama lambatnya dengan pemindaian sekuensial. Untuk alasan ini, mengandalkan pemilihan indeks implisit dengan tidak dapat diandalkan dan harus dianggap sebagai fallback, `REPLICA IDENTITY FULL` bukan konfigurasi yang disarankan.

### Bagaimana REPLICA IDENTITY FULL menyebabkan kelambatan replikasi
<a name="PostgreSQL.ReplicaIdentityFull.ReplicationLag"></a>

Dua masalah yang dijelaskan di atas — WAL yang lebih besar pada penerbit dan pencarian baris yang lebih lambat pada pelanggan — bergabung untuk menyebabkan kelambatan replikasi.

Secara default, replikasi logis PostgreSQL menggunakan proses *apply worker tunggal* per langganan untuk menerima perubahan dari penerbit dan menerapkannya ke tabel pelanggan. Proses apply worker berubah secara serial, satu baris pada satu waktu, dalam urutan komit. Ini berarti throughput pelanggan dibatasi oleh seberapa cepat ia dapat menerapkan setiap perubahan individu.

Ketika `REPLICA IDENTITY FULL` diatur di atas meja tanpa indeks yang sesuai, setiap `UPDATE` dan `DELETE` membutuhkan pemindaian sekuensial dari seluruh tabel untuk menemukan baris yang cocok. Jika tabel memiliki jutaan baris, masing-masing operasi ini dapat memakan waktu beberapa detik atau lebih lama. Hasilnya adalah masalah cascading:

1. **Penerbit menghasilkan perubahan lebih cepat daripada yang dapat diterapkan pelanggan.** Beban kerja penulisan penerbit berlanjut pada kecepatan normal, tetapi pekerja lamaran pelanggan terhambat pada pemindaian berurutan atau indeks selektif yang buruk untuk setiap pencarian baris.

1. **WAL terakumulasi pada penerbit dan dapat menghabiskan penyimpanan.** PostgreSQL tidak dapat merebut kembali segmen WAL sampai pelanggan mengonfirmasi telah menerapkannya. Ketika pelanggan tertinggal lebih jauh, penerbit mengakumulasi WAL pada disk. Pada , ini tampaknya tumbuh. `OldestReplicationSlotLag` CloudWatch Dalam kasus yang parah, ini dapat menghabiskan semua penyimpanan yang tersedia dan menyebabkan penerbit berhenti menerima penulisan.

1. **Kelambatan itu memperkuat diri sendiri.** Ketika pelanggan tertinggal, tabel pada pelanggan terus tumbuh dari sisipan yang direplikasi, membuat setiap pemindaian berurutan menjadi lebih lambat. Tanpa intervensi, lag tumbuh tanpa terikat.

Masalah ini sangat parah untuk tabel yang sering menerima `UPDATE` atau `DELETE` operasi. `INSERT`operasi tidak terpengaruh karena tidak memerlukan pencarian baris pada pelanggan.

**catatan**  
Dimulai dengan PostgreSQL 16, apply worker dapat menggunakan parallel apply untuk transaksi streaming besar, yang dapat membantu throughput. Namun, hambatan pencarian baris mendasar untuk `REPLICA IDENTITY FULL` tanpa indeks tetap ada, karena setiap baris individu masih memerlukan pemindaian untuk menemukannya.

### Dampak pada blue/green penerapan
<a name="PostgreSQL.ReplicaIdentityFull.BlueGreen"></a>

Blue/green penerapan di Amazon Amazon RDS menggunakan replikasi logis secara internal untuk menjaga lingkungan hijau disinkronkan dengan lingkungan biru dengan menyiapkan satu langganan per database. Proses penerapan replikasi logis di lingkungan hijau adalah *single-threaded*. Proses pekerja terapkan tunggal menerima semua perubahan dari lingkungan biru dan menerapkannya satu per satu, dalam urutan komit. Tidak ada penerapan paralel di jalur blue/green replikasi.

Desain ulir tunggal ini berarti kemampuan lingkungan hijau untuk mengikuti lingkungan biru sepenuhnya bergantung pada seberapa cepat seseorang pekerja terapkan dapat memproses setiap perubahan individu. Ketika tabel digunakan `REPLICA IDENTITY FULL` tanpa kunci utama atau indeks yang sesuai, dampaknya pada pekerja terapan bergantung pada versi PostgreSQL. Dalam versi sebelum 16, setiap `UPDATE` dan `DELETE` pada tabel tersebut memaksa pekerja apply untuk melakukan pemindaian sekuensial dari seluruh tabel untuk menemukan baris yang cocok. Di versi 16 dan yang lebih baru, PostgreSQL akan menggunakan indeks [yang sesuai](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=89e46da5e) jika tersedia, tetapi jika tidak ada indeks yang memenuhi syarat, pekerja terapkan masih kembali ke pemindaian berurutan. Sementara pekerja terapkan memindai tabel besar untuk satu baris, semua perubahan tertunda lainnya di semua tabel mengantri dan menunggu.

Konsekuensi untuk blue/green penyebaran sangat signifikan:
+ **Kelambatan replikasi tumbuh terus menerus.** Jika lingkungan biru menghasilkan lalu lintas tulis lebih cepat daripada yang dapat diproses oleh pekerja aplikasi tunggal, lingkungan hijau semakin tertinggal. Karena pekerja terapan berulir tunggal, tidak ada cara untuk memparalelkan pengejaran.
+ **Switchover dapat diblokir.** blue/green Peralihan membutuhkan lingkungan hijau untuk sepenuhnya disinkronkan dengan lingkungan biru. Jika kelambatan replikasi terlalu tinggi, peralihan tidak dapat diselesaikan dalam periode batas waktu.
+ **Lingkungan hijau mungkin tidak akan pernah mengejar ketinggalan.** Untuk beban kerja berat tulis dengan tabel besar menggunakan `REPLICA IDENTITY FULL` dan tidak ada indeks, tingkat penerapan bisa sangat lambat sehingga lingkungan hijau tertinggal secara permanen, membuat peralihan tidak mungkin dilakukan tanpa terlebih dahulu menyelesaikan konfigurasi identitas replika.
+ **WAL terakumulasi di lingkungan biru.** Sementara lingkungan hijau berada di belakang, lingkungan biru mempertahankan segmen WAL untuk slot replikasi. Hal ini meningkatkan penggunaan penyimpanan pada lingkungan biru (produksi) dan dapat mempengaruhi kinerja produksi.

Untuk menghindari masalah ini, pastikan bahwa semua tabel memiliki kunci utama atau indeks unik yang sesuai yang secara eksplisit dikonfigurasi sebagai identitas replika yang digunakan `ALTER TABLE ... REPLICA IDENTITY USING INDEX` *sebelum membuat penerapan*. blue/green Jangan mengandalkan `REPLICA IDENTITY FULL` pemilihan indeks implisit di PostgreSQL 16\+, karena pelanggan mungkin memilih indeks selektif yang buruk atau kembali ke pemindaian berurutan. Uji penerapan dengan beban kerja tulis yang representatif untuk mengonfirmasi bahwa lingkungan hijau dapat mengikuti.

Untuk informasi selengkapnya tentang batasan blue/green penerapan, lihat[Keterbatasan dan pertimbangan untuk penerapan Amazon RDS Amazon blue/green](blue-green-deployments-considerations.md). Untuk praktik terbaik, lihat [RDS untuk PostgreSQL praktik terbaik untuk penerapan blue/green](blue-green-deployments-best-practices.md#blue-green-deployments-best-practices-postgres).

## Cara mengidentifikasi tabel menggunakan REPLICA IDENTITY FULL
<a name="PostgreSQL.ReplicaIdentityFull.Identify"></a>

Jalankan kueri berikut untuk menemukan semua tabel dengan`REPLICA IDENTITY FULL`:

```
SELECT n.nspname AS schema, c.relname AS table_name, c.relreplident
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
  AND c.relreplident = 'f'
  AND n.nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY n.nspname, c.relname;
```

Nilai `relreplident` kolom adalah:
+ `d`— default (kunci utama)
+ `n`- tidak ada
+ `f`— penuh (seluruh baris)
+ `i`— indeks tertentu

## Solusi dan praktik terbaik
<a name="PostgreSQL.ReplicaIdentityFull.Workarounds"></a>

### Tambahkan kunci utama sedapat mungkin
<a name="PostgreSQL.ReplicaIdentityFull.AddPrimaryKey"></a>

Solusi paling efektif adalah menambahkan kunci utama ke tabel yang tidak memiliki satu. Ketika kunci utama ada, PostgreSQL menggunakannya sebagai identitas replika default, yang menyediakan pencarian baris yang efisien pada pelanggan dan meminimalkan overhead WAL pada penerbit.

```
ALTER TABLE my_table ADD COLUMN id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY;
```

**penting**  
Pernyataan ini memperoleh `ACCESS EXCLUSIVE` kunci dan menulis ulang seluruh tabel, karena ekspresi nilai default menggunakan `nextval()` yang volatile. Semua membaca dan menulis ke tabel diblokir selama durasi penulisan ulang. Pada tabel besar, ini dapat menyebabkan downtime yang signifikan. Rencanakan perubahan ini selama jendela pemeliharaan atau pertimbangkan pendekatan alternatif seperti membuat kolom sebagai nullable terlebih dahulu, kemudian mengisi ulang dan menambahkan kendala dalam langkah terpisah.

Jika menambahkan kunci primer tidak layak karena kendala aplikasi, pertimbangkan untuk menambahkan indeks unik pada sekumpulan `NOT NULL` kolom dan mengaturnya sebagai identitas replika:

```
CREATE UNIQUE INDEX my_table_replica_idx ON my_table (col1, col2);
ALTER TABLE my_table REPLICA IDENTITY USING INDEX my_table_replica_idx;
```

**catatan**  
Untuk menghindari pemblokiran penulisan saat indeks dibuat, gunakan [https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY](https://www.postgresql.org/docs/current/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY)klausa: `CREATE UNIQUE INDEX CONCURRENTLY my_table_replica_idx ON my_table (col1, col2);`

**catatan**  
Indeks yang digunakan untuk identitas replika harus unik, tidak boleh sebagian, tidak boleh ditangguhkan, dan harus menyertakan hanya kolom dengan batasan. `NOT NULL`

### Jangan mengandalkan pemilihan indeks implisit (PostgreSQL 16\+)
<a name="PostgreSQL.ReplicaIdentityFull.SubscriberIndexes"></a>

Dimulai dengan PostgreSQL 16, pekerja aplikasi pelanggan dapat menggunakan btree atau indeks hash untuk pencarian baris ketika identitas replika disetel `FULL` ke, bahkan jika indeks tersebut tidak secara eksplisit dikonfigurasi sebagai identitas replika. Meskipun ini mencegah pemindaian berurutan dalam beberapa kasus, mengandalkan perilaku implisit ini adalah anti-pola karena alasan berikut:
+ **Anda tidak mengontrol indeks mana yang dipilih.** PostgreSQL memilih indeks kualifikasi pertama yang ditemukan dalam urutan katalog, bukan yang paling selektif atau efisien. Jika tabel memiliki beberapa indeks kualifikasi, yang dipilih mungkin memiliki selektivitas rendah, yang menyebabkan kinerja pencarian buruk.
+ **Perilakunya rapuh.** Menambahkan, menjatuhkan, atau membangun kembali indeks dapat mengubah indeks mana yang digunakan pekerja aplikasi, yang berpotensi menyebabkan regresi kinerja yang tidak terduga dalam replikasi.
+ **Ini menutupi masalah yang mendasarinya.** Tabel tanpa kunci primer atau identitas replika eksplisit secara inheren berisiko untuk replikasi logis. Mengandalkan pemilihan indeks implisit menunda masalah daripada menyelesaikannya.

Sebagai gantinya, konfigurasikan identitas replika secara eksplisit untuk setiap tabel yang direplikasi:
+ **Opsi terbaik:** Tambahkan kunci utama. Ini adalah identitas replika yang paling andal dan efisien.
+ **Alternatif:** Gunakan `ALTER TABLE ... REPLICA IDENTITY USING INDEX` untuk menunjuk indeks unik, non-parsi, tidak dapat ditangguhkan tertentu dengan hanya kolom. `NOT NULL` Ini memberi Anda kontrol eksplisit atas kolom mana yang digunakan untuk identifikasi baris.

Cadangan `REPLICA IDENTITY FULL` hanya untuk tabel di mana tidak ada opsi yang layak, dan pahami bahwa kinerja bergantung pada faktor-faktor di luar kendali langsung Anda.

### Memantau kelambatan replikasi
<a name="PostgreSQL.ReplicaIdentityFull.MonitorLag"></a>

Saat menggunakan`REPLICA IDENTITY FULL`, pantau kelambatan replikasi dengan cermat untuk mendeteksi pelambatan penerapan pelanggan sebelum menjadi kritis.

**Pada penerbit**, periksa jeda antara posisi WAL saat ini dan apa yang telah dikonfirmasi pelanggan:

```
SELECT slot_name, confirmed_flush_lsn, pg_current_wal_lsn(),
       (pg_current_wal_lsn() - confirmed_flush_lsn) AS lag_bytes
FROM pg_replication_slots
WHERE slot_type = 'logical';
```

`lag_bytes`Nilai yang terus meningkat menunjukkan pelanggan tertinggal. `pg_stat_replication_slots`Tampilan memberikan statistik tambahan tentang penggunaan setiap slot replikasi.

**Pada pelanggan**, `pg_stat_subscription` tampilan menunjukkan status setiap pekerja yang mendaftar, termasuk lokasi WAL terakhir yang diterima dan dilaporkan:

```
SELECT subname, received_lsn, latest_end_lsn,
       last_msg_send_time, last_msg_receipt_time
FROM pg_stat_subscription;
```

**catatan**  
Pada PostgreSQL 16 dan yang lebih baru, Anda juga dapat `worker_type` memilih untuk membedakan antara pekerja aplikasi utama dan pekerja aplikasi paralel.

Kesenjangan besar antara `received_lsn` dan`latest_end_lsn`, atau stempel waktu basi`last_msg_send_time`, dapat mengindikasikan pekerja lamaran sedang berjuang untuk mengikutinya. `pg_stat_subscription_stats`Tampilan juga melacak kesalahan dan konflik terapan yang mungkin menyebabkan kelambatan.

**Untuk , Anda juga dapat memantau metrik, yang melacak lag** dalam byte dari slot replikasi paling `OldestReplicationSlotLag` CloudWatch belakang. Nilai yang meningkat adalah tanda peringatan dini kelambatan replikasi. 

**Memeriksa tabel mana yang mungkin menggunakan indeks sub-optimal selama penerapan**

Pada pelanggan, Anda dapat mengidentifikasi tabel tempat pekerja terapkan melakukan pembacaan heap yang berlebihan, yang dapat menunjukkan bahwa tabel tidak memiliki indeks yang efisien untuk pencarian baris selama penerapan. Jalankan kueri berikut pada pelanggan:

```
SELECT relname, heap_blks_read, heap_blks_hit,
       idx_blks_read, idx_blks_hit,
       heap_blks_read + heap_blks_hit AS total_heap_access
FROM pg_statio_user_tables
WHERE heap_blks_read > 0
ORDER BY heap_blks_read DESC
LIMIT 10;
```

Tabel dengan `heap_blks_read` nilai relatif tinggi `idx_blks_read` dapat menunjukkan bahwa pekerja terapan tidak menggunakan indeks yang efisien untuk menemukan baris `UPDATE` dan `DELETE` operasi. Ini adalah sumber umum kelambatan replikasi saat `REPLICA IDENTITY FULL` digunakan.

**catatan**  
Kueri ini membutuhkan [https://www.postgresql.org/docs/current/runtime-config-statistics.html#GUC-TRACK-COUNTS](https://www.postgresql.org/docs/current/runtime-config-statistics.html#GUC-TRACK-COUNTS)parameter untuk diaktifkan pada pelanggan. Parameter ini aktif secara default.

### Evaluasi apakah REPLICA IDENTITY FULL diperlukan
<a name="PostgreSQL.ReplicaIdentityFull.Evaluate"></a>

Sebelum mengatur`REPLICA IDENTITY FULL`, pertimbangkan apakah Anda benar-benar membutuhkannya. Alasan umum untuk menggunakannya meliputi:
+ Tabel tidak memiliki kunci utama atau indeks unik.
+ Anda memerlukan gambar baris sebelum gambar lengkap untuk konsumen change data capture (CDC).
+ Anda memerlukan nilai kolom ToAasted yang disertakan dalam peristiwa replikasi untuk pembaruan yang tidak mengubah kolom tersebut.

Jika satu-satunya alasan Anda adalah kurangnya kunci utama, menambahkannya hampir selalu merupakan jalan yang lebih baik. Jika Anda memerlukan gambar sebelumnya lengkap untuk CDC, pertimbangkan apakah konsumen CDC Anda dapat merekonstruksi baris penuh dengan mempertahankan status secara eksternal, yang menghindari WAL dan overhead pelanggan. `REPLICA IDENTITY FULL`

## Ringkasan rekomendasi
<a name="PostgreSQL.ReplicaIdentityFull.Summary"></a>


| Skenario | Rekomendasi | 
| --- | --- | 
| Tabel memiliki kunci utama | Gunakan identitas replika default (tidak perlu tindakan) | 
| Tabel memiliki indeks NOT NULL yang unik | Tetapkan indeks itu sebagai identitas replika dengan ALTER TABLE ... REPLICA IDENTITY USING INDEX | 
| Tabel tidak memiliki kunci yang cocok (PostgreSQL 16\+) | Tambahkan kunci primer atau indeks unik. Menggunakan REPLICA IDENTITY FULL dengan pemilihan indeks implisit tidak dapat diandalkan dan harus menjadi pilihan terakhir | 
| Tabel tidak memiliki kunci yang cocok (sebelum PostgreSQL 16) | Tambahkan kunci primer atau indeks unik; hindari REPLICA IDENTITY FULL jika memungkinkan | 
| Write-heavy beban kerja dengan kolom large/TOASTed  | Hindari REPLICA IDENTITY FULL karena amplifikasi volume WAL | 