Gesaan dan Konteks yang Berkenaan
Jadual PostgreSQL customer_contacts mempunyai 200 juta baris. Percubaan semula (retries) dalam pengimport lama telah mencipta berbilang baris untuk (tenant_id, external_id) yang sama. Baris terkini mengikut updated_at harus dikekalkan; jika cap masa (timestamp) terikat sama, id terbesar menang. Kunci asing contact_events.contact_id mungkin merujuk kepada mana-mana salinan, jadi memadamkan rekod yang kalah sebelum menghalakan semula rujukan akan gagal atau menghilangkan hubungan tersebut.
customer_contacts(
id bigint primary key,
tenant_id bigint not null,
external_id text not null,
updated_at timestamptz not null,
payload jsonb not null
)
contact_events(
id bigint primary key,
contact_id bigint not null references customer_contacts(id),
event_type text not null,
created_at timestamptz not null
)Reka bentuk pembaikan PostgreSQL yang mengekalkan satu kenalan kanonikal deterministik bagi setiap kunci perniagaan, memelihara setiap peristiwa, memproses kerja pemadaman dalam transaksi bersempadan (bounded transactions), boleh diaudit dan dicuba semula, serta berakhir dengan keunikan yang dikuatkuasakan pangkalan data. Operasi membaca mesti kekal tersedia. Jeda penulisan akhir yang terkawal dibenarkan, tetapi tempohnya mesti diukur dan bukannya diandaikan.
200 juta baris dan jeda penulisan akhir adalah kekangan temu duga. Ini ialah soalan kejuruteraan data dan SQL yang sukar kerana fungsi tetingkap (window function) hanyalah mekanisme pemilihan; jawapan sebenar mesti turut melindungi integriti rujukan, konkurensi, rollback, kesihatan storan, dan operasi penulisan masa hadapan. Bahan temu duga SQL awam semasa terus menggunakan deduplikasi ROW_NUMBER dan pemutus seri yang deterministik sebagai corak temu duga yang eksplisit. Tiada atribusi syarikat yang boleh disahkan tersedia, jadi companyName adalah null.
Perkara yang Dinilai oleh Penemu Duga
Isyarat pertama ialah sama ada calon mentakrifkan "pendua" dan "terkini" sebelum menulis DELETE. Kunci perniagaan ialah (tenant_id, external_id), manakala id mengenal pasti baris fizikal. Susunan menyeluruh (total order) updated_at DESC, id DESC menjadikan tepat satu baris sebagai pemenang walaupun cap masa terikat sama. RANK boleh mengekalkan beberapa baris yang terikat; ROW_NUMBER menetapkan tepat satu kedudukan 1.
Isyarat kedua ialah kawalan ke atas sempadan pemadaman. Jawapan tahap produksi mempratonton kiraan dan sampel, membekukan pemetaan tak boleh ubah (immutable) daripada rekod kalah kepada pemenang, memelihara baris yang kalah atau snapshot yang boleh dipulihkan, dan memadam mengikut kunci utama. Mengira semula kedudukan secara berasingan semasa setiap kelompok boleh mengubah set sasaran semasa penulisan berterusan dan menjadikan larian sukar dijelaskan.
Isyarat ketiga ialah ketepatan rujukan dan transaksi. Setiap rujukan anak mesti beralih daripada rekod kalah kepada pemenang sebelum induknya dipadamkan. Peraturan keunikan jadual anak mungkin menyebabkan kemas kini itu bertembung, jadi "kemas kini semua kunci asing" adalah tidak lengkap tanpa inventori dan dasar konflik. Setiap kelompok haruslah idempoten, pendek, boleh diperhati (observable), dan selamat untuk dihentikan.
Isyarat terakhir ialah sama ada pembersihan menyelesaikan punca utama. Pertanyaan yang membuang pendua hari ini membiarkan pengimport esok bebas untuk menciptanya semula. Invarian yang tahan lama sepatutnya berada dalam kekangan unik (unique constraint) atau indeks unik, dengan kontrak null dan normalisasi yang eksplisit. Calon juga harus membezakan bukti ketepatan daripada bukti prestasi: sifar kunci pendua dan sifar rujukan yatim (orphan references) membuktikan semantik; EXPLAIN, masa menunggu kunci (lock waits), kadar WAL, kelengahan replikasi (replication lag), dead tuples, dan tingkah laku autovacuum menentukan kadar operasi yang boleh diterima.
Soalan untuk Dijelaskan Sebelum Menjawab
- Lajur manakah yang mentakrifkan entiti perniagaan? Di sini ia adalah pasangan tepat
(tenant_id, external_id). Penukaran huruf besar/kecil (case folding), pemangkasan ruang putih (whitespace trimming), atau normalisasi Unicode akan mentakrifkan kunci yang berbeza dan mesti dipersetujui sebelum pembaikan. - Baris manakah yang menang?
updated_atterbesar, kemudianidterbesar. Cap masa sahaja bukanlah susunan menyeluruh apabila dua baris terikat sama. - Bolehkah baris yang kalah mengandungi data unik? Jika muatan data (payloads) mesti digabungkan, "kekalkan yang terkini" adalah tidak mencukupi. Gesaan ini menganggap muatan data terkini sebagai berwibawa (authoritative) dan mengarkibkan rekod yang kalah untuk semakan.
- Jadual manakah yang merujuk kepada
customer_contacts.id? Buat inventori kunci asing yang diisytiharkan dan rujukan peringkat aplikasi.contact_eventsditunjukkan, tetapi larian sebenar mesti mencari katalog dan dokumentasi pemilikan. - Bolehkah penghalaan semula mencipta pendua anak? Jika anak mempunyai
UNIQUE(contact_id, event_type, created_at), dua peristiwa yang setara mungkin bertembung selepas penumpuan (convergence). Tentukan sama ada untuk menggabungkan, mengekalkan, atau menolaknya sebelum pengemaskinian. - Bolehkah operasi penulisan diteruskan semasa pembaikan? Kelompok sejarah boleh dijalankan semasa penulis yang dikawal diteruskan, tetapi imbasan pendua akhir dan penyerahan keunikan memerlukan sama ada kawalan penulisan peringkat pangkalan data yang disahkan atau jeda penulisan yang diukur. Konvensyen aplikasi yang dipintas oleh sesetengah penulis bukanlah satu jaminan.
- Apakah keperluan rollback? Simpan sandaran atau baris kalah yang diarkibkan berserta pemetaan anak yang asal sehingga pengesahan dan tetingkap rollback selesai. Pemetaan kalah-ke-menang sahaja tidak boleh membina semula muatan data yang dibuang.
- Berapakah beban yang boleh diterima? Saiz kelompok adalah alat kawalan, bukan pemalar. Mulakan dengan saiz kecil dan kawal kadar (throttle) berdasarkan masa menunggu kunci, p99 penulisan, WAL, kelengahan replikasi, dead tuples, dan kemajuan autovacuum.
- Bagaimanakah nilai null harus dikendalikan? Kedua-dua lajur kunci di sini adalah bukan null. Jika nilai null dibenarkan dan sepatutnya bertembung, keunikan PostgreSQL memerlukan
NULLS NOT DISTINCT; semantik unik lalai membenarkan berbilang nilai null.
Rangka Kerja Jawapan 30 Saat
"Saya akan membekukan peraturan perniagaan terlebih dahulu: pendua berkongsi (tenant_id, external_id), dan pemenang ialah updated_at terbesar, kemudian id terbesar. Saya akan mempratonton hasil yang disusun kedudukan, mengarkibkan baris yang kalah, dan menzahirkan satu pemetaan run_id tak boleh ubah daripada setiap rekod kalah kepada pemenangnya. Dalam kelompok idempoten yang pendek, saya akan merekod dan menghalakan semula semua rujukan anak, mengesahkan tiada lagi yang menyasarkan rekod kalah, kemudian memadamkan rekod kalah mengikut kunci utama. Saya akan menyelaraskan kiraan, sampel muatan data, kunci pendua, dan rujukan yatim selepas setiap fasa. Akhir sekali, di bawah kawalan penulis yang disahkan atau jeda penulisan yang diukur, saya akan menjalankan pembersihan delta terakhir dan membina indeks unik, kemudian memastikan semua operasi penulisan menggunakan kontrak kunci yang sama. Saya akan mengawal kadar pemprosesan berdasarkan metrik produksi dan mengekalkan arkib sehingga tetingkap rollback tamat."
Rangka kerja ini menyatakan pemilihan, urutan kebergantungan, kawalan pemadaman, penumpuan, dan pencegahan. SQL mengikut keputusan tersebut; ia tidak menggantikannya.
Jawapan Terperinci Langkah demi Langkah
Langkah 1: Tetapkan Invarian, Pemilikan, dan Sempadan Larian
Tulis pasca-syarat sebelum menyentuh data:
- tepat satu kenalan wujud untuk setiap
(tenant_id, external_id); - ID-nya adalah maksimum di bawah susunan
(updated_at, id); - setiap baris
contact_eventssedia ada masih wujud dan merujuk kepada pemenang tersebut; - tiada rujukan yang diisytiharkan atau pada peringkat aplikasi merujuk kepada ID yang dipadamkan;
- percubaan semula kelompok yang telah selesai mengubah sifar baris;
- penulisan baharu tidak boleh mencipta pendua kunci perniagaan yang lain.
Tetapkan run_id, pemilik, snapshot sumber atau sandaran, masa mula, versi kod, kursor kelompok, papan pemuka, ambang jeda, dan tarikh akhir rollback. Rekod kiraan baris pra-pembaikan, kiraan kunci perniagaan yang berbeza, kiraan kunci pendua, kiraan rekod kalah, dan kiraan peristiwa. Simpan jumlah kawalan bagi setiap penyewa supaya jumlah global tidak menyembunyikan kehilangan pada peringkat penyewa.
Langkah 2: Pratonton Kedudukan Deterministik
Mulakan dengan pertanyaan baca sahaja dan periksa kiraan serta perbezaan muatan data:
WITH ranked AS (
SELECT
id,
tenant_id,
external_id,
updated_at,
ROW_NUMBER() OVER (
PARTITION BY tenant_id, external_id
ORDER BY updated_at DESC, id DESC
) AS rn
FROM customer_contacts
)
SELECT tenant_id, external_id, COUNT(*) AS loser_count
FROM ranked
WHERE rn > 1
GROUP BY tenant_id, external_id
ORDER BY loser_count DESC, tenant_id, external_id
LIMIT 100;ROW_NUMBER adalah sesuai kerana kontrak memerlukan satu pemenang. RANK atau DENSE_RANK akan menetapkan kedudukan yang sama kepada nilai susunan yang sama melainkan id disertakan, dan ketiadaan pemutus seri yang deterministik akan membolehkan larian berulang memilih pemenang yang berbeza. DISTINCT hanya membuang baris output terpilih yang sama; ia tidak dapat mengekalkan baris pilihan yang lengkap mengikut peraturan perniagaan.
Langkah 3: Zahirkan Pemetaan Kalah-ke-Menang yang Tak Boleh Ubah
Jangan kira semula pemenang secara berasingan untuk pengemaskinian anak dan pemadaman. Zahirkan keputusan sekali sahaja di bawah sempadan larian yang dipersetujui. Coretan kod menggunakan :name untuk menandakan parameter ikatan (bind parameters) yang dibekalkan oleh pelaksana pembaikan:
WITH ranked AS (
SELECT
id,
tenant_id,
external_id,
ROW_NUMBER() OVER w AS rn,
FIRST_VALUE(id) OVER w AS winner_id
FROM customer_contacts
WINDOW w AS (
PARTITION BY tenant_id, external_id
ORDER BY updated_at DESC, id DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
)
)
INSERT INTO contact_dedup_map (
run_id, loser_id, winner_id, tenant_id, external_id
)
SELECT :run_id, id, winner_id, tenant_id, external_id
FROM ranked
WHERE rn > 1;contact_dedup_map harus mempunyai PRIMARY KEY(run_id, loser_id), semakan bahawa rekod kalah dan pemenang adalah berbeza, serta indeks pada (run_id, winner_id). Arkibkan baris kenalan yang kalah sepenuhnya di bawah run_id yang sama, atau kekalkan laluan pemulihan titik-dalam-masa (point-in-time restore) yang telah diuji. Sahkan bahawa setiap pemenang pemetaan wujud, tiada pemenang turut muncul sebagai rekod kalah dalam larian yang sama, setiap rekod kalah dipetakan sekali sahaja, dan saiz pemetaan menyamai kiraan rekod kalah yang diukur.
Bagi 200 juta baris, penyusunan kedudukan mungkin memerlukan imbasan dan pengisihan yang besar. Jalankan EXPLAIN pada data berbentuk produksi, sahkan ruang sementara serta impak beban kerja, dan jadualkan atau kawal kadar mengikut kesesuaian. Indeks penutup (covering index) mungkin membantu pelan yang diukur tetapi mahal untuk dibina dan tidak sepatutnya ditetapkan tanpa bukti.
Langkah 4: Halakan Semula Rujukan Sebelum Memadam Induk
Buat inventori setiap rujukan terlebih dahulu. Untuk contact_events, pelihara pemetaan asal dalam jadual audit dan kemudian kemas kini julat ID yang bersempadan:
UPDATE contact_events AS e
SET contact_id = m.winner_id
FROM contact_dedup_map AS m
WHERE m.run_id = :run_id
AND e.contact_id = m.loser_id
AND e.id > :after_event_id
AND e.id <= :batch_end_event_id;Percubaan semula adalah idempoten: selepas sesuatu peristiwa merujuk kepada pemenang, ia tidak lagi sepadan dengan m.loser_id. Lakukan komit pada setiap kelompok bersempadan, simpan kursor hanya selepas komit, dan selaraskan baris yang dikemas kini dengan baris audit rujukan. Jika kekangan keunikan anak bertembung, gunakan peraturan penggabungan atau penolakan yang dipersetujui sebelumnya; jangan lumpuhkan kekangan dan berharap keadaan akhir adalah sah.
Sebelum pemadaman induk, pertanyaan ini mesti mengembalikan sifar untuk setiap jadual anak:
SELECT COUNT(*) AS remaining_loser_references
FROM contact_events AS e
JOIN contact_dedup_map AS m
ON m.run_id = :run_id
AND m.loser_id = e.contact_id;Langkah 5: Padam Rekod Kalah dalam Kelompok Pendek yang Boleh Dimulakan Semula
Padam hanya ID daripada pemetaan yang dibekukan:
WITH batch AS (
SELECT loser_id
FROM contact_dedup_map
WHERE run_id = :run_id
AND loser_id > :after_loser_id
ORDER BY loser_id
LIMIT 10000
)
DELETE FROM customer_contacts AS c
USING batch AS b
WHERE c.id = b.loser_id
RETURNING c.id, c.tenant_id, c.external_id;10000 ialah nilai temu duga permulaan, bukan optimum sejagat. Laraskan saiz kelompok dan kelewatan daripada tempoh kunci yang diukur, p99, WAL, kelengahan replika, dead tuples, dan autovacuum. Output RETURNING menjadi bukti pemadaman. Jika proses terhenti secara tiba-tiba selepas komit tetapi sebelum kursor disimpan, memainkan semula kelompok tersebut hanya akan menemui ID yang telah dipadamkan dan kekal selamat.
Pemadaman PostgreSQL yang besar mencipta dead tuples dan WAL. Rancang VACUUM biasa dan pantau pembengkakan (bloat) jadual serta indeks; jangan gunakan VACUUM FULL secara lalai, yang menulis semula jadual dan mengambil kunci yang kuat. Operasi membaca kekal tersedia, tetapi ketepuan sumber masih boleh melanggar SLO perkhidmatan.
Langkah 6: Tumpukan Operasi Penulisan dan Pasang Penghalang yang Tahan Lama
Pembaikan pukal sahaja tidak dapat menangani sasaran yang sentiasa bergerak. Sebelum penumpuan akhir, gunakan satu kontrak penulisan yang menyusun siri penciptaan untuk kunci perniagaan dinormalisasi yang sama dan mengemas kini baris kanonikal dan bukannya memasukkan salinan lain. Sahkan setiap API, pengimport, tugasan (job), dan penulis pangkalan data langsung mengikutinya. Jika bukti itu tidak tersedia, jedakan operasi penulisan untuk pembersihan delta terakhir dan penyerahan indeks.
Selepas imbasan pendua terakhir mengembalikan sifar, cipta indeks unik di luar blok transaksi:
CREATE UNIQUE INDEX CONCURRENTLY customer_contacts_business_key_uidx
ON customer_contacts (tenant_id, external_id);CONCURRENTLY menghalang operasi penulisan daripada disekat oleh kunci binaan indeks biasa, tetapi ia melakukan lebih banyak kerja, tidak boleh dijalankan di dalam blok transaksi, dan boleh gagal pada pelanggaran keunikan sambil meninggalkan indeks yang tidak sah. Periksa kesahan, diagnosis perlumbaan data atau penulis, gugurkan indeks yang tidak sah mengikut runbook, dan cuba semula; jangan menganggap IF NOT EXISTS membuktikan kejayaan. Setelah sah, lampirkannya sebagai kekangan unik bernama dalam langkah DDL terkawal yang pendek jika tadbir urus skema memerlukan semantik kekangan.
Jika semua penulisan dijeda dan binaan biasa yang diukur muat dalam tetingkap yang dibenarkan, indeks unik bukan serentak adalah lebih mudah dan lebih pantas. Bentuk serentak (concurrent) ialah pertukaran yang tepat apabila penulisan terkawal yang disahkan perlu diteruskan; SLO dan latihan percubaan menentukan pilihan antara keduanya.
Penulis akhir harus menggunakan invarian keunikan pangkalan data dan dasar konflik yang eksplisit. Jangan anggap menangkap pelanggaran keunikan selepas pembersihan sebagai satu-satunya reka bentuk keidempotenan: tentukan sama ada kunci yang sama mengemas kini baris kanonikal, menolak muatan data yang berkonflik, atau dimasukkan ke dalam semakan.
Langkah 7: Sahkan, Perhati, dan Tutup Tetingkap Rollback
Selaraskan sekurang-kurangnya semakan ini:
- jumlah kenalan selepas pembaikan menyamai kenalan sebelum pembaikan tolak rekod kalah yang diarkibkan;
GROUP BY tenant_id, external_id HAVING COUNT(*) > 1mengembalikan sifar;- setiap pemenang pemetaan wujud dan setiap rekod kalah tiada;
- kiraan peristiwa anak tidak berubah, baki rujukan rekod kalah adalah sifar, dan rujukan yatim adalah sifar;
- sampel pemenang sepadan dengan peraturan
(updated_at DESC, id DESC)dan muatan data yang diarkibkan; - sisipan pendua ditolak atau mengikut dasar pengemaskinian yang diisytiharkan;
- menjalankan semula kelompok pemetaan, penghalaan semula, dan pemadaman yang telah selesai mengubah sifar baris.
Pantau kadar kelompok, ralat, masa menunggu kunci baris, p50/p95/p99 penulisan, bait WAL, kelengahan replikasi, dead tuples, kemajuan autovacuum, pertumbuhan pemetaan/arkib, baki rujukan rekod kalah, baki kunci pendua, dan kemajuan pembinaan indeks unik. Simpan data arkib dan audit rujukan sepanjang tetingkap rollback yang diuji. Hanya selepas itu keluarkan kawalan penulisan sementara dan jadual pembaikan mengikut dasar pengekalan.
Contoh Jawapan Berkualiti Tinggi
"Saya akan mentakrifkan pendua sebagai (tenant_id, external_id) yang sama dan memilih baris kanonikal mengikut updated_at DESC, id DESC; ID unik menjadikan keputusan seri adalah deterministik. Sebelum pemadaman, saya akan merekodkan kiraan garis dasar, memeriksa perbezaan muatan data, menginventori setiap rujukan, mengambil sandaran yang boleh dipulihkan, dan mencipta pemetaan run_id tak boleh ubah daripada setiap ID rekod kalah kepada pemenangnya menggunakan ROW_NUMBER dan FIRST_VALUE.
Saya akan mengarkibkan baris yang kalah dan pemetaan anak yang asal. Kemudian saya akan menghalakan semula contact_events dalam julat kunci utama yang pendek. Setiap kelompok melakukan komit sebelum kursornya maju, dan percubaan semula adalah idempoten kerana peristiwa yang telah dialihkan tidak lagi sepadan dengan ID rekod kalah. Kekangan anak kekal diaktifkan; sebarang pertembungan mengikut peraturan penggabungan atau penolakan yang eksplisit. Hanya selepas setiap jadual anak melaporkan sifar rujukan rekod kalah, barulah saya memadamkan kenalan mengikut ID daripada pemetaan yang dibekukan, menggunakan transaksi bersempadan dan RETURNING sebagai bukti.
Kadar operasi ditentukan oleh masa penguncian, p99 permintaan, WAL, kelengahan replikasi, dead tuples, dan autovacuum dan bukannya nombor kelompok yang tetap. Saya akan mengesahkan pengekalan kiraan baris, satu baris bagi setiap kunci perniagaan, tiada rekod kalah, tiada rujukan yatim, kiraan peristiwa yang tidak berubah, sampel pemenang deterministik, dan percubaan semula dengan sifar perubahan.
Bagi mengelakkan pengulangan, semua penulis mesti menumpu pada kunci perniagaan dinormalisasi yang sama. Di bawah kawalan penulis yang disahkan atau jeda yang diukur, saya akan melakukan pembersihan delta terakhir dan membina indeks unik serentak pada (tenant_id, external_id) di luar transaksi. Saya akan mengesahkan bahawa indeks itu sah kerana binaan serentak yang gagal boleh meninggalkan indeks yang tidak sah. Laluan penulisan kekal kemudiannya mengemas kini, menolak, atau menyemak konflik mengikut kontrak yang diisytiharkan. Arkib dikekalkan sehingga bukti rollback dan tetingkap pemerhatian selesai."
Jawapan ini menjadikan operasi yang tidak boleh dibalikkan bergantung pada get kelulusan yang eksplisit dan mengekalkan SQL, kunci asing, tingkah laku penulis, serta kekangan pangkalan data di bawah satu kontrak kunci perniagaan.
Kesilapan Biasa
- Menjalankan
DELETEserta-merta selepas menemui pendua → rujukan, muatan data, dan bukti rollback boleh hilang → pratonton, arkib, petakan, halakan semula, sahkan, kemudian padam. - Membahagikan (partitioning) mengikut
external_idsahaja → ID luaran yang sama dalam penyewa berbeza akan runtuh bersama → gunakan kunci perniagaan lengkap(tenant_id, external_id). - Menyusun hanya mengikut
updated_at→ cap masa yang terikat membolehkan pemenang berbeza dipilih pada larian lain → tambahidunik sebagai pemutus seri akhir. - Menggunakan
RANKdenganrn > 1→ baris terkini yang terikat boleh kedua-duanya menerima kedudukan 1 → gunakanROW_NUMBERdengan susunan menyeluruh apabila tepat satu pemenang diperlukan. - Mengira semula pemenang dalam setiap kelompok → perubahan serentak boleh mengalihkan sasaran dan memusnahkan kebolehauditan → bekukan satu pemetaan kalah-ke-menang berversi.
- Memadam induk sebelum menghalakan semula anak → kunci asing menolak pemadaman atau kaskad membuang sejarah yang sah → inventori dan migrasikan semua rujukan terlebih dahulu.
- Melumpuhkan kekangan demi kelajuan → pembaikan boleh mencipta rujukan yatim senyap atau pertembungan anak → kekalkan kekangan aktif dan tentukan pengendalian konflik.
- Menyatakan 10,000 sebagai saiz kelompok yang betul → perkakasan dan beban kerja menentukan daya pemprosesan yang selamat → mulakan dengan kecil dan sesuaikan daripada metrik SLO, WAL, kelengahan, dan vacuum.
- Berhenti selepas pembersihan → penulis yang bermasalah mencipta semula pendua → tumpukan penulis dan kuatkan keunikan dalam PostgreSQL.
- Mengandaikan
CREATE UNIQUE INDEX CONCURRENTLYsentiasa berjaya → keadaan perlumbaan atau pendua boleh meninggalkan indeks yang tidak sah → periksa kesahan dan ikuti prosedur pemulihan yang eksplisit. - Menggunakan
VACUUM FULLsebagai pembersihan rutin → ia menulis semula dan mengunci jadual secara kuat → pantau vacuum biasa dan pembengkakan, kemudian jadualkan penulisan semula luar biasa secara berasingan.
Soalan Susulan dan Maklum Balas
Susulan 1: Mengapa tidak memadam dengan satu CTE dan menyelesaikannya dalam satu transaksi tunggal?
Bagi jadual terpencil yang kecil tanpa rujukan dan penulis yang dijeda, CTE DELETE mungkin mencukupi. Pada 200 juta baris, satu transaksi boleh mengekalkan kunci dan versi baris lama, menjana lonjakan WAL yang besar, melengahkan replika, menyukarkan vacuum, dan mencipta peristiwa pemulihan jenis semua-atau-tiada (all-or-nothing). Pemetaan yang dibekukan berserta kelompok bersempadan memberikan kemajuan, kebolehauditan, kawalan kadar, dan kebolehmulaan semula. Gunakan pertanyaan yang lebih mudah hanya selepas membuktikan andaian skala dan kebergantungannya.
Susulan 2: Bagaimana jika dua baris berkongsi cap masa yang sama tetapi mempunyai muatan data yang berbeza?
Peraturan gesaan memilih id terbesar, jadi hasilnya adalah deterministik, tetapi determinisme tidak membuktikan muatan data adalah betul dari segi semantik. Ukur muatan data yang berkonflik sebelum pembaikan dan salurkan medan berisiko tinggi untuk semakan atau peraturan penggabungan khusus domain. Arkibkan kedua-dua baris. Jangan mereka peraturan penggabungan medan demi medan selepas pemadaman dimulakan.
Susulan 3: Bagaimana jika external_id boleh menjadi null?
Mula-mula tentukan sama ada null bermaksud "tidak diketahui dan bebas" atau satu nilai pendua. Kekangan dan indeks unik PostgreSQL menganggap nilai null sebagai berbeza secara lalai, jadi beberapa kunci null dibenarkan. Jika nilai null sepatutnya bertembung, gunakan NULLS NOT DISTINCT dan susun kedudukan dengan semantik yang sama; jika kenalan yang tidak diketahui adalah bebas, kekalkan tingkah laku lalai atau gunakan peraturan keunikan separa untuk ID bukan null. Pembersihan dan kekangan mesti sepadan.
Susulan 4: Bolehkah pembaikan dilakukan secara dalam talian sepenuhnya tanpa sebarang jeda penulisan?
Ya, hanya jika setiap penulis terbukti menggunakan peraturan penyusunan siri atau tempahan yang disokong pangkalan data untuk kunci perniagaan sebelum imbasan akhir. Kemudian pembersihan sejarah boleh menumpu dan indeks unik serentak boleh dibina. Jika pengimport legasi atau penulis langsung memintas kawalan tersebut, pendua baharu boleh muncul semasa pembinaan dan menyebabkannya gagal. Jadual bayangan (shadow table) dan pertukaran terkawal (cutover) ialah pilihan lain, tetapi ia menambah kerumitan penulisan dua kali (dual-write), pengisian semula (backfill), kunci asing, dan rollback, serta harus diwajarkan oleh SLO masa henti.
Susulan 5: Bagaimanakah anda mencari setiap kunci asing dan rujukan tersembunyi?
Buat pertanyaan pada katalog PostgreSQL untuk kunci asing yang hubungan rujukannya ialah customer_contacts, kemudian periksa view, trigger, pengguna CDC, indeks carian, eksport data, dan skema aplikasi untuk ID kenalan yang disimpan. Kunci asing yang diisytiharkan menyediakan bukti yang boleh dikuatkuasakan; dokumentasi pemilikan dan carian kod merangkumi rujukan peringkat aplikasi. Get pemadaman menyenaraikan setiap pengguna yang ditemui dan semakan penyelarasan masing-masing.
Susulan 6: Bagaimana jika menghalakan semula peristiwa melanggar kekangan unik anak?
Jeda sebelum mengemas kini. Pertembungan bermakna dua baris anak menjadi sama selepas ID induknya menumpu. Tentukan sama ada ia adalah peristiwa pendua, pemerhatian berbeza yang memerlukan kunci baharu, atau konflik data. Zahirkan kumpulan pertembungan, arkibkannya, dan gunakan peraturan penggabungan atau penolakan deterministik sebelum menghalakan semula. Menggugurkan kekangan anak mengubah semantik perniagaan dan bukanlah pelan pembaikan.
Susulan 7: Bagaimanakah anda membuktikan pemenang yang dipilih adalah yang terkini selepas jadual berubah semasa larian yang panjang?
Pemetaan membuktikan pemenang di bawah snapshot yang direkodkan atau sempadan lariannya, bukan di bawah penulisan masa hadapan yang tidak terhad. Kawal penulis supaya operasi kemudian mengemas kini baris kanonikal yang dipetakan, rekod versi sumber, dan lakukan imbasan delta akhir sebelum penyerahan keunikan. Jika peraturan perniagaan memerlukan kemas kini kemudian untuk menggantikan muatan data, kemas kini pemenang; jangan cipta kenalan fizikal yang lain. Nyatakan jaminan temporal dalam rekod audit.