Computer >> कंप्यूटर ट्यूटोरियल >  >> प्रोग्रामिंग >> Ruby

ActiveRecord का अनुकूलन: अनावश्यक डेटाबेस क्वेरीज़ को खत्म करने के लिए कैशिंग का लाभ उठाना

कैशिंग का वर्णन करने का एक सामान्य तरीका है कुछ कोड के परिणाम को संग्रहीत करना ताकि हम बाद में इसे जल्दी से पुनः प्राप्त कर सकें। कुछ मामलों में, इसका मतलब बाद में पुनर्गणना से बचने के लिए एक गणना किए गए मान को संग्रहीत करना है। हालांकि, हम किसी हार्ड ड्राइव से पढ़ने या नेटवर्क अनुरोध करने से बचने के लिए, बिना किसी गणना किए, बस इसे मेमोरी में रखकर भी डेटा को कैश कर सकते हैं।

यह बाद वाला रूप ActiveRecord के लिए विशेष रूप से प्रासंगिक है, जहां डेटाबेस अक्सर एक अलग सर्वर पर चलता है। इस प्रकार, सभी अनुरोध नेटवर्क-ट्रैफिक ओवरहेड उठाते हैं, उस लोड का उल्लेख नहीं करने के लिए जो डेटाबेस सर्वर पर तब पड़ता है जब क्वेरी फिर से की जाती है।

सौभाग्य से, रेल्स डेवलपर्स के लिए, ActiveRecord स्वयं पहले से ही हमारे लिए इसका बहुत कुछ संभालता है, शायद बिना हमें इसके प्रति जागरूक हुए। यह उत्पादकता के लिए अच्छा है, लेकिन कभी-कभी, यह जानना महत्वपूर्ण है कि पर्दे के पीछे क्या कैश किया जा रहा है। उदाहरण के लिए, जब आप जानते हैं (या उम्मीद करते हैं) कि किसी अन्य प्रक्रिया द्वारा मान बदला जा रहा है, या आपके पास बिल्कुल सबसे अद्यतन मान होना चाहिए। ऐसे मामलों में, ActiveRecord डेटा का अनकैश्ड रीड करने के लिए मजबूर करने के लिए कुछ 'एस्केप हैच' प्रदान करता है।

ActiveRecord का आलसी मूल्यांकन

ActiveRecord का आलसी मूल्यांकन कैशिंग प्रति से नहीं है, लेकिन हम इसे बाद में कोड उदाहरणों में देखेंगे, इसलिए हम एक संक्षिप्त अवलोकन प्रदान करेंगे। जब आप एक ActiveRecord क्वेरी का निर्माण करते हैं, कई मामलों में, कोड डेटाबेस को तत्काल कॉल जारी नहीं करता है। यह वही है जो हमें डेटाबेस को हर बार हिट किए बिना कई .where क्लॉज को चेन करने की अनुमति देता है:

@posts = Post.where(published: true) # अभी तक कोई DB हिट नहीं
@posts = @posts.where(publied_at: Date.today) # अभी भी कुछ नहीं
@posts.count # SELECT COUNT(*) FROM "posts" WHERE...

इसके कुछ अपवाद हैं। उदाहरण के लिए, जब .find, .find_by, .pluck, .to_a, या .first का उपयोग करते हैं, तो अतिरिक्त क्लॉज को चेन करना असंभव है। नीचे दिए गए अधिकांश उदाहरणों में, मैं DB कॉल को मजबूर करने के एक सरल तरीके के रूप में .to_a का उपयोग करूंगा।

ध्यान दें कि यदि आप रेल्स कंसोल में इसके साथ प्रयोग कर रहे हैं, तो आपको 'इको' मोड बंद करना होगा। अन्यथा, कंसोल (या तो irb या pry) एक बार जब आप 'एंटर' दबाते हैं तो ऑब्जेक्ट पर .inspect को कॉल करता है, जो एक DB क्वेरी को मजबूर करता है। इको मोड को अक्षम करने के लिए, आप निम्न कोड का उपयोग कर सकते हैं:

conf.echo = false # irb के लिए
pry_instance.config.print = proc {} # pry के लिए

ActiveRecord रिलेशन्स

