RUSSIAN DOLL CACHING
This post hasn't been updated for 9 years
Bài viết được thực hiện tháng 01/2013 với AR 3.2. Bắt đầu từ AR 4.x trở đi, Russian Doll Caching được builtin sẵn trong Rails, và ta có thể sử dụng kỹ thuật này mà không cần phải cài thêm gem
Caching là một trong những kỹ thuật được sử dụng nhiều trong các dự án Công Nghệ Thông Tin (CNTT) nhằm tăng hiệu năng của sản phẩm. Có rất nhiều kỹ thuật caching đã và đang được sử dụng như memcache
, ProxyCache
, GatewayCache
, sử dụng Redis
… nhằm mục đích giải quyết bài toán caching phù hợp với từng hệ thống thông tin.
Một trong những vấn đề liên quan tới hiệu năng của chính kỹ thuật caching, đó là tăng tỷ lệ Cache Hit.
Russian Doll Caching
là một giải pháp hiệu quả cho vấn đề này
Russian Doll Caching
-
Nested Cache::Russian Doll Caching
Nested cache là một ý tưởng chia nhỏ những phần cần cache ra, mục đích nhằm tăng khả năng sử dụng lại: chỉ những phần có thay đổi mới cần được xử lý logic bởi hệ thống, còn lại những phần không có thay đổi sẽ được sử dụng từ cache.
Như vậy tỷ lệ Cache Hit sẽ cao hơn rất nhiều.
Một ví dụ đơn giản, với 1 page có 2 phần có thể bị thay đổi, với tỷ lệ 50% thay đổi trên mỗi request
Với cache thông thường, chỉ 1 trong 2 thay đổi là cache sẽ hỏng. Xác suất 1 trong 2 phần có thay đổi là 50% + 50%*(100%-50%) = 75%
Như vậy CacheMiss = 75% và CacheHit = 25%
Với Nested Cache, 2 phần là hoàn toàn độc lập với nhau, như vậy CacheMiss = 50% và CacheHit = 50% cho mỗi phần
Về mặt ý tưởng thì nested cache là 1 điều tuyệt vời, tuy nhiên việc quản lý các phần nhỏ đó như thế nào, perfomance tới đâu là một dấu hỏi lớn Russian Doll Caching được coi là phương thức quản lý nested cache tốt nhất hiện tại
Russian Doll Caching quy định rõ ràng từng lớp nằm trong 1 tập hợp nested-cache, với nguyên lý thống nhất 1 chiều:
- Khi 1 thay đổi xảy ra tại lớp cha ngoài cùng, sẽ chỉ có 1 mình lớp đó thay đổi, những lớp con sẽ vẫn tồn tại, và có thể sử dụng lại.
- Khi 1 thay đổi xảy ra tại lớp con trong cùng, nó sẽ bắt đầu 1 chuỗi dây chuyền khiến tất cả các lớp cha của nó phải thay đổi.
- Ghép tất cả những phần có thể sử dụng lại ta sẽ được phần cache mà hệ thống trả về cho dịch vụ Như vậy có thể hình dung Russian Doll Caching là sự kết hợp của Russian Doll và Puzzle Game
-
Usage
Để sử dụng Russian Doll Caching, trước tiên hệ thống Rails phải được enable khả năng caching.
# config/environments/development.rb
config.action_controller.perform_caching = true
Một ví dụ cho 2 model có quan hệ với nhau
# team.rb
class Team < ActiveRecord::Base
has_many :members
end
# member.rb
class Member < ActiveRecord::Base
belongs_to :team, touch: true
end
Trong Russian Doll Caching thì việc thiết lập touch: true
là rất quan trọng để báo rằng: khi có 1 member thay đổi, thì toàn bộ team sẽ được refresh để đảm bảo nguyên tắc khi lớp con thay đổi, lớp cha sẽ ngưng cache ( cũng thay đổi )
Trên views ta sẽ thiết lập Russian Doll Caching
<!-- app/views/teams/show.html.erb -->
<% cache ["nest-name", @team] do %>
<% @team.members.each do |member|%>
<%= render partial: 'members/member_info', locals: { member: member}%>
<% end -%>
<% end %>
<!-- app/views/members/_member_info.html.erb -->
<% cache ["nest-name", member] do %>
<%= render partial: 'teams/team_info', locals: {team: member.team}%>
<% end%>
Với phần code như trên:
- app/views/members/_member_infoinfo.html.erb
là phần con và app/views/teams/show.html.erb
là phần cha.
- nest-name
là tên của bộ cache
- @team
và member
là phần cho Russian Doll Caching biết sẽ ngưng cache khi đối tượng này thay đổi nội dung
3. ### Result
Trước tiên, về phần cache sinh ra, có thể thấy các fragment được tạo ra trong tmp/cache/
có dạng
views/nest-name/members/1-20130105083956
views/nest-name/members/2-20130105083956
views/nest-name/teams/1-20130105083956
Khi load lại trang, log server có:
Read fragment views/nest-name/teams/1-20130105083956 (0.1ms)
Rendered teams/show.html.erb within layouts/application (1.2ms)
log chỉ ra rằng kết quả trả về được đọc ra từ cache, với thời gian render rất nhỏ hơn so với lần load trang đầu tiên.
Khi thay đổi @team
, chúng ta sẽ nhận được 3 dòng log
Read fragment views/nest-name/members/1-20130105083956 (0.1ms)
Read fragment views/nest-name/members/2-20130105083956 (0.1ms)
Write fragment views/nest-name/teams/1-20130105083956 (1.6ms)
Như vậy chỉ có lớp cha phía ngoài được ghi mới, còn 2 member bên trong hoàn toàn được sử dụng lại. Khi thay đổi member ta sẽ nhận được
Write fragment views/nest-name/teams/1-20130105083956 (3.4ms)
Write fragment views/nest-name/members/1-20130105083956 (2.1ms)
Write fragment views/nest-name/members/2-20130105083956 (2.3ms)
cache đã được làm mới hoàn toàn khi lớp con thay đổi. Bạn có thể thấy rõ hơn hiệu quả của Russian Doll Caching khi sử dụng với 3-4 hay nhiều hơn nữa lớp con. 4. ### Advantages and Disadvantages
- Như ở ví dụ phía trên, ta có thể thấy Russian Doll Caching làm tăng tỷ lệ CacheHit lên rất cao so với việc cache full page. Điều này làm cải thiện đáng kể hiệu quả của cache
- Việc phải quản lý tên của bộ cache là vấn đề lớn nhất của Russian Doll Caching. Với 1 dự án lớn, nhiều lớp và nhiều version, một sơ suất trong việc thống nhất tên bộ cache sẽ không những làm Russian Doll Caching giảm hiệu năng, mà còn sinh ra một lượng cache dư thừa ( theo đúng tên của bộ cache đặt sai), thậm chí phản tác dụng nếu như việc nhầm lẫn xảy ra ngay ở những lớp ngoài cùng.
Cache Digests gem
Russian Doll Caching là 1 kỹ thuật caching hiệu năng cao, nhưng bị hạn chế bởi phương thức quản lý cache bằng tay hoàn toàn. Trong Ruby on Rails, ta có thể làm việc này một cách tự động bằng gem Cache_Digests
Cache Digests kế thừa lại toàn bộ tư tưởng của Russian Doll Caching, thay thế việc manual quản lý tên bộ cache bằng việc tự động.
Để cài đặt Cache Digests, thêm dòng sau vào Gemfile
# Gemfile
gem 'cache_digests'
Cách dùng của Cache Digests không khác nhiều so với Russian Doll Caching:
<!-- app/views/teams/show.html.erb -->
<% cache @team do %>
<% @team.members.each do |member|%>
<%= render partial: 'members/member_info', locals: { member: member}%>
<% end -%>
<% end %>
<!-- app/views/members/_member_info.html.erb -->
<% cache member do %>
<%= render partial: 'teams/team_info', locals: {team: member.team}%>
<% end%>
Như vậy tên của bộ cache được bỏ đi, và ta chỉ cần quan tâm đến nhân tố nào cầm cờ cho việc cache là @team
và member
Tên các phần tử cache sẽ có dạng
views/teams/1-20130104202410/fe43c9f1a9faa67f9429fb39bd24d0e3
views/members/1-20130104202410/fe43c9f1a9faa67f9429fb39bd24d0e3
views/members/2-20130104202410/fe43c9f1a9faa67f9429fb39bd24d0e3
Dãy MD5 phía sau được CacheDigests tự động sinh và đóng vai trò như tên bộ cache, được CacheDigests quản lý tự động, thay mới sau mỗi lần restart server. Ta cũng có thể xem mô hình nested cache của project bằng câu lệnh
rake cache_digests:nested_dependencies TEMPLATE=teams/show
Cache Digests hiện tại có thể hiểu được những câu lệnh sau như mốc đánh dấu lớp
render partial: "comments/comment", collection: commentable.comments
render "comments/comments"
render 'comments/comments'
render('comments/comments')
render "header" => render("comments/header")
render(@topic) => render("topics/topic")
render(topics) => render("topics/topic")
render(message.topics) => render("topics/topic")
Ngoài ra các kết quả khác không có gì thay đổi so với Russian Doll Caching Bạn có thể sử dụng ví dụ của tôi trên đây để hiểu hơn về Cache Digests cũng như Russian Doll Caching tại Github của tôi
# Github
https://github.com/yeuem1vannam/Sat5thJan2013.git
# Branch
russian_doll_caching
cache_digests
Sau khi clone về và setup cài đặt, để seed một database mới
rake db:migrate
rake db:reset
Conclusion
Bài viết giới thiệu một kỹ thuật đặc biệt trong bài toán caching, mà ở đây hiệu năng của cache được tăng lên đáng kể, đồng thời khiến vai trò định nghĩa cache không chỉ bó gọn trong config server dịch vụ, mà còn có thể được định nghĩa bởi chính developer, và được developer điều khiển theo ý muốn. Rails 4 sẽ được release trong năm 2013, và Russian Doll Caching sẽ trở thành 1 phần mặc định của nó thông qua CacheDigests, vì thế việc bắt kịp và đón đầu những công nghệ như thế này sẽ là 1 trong những mục tiêu trọng tâm của những người dùng Rails.
References
All Rights Reserved