Topik temu duga representatif

Temu Duga Backend: Bagaimana Anda Memastikan Pangkalan Data dan Cache Kekal Konsisten?

BackendSukar
Pasukan Editorial Offer.ccDiterbitkan Dikemas kini

Soalan

PostgreSQL ialah sumber kebenaran (source of truth), dan Redis mencache data paparan produk. Sistem ini mengendalikan 20,000 bacaan dan 500 penulisan sesaat merentasi kira-kira 2,000 produk aktif, dengan keusangan maksimum 5 saat dibenarkan untuk data paparan. Reka bentuk laluan baca, tulis, dan pembatalan, kendalikan pengisian serentak serta kelengahan replika, dan kenal pasti data yang tidak boleh bergantung pada cache ini.

Prompt dan Konteks Berkenaan

PostgreSQL ialah sumber kebenaran bagi data produk. Redis menyimpan snapshot paparan yang menggunakan kunci product_id. Sistem ini mengendalikan kira-kira 20,000 bacaan dan 500 penulisan sesaat merentasi sekitar 2,000 produk aktif. Nama, imej, dan teks paparan boleh menjadi lapuk selama maksimum 5 saat. Penolakan inventori, harga checkout, kebenaran (permissions), dan baki akaun berada di luar kontrak cache tersebut kerana ia menentukan ketepatan perniagaan (business correctness).

Reka bentuk laluan baca, kemas kini, dan pembatalan cache-aside. Rangkumi kes-kes ini:

  • pangkalan data melakukan komit, tetapi proses ranap sebelum memadamkan entri cache;
  • pembaca memperoleh versi pangkalan data yang lama dan hanya menyelesaikan pengisiannya selepas penulis membatalkan cache;
  • pengisian membaca data yang lengah daripada replika pangkalan data tak segerak (asynchronous);
  • peristiwa pembatalan berulang (duplicated), bertukar urutan (reordered), atau tertunggak (backlogged);
  • Redis tidak tersedia buat sementara waktu;
  • pasukan produk mahukan "keusangan maksimum 5 saat" menjadi kontrak yang dipantau dan diuji.

Dayaproses (throughput), bilangan kunci aktif, dan belanjawan 5 saat ialah andaian temu duga, bukannya tetapan sejagat. Bahan temu duga backend dan caching senior awam tahun 2026 semasa secara eksplisit merangkumi cache-aside, pembatalan selepas penulisan, dan kekonsistenan cache. Kemahiran utama ialah protokol backend dan semantik kegagalan selepas komit pangkalan data, jadi kategorinya ialah backend. Ini berbeza daripada pencegahan cache stampede apabila kunci hangat tamat tempoh: kawalan stampede mengehadkan bacaan sumber secara serentak, manakala masalah ini menanyakan bila nilai lama boleh terus wujud selepas penulisan, mengapa ia kekal, dan berapa lama ia boleh berada di situ.

Perkara yang Dinilai oleh Penemu Duga

Pertama, bolehkah calon mentakrifkan sasaran kekonsistenan sebelum memilih corak? "Pangkalan data dan cache sentiasa konsisten" tidak menyatakan hasil bacaan mana yang sah di sisi undang-undang sistem. Jawapan yang mantap memisahkan keusangan berbatas 5 saat untuk data paparan, read-your-writes untuk pemanggil yang melakukan kemas kini, dan bacaan berwibawa (authoritative reads) untuk inventori atau kebenaran. Kontrak-kontrak tersebut memerlukan laluan yang berbeza.

Kedua, bolehkah mereka menerangkan urutan penulisan? Laluan penulisan cache-aside yang biasa mengkomit pangkalan data dan kemudian memadamkan entri cache. Memadamkan dahulu mewujudkan keadaan perlumbaan (race condition) yang jelas: pembaca mengalami miss di antara kedua-dua operasi tersebut, mengambil nilai pangkalan data yang lama, dan meletakkannya kembali ke dalam cache. Pangkalan-data-dahulu masih bukan dwi-tulis atomik (atomic dual write). Terdapat tetingkap komit-ke-padam yang singkat, dan pemadaman yang gagal boleh meninggalkan entri lama sehingga TTL-nya tamat.

Ketiga, bolehkah mereka melakarkan garis masa pengisian lapuk yang lewat (late stale-fill)? Pembaca mungkin mengambil versi 41 sebelum kemas kini pangkalan data. Penulis kemudiannya mengkomit versi 42 dan memadamkan cache, dan selepas itu pembaca lama meletakkan kembali versi 41. Menambah versi pada nilai yang dicache sahaja tidak selalu mencukupi. Selepas pemadaman, tiada versi yang dicache untuk dibandingkan, jadi peraturan seperti "tulis apabila versi sumber sekurang-kurangnya versi yang dicache" akan menerima 41. Reka bentuk memerlukan version fence yang kekal wujud selepas pemadaman nilai, pajakan pengisian (fill lease) yang dibatalkan oleh operasi tulis, atau semakan versi berwibawa lain sebelum pengisian.