ActiveRecord के बिल्ट-इन कैशिंग का पहला भाग जिसे हम देखेंगे वह रिलेशन्स है। उदाहरण के लिए, हमारे पास एक सामान्य User-Posts रिलेशनशिप है:

# app/models/user.rb
class User < ApplicationRecord
  has_many :posts
end

# app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user
end

यह हमें संबंधित रिकॉर्ड(स) को खोजने के लिए डेटाबेस क्वेरी करने के लिए आसान user.posts और post.user मेथड्स देता है। मान लें कि हम इन्हें एक कंट्रोलर और व्यू में उपयोग कर रहे हैं:

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    @user = User.find(params[:user_id])
    @posts = @user.posts
  end
  ...
# app/views/posts/index.html.erb
...
<%= render 'shared/sidebar' %>
<% @posts.each do |post| %>
  <%= render post %>
<% end %>
# app/views/shared/_sidebar.html.erb
...
<% @posts.each do |post| %>
  <li><%= post.title %></li>
<% end %>

हमारे पास एक बुनियादी index एक्शन है जो @user.posts को पकड़ता है। पिछले अनुभाग के समान, इस बिंदु पर डेटाबेस क्वेरी नहीं चली है। रेल्स तब हमारे index व्यू को रेंडर करता है, जो बदले में साइडबार को रेंडर करता है। साइडबार @posts.each ... को कॉल करता है, और इस बिंदु पर, ActiveRecord डेटा प्राप्त करने के लिए डेटाबेस क्वेरी चलाता है।

हम तब अपने index टेम्पलेट के बाकी हिस्से पर लौटते हैं, जहां हमारे पास एक और @posts.each है; हालांकि, इस बार, कोई डेटाबेस कॉल नहीं है। क्या हो रहा है कि ActiveRecord इन सभी पोस्ट्स को हमारे लिए कैश कर रहा है और डेटाबेस से फिर से पढ़ने की जहमत नहीं उठाता।

एस्केप हैच

कभी-कभी हम ActiveRecord को संबंधित रिकॉर्ड्स को फिर से लाने के लिए मजबूर करना चाह सकते हैं; शायद, हम जानते हैं कि इसे किसी अन्य प्रक्रिया (उदाहरण के लिए, एक बैकग्राउंड जॉब) द्वारा बदला जा रहा है। एक और आम स्थिति स्वचालित टेस्ट में है जहां हम डेटाबेस में नवीनतम मान प्राप्त करना चाहते हैं ताकि यह सत्यापित किया जा सके कि कोड ने इसे सही ढंग से अपडेट किया है।

इसके दो सामान्य तरीके हैं, स्थिति के आधार पर। मुझे लगता है कि सबसे आम तरीका एसोसिएशन पर बस .reload कॉल करना है, जो ActiveRecord को बताता है कि हम जो कुछ भी कैश किया गया है उसे नज़रअंदाज करना चाहते हैं और डेटाबेस से नवीनतम संस्करण प्राप्त करना चाहते हैं:

@user = User.find(1)
@user.posts # DB कॉल
@user.posts # कैश्ड, कोई DB कॉल नहीं
@user.posts.reload # DB कॉल
@user.posts # कैश्ड नया संस्करण, कोई DB कॉल नहीं

एक अन्य विकल्प केवल ActiveRecord मॉडल का एक नया इंस्टेंस प्राप्त करना है (उदाहरण के लिए, find को फिर से कॉल करके):

@user = User.find(1)
@user.posts # DB कॉल
@user.posts # कैश्ड, कोई DB कॉल नहीं
@user = User.find(1) # @user अब User का एक नया इंस्टेंस है
@user.posts # DB कॉल, इस इंस्टेंस में कोई कैश नहीं

रिलेशनशिप्स को कैश करना अच्छा है, लेकिन हम अक्सर सरल रिलेशनशिप लुकअप से परे जटिल .where(...) क्वेरीज़ के साथ समाप्त होते हैं। यहीं ActiveRecord का SQL कैश आता है।

ActiveRecord का SQL कैश

