Bagaimana cara kerja cache berbasis kunci?

10

Baru-baru ini saya membaca sebuah artikel di blog 37Signals dan saya bertanya-tanya bagaimana mereka bisa mendapatkan kunci cache.

Semuanya baik dan bagus memiliki kunci cache yang menyertakan stempel waktu objek (ini berarti bahwa ketika Anda memperbarui objek, cache akan tidak valid); tetapi bagaimana Anda kemudian menggunakan kunci cache dalam templat tanpa menyebabkan hit DB untuk objek yang Anda coba ambil dari cache.

Khususnya, bagaimana hal ini memengaruhi hubungan One to Many di mana Anda memberikan Komentar Posting, misalnya.

Contoh dalam Django:

{% for comment in post.comments.all %}
   {% cache comment.pk comment.modified %}
     <p>{{ post.body }}</p>
   {% endcache %}
{% endfor %}

Apakah caching di Rails berbeda dengan hanya permintaan untuk memcached misalnya (saya tahu mereka mengonversi kunci cache Anda ke sesuatu yang berbeda). Apakah mereka juga menyimpan kunci cache?

Dominic Santos
sumber
Lihatlah rossp.org/blog/2012/feb/29/fragment-caching untuk contoh Django!
vdboor
Saya sudah melihat itu dan sepertinya menderita masalah yang sama persis. Data yang ia coba cache diperlukan untuk mengakses cache. Satu-satunya hal yang tampaknya dia hemat adalah dalam operasi mahal internal yang tidak seperti kebanyakan kasus penggunaan untuk jenis caching ini.
Dominic Santos
Itu benar, sebuah juga terjadi dengan kode sinyal, difokuskan pada kode rendering. Caranya adalah dengan men-cache seluruh daftar di wadah lain juga, atau cache pengambilan objek di tempat lain.
vdboor
Sebenarnya strategi caching mereka tampaknya sedikit lebih berpendidikan. Saya merekomendasikan artikel ini juga: 37signals.com/svn/posts/…
JensG
Sepertinya potongan kode Anda memiliki kesalahan ketik - yang post.bodydimaksudkan comment.body?
Izkata

Jawaban:

3

Untuk melakukan caching dump langsung dari satu objek yang sudah dimuat, ya, Anda tidak mendapatkan apa-apa atau hampir tidak ada. Bukan itu yang dijelaskan oleh contoh-contoh itu - mereka menggambarkan hierarki, di mana setiap perubahan ke sesuatu yang lebih rendah juga harus memicu pembaruan untuk segala sesuatu yang lebih tinggi dalam hierarki.

Contoh pertama, dari blog 37signals, digunakan Project -> Todolist -> Todosebagai hierarki. Contoh populasi mungkin terlihat seperti ini:

Project: Foo (last_modified: 2014-05-10)
   Todolist:  Bar1 (last_modified: 2014-05-10)
       Todo:  Bang1 (last_modified: 2014-05-09)
       Todo:  Bang2 (last_modified: 2014-05-09)

   Todolist:  Bar2 (last_modified: 2014-04-01)
       Todo:  Bang3 (last_modified: 2014-04-01)
       Todo:  Bang4 (last_modified: 2014-04-01)

Jadi, katakanlah Bang3sudah diperbarui. Semua orang tuanya juga mendapatkan pembaruan:

Project: Foo (last_modified: 2014-05-16)
   Todolist:  Bar2 (last_modified: 2014-05-16)
       Todo:  Bang3 (last_modified: 2014-05-16)

Maka ketika tiba saatnya untuk merender, memuat Projectdari basis data pada dasarnya tidak terhindarkan. Anda perlu titik untuk memulai. Namun, karena last_modifiedini merupakan indikator semua anak-anaknya , itulah yang Anda gunakan sebagai kunci cache sebelum mencoba memuat anak-anak.


Sementara posting blog menggunakan templat terpisah, saya akan menyatukannya menjadi satu. Semoga melihat interaksi yang lengkap di satu tempat akan membuatnya sedikit lebih jelas.

Jadi, template Django mungkin terlihat seperti ini:

{% cache 9999 project project.cache_key %}
<h2>{{ project.name }}<h2>
<div>
   {% for list in project.todolist.all %}
   {% cache 9999 todolist list.cache_key %}
      <ul>
         {% for todo in list.todos.all %}
            <li>{{ todo.body }}</li>
         {% endfor %}
      </ul>
   {% endcache %}
   {% endfor %}
