การแคชแบบใช้คีย์ทำงานอย่างไร


10

ฉันเพิ่งอ่านบทความในบล็อก 37Signalsและฉันก็สงสัยว่าพวกเขาได้รับรหัสแคชอย่างไร

ทุกอย่างดีและดีมีคีย์แคชที่มีการประทับเวลาของวัตถุ (ซึ่งหมายความว่าเมื่อคุณอัปเดตวัตถุแคชจะไม่ถูกต้อง) แต่คุณจะใช้แคชคีย์ในเทมเพลตได้อย่างไรโดยไม่ทำให้ DB มีผลกระทบต่อวัตถุที่คุณพยายามดึงข้อมูลจากแคช

โดยเฉพาะสิ่งนี้มีผลต่อความสัมพันธ์แบบหนึ่งต่อหลายอย่างที่คุณแสดงความคิดเห็นของโพสต์อย่างไร

ตัวอย่างใน Django:

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

การแคชใน Rails นั้นแตกต่างจากการร้องขอเพื่อ memcached หรือไม่ (ฉันรู้ว่าพวกเขาแปลงแคชคีย์ของคุณเป็นอย่างอื่น) พวกเขายังแคชรหัสแคชหรือไม่


ลองดูที่rossp.org/blog/2012/feb/29/fragment-cachingเพื่อเป็นตัวอย่าง Django!
vdboor

ฉันได้ดูแล้วและดูเหมือนว่าจะประสบปัญหาเดียวกัน จำเป็นต้องใช้ข้อมูลที่เขาพยายามแคชเพื่อเข้าถึงแคช สิ่งเดียวที่เขาดูเหมือนจะประหยัดคือในการดำเนินการที่มีราคาแพงซึ่งไม่เหมือนกับกรณีการใช้งานส่วนใหญ่สำหรับการแคชประเภทนี้
โดมินิคซานโตส

นั่นเป็นความจริงและยังเกิดขึ้นกับรหัส 37signs ซึ่งจะเน้นไปที่รหัสการแสดงผล เคล็ดลับคือการแคชรายการทั้งหมดในคอนเทนเนอร์อื่นด้วยหรือแคชการดึงข้อมูลวัตถุอื่น
vdboor

ที่จริงแล้วกลยุทธ์การแคชของพวกเขาดูเหมือนจะมีการศึกษาเพิ่มขึ้น ฉันแนะนำบทความนี้ด้วย: 37signals.com/svn/posts/…
JensG

ดูเหมือนว่าข้อมูลโค้ดของคุณมีตัวพิมพ์ผิด - post.bodyตั้งใจจะเป็นcomment.bodyอย่างไร
Izkata

คำตอบ:


3

สำหรับการแคชดัมพ์ของวัตถุที่โหลดแล้วเดียวใช่แล้วคุณจะไม่ได้อะไรเลยหรือสิ่งที่ไม่ทำอะไรเลย นั่นไม่ใช่ตัวอย่างที่อธิบาย - พวกเขากำลังอธิบายลำดับชั้นที่การเปลี่ยนแปลงใด ๆ ที่ต่ำกว่าควรทำให้เกิดการอัปเดตทุกอย่างที่สูงกว่าในลำดับชั้น

ตัวอย่างแรกจากบล็อก 37signals ใช้Project -> Todolist -> Todoเป็นลำดับชั้น ตัวอย่างที่มีประชากรอาจมีลักษณะเช่นนี้:

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)

ดังนั้นสมมติว่าBang3มีการปรับปรุง ผู้ปกครองทั้งหมดได้รับการปรับปรุงด้วย:

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

จากนั้นเมื่อถึงเวลาที่ต้องแสดงผลการโหลดProjectจากฐานข้อมูลนั้นเป็นสิ่งที่หลีกเลี่ยงไม่ได้ คุณต้องมีจุดเริ่มต้น อย่างไรก็ตามเนื่องจากlast_modifiedเป็นตัวบ่งชี้ถึงชายด์ทั้งหมดนั่นคือสิ่งที่คุณใช้เป็นคีย์แคชก่อนที่จะพยายามโหลดเด็ก ๆ


ในขณะที่โพสต์บล็อกใช้แม่แบบที่แยกจากกันฉันจะรวมเข้าด้วยกันเป็นหนึ่งเดียว หวังว่าการได้เห็นการโต้ตอบที่สมบูรณ์แบบในที่เดียวจะทำให้ชัดเจนขึ้น

ดังนั้นเทมเพลต Django อาจมีลักษณะเช่นนี้:

{% 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 %}

สมมติว่าเราผ่านโครงการที่cache_keyยังคงมีอยู่ในแคช เนื่องจากเราเผยแพร่การเปลี่ยนแปลงวัตถุที่เกี่ยวข้องทั้งหมดไปยังผู้ปกครองความจริงที่ว่าคีย์เฉพาะยังคงมีอยู่หมายความว่าเนื้อหาที่แสดงผลทั้งหมดสามารถดึงจากแคช