ActiveRecord अपने द्वारा की गई क्वेरीज़ का एक आंतरिक कैश रखता है ताकि प्रदर्शन को गति दी जा सके। ध्यान दें, हालांकि, यह कैश विशेष एक्शन से जुड़ा हुआ है; यह एक्शन की शुरुआत में बनाया जाता है और एक्शन के अंत में नष्ट हो जाता है। इसका मतलब है कि आप इसे केवल तभी देखेंगे जब आप एक कंट्रोलर एक्शन के भीतर दो बार एक ही क्वेरी कर रहे हों। यह भी का अर्थ है कि कैश रेल्स कंसोल में उपयोग नहीं किया जाता है। कैश हिट्स रेल्स लॉग में CACHE के साथ दिखाए जाते हैं। उदाहरण के लिए,

class PostsController < ApplicationController
  def index
    ...
    Post.all.to_a # DB क्वेरी को मजबूर करने के लिए to_a
    ...
    Post.all.to_a # DB क्वेरी को मजबूर करने के लिए to_a

निम्न लॉग आउटपुट उत्पन्न करता है:

Post Load (2.1ms) SELECT "posts".* FROM "posts"
↳ app/controllers/posts_controller.rb:11:in `index'
CACHE Post Load (0.0ms) SELECT "posts".* FROM "posts"
↳ app/controllers/posts_controller.rb:13:in `index'

आप ActiveRecord::Base.connection.query_cache (या केवल SQL क्वेरी के लिए ActiveRecord::Base.connection.query_cache.keys) को प्रिंट करके एक एक्शन के लिए कैश में क्या है, इस पर एक नज़र डाल सकते हैं।

एस्केप हैच

शायद ऐसे कई कारण नहीं हैं जिनसे आप SQL कैश को बायपास करना चाहेंगे, लेकिन फिर भी, आप ActiveRecord::Base पर uncached मेथड का उपयोग करके ActiveRecord को अपने SQL कैश को बायपास करने के लिए मजबूर कर सकते हैं:

class PostsController < ApplicationController
  def index
    ...
    Post.all.to_a # DB क्वेरी को मजबूर करने के लिए to_a
    ...
    ActiveRecord::Base.uncached do
      Post.all.to_a # DB क्वेरी को मजबूर करने के लिए to_a
    end

चूंकि यह ActiveRecord::Base पर एक मेथड है, आप इसे अपने मॉडल क्लासेस में से किसी एक के माध्यम से भी कॉल कर सकते हैं यदि यह पठनीयता में सुधार करता है; उदाहरण के लिए,

Post.uncached do
  Post.all.to_a
end

काउंटर कैश

वेब एप्लिकेशन में रिलेशनशिप में रिकॉर्ड्स को गिनना बहुत आम है (उदाहरण के लिए, एक उपयोगकर्ता के X पोस्ट हैं या एक टीम अकाउंट के Y उपयोगकर्ता हैं)। यह कितना आम है, इसके कारण ActiveRecord में स्वचालित रूप से एक काउंटर को अद्यतन रखने का एक तरीका शामिल है ताकि आपके पास डेटाबेस संसाधनों का उपयोग करने वाले .count कॉल्स का एक गुच्छा न हो। इसे सक्षम करने में केवल कुछ चरण लगते हैं। पहले, हम रिलेशनशिप में counter_cache जोड़ते हैं ताकि ActiveRecord को पता चले कि हमें गिनती कैश करनी है:

class Post < ApplicationRecord
  belongs_to :user, counter_cache: true
end

हमें User में एक नया कॉलम भी जोड़ना होगा, जहां गिनती संग्रहीत की जाएगी। हमारे उदाहरण में, यह User.posts_count होगा। यदि आवश्यक हो तो आप कॉलम नाम निर्दिष्ट करने के लिए counter_cache को एक सिम्बल पास कर सकते हैं।

rails generate migration AddPostsCountToUsers posts_count:integer
rails db:migrate

काउंटर्स अब 0 पर सेट हो जाएंगे (डिफ़ॉल्ट)। यदि आपके एप्लिकेशन में पहले से ही कुछ पोस्ट हैं, तो आपको उन्हें अपडेट करना होगा। ActiveRecord निटी-ग्रिट्टी विवरणों को संभालने के लिए एक reset_counters मेथड प्रदान करता है, इसलिए आपको बस इसे आईडी पास करने और यह बताने की आवश्यकता है कि कौन सा काउंटर अपडेट करना है:

User.all.each do |user|
  User.reset_counters(user.id, :posts)
end

अंत में, हमें उन स्थानों की जांच करनी होगी जहां इस गिनती का उपयोग किया जा रहा है। ऐसा इसलिए है क्योंकि .count कॉल करने से काउंटर बायपास हो जाएगा और हमेशा एक COUNT() SQL क्वेरी चलाएगा। इसके बजाय, हम .size का उपयोग कर सकते हैं, जो जानता है कि यदि यह मौजूद है तो काउंटर कैश का उपयोग करना है। एक तरफ, आप हर जगह .size का उपयोग करने का डिफ़ॉल्ट चाह सकते हैं, क्योंकि यह एसोसिएशन्स को भी रीलोड नहीं करता है यदि वे पहले से मौजूद हैं, जो संभावित रूप से डेटाबेस की यात्रा को बचाता है।

निष्कर्ष

अधिकांश भाग के लिए, ActiveRecord की आंतरिक कैशिंग "बस काम करती है"। मैं यह नहीं कह सकता कि मैंने कई ऐसे मामले देखे हैं जिन्हें इसे बायपास करने की आवश्यकता है, लेकिन सभी चीजों की तरह, यह जानना कि "हुड के नीचे" क्या चल रहा है, जब आप एक ऐसी स्थिति में फंस जाते हैं जिसमें कुछ असाधारण की आवश्यकता होती है, तो आपका कुछ समय और पीड़ा बचा सकता है।

बेशक, डेटाबेस एकमात्र स्थान नहीं है जहां रेल्स हमारे लिए पर्दे के पीछे कुछ कैशिंग कर रहा है। HTTP स्पेसिफिकेशन में हेडर शामिल हैं जिन्हें क्लाइंट और सर्वर के बीच भेजा जा सकता है ताकि उन डेटा को फिर से भेजने से बचा जा सके जो नहीं बदले हैं। इस कैशिंग श्रृंखला के अगले लेख में, हम 304 (Not Modified) HTTP स्टेटस कोड पर एक नज़र डालेंगे, रेल्स इसे आपके लिए कैसे संभालता है, और आप इस हैंडलिंग को कैसे ट्वीक कर सकते हैं।

  1. MS SQL Server में डेटाबेस हटाना MS SQL Server में डेटाबेस हटाना

    MS SQL Server में डेटाबेस हटाना MS SQL Server में डेटाबेस को हटाने के लिए, हम DROP कमांड का उपयोग करते हैं। इस कमांड का उपयोग करने के 2 तरीके यहाँ दिए गए हैं। विधि 1: T-SQL स्क्रिप्ट का उपयोग करें MS SQL Server में डेटाबेस को हटाने के लिए सिंटैक्स यहाँ दिया गया है। Drop database उदाहरण के लिए, Tes

  1. Windows 10 में F1 सहायता कुंजी कैसे अक्षम करें ताकि गलती से सहायता न खुले Windows 10 में F1 सहायता कुंजी कैसे अक्षम करें ताकि गलती से सहायता न खुले

    Windows कीबोर्ड पर फ़ंक्शन कुंजियां (F1 से F12) की विशेष रूप से निर्दिष्ट भूमिकाएं होती हैं। उदाहरण के लिए, F1 आपके द्वारा वर्तमान में उपयोग किए जा रहे किसी भी एप्लिकेशन का सहायता पृष्ठ खोलता है। यह कई स्थितियों में उपयोगी है। हालांकि, आप किसी कारण से इसे अक्षम करना चाह सकते हैं। Windows ऑपरेटिंग स

  1. टालमटोल को हराएं: फोकस और प्रोडक्टिविटी के लिए सिद्ध ऐप रणनीतियां टालमटोल को हराएं: फोकस और प्रोडक्टिविटी के लिए सिद्ध ऐप रणनीतियां

    हम अक्सर खुद को किसी न किसी सोशल मीडिया ऐप में पाते हैं। यह हमारे जीवन का अभिन्न अंग बन गया है और कभी-कभी यह सभी आवश्यक कार्यों पर हावी होता हुआ देखा जाता है। देखें कि अगर आप उनके जीवन से स्मार्टफोन हटा दें तो लोग कैसे दिखेंगे। चीजों में देरी करना या स्थगित करना एक बड़ी समस्या है और इस आदत के साथ लक