Penyimpanan cache 2R2L

Caching adalah topik yang dipublikasikan dengan baik dan terkenal. Tetapi solusi baru mungkin juga muncul di dalamnya. Secara khusus - di bidang produk tingkat tinggi (misalnya, dalam pengembangan web). Menghadapi kekurangan dari pendekatan klasik, saya mencoba menyimpulkan skema caching yang ideal untuk kasus ketika relevansi data tidak penting. Kemudian saya mencoba menemukan deskripsi skema serupa, atau solusi siap pakai yang lebih baik. Belum ditemukan. Oleh karena itu, saya menamainya sendiri - 2R2L (2 Range 2 Location) - dua jarak dua- "spasial" caching. Meskipun mungkin sudah diterapkan di suatu tempat.



Semuanya dimulai dengan tugas sederhana - untuk menampilkan produk baru kepada pengguna, dengan mempertimbangkan preferensi masing-masing. Dan jika tidak ada masalah dengan mendapatkan produk baru, maka menghubungkan produk baru dengan preferensi (analisis statistik) sudah menciptakan beban nyata (misalnya, mari kita tentukan dalam 4 detik). Keunikan tugasnya adalah bahwa seluruh organisasi dapat bertindak sebagai pengguna. Dan tidak jarang 200-300 permintaan yang berkaitan dengan satu pengguna tiba di server sekaligus (dalam 2-3 detik). Itu. blok yang sama dibuat untuk banyak pengguna sekaligus.



Solusi yang jelas adalah dengan menyimpannya dalam cache di RAM (jangan sampai DBMS terkena kekerasan, memaksanya untuk memproses aliran panggilan yang besar). Skema klasik:



  1. Permintaan datang
  2. Memeriksa cache. Jika ada data di dalamnya, dan tidak ketinggalan zaman, kita kembalikan saja.
  3. Tidak ada data => menimbulkan masalah
  4. Kami mengirim ke pengguna
  5. Selain itu, kami menambahkannya ke cache, menunjukkan TTL


Kerugian dari solusi ini: jika tidak ada data di cache, semua permintaan yang datang selama generasi pertama akan menghasilkannya, menghabiskan sumber daya server untuk ini (memuat puncak). Dan tentu saja, semua pengguna akan menunggu di "panggilan pertama".



Perhatikan juga bahwa dengan nilai cache individual, jumlah record dapat bertambah banyak sehingga RAM server yang tersedia tidak cukup. Maka tampaknya logis untuk menggunakan server HDD lokal sebagai penyimpanan cache. Tapi kami segera kehilangan kecepatan.



Bagaimana menjadi?



Hal pertama yang terlintas dalam pikiran: akan sangat bagus untuk menyimpan catatan di 2 tempat - dalam RAM (sering diminta) dan HDD (semua atau hanya jarang diminta). Konsep "data panas dan dingin" dalam bentuknya yang paling murni. Ada banyak penerapan dari pendekatan ini, jadi kami tidak akan membahasnya. Mari kita tetapkan komponen ini sebagai 2L. Dalam kasus saya, ini berhasil diimplementasikan berdasarkan Scylla DBMS.



Tetapi bagaimana cara menghilangkan drawdown saat cache kedaluwarsa? Dan di sini kami menyertakan konsep 2R, yang artinya sederhana: untuk rekaman cache, Anda tidak perlu menentukan 1 nilai TTL, tetapi 2. TTL1 adalah stempel waktu, yang berarti "data sudah usang, harus dibuat ulang, tetapi Anda masih dapat menggunakannya"; TTL2 - "semuanya sudah usang sehingga tidak bisa digunakan lagi."



Jadi, kami mendapatkan skema caching yang sedikit berbeda:



  1. Permintaan datang
  2. Kami mencari data di cache. Jika datanya ada dan tidak usang (t <TTL1) - kami mengembalikannya kepada pengguna, seperti biasa dan tidak melakukan yang lain.
  3. Datanya ada di sana, kedaluwarsa, tetapi Anda dapat menggunakan (TTL1 <t <TTL2) - berikan kepada pengguna DAN inisialisasi prosedur untuk memperbarui catatan cache
  4. Tidak ada data sama sekali (dihentikan setelah TTL2 kedaluwarsa) - kami membuatnya "seperti biasa" dan menulisnya ke cache.
  5. Setelah menyajikan konten kepada pengguna atau dalam aliran paralel, kami menjalankan prosedur untuk memperbarui catatan cache.


Hasilnya, kami memiliki:



  • jika catatan cache cukup sering digunakan, pengguna tidak akan pernah berada dalam situasi "menunggu cache diperbarui" - dia akan selalu menerima hasil yang sudah jadi.
  • jika antrian "update" diatur dengan benar, maka dimungkinkan untuk mencapai fakta bahwa dalam kasus beberapa akses simultan ke record dengan TTL1 <t <TTL2, hanya akan ada 1 tugas update dalam antrian, dan bukan beberapa yang identik.


Sebagai contoh: untuk feed produk baru, Anda dapat menentukan TTL1 = 1 jam (namun, konten baru tidak muncul secara intensif), dan TTL2 - 1 minggu.



Dalam kasus paling sederhana, kode PHP untuk mengimplementasikan 2R bisa jadi:



$tmp = cache_get($key);
If (!$tmp){
	$items = generate_items();
	cache_set($items, 60*60, 60*60*24*7);
}else{
	$items = $tmp[‘items’];
	If (time()-$tmp[‘tm’] > 60*60){
		$need_rebuild[] = [‘to’=>$key, ‘method’=>’generate_items’];
}
}
//   
echo json_encode($items);
//     ,   
If (isset($need_rebuild) && count($need_rebuild)>0){
	foreach($need_rebuild as $k=>$v){
		$tmp = ['tm'=>time(), 'items'=>$$v[‘method’]];
		cache_set($tmp, 60*60, 60*60*24*7);
}
}


Dalam praktiknya, tentu saja, implementasi cenderung lebih sulit. Misalnya, generator catatan cache adalah skrip terpisah yang diluncurkan sebagai layanan; antrian - melalui Rabbit, tanda "kunci seperti itu sudah dalam antrian untuk regenerasi" - via Redis atau Scylla.



Jadi, jika kita menggabungkan pendekatan "dua jalur" dan konsep data "panas / dingin", kita mendapatkan 2R2L.



Terima kasih!



All Articles