ActiveRecord Ruby on Rails की सबसे जादुई विशेषता है। हमें आमतौर पर इसके आंतरिक कामकाज के बारे में चिंता करने की ज़रूरत नहीं होती, लेकिन जब हम करते हैं, तो यहां बताया गया है कि AppSignal हमें हुड के नीचे क्या हो रहा है यह जानने में कैसे मदद कर सकता है।
ActiveRecord क्या है?
ActiveRecord के बारे में बात करने के लिए, हमें पहले फ्रेमवर्क के बारे में सोचने की ज़रूरत है, विशेष रूप से MVC फ्रेमवर्क के बारे में। MVC का मतलब मॉडल-व्यू-कंट्रोलर है, और यह ग्राफिकल और वेब एप्लिकेशन के लिए एक लोकप्रिय सॉफ्टवेयर डिज़ाइन पैटर्न है।
MVC फ्रेमवर्क निम्न से बने होते हैं:
- मॉडल: व्यावसायिक तर्क और डेटा स्थिरता को संभालता है।
- व्यू: प्रस्तुति परत को चलाता है और यूजर इंटरफ़ेस बनाता है।
- कंट्रोलर: सब कुछ एक साथ जोड़ता है।
ActiveRecord Ruby on Rails फ्रेमवर्क में मॉडल घटक है। यह कोड और डेटा के बीच एक अमूर्त परत पेश करता है, इसलिए हमें स्वयं SQL कोड लिखने की आवश्यकता नहीं है। प्रत्येक मॉडल एक टेबल पर मैप किया जाता है और CRUD ऑपरेशन (बनाना, पढ़ना, अपडेट करना और हटाना) करने के लिए विभिन्न मेथड प्रदान करता है।
AppSignal के साथ ActiveRecord की निगरानी
अमूर्तताएं जादुई महसूस होती हैं — वे हमें उन विवरणों को अनदेखा करने में मदद करती हैं जिन्हें हमें जानने की आवश्यकता नहीं है और हाथ में काम पर ध्यान केंद्रित करती हैं। लेकिन जब चीजें उम्मीद के मुताबिक काम नहीं करती हैं, तो वे जो जटिलता जोड़ते हैं, वह मूल कारण निर्धारित करना कठिन बना सकती है। AppSignal हमें Rails में वास्तव में क्या हो रहा है इसका विस्तृत ब्रेकडाउन दे सकता है।
प्रतिक्रिया समय ग्राफ
आइए समस्या में पहली नज़र डालें। प्रदर्शन मुद्दों का निवारण एक पुनरावृत्त प्रक्रिया है। एक बार जब आपका Rails एप्लिकेशन AppSignal को रिपोर्ट कर रहा है, तो घटनाएं > प्रदर्शन पर जाएं।
प्रतिक्रिया समय ग्राफ प्रत्येक नेमस्पेस के लिए प्रतिक्रिया समय पर्सेंटाइल दिखाएगा। ActiveRecord घटनाओं को स्वचालित रूप से उस नेमस्पेस को सौंपा जाता है जिसमें क्वेरी निष्पादित की जाती हैं।
इसके बाद, इवेंट ग्रुप ग्राफ देखें। यह दर्शाता है कि श्रेणी के अनुसार कितना समय खपत होता है। जांचें कि active_record द्वारा कितना सापेक्ष समय खर्च किया जाता है। अपने सभी नेमस्पेस में उपयोग की जांच करें।
ग्राफ हमें तुरंत बताएगा कि हमें अपने कोड-अनुकूलन प्रयासों पर ध्यान केंद्रित करना चाहिए।
जब आप प्रदर्शन ग्राफ डैशबोर्ड में हैं, तो प्रतिक्रिया समय और थ्रूपुट की जांच करें ताकि यह सुनिश्चित हो सके कि आपके एप्लिकेशन पर सामान्य से अधिक गतिविधि नहीं है।
धीमी क्वेरी डैशबोर्ड
अब जब हमने पहचान लिया है कि समस्या डेटा-बाउंड है, तो आइए देखें कि क्या हम मूल कारण निर्धारित करने के लिए ज़ूम इन कर सकते हैं।
सुधारें > धीमी क्वेरी डैशबोर्ड खोलें। यह पृष्ठ समग्र समय पर प्रभाव के आधार पर रैंक की गई SQL क्वेरी की सूची दिखाता है। सभी ActiveRecord-उत्पन्न क्वेरी sql.active_record घटनाओं के रूप में दिखाई जाती हैं।
शीर्ष क्वेरी पर क्लिक करके उसके विवरण देखें। डैशबोर्ड औसत अवधि और क्वेरी टेक्स्ट दिखाता है।
नीचे स्क्रॉल करने से आपको पिछले कुछ घंटों में क्वेरी का प्रतिक्रिया समय और उत्पत्ति एक्शन दिखाई देगा।
आप पा सकते हैं कि कुछ एक्शन से एक संबद्ध घटना है। इसका मतलब है कि AppSignal ने क्वेरी चलने के दौरान एक प्रदर्शन घटना बनाई, लेकिन इसका मतलब यह नहीं है कि ActiveRecord इसका कारण है।
प्रदर्शन माप डैशबोर्ड
प्रदर्शन माप घटनाएं तब खोली जाती हैं जब AppSignal एक नए एंडपॉइंट या पृष्ठभूमि जॉब को रिकॉर्ड करता है।
घटनाएं प्रदर्शन > समस्या सूची डैशबोर्ड पर स्थित हैं।
घटना पृष्ठ प्रत्येक MVC घटक के लिए बीता हुआ समय और आवंटन की संख्या दिखाता है। ActiveRecord समस्याएं active_record श्रेणी में लंबी अवधि प्रदर्शित करेंगी।
घटना समयरेखा दिखाती है कि घटना समय के साथ कैसे आगे बढ़ी।
ActiveRecord मुद्दों को ढूंढना
इस खंड में, हम देखेंगे कि AppSignal कुछ सामान्य ActiveError मुद्दों की पहचान करने में कैसे मदद कर सकता है।
प्रासंगिक कॉलम चुनना
डेटाबेस ज्ञान कहता है कि हमें हमेशा काम के लिए आवश्यक कॉलम प्राप्त करने चाहिए। उदाहरण के लिए, SELECT * FROM people के बजाय हमें SELECT first_name, surname, birthdate FROM people करना चाहिए। यह सब ठीक है, लेकिन हम इसे Rails पर कैसे करते हैं?
डिफ़ॉल्ट रूप से, ActiveRecord सभी कॉलम प्राप्त करता है।
Person.all.each { # डेटा प्रोसेस करें }
सौभाग्य से, हमारे पास आवश्यक कॉलम चुनने के लिए select मेथड है:
Person.select(:name, :address, :birthdate).each { # डेटा प्रोसेस करें }
यह जुनूनी लग सकता है छोटे विवरणों के बारे में। लेकिन चौड़ी टेबल पर, सभी कॉलम चुनना केवल बेकार है। आप देखेंगे कि जब ऐसा होता है, तो ActiveRecord मेमोरी के बड़े हिस्से आवंटित करता है:
N+1 समस्याएं
N+1 समस्या तब होती है जब एप्लिकेशन डेटाबेस से रिकॉर्ड का एक सेट प्राप्त करता है और इसके माध्यम से लूप करता है। इससे एप्लिकेशन N+1 क्वेरी निष्पादित करता है, जहां N प्रारंभ में प्राप्त पंक्तियों की संख्या है। जैसा कि आप कल्पना कर सकते हैं, यह पैटर्न टेबल बढ़ने के साथ खराब स्केल करता है। यह इतना हानिकारक समस्या है कि AppSignal विशेष रूप से आपको इसके बारे में चेतावनी देता है:
N+1 समस्याएं आमतौर पर संबंधित मॉडल के साथ दिखाई देती हैं। कल्पना करें कि हमारे पास एक Person मॉडल है:
class Person < ApplicationRecord has_many :addresses end
प्रत्येक व्यक्ति के कई पते हो सकते हैं:
class Address < ApplicationRecord belongs_to :person end
डेटा प्राप्त करने का सबसे सीधा तरीका N+1 समस्या की ओर ले जाता है:
class RelatedTablesController < ApplicationController def index Person.all.each do |person| person.addresses.each do |address| address.address end end end end
आप AppSignal में देख सकते हैं कि एप्लिकेशन प्रति व्यक्ति एक SELECT चला रहा है:
इस विशेष मामले के लिए फिक्स सरल है: includes का उपयोग करें, जो ActiveRecord को आवश्यक कॉलम के लिए क्वेरी को अनुकूलित करने के लिए कहता है:
class RelatedTablesController < ApplicationController def index Person.all.includes(:addresses).each do |person| person.addresses.each do |address| address.address end end end end
अब हमारे पास N+1 के बजाय दो क्वेरी हैं:
Processing by RelatedTablesController#index as HTML (0.2ms) SELECT sqlite_version(*) ↳ app/controllers/related_tables_controller.rb:12:in `index' Person Load (334.6ms) SELECT "people".* FROM "people" ↳ app/controllers/related_tables_controller.rb:12:in `index' Address Load (144.4ms) SELECT "addresses".* FROM "addresses" WHERE "addresses"."person_id" IN (1, 2, 3, . . .)
प्रति टेबल कम से कम आवश्यक क्वेरी निष्पादित करें
यह कभी-कभी N+1 समस्या के साथ भ्रमित होता है, लेकिन यह थोड़ा अलग है। जब हम एक टेबल क्वेरी करते हैं, तो हमें उन सभी डेटा को प्राप्त करना चाहिए जो हमें लगता है कि हमें पढ़ने के ऑपरेशन को कम करने के लिए आवश्यक होंगे। हालांकि, बहुत सारे मासूम दिखने वाले कोड हैं जो अनावश्यक क्वेरी ट्रिगर करते हैं। उदाहरण के लिए, देखें कि व्यू में count कैसे हमेशा SELECT COUNT(*) क्वेरी का परिणाम देता है:
<ul> <% @people.each do |person| %> <li><%= person.name %></li> <% end %> </ul> <h2>व्यक्तियों की संख्या: <%= @people.count %></h2>
अब ActiveRecord दो क्वेरी करता है:
Rendering duplicated_table_query/index.html.erb within layouts/application (69.1ms) SELECT COUNT(*) FROM "people" WHERE "people"."name" = ? [["name", "John Waters"]] ↳ app/views/duplicated_table_query/index.html.erb:3 Person Load (14.6ms) SELECT "people".* FROM "people" WHERE "people"."name" = ? [["name", "John Waters"]] ↳ app/views/duplicated_table_query/index.html.erb:6
AppSignal में, आप जो लक्षण देखेंगे वह यह है कि एक ही टेबल पर दो active_record घटनाएं हैं:
वास्तविकता यह है कि हमें दो क्वेरी की आवश्यकता नहीं है; हमारे पास मेमोरी में पहले से ही सभी डेटा हैं जिनकी हमें आवश्यकता है। इस मामले में, समाधान count को size से बदलना है:
<ul> <% @people.each do |person| %> <li><%= person.name %></li> <% end %> </ul> <h2>व्यक्तियों की संख्या: <%= @people.size %></h2>
अब हमारे पास एक ही SELECT है, जैसा होना चाहिए:
Rendering duplicated_table_query/index.html.erb within layouts/application Person Load (63.2ms) SELECT "people".* FROM "people" WHERE "people"."name" = ? [["name", "Abdul Strosin"]] ↳ app/views/duplicated_table_query/index.html.erb:5
एक और समाधान डेटा को मेमोरी में कैश करने के लिए preload का उपयोग करना है।
Rails में एकत्रित डेटा की गणना
एकत्रीकरण का उपयोग डेटा के एक सेट के आधार पर एक मूल्य की गणना के लिए किया जाता है। डेटाबेस बड़े डेटासेट के साथ काम करने में महान हैं। यह वही है जो वे करते हैं और जिसके लिए हम उनका उपयोग करते हैं। दूसरी ओर, Rails का उपयोग करके एकत्रीकरण स्केल नहीं करता क्योंकि इसके लिए डेटाबेस से सभी रिकॉर्ड प्राप्त करने, उन्हें मेमोरी में रखने, और फिर उच्च-स्तरीय कोड का उपयोग करके गणना करने की आवश्यकता होती है।
हम Rails में एकत्रीकरण कर रहे हैं जब भी हम ActiveRecord एलिमेंट्स या अन्य एन्यूमरेबल्स पर max, min, या sum जैसे Ruby फ़ंक्शन का सहारा लेते हैं।
class AggregatedColumnsController < ApplicationController def index @mean = Number.pluck(:number).sum() end end
सौभाग्य से, ActiveRecord मॉडल में विशिष्ट मेथड शामिल हैं जो डेटाबेस में एकत्रीकरण फ़ंक्शन पर मैप करते हैं। उदाहरण के लिए, निम्न क्वेरी SELECT SUM(number) FROM ... पर मैप करती है, जो पिछले उदाहरण की तुलना में चलाने के लिए बहुत तेज और सस्ता है:
# कंट्रोलर class AggregatedColumnsController < ApplicationController def index @mean = Number.sum(:number) end end
Processing by AggregatedColumnsController#index as */* (2.4ms) SELECT SUM("numbers"."number") FROM "numbers"
यदि आपको अधिक जटिल या संयुक्त एकत्रीकरण फ़ंक्शन की आवश्यकता है, तो आपको थोड़ा कच्चा SQL कोड शामिल करना पड़ सकता है:
sql = "SELECT AVG(number), STDDEV(number), VAR(number) FROM ..." @results = ActiveRecord::Base.connection.execute(sql)
बड़े लेनदेन का प्रबंधन
SQL लेनदेन सुसंगत और परमाणु अपडेट सुनिश्चित करते हैं। जब हम परिवर्तन करने के लिए लेनदेन का उपयोग करते हैं, या तो हर पंक्ति सफलतापूर्वक अपडेट होती है, या पूरी चीज रोल बैक हो जाती है। किसी भी मामले में, डेटाबेस हमेशा एक सुसंगत स्थिति में रहता है।
हम ActiveRecord::Base.transaction के साथ परिवर्तनों के एक बैच को एक ही लेनदेन में बंडल कर सकते हैं।
class BigTransactionController < ApplicationController def index ActiveRecord::Base.transaction do (1..1000).each do Person.create(name: 'Les Claypool') end end end end
बड़े लेनदेन का उपयोग करने के बहुत सारे वैध मामले हैं। हालांकि, ये डेटाबेस को धीमा करने का जोखिम उठाते हैं। इसके अलावा, कुछ सीमा से अधिक लेनदेन के परिणामस्वरूप डेटाबेस उन्हें अस्वीकार कर देगा।
लेनदेन के बहुत बड़े होने का पहला संकेत commit transaction घटनाओं पर बहुत समय खर्च करना है:
डेटाबेस पर कॉन्फ़िगरेशन मुद्दों को छोड़कर, समाधान लेनदेन को छोटे टुकड़ों में तोड़ना है।
डेटाबेस की निगरानी
कभी-कभी भले ही हम कोड खोजते हैं, हम इसके साथ कुछ भी गलत नहीं पा सकते। तब, डेटाबेस में ही कोई समस्या हो सकती है। डेटाबेस इंजन जटिल हैं, और कई चीजें गलत हो सकती हैं: कम मेमोरी, डिफ़ॉल्ट सेटिंग्स, गायब इंडेक्स, असुविधाजनक रूप से शेड्यूल किए गए बैकअप जॉब। तस्वीर पूरी नहीं हो सकती जब तक हमें डेटाबेस चलाने वाली मशीन के बारे में जानकारी नहीं मिलती।
यदि हम अपने स्वयं के डेटाबेस चला रहे हैं, तो हम होस्ट मेट्रिक्स को कैप्चर करने के लिए स्टैंडअलोन एजेंट इंस्टॉल कर सकते हैं। स्टैंडअलोन एजेंट का उपयोग कैसे करें, इसके बारे में अधिक जानने के लिए पढ़ें: StatsD और AppSignal के स्टैंडअलोन एजेंट के साथ किसी भी सिस्टम की निगरानी।
निम्नलिखित संकेत दिखा सकते हैं कि डेटाबेस के साथ कुछ हो रहा है। संसाधन उपयोग देखने के लिए निरीक्षण > होस्ट मेट्रिक्स डैशबोर्ड पर जाएं:
- उच्च मेमोरी उपयोग: डेटाबेस को सही ढंग से चलाने के लिए बहुत अधिक मेमोरी की आवश्यकता होती है — अधिकांश अन्य सिस्टम से अधिक। जैसे-जैसे डेटासेट बढ़ता है, मेमोरी आवश्यकताएं आमतौर पर साथ बढ़ती हैं। हमें समय-समय पर या तो अधिक मेमोरी जोड़नी होगी या डेटासेट को विभिन्न मशीनों में विभाजित करना होगा।
- स्वैप उपयोग: आदर्श रूप से, डेटाबेस मशीनों को स्वैप मेमोरी की बिल्कुल भी आवश्यकता नहीं होनी चाहिए। स्वैपिंग डेटाबेस प्रदर्शन को मार देती है। इसकी उपस्थिति का मतलब है कि अधिक गहरा कॉन्फ़िगरेशन या मेमोरी समस्या है।
- उच्च I/O उपयोग: डिस्क गतिविधि में चोटियां री-इंडेक्सिंग या डेटाबेस का बैकअप लेने जैसे रखरखाव कार्यों के कारण हो सकती हैं। पीक आवर्स के दौरान ये कार्य करना निश्चित रूप से प्रदर्शन को प्रभावित करेगा।
👋 यदि आप इस लेख को पसंद करते हैं, तो हमने Ruby (on Rails) प्रदर्शन के बारे में बहुत कुछ लिखा है, हमारी Ruby प्रदर्शन निगरानी चेकलिस्ट देखें।
निष्कर्ष
प्रदर्शन मुद्दों का निदान कभी भी आसान काम नहीं है। आज, हमने सीखा कि कैसे Appsignal का उपयोग करके समस्या के स्रोत को जल्दी से इंगित किया जाए।
आइए AppSignal और Ruby on Rails के बारे में सीखते रहें:
- ActiveRecord प्रदर्शन: N+1 क्वेरी एंटी-पैटर्न
- Rails तेज है: अपने व्यू प्रदर्शन को अनुकूलित करें
- Ruby on Rails पैटर्न और एंटी-पैटर्न का परिचय
P.S. यदि आप Ruby Magic पोस्ट को प्रेस से निकलते ही पढ़ना चाहते हैं, तो हमारे Ruby Magic न्यूज़लेटर की सदस्यता लें और कभी भी एक भी पोस्ट मिस न करें!