Keempat, bolehkah mereka membezakan pembatalan yang boleh dipercayai daripada batas masa yang terhingga? Transactional outbox atau pemintasan data perubahan (change data capture - CDC) merapatkan jurang di mana pangkalan data mengkomit tetapi tiada mesej pembatalan direkodkan secara tahan lasak (durably). Penghantaran sekurang-kurangnya sekali (at-least-once delivery) dan pemadaman idempoten bertolak ansur dengan duplikasi. Percubaan semula (retries) mewujudkan pemprosesan akhirnya (eventual processing); ia tidak membuktikan penyelesaian dalam masa 5 saat. Keusangan berbatas memerlukan tambahan TTL tegar (hard TTL), get kelengahan pembatalan (invalidation-lag gate), atau peraturan yang memintas cache sebaik sahaja talian paip (pipeline) melebihi belanjawannya.

Kelima, bolehkah mereka mengendalikan sumber kebenaran, replika pangkalan data, dan pengesahan operasi? Komit pangkalan data yang berjaya mencipta fakta baharu. Mengisi daripada replika yang lengah boleh memperkenalkan semula nilai lama selepas pembatalan yang betul. Jawapan yang mantap mengehadkan sumber pengisian, memerhatikan versi peristiwa dan cache, serta menguji garis masa keserentakan terkawal dan kegagalan dan bukannya sekadar melaporkan kadar hit cache.

Soalan untuk Dijelaskan Sebelum Menjawab

  • Apakah kontrak kekonsistenan? Jika keusangan sehingga 5 saat dibenarkan, pembatalan tak segerak berserta batas masa tegar boleh berfungsi. Jika read-your-writes diperlukan, permintaan seterusnya daripada penulis mesti memintas cache secara ringkas atau membawa versi minimum. Jika tiada bacaan lapuk dibenarkan, baca stor berwibawa atau gunakan laluan storan dengan kontrak kekonsistenan yang diperlukan.
  • Medan manakah yang membenarkan tindakan yang tidak boleh diubah (irreversible actions)? Nama paparan atau imej mungkin lapuk. Pengesahan inventori, kelayakan diskaun, kebenaran, baki, dan harga yang dicaj tidak boleh menganggap snapshot yang dicache sebagai autoriti. Jika medan-medan tersebut berkongsi satu objek, pisahkan kontrak bacaan atau baca semula keadaan berwibawa semasa tindakan kritikal.
  • Bilakah jam 5 saat bermula? Dalam prompt ini, ia bermula pada masa komit pangkalan data. Jika ia bermula apabila nilai memasuki Redis, replika yang sudah 4 saat ketinggalan boleh dicache selama 5 saat lagi, menjadikan usia data sebenar hampir 9 saat.
  • Adakah satu perkhidmatan mengawal setiap laluan penulisan? Tugas kelompok (batch jobs), alatan pentadbir, dan perkhidmatan lain boleh memintas DEL peringkat API. Letakkan rekod pembatalan dalam transaksi pangkalan data yang sama atau tangkap setiap perubahan yang disokong daripada log pangkalan data.
  • Adakah miss diisi daripada primer atau replika? Tetapkan batas kelengahan replika dan sama ada read-your-writes peringkat sesi wujud. Jika replika tidak dapat dibuktikan terkini dalam belanjawan, pengisian pertama pasca-pembatalan harus menggunakan primer atau memerlukan hasil yang sekurang-kurangnya sebaharu versi minimum pemanggil.
  • Berapa banyak trafik pengisian yang boleh diserap oleh pangkalan data? Dengan TTL 5 saat, 2,000 kunci aktif yang tamat tempoh secara seragam dan kesemuanya dibaca menghasilkan purata kira-kira 2000 / 5 = 400 pengisian sesaat. Pengagihan akses, jitter, dan penggabungan permintaan (request coalescing) mengubah nilai sebenar. Jika pangkalan data kekurangan belanjawan tersebut, memendekkan TTL tidak semestinya memenuhi kekonsistenan secara ajaib.
  • Apabila Redis tidak tersedia, adakah kesegaran atau ketersediaan lebih penting? Data paparan boleh mengalami penurunan prestasi (degrade) dalam batas nilai lapuk yang jelas. Medan berwibawa mesti menggunakan laluan sumber yang dilindungi. Jika pangkalan data tidak dapat menyerap semua miss, gunakan had kadar (rate limits), bulkhead, dan kegagalan eksplisit dan bukannya sandaran (fallback) tanpa batas.

Kerangka Jawapan 30 Saat