</div>
{% endcache %}

Katakanlah kita lulus dalam Proyek yang cache_keymasih ada di cache. Karena kami menyebarkan perubahan ke semua objek terkait ke induk, fakta bahwa kunci tertentu masih ada berarti seluruh konten yang diberikan dapat ditarik dari cache.

Jika Proyek tertentu itu baru saja diperbarui - misalnya, seperti di Fooatas - maka ia harus membuat anak-anaknya, dan hanya kemudian akan menjalankan permintaan untuk semua Todolists untuk Proyek itu. Demikian juga untuk Todolist tertentu - jika cache_key daftar itu ada, maka todos di dalamnya belum berubah, dan semuanya dapat ditarik dari cache.

Perhatikan juga bagaimana saya tidak menggunakan todo.cache_keydalam template ini. Itu tidak layak, karena seperti yang Anda katakan dalam pertanyaan, bodytelah ditarik dari basis data. Namun, hit basis data bukan satu-satunya alasan Anda menyimpan sesuatu. Sebagai contoh, mengambil teks markup mentah (seperti apa yang kita ketik ke dalam kotak pertanyaan / jawaban di StackExchange) dan mengonversinya menjadi HTML mungkin memerlukan waktu yang cukup agar hasil cache lebih efisien.

Jika demikian, loop dalam template mungkin terlihat lebih seperti ini:

         {% for todo in list.todos.all %}
            {% cache 9999 todo todo.cache_key %}
               <li>{{ todo.body|expensive_markup_parser }}</li>
            {% endcache %}
         {% endfor %}

Jadi, untuk menyatukan semuanya, mari kembali ke data asli saya di bagian atas jawaban ini. Jika kita mengasumsikan:

  • Semua objek telah di-cache dalam keadaan aslinya
  • Bang3 baru saja diperbarui
  • Kami sedang merender template yang dimodifikasi (termasuk expensive_markup_parser)

Maka ini adalah bagaimana semuanya akan dimuat:

  • Foo diambil dari database
  • Foo.cache_key (2014-05-16) tidak ada di cache
  • Foo.todolists.all()dipertanyakan: Bar1dan Bar2diambil dari basis data
  • Bar1.cache_key(2014-05-10) sudah ada di cache ; ambil dan keluarkan
  • Bar2.cache_key (2014-05-16) tidak ada di cache
  • Bar2.todos.all()dipertanyakan: Bang3dan Bang4diambil dari basis data
  • Bang3.cache_key (2014-05-16) tidak ada di cache
  • {{ Bang3.body|expensive_markup_parser }} diberikan
  • Bang4.cache_key(2014-04-01) sudah ada di cache ; ambil dan keluarkan

Penghematan dari cache dalam contoh kecil ini adalah:

  • Hit basis data dihindari: Bar1.todos.all()
  • expensive_markup_parserdihindari 3 kali: Bang1, Bang2, danBang4

Dan tentu saja, lain kali dilihat, Foo.cache_keyakan ditemukan, jadi satu-satunya biaya untuk rendering adalah mengambil Foosendiri dari database dan meminta cache.

Izkata
sumber
-2

Contoh Anda bagus jika perlu pengambilan data atau pemrosesan untuk setiap komentar. Jika Anda hanya mengambil tubuh dan menampilkannya - cache tidak akan berguna. Tetapi Anda dapat men-cache semua pohon komentar (termasuk {% untuk%}). Dalam hal ini Anda perlu membatalkannya dengan setiap komentar yang ditambahkan, sehingga Anda dapat memasukkan stempel waktu komentar terakhir atau komentar menghitung di suatu tempat ke dalam Posting dan membangun kunci cache komentar dengannya. Jika Anda lebih suka data yang lebih normal, dan menggunakan komentar hanya pada satu halaman, Anda bisa menghapus kunci cache pada penyimpanan komentar.

Bagi saya, menyimpan jumlah komentar dalam Posting terlihat cukup baik (jika Anda tidak mengizinkan untuk menghapus dan mengedit komentar) - Anda memiliki nilai untuk ditampilkan di mana saja dengan Post dan kunci untuk caching.

ilvar
sumber