หากโครงการนั้นเพิ่งได้รับการอัปเดต - ตัวอย่างเช่นเดียวกับFooด้านบน - จากนั้นจะต้องแสดงผลลูกของมันและเฉพาะจากนั้นจะเรียกใช้แบบสอบถามสำหรับ Todolists ทั้งหมดสำหรับโครงการนั้น ในทำนองเดียวกันสำหรับ Todolist ที่เฉพาะเจาะจง - ถ้ารายการของแคชนั้นมีอยู่แล้วโทโปลภายในก็ไม่เปลี่ยนแปลงและสิ่งทั้งหมดสามารถดึงออกมาจากแคชได้

โปรดสังเกตว่าฉันไม่ได้ใช้todo.cache_keyงานแม่แบบนี้ด้วย มันไม่คุ้มค่าอย่างที่คุณพูดในคำถามเพราะbodyถูกดึงออกมาจากฐานข้อมูลแล้ว อย่างไรก็ตามความนิยมของฐานข้อมูลไม่ใช่เหตุผลเดียวที่คุณอาจแคชบางสิ่ง ตัวอย่างเช่นการใช้ข้อความมาร์กอัปแบบดิบ (เช่นสิ่งที่เราพิมพ์ลงในกล่องคำถาม / คำตอบบน StackExchange) และการแปลงเป็น HTML อาจใช้เวลาเพียงพอที่การแคชผลลัพธ์จะมีประสิทธิภาพมากขึ้น

หากเป็นเช่นนั้นการวนซ้ำภายในในเทมเพลตอาจมีลักษณะเช่นนี้มากขึ้น:

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

เพื่อดึงทุกอย่างเข้าด้วยกันลองกลับไปที่ข้อมูลดั้งเดิมของฉันที่ด้านบนของคำตอบนี้ หากเราถือว่า:

  • วัตถุทั้งหมดถูกแคชในสถานะดั้งเดิม
  • Bang3 เพิ่งได้รับการอัพเดต
  • เรากำลังแสดงเทมเพลตที่แก้ไขแล้ว (รวมถึงexpensive_markup_parser)

จากนั้นนี่คือวิธีโหลดทุกสิ่ง:

  • Foo ถูกดึงจากฐานข้อมูล
  • Foo.cache_key (2014-05-16) ไม่มีอยู่ในแคช
  • Foo.todolists.all()ถูกสอบถาม: Bar1และBar2ถูกดึงจากฐานข้อมูล
  • Bar1.cache_key(2014/05/10) ที่มีอยู่แล้วในแคช ; ดึงและส่งออกมัน
  • Bar2.cache_key (2014-05-16) ไม่มีอยู่ในแคช
  • Bar2.todos.all()ถูกสอบถาม: Bang3และBang4ถูกดึงจากฐานข้อมูล
  • Bang3.cache_key (2014-05-16) ไม่มีอยู่ในแคช
  • {{ Bang3.body|expensive_markup_parser }} มีการแสดงผล
  • Bang4.cache_key(2014-04-01) มีอยู่ในแคชแล้ว ดึงและส่งออกมัน

ประหยัดจากแคชในตัวอย่างเล็ก ๆ นี้คือ:

  • หลีกเลี่ยงการเข้าถึงฐานข้อมูล: Bar1.todos.all()
  • expensive_markup_parserหลีกเลี่ยง 3 ครั้ง: Bang1, Bang2และBang4

และแน่นอนว่าในครั้งต่อไปที่ดูFoo.cache_keyจะพบดังนั้นค่าใช้จ่ายในการเรนเดอร์Fooเดียวคือการดึงข้อมูลเพียงอย่างเดียวจากฐานข้อมูลและทำการแคช


-2

ตัวอย่างของคุณดีถ้าต้องการการดึงข้อมูลหรือการประมวลผลสำหรับแต่ละความคิดเห็น หากคุณเพียงแค่ใช้ร่างกายและแสดงมัน - แคชจะไร้ประโยชน์ แต่คุณสามารถแคชแผนผังความคิดเห็นทั้งหมด (รวมถึง {% สำหรับ%}) ในกรณีนี้คุณต้องทำให้เป็นโมฆะกับทุกความคิดเห็นที่เพิ่มเข้ามาดังนั้นคุณสามารถใส่ความคิดเห็นล่าสุดหรือความคิดเห็นนับรวมอยู่ในโพสต์และสร้างคีย์แคชของความคิดเห็นได้ หากคุณต้องการข้อมูลที่ได้รับการปรับให้เป็นมาตรฐานมากขึ้นและใช้ความคิดเห็นในหน้าเดียวเท่านั้นคุณสามารถล้างแคชคีย์ในการบันทึกความคิดเห็นได้

สำหรับฉันแล้วการบันทึกความคิดเห็นในโพสต์นั้นดูดีพอ (ถ้าคุณไม่อนุญาตให้ลบและแก้ไขความคิดเห็น) - คุณมีค่าที่จะแสดงที่ใดก็ได้ด้วยโพสต์และกุญแจสำหรับการแคช

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.