"Mula-mula saya mentakrifkan kontrak mengikut jenis data. Data paparan produk boleh lapuk maksimum 5 saat dari masa komit pangkalan data, manakala inventori dan kebenaran sentiasa menggunakan laluan berwibawa. Operasi baca menyemak Redis, menggabungkan miss kunci yang sama, mengambil baris berversi daripada sumber yang memenuhi keperluan kesegaran, dan mengisi cache secara bersyarat. Operasi tulis mengemas kini baris perniagaan dan memasukkan rekod outbox yang mengandungi versi baris dalam satu transaksi PostgreSQL. Selepas komit, ia mencuba pemadaman cache pantas, manakala laluan outbox atau CDC menyediakan pembaikan yang boleh dicuba semula.

Saya mengkomit pangkalan data sebelum memadamkan cache. Untuk menghalang bacaan lama daripada mengisi selepas pemadaman tersebut, sesuatu miss menangkap generasi pengisian; pembatalan memajukan version fence dan memadamkan nilai; pengisian hanya berjaya jika generasi tidak berubah dan versi sumber memenuhi keperluan fence. Percubaan semula akhirnya tidak membuktikan batas 5 saat, jadi cache mempunyai TTL tegar dalam belanjawan, dan operasi baca memintasnya apabila kelengahan pembatalan menghampiri had. Pengisian pasca-pembatalan menggunakan primer apabila kelengahan replika tidak dapat memenuhi kontrak. Saya mengesahkan reka bentuk dengan ujian kegagalan komit-kemudian-ranap, pengisian lewat, peristiwa berulang dan bertukar urutan, serta kelengahan replika, dengan menegaskan usia lapuk, versi monotonik, dan beban pangkalan data."

Panduan Mendalam Langkah demi Langkah

Langkah 1: Tukarkan kekonsistenan kepada kontrak hasil bacaan

Pisahkan data mengikut akibat perniagaan sebelum memilih corak cache:

LaluanHasil yang sahLaluan bacaan yang disyorkan
Paparan produkMaksimum 5 saat lapuk dari komit pangkalan dataCache-aside Redis, TTL tegar, dan get kelengahan pembatalan
Bacaan seterusnya oleh penulisSekurang-kurangnya versi yang baru dikomit oleh pemanggil tersebutBawa min_version; gunakan primer jika cache lebih lama
Inventori, kebenaran, baki, checkoutKeputusan mesti menggunakan keadaan berwibawa semasaPintas cache paparan dan sahkan dalam transaksi atau perkhidmatan berwibawa

Klasifikasi ini memacu keseluruhan reka bentuk yang selebihnya. Cache mempercepatkan salinan yang boleh dibina semula. Ia tidak boleh membenarkan penolakan inventori menggunakan nilai yang boleh ditangguhkan, disingkirkan, atau hilang. Kadar hit 99.9% tidak menceritakan apa-apa tentang sama ada baki 0.1% menghasilkan lebihan jualan (overselling) atau akses tanpa kebenaran.

Setiap entri cache memerlukan sekurang-kurangnya versi sumber dan maklumat pemasaan. Berikut ialah struktur pseudo, bukan jenis yang boleh dilaksanakan dalam bahasa tertentu:

text
ProductCacheEntry {
  value
  source_version
  source_committed_at
  cached_at
}

source_committed_at mengukur usia lapuk sebenar; cached_at hanya memberitahu bila Redis menerima nilai tersebut. Versi sumber boleh berupa versi baris yang meningkat secara monotonik, jujukan komit, atau versi domain lain dengan urutan yang ditakrifkan. Cap masa jam dinding (wall-clock timestamp) sahaja merupakan bukti susunan yang lemah jika nilai boleh terikat atau jam boleh terpesong (drift).

Langkah 2: Wujudkan laluan asas cache-aside

Laluan baca hanya mengembalikan hit cache apabila ia memenuhi kedua-dua versi minimum pemanggil dan belanjawan usia lapuk. Apabila berlaku miss, gabungkan pengisian untuk product_id yang sama supaya 20,000 bacaan tidak mencecah pangkalan data secara serentak. Ambil baris berversi, kemudian cuba penulisan Redis bersyarat. Berikut ialah pseudokod aliran:

text
read(product_id, min_version = none):
  entry = cache.get(product_id)
  if entry satisfies age_budget and min_version:
    return entry.value

  return singleflight(product_id):
    recheck cache
    row = read_authoritative_version(product_id)
    conditional_fill(product_id, row)
    return row.value

Laluan penulisan mengemas kini baris perniagaan dan memasukkan rekod outbox dalam transaksi PostgreSQL yang sama. Hanya COMMIT yang berjaya menjadikan perubahan perniagaan kelihatan kepada transaksi lain dan tahan lasak. Aplikasi kemudiannya melakukan percubaan pembatalan pantas. Geganti (relay) atau pengguna CDC yang berasingan memproses peristiwa pembatalan tahan lasak untuk percubaan semula dan pembaikan. Pseudokod laluan penulisan ialah:

text
transaction:
  row = update product and increment source_version
  insert outbox(product_id, source_version, committed_at)
commit

best_effort_invalidate(product_id, source_version)
return committed source_version

Pembatalan langsung mengurangkan tetingkap kes biasa. Outbox merapatkan jurang kehilangan mesej apabila proses ranap selepas komit. Jika kedua-dua laluan mengendalikan perubahan yang sama, pemadaman mestilah idempoten: menerima versi sumber yang sama sekali lagi tidak boleh menyebabkan kesan sampingan perniagaan yang baharu.

Langkah 3: Analisis ketiga-tiga tetingkap perlumbaan secara eksplisit

Tetingkap 1: antara komit pangkalan data dan pemadaman cache.

text
W: COMMIT version 42
R: read cached version 41
W: DEL cache key

Pembaca melihat versi 41 secara ringkas, yang sah di bawah keusangan berbatas. Jika tiada hasil lapuk yang boleh diterima, menunggu operasi cache sebelum mengembalikan respons tulis masih tidak menyelesaikan setiap kekaburan rangkaian. Kontrak yang lebih jelas menghantar bacaan kritikal ke sumber kebenaran atau membiarkan pemanggil memerlukan min_version=42.

Tetingkap 2: pangkalan data melakukan komit tetapi pembatalan tidak berjalan.

text
W: COMMIT version 42 plus outbox record
W: process crashes before DEL
R: cache still contains version 41
relay: retries invalidation for version 42

Tugasan dalam memori yang dicipta selepas COMMIT boleh hilang. Transactional outbox mengkomit perubahan perniagaan dan kewajipan untuk membatalkan secara bersama. Geganti mungkin menghantar semula, manakala pengguna memadam mengikut kunci dan merekodkan versi yang dikendalikan. Jika geganti terhenti, TTL tegar atau get kesegaran menguatkuasakan batas 5 saat.

Tetingkap 3: bacaan lama mengisi selepas pembatalan.

text
R: cache miss; captures generation 7
R: reads database version 41
W: commits version 42
W: advances generation to 8 and deletes cache value
R: tries to fill version 41 with generation 7; rejected

Ini merupakan perlumbaan yang sering terlepas pandang. Jika reka bentuk hanya memadamkan nilai, maka version 41 >= no version berjaya dan menghidupkan semula data lapuk. Satu penyelesaian memisahkan nilai daripada fencenya. Pembatalan memajukan generasi jangka pendek atau kunci versi-sumber-minimum. Skrip Redis menyemak secara atomik bahawa generasi pengisian masih sepadan dengan generasi yang ditangkap pada masa miss dan bahawa versi sumber memenuhi minimum sebelum menulis nilai. Dalam Redis Cluster, kunci nilai dan fence mesti menggunakan slot cincangan (hash slot) yang sama supaya skrip boleh mengakses kedua-duanya secara atomik. Pelaksanaan lain mengeluarkan pajakan miss (miss lease) yang dibatalkan oleh kemas kini pangkalan data. Membaca semula versi primer sebelum mengisi boleh membantu, tetapi menambah bacaan pangkalan data dan masih memerlukan sempadan atomik yang jelas antara semakan dan penulisan cache.

Kekalkan version fence cukup lama untuk menampung tempoh pengisian maksimum, percubaan semula, dan jeda proses atau rangkaian. Mengeluarkannya terlalu awal membuka semula tetingkap pengisian lewat. Fence melindungi susunan penulisan cache; ia tidak menggantikan kawalan keserentakan pangkalan data atau memberitahu pembaca biasa bahawa pangkalan data mengandungi versi lebih baharu yang pembatalannya belum tiba lagi.

Langkah 4: Jadikan pembatalan boleh dipulihkan tanpa melebih-lebihkan jaminan penghantaran

Baris outbox dan kemas kini perniagaan ditulis dalam satu transaksi. Geganti menghantar pembatalan ke saluran yang tahan lasak. Pengguna boleh menjejaki versi tertinggi yang diperhatikan bagi setiap product_id dan menggunakan peraturan ini:

  1. Peristiwa luar aturan di bawah versi tertinggi yang diperhatikan diakui secara idempoten.
  2. Versi baharu memajukan fence versi minimum dan memadamkan nilai yang dicache.
  3. Arahan cache yang gagal dicuba semula; kerja yang habis percubaan memasuki giliran kuarantin yang kelihatan.
  4. Tugas penyelarasan (reconciliation job) membandingkan versi pangkalan data, kemajuan outbox, dan versi cache yang disampelkan.

Penghantaran sekurang-kurangnya sekali berfungsi dengan baik dengan pemadaman kerana DEL pendua tidak mempunyai makna perniagaan tambahan. Jangan mendakwa bahawa percubaan semula menghasilkan pelaksanaan tepat-sekali (exactly-once execution). Pengguna boleh memadam dalam Redis, ranap sebelum mengakui mesej, dan memadam sekali lagi. Pengulangan yang selamat dan jurang yang boleh dikesan ialah sifat yang berguna.

Perhatikan selang masa dari komit pangkalan data hingga pembatalan yang berjaya, bukan sahaja usia giliran broker. Metrik yang berguna termasuk invalidation_lag_seconds, kegagalan dan percubaan semula pembatalan, usia kuarantin tertua, kelengahan versi cache-ke-sumber, pemintasan cache lebih belanjawan, QPS pengisian pangkalan data, dan bilangan panggilan kunci sama yang dikongsi melalui singleflight.

Langkah 5: Buktikan batas 5 saat dengan TTL dan get

Peristiwa yang boleh dipercayai akhirnya akan tiba, tetapi "akhirnya" tiada unit masa. Kontrak keusangan maksimum 5 saat dari masa komit memerlukan pertahanan masa terhingga yang bebas:

  • Tetapkan TTL cache fizikal di bawah 5 saat selepas memperuntukkan margin untuk jam, penjadualan, dan pengesanan, dan kira usia lapuk daripada masa komit versi berwibawa.
  • Sebagai alternatif, apabila kelengahan pembatalan menghampiri belanjawan, berhenti mempercayai cache secara global atau mengikut partition yang terjejas dan baca sumber yang memenuhi keperluan kesegaran.
  • Jika kedua-duanya tidak dapat dilakukan, kontrak tersebut ialah kekonsistenan akhirnya (eventual consistency) dan tidak boleh terus mendakwa batas 5 saat.

TTL ialah sandaran sempadan (boundary backstop), bukan mekanisme pembatalan utama. Tambah jitter supaya 2,000 kunci tidak tamat tempoh serentak, dan gabungkan miss untuk setiap kunci. Jika kesemua 2,000 kunci aktif dibaca sekurang-kurangnya sekali bagi setiap selang 5 saat, tamat tempoh yang diagihkan secara seragam menghasilkan purata kira-kira 400 pengisian sesaat. Anggaran tersebut bukanlah jaminan kapasiti. Kecondongan populariti (popularity skew), tamat tempoh kelompok, kegagalan Redis, dan pertanyaan perlahan boleh menghasilkan lonjakan, jadi uji terhadap QPS selamat pangkalan data dan bulkhead keserentakan.

Jika TTL 5 saat melebihi kapasiti pangkalan data, terdapat tiga pilihan yang jujur: tambah kapasiti pengisian yang selamat, kurangkan data aktif yang memerlukan kontrak ini, atau longgarkan belanjawan keusangan. Meningkatkan TTL secara senyap melanggar kehendak prompt.

Langkah 6: Kendalikan kelengahan replika dan read-your-writes

Walaupun selepas pemadaman yang betul, replika yang lengah boleh mengisi semula versi lama. Untuk miss dengan kontrak 5 saat yang tegar, gunakan susunan keutamaan ini:

  1. Baca primer untuk pengisian pertama selepas pembatalan.
  2. Gunakan replika hanya apabila kedudukan main semula (replay position) yang boleh diperhatikan telah mencapai kedudukan komit yang diperlukan.
  3. Kembalikan source_version daripada operasi tulis dan biarkan bacaan seterusnya membawa min_version; halakan ke primer apabila cache atau replika ketinggalan.
  4. Buka pemutus litar kesegaran (freshness circuit breaker) apabila kelengahan replika atau pembatalan melebihi belanjawan, dan berhenti mengembalikan nilai dicache yang tidak layak.

TTL 5 saat tidak membuktikan usia data 5 saat jika pengisian datang daripada replika yang sudah 4 saat ketinggalan. Usia lapuk bermula pada masa komit sumber kebenaran, jadi kelewatan replika, saluran peristiwa, dan cache semuanya diambil kira.

Langkah 7: Bandingkan alternatif dan batasannya

Kemas kini cache secara segerak (synchronously). Menulis Redis serta-merta selepas pangkalan data boleh menambah baik read-your-writes, tetapi dua sistem bebas masih mempunyai kegagalan separa. Pangkalan data boleh mengkomit manakala kemas kini cache gagal, dan penulisan pangkalan data serentak boleh sampai ke cache dalam urutan yang berbeza. Laluan ini masih memerlukan syarat versi sumber dan pembaikan; memanggilnya write-through tidak menjadikan kedua-dua penulisan itu transaksi atomik.

Padam dahulu, kemudian kemas kini pangkalan data. Ini mudah tetapi membolehkan pembaca mengisi semula data lama sebelum komit pangkalan data. Pemadaman kedua yang ditangguhkan (delayed second delete) mengurangkan kebarangkalian pemasaan tertentu, tetapi kelewatan tetap tidak dapat menampung kelengahan replika tanpa batas, jeda proses, atau kerosakan rangkaian. Proses ini juga boleh ranap sebelum pemadaman kedua. Ia mungkin menjadi langkah tambahan, tetapi ia tidak membuktikan keusangan berbatas dengan sendirinya.

Gunakan hanya TTL pendek. Ini boleh menjadi pilihan memadai yang paling mudah apabila penulisan jarang berlaku, belanjawan keusangan adalah besar, dan pangkalan data boleh menyerap pengisian. Kosnya ialah setiap penulisan mungkin diikuti oleh bacaan lapuk untuk keseluruhan tempoh TTL, manakala tamat tempoh yang disegerakkan boleh meningkatkan beban pangkalan data.

Baca segala-galanya daripada pangkalan data. Untuk data dengan dayaproses rendah atau kritikal dari segi ketepatan, ini selalunya merupakan penyelesaian yang paling jelas. Cache adalah pilihan. Apabila kos penyelarasan kekonsistenan melebihi penjimatan bacaan pangkalan data, menyingkirkan cache adalah wajar.

Langkah 8: Uji invariant dan laluan kegagalan

Lakukan lebih daripada sekadar memuat semula halaman secara manual selepas kemas kini. Gunakan barrier untuk menghasilkan semula pengisian lapuk yang lewat. Jedakan pembaca selepas ia memperoleh versi 41. Biarkan penulis mengkomit versi 42, memajukan fence, dan memadamkan nilai. Lepaskan pembaca lama dan tegaskan (assert) bahawa pengisian bersyaratnya gagal. Kemudian uji kes-kes ini:

  • tamatkan penulis serta-merta selepas PostgreSQL mengkomit dan sahkan bahawa outbox akhirnya membatalkan cache;
  • duplikasi dan tukar urutan satu peristiwa pembatalan dan sahkan bahawa versi tertinggi tidak pernah berbalik (regress);
  • gagalkan pemadaman Redis dan sahkan percubaan semula, kuarantin, dan sandaran TTL;
  • jedakan main semula replika dan sahkan bahawa pengisian menggunakan primer atau menolak hasil yang tidak layak;
  • lewatkan penggunaan pembatalan melebihi getnya dan sahkan bahawa bacaan memintas cache;
  • kumpulkan 2,000 kunci yang hampir tamat tempoh bersama-sama dan sahkan jitter, singleflight, dan bulkhead pangkalan data;
  • jadikan Redis tidak tersedia dan sahkan penurunan prestasi berbelanjawan untuk bacaan paparan manakala keputusan berwibawa mengekalkan laluan yang diperlukan.

Invariant teras ialah: versi cache yang dikembalikan tidak pernah berada di bawah min_version pemanggil; generasi pengisian yang dibatalkan tidak boleh menulis; tindakan perniagaan berwibawa tidak pernah mempercayai cache paparan; usia lapuk tidak melebihi 5 saat; dan pengisian pangkalan data kekal dalam QPS dan keserentakan selamat yang diuji.

Contoh Jawapan Berkualiti Tinggi

"Saya tidak akan bermula dengan menjanjikan bahawa PostgreSQL dan Redis sepadan pada setiap saat. Saya akan membahagikan kontrak bacaan. Nama dan imej produk boleh lapuk maksimum 5 saat dari komit PostgreSQL. Penolakan inventori, kebenaran, baki, dan keputusan checkout terus menggunakan keadaan berwibawa. Apabila penulis memerlukan read-your-writes, API penulisan mengembalikan versi sumber. Bacaan seterusnya membekalkan versi minimum tersebut dan menggunakan primer apabila Redis lebih lama.

Corak asas ialah cache-aside. Operasi baca mengembalikan hit hanya apabila usia dan versinya layak. Sesuatu miss menggunakan singleflight mengikut product_id, mengambil baris berversi secara monotonik, dan melakukan pengisian bersyarat. Satu transaksi PostgreSQL mengemas kini produk, menambah versinya, dan memasukkan baris outbox. Selepas komit, aplikasi cuba memadamkan Redis serta-merta. Pengguna geganti atau CDC mencuba semula daripada peristiwa yang tahan lasak, jadi keranapan selepas komit tidak menghilangkan kewajipan pembatalan secara kekal.

Urutannya ialah pangkalan data dahulu, pemadaman cache kedua. Memadam dahulu membolehkan pembaca memulihkan data lama sebelum pangkalan data mengkomit. Walaupun dengan urutan yang betul, pengisian lewat tetap wujud: pembaca mendapat versi 41, penulis mengkomit 42 dan memadam, kemudian pembaca lama menetapkan 41. Versi di dalam nilai dicache sahaja tidak mencukupi kerana tiada 42 yang tinggal selepas pemadaman. Saya meminta operasi miss menangkap satu generasi. Pembatalan memajukan generasi dan versi minimum sebelum memadamkan nilai. Skrip atomik menolak pengisian melainkan generasi tidak berubah dan versi sumber memenuhi keperluan fence. Jika replika tidak dapat membuktikan ia telah memainkan semula komit yang diperlukan, pengisian pasca-pembatalan membaca primer.

Outbox membuktikan pembatalan akhirnya yang boleh dipulihkan, bukan batas masa 5 saat. Saya menetapkan TTL tegar dalam belanjawan, dengan margin operasi, dan mengukur kelengahan komit-ke-pembatalan. Apabila kelengahan itu menghampiri belanjawan, operasi baca memintas Redis. Jika kesemua 2,000 kunci aktif diakses setiap 5 saat, pengisian seragam purata kira-kira 400 sesaat, jadi saya juga menggunakan jitter TTL, penggabungan permintaan bagi setiap kunci, dan bulkhead keserentakan pangkalan data.

Untuk pengesahan, saya mengawal susunan benang (threads) dan menyuntik kegagalan komit-kemudian-ranap, pembaca lewat, peristiwa berulang dan bertukar urutan, pemadaman Redis gagal, serta kelengahan replika. Penerimaan bermakna generasi lama tidak boleh mengisi, versi sumber tidak berbalik, usia lapuk kekal dalam 5 saat, tindakan berwibawa memintas cache paparan, dan beban pangkalan data kekal dalam belanjawan yang diukur."

Kesilapan Biasa

  • Mendakwa "kekonsistenan kukuh antara pangkalan data dan cache" → Jawapan tidak mentakrifkan bacaan yang sah, batas masa kegagalan, atau sempadan transaksi merentasi kedua-dua sistem → Nyatakan kontrak berasingan untuk keusangan berbatas, read-your-writes, dan bacaan berwibawa.
  • Memadamkan cache sebelum mengemas kini pangkalan data → Sesuatu miss di antara operasi tersebut membaca dan memulihkan nilai pangkalan data yang lama → Komit pangkalan data dahulu, kemudian batalkan, dengan pembaikan tahan lasak.
  • Hanya menerbitkan mesej dalam memori selepas komit → Keranapan sebelum penerbitan menghilangkan pembatalan secara kekal → Tulis outbox dalam transaksi yang sama atau tangkap perubahan log pangkalan data.
  • Menganggap DEL menamatkan setiap perlumbaan → Bacaan yang lebih lama boleh menyelesaikan penetapannya selepas pemadaman → Tolak nilai lapuk lewat dengan pajakan pengisian atau version fence yang kekal wujud selepas pemadaman nilai.
  • Meletakkan versi hanya di dalam nilai cache → Cache yang kosong tidak mempunyai versi yang lebih baharu untuk dibandingkan dan boleh menerima nilai lama → Simpan versi minimum atau generasi dalam fence yang berasingan dan semak pengisian secara atomik.
  • Menganggap penggunaan pendua sebagai ralat → Keranapan pengguna sebelum pengakuan secara semula jadi menghasilkan penghantaran semula → Jadikan pemadaman dan pemajuan versi tertinggi idempoten di bawah penghantaran sekurang-kurangnya sekali.
  • Menggunakan outbox untuk menjanjikan 5 saat → Kebolehulangan membuktikan pengendalian akhirnya, bukan kelewatan terhingga → Tambah TTL tegar atau get pintasan lebih belanjawan dan ukur kelengahan komit-ke-pembatalan hujung-ke-hujung.
  • Mengisi daripada mana-mana replika bacaan → Kelengahan replika boleh mengisi semula data lama selepas pembatalan yang betul → Semak kedudukan main semula, gunakan primer, atau perlukan versi sumber minimum.
  • Mencache semua medan dalam satu snapshot paparan → Kelonggaran keusangan data paparan bocor ke dalam inventori, kebenaran, dan checkout → Pisahkan kontrak data dan baca semula keadaan berwibawa untuk tindakan yang tidak boleh diubah.
  • Menghantar semua trafik ke pangkalan data apabila Redis gagal → 20,000 bacaan sesaat mungkin membebani sumber kebenaran → Lindunginya dengan bulkhead, had kadar, penurunan prestasi eksplisit, dan pemulihan terkawal.
  • Menggunakan pemadaman berganda tertangguh yang tetap (fixed delayed double delete) → Tidur (sleep) tidak dapat menampung kelengahan tanpa batas dan pemadaman kedua juga boleh hilang → Anggap ia sebagai pengoptimuman kebarangkalian sambil mengekalkan pembatalan tahan lasak, fence, dan sandaran masa terhingga.

Soalan Susulan dan Maklum Balas

Susulan 1: Bagaimana jika harga produk juga memerlukan read-your-writes?

Kembalikan source_version yang telah dikomit daripada API penulisan. Bacaan seterusnya dalam sesi tersebut menghantar min_version; jika Redis lebih lama, baca primer dan cache secara bersyarat hanya versi baharu. Jika penulis hanya perlu memaparkan hasilnya, kembalikan baris yang dikomit secara terus dan pintas cache buat seketika. Checkout mesti tetap mengesahkan semula harga dalam transaksi berwibawa; read-your-writes bukan kebenaran pembayaran.

Susulan 2: Mengapa version fence tidak boleh berada di dalam nilai yang dicache sahaja?

Pembatalan memadamkan nilai tersebut, jadi perbandingan biasa tidak lagi dapat melihat versi 42. Permintaan yang sebelum ini membaca versi 41 kemudiannya boleh menganggap 41 sebagai nilai yang sah untuk kunci yang kosong. Fence berasingan atau pajakan miss kekal wujud selepas pemadaman nilai dan merekodkan sama ada versi 42 wujud atau kebenaran pengisian lama telah dibatalkan.

Susulan 3: Bolehkah transactional outbox menghantar pendua?

Ya. Geganti boleh ranap selepas sistem hiliran menerima peristiwa tetapi sebelum ia menandakan baris outbox selesai. Pengguna mengendalikan (product_id, source_version) secara idempoten: versi lama tidak memajukan keadaan, versi baharu memajukan maksimum dan memadamkan nilai, serta pemadaman pendua adalah selamat. Jaminan yang berguna ialah tiada kehilangan pembatalan senyap berserta penyelarasan, bukannya pelaksanaan tepat-sekali.

Susulan 4: Bolehkah TTL menjadi 30 minit jika CDC mengendalikan pembatalan?

Itu boleh menjadi pertukaran (trade-off) yang sah apabila perniagaan hanya memerlukan kekonsistenan akhirnya dan menerima gangguan pembatalan yang berpanjangan. Prompt ini menjanjikan keusangan maksimum 5 saat. TTL 30 minit melanggar batas tersebut apabila CDC terhenti melainkan sistem memintas cache secara automatik apabila kelengahan pembatalan menghampiri 5 saat. Jika tidak, kekalkan batas masa tegar dalam belanjawan.

Susulan 5: Kelengahan replika biasanya hanya berpuluh-puluh milisaat. Mengapa menggunakan primer?

"Biasanya" bukanlah batas atas. Penggunaan (deployments), pemecahan rangkaian (network partitions), transaksi yang panjang, dan pemulihan boleh meningkatkan kelengahan. Replika kekal boleh digunakan apabila kedudukan main semulanya terbukti merangkumi komit yang diperlukan atau bacaan membawa versi minimum. Jika itu tidak dapat dibuktikan, halakan miss kritikal ke primer. Pilihan tersebut mengikut kontrak 5 saat dan belanjawan kapasiti pangkalan data.

Susulan 6: Mengapa tidak mengemas kini PostgreSQL dan Redis semasa memegang satu kunci teragih (distributed lock)?

Kunci hanya mengekang peserta yang mematuhi protokol tersebut. Ia tidak boleh menjadikan dua sistem mengkomit secara atomik, dan tidak menghapuskan keranapan pemegang kunci, tamat tempoh kunci, pemecahan rangkaian, atau penulis pangkalan data luar. Transaksi pangkalan data menetapkan fakta, dan pembatalan tahan lasak membaiki sistem kedua. Kunci mungkin mengurangkan keserentakan kunci yang sama, tetapi ia bukan bukti kekonsistenan.

Susulan 7: Bagaimana anda mengekalkan batas 5 saat apabila Redis tidak tersedia sepenuhnya?

Bacaan paparan memintas Redis, tetapi setiap bacaan sumber melalui bulkhead dan had kadar pangkalan data. Jika kapasiti tidak mencukupi, kembalikan hasil penurunan prestasi eksplisit atau kegagalan. Salinan lama setempat proses (process-local) boleh memberi perkhidmatan secara ringkas semasa masih berada dalam belanjawan 5 saat; selepas itu ia hilang kelayakan. Pulihkan dengan pemanasan berkadar terhad (rate-limited warming) sambil mengalirkan pembatalan supaya cache sejuk tidak membebani pangkalan data sekali lagi.

Susulan 8: Bagaimana anda membuktikan pengeluaran (production) tidak mengandungi nilai lapuk jangka panjang?

Bagi kunci yang disampelkan, baca versi sumber pangkalan data dan versi yang dicache bersama-sama, serta rekodkan kedua-dua jarak versi dan usia dari komit sumber. Selaraskan perubahan pangkalan data, kemajuan outbox, dan penanda aras tinggi (high-water marks) pengguna. Beri amaran tentang kelengahan pembatalan hujung-ke-hujung, kerja kuarantin tertua, dan pintasan lebih belanjawan. Suntik kegagalan komit-kemudian-ranap, kegagalan arahan Redis, dan jeda replika secara berkala untuk menunjukkan bahawa get dan TTL masih melindungi invariant di bawah kegagalan sebenar.

Sumber awam

Soalan berkaitan