आपकी क्वेरी को निष्पादित करने में 2 घंटे लगने के लिए कुछ गंभीर रूप से गलत होना चाहिए, जब मैं समान हार्डवेयर पर 60 सेकंड से कम समय में वही काम कर सकता हूं।
निम्न में से कुछ मददगार साबित हो सकते हैं...
अपने इंजन के लिए MySQL ट्यून करें
अपने सर्वर कॉन्फ़िगरेशन की जाँच करें और तदनुसार अनुकूलित करें। निम्नलिखित में से कुछ संसाधन उपयोगी होने चाहिए।
- http ://www.mysqlperformanceblog.com/2006/09/29/what-to-tune-in-mysql-server-after-installation/
- http://www.mysqlperformanceblog.com/
- http://www.highperfmysql.com/
- http://forge.mysql.com/wiki/ServerVariables
- http://dev.mysql. com/doc/refman/5.0/hi/server-system-variables.html
- http:/ /www.xaprb.com/blog/2006/07/04/how-to-exploit-mysql-index-optimizations/
- http://jpipes.com/presentations/perf_tuning_best_practices.pdf
- http://jpipes.com/presentations/index_coding_optimization.pdf
- http://www.jasny.net/?p=36
अब कम स्पष्ट के लिए...
डेटा सर्वर पक्ष को संसाधित करने के लिए संग्रहीत कार्यविधि का उपयोग करने पर विचार करें
MySQL के अंदर सभी डेटा को संसाधित क्यों न करें ताकि आपको अपनी एप्लिकेशन परत पर बड़ी मात्रा में डेटा न भेजना पड़े? निम्न उदाहरण 2 मिनट से कम समय में 50M पंक्तियों को सर्वर साइड से लूप और प्रोसेस करने के लिए एक कर्सर का उपयोग करता है। मैं कर्सर का बहुत बड़ा प्रशंसक नहीं हूं, विशेष रूप से MySQL में जहां वे बहुत सीमित हैं, लेकिन मुझे लगता है कि आप परिणामसेट को लूप कर रहे होंगे और कुछ प्रकार के संख्यात्मक विश्लेषण कर रहे होंगे, इसलिए इस मामले में कर्सर का उपयोग उचित है।पी>
सरलीकृत myisam परिणाम तालिका - आपके आधार पर कुंजियां.
drop table if exists results_1mregr_c_ew_f;
create table results_1mregr_c_ew_f
(
id int unsigned not null auto_increment primary key,
rc tinyint unsigned not null,
df int unsigned not null default 0,
val double(10,4) not null default 0,
ts timestamp not null default now(),
key (rc, df)
)
engine=myisam;
मैंने आपके उदाहरण में लगभग समान कार्डिनैलिटी वाले प्रमुख क्षेत्रों के साथ डेटा की 100M पंक्तियाँ उत्पन्न कीं:
show indexes from results_1mregr_c_ew_f;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Index_type
===== ========== ======== ============ =========== ========= =========== ==========
results_1mregr_c_ew_f 0 PRIMARY 1 id A 100000000 BTREE
results_1mregr_c_ew_f 1 rc 1 rc A 2 BTREE
results_1mregr_c_ew_f 1 rc 2 df A 223 BTREE
संग्रहीत प्रक्रिया
मैंने एक सरल संग्रहित प्रक्रिया बनाई है जो आवश्यक डेटा प्राप्त करती है और इसे संसाधित करती है (आपके उदाहरण के समान स्थिति का उपयोग करती है)
drop procedure if exists process_results_1mregr_c_ew_f;
delimiter #
create procedure process_results_1mregr_c_ew_f
(
in p_rc tinyint unsigned,
in p_df int unsigned
)
begin
declare v_count int unsigned default 0;
declare v_done tinyint default 0;
declare v_id int unsigned;
declare v_result_cur cursor for select id from results_1mregr_c_ew_f where rc = p_rc and df > p_df;
declare continue handler for not found set v_done = 1;
open v_result_cur;
repeat
fetch v_result_cur into v_id;
set v_count = v_count + 1;
-- do work...
until v_done end repeat;
close v_result_cur;
select v_count as counter;
end #
delimiter ;
निम्नलिखित रनटाइम देखे गए:
call process_results_1mregr_c_ew_f(0,60);
runtime 1 = 03:24.999 Query OK (3 mins 25 secs)
runtime 2 = 03:32.196 Query OK (3 mins 32 secs)
call process_results_1mregr_c_ew_f(1,60);
runtime 1 = 04:59.861 Query OK (4 mins 59 secs)
runtime 2 = 04:41.814 Query OK (4 mins 41 secs)
counter
========
23000002 (23 million rows processed in each case)
हम्म्म्म, अगले विचार पर प्रदर्शन थोड़ा निराशाजनक है।
इनोडब इंजन (शॉक हॉरर) का उपयोग करने पर विचार करें
क्यों मासूम ?? क्योंकि इसमें क्लस्टर इंडेक्स हैं! आप innodb का उपयोग करके धीमी गति से सम्मिलित करना पाएंगे, लेकिन उम्मीद है कि यह पढ़ने में तेज़ होगा इसलिए यह एक व्यापार बंद है जो इसके लायक हो सकता है।
क्लस्टर्ड इंडेक्स के माध्यम से एक पंक्ति तक पहुंचना तेज़ है क्योंकि पंक्ति डेटा उसी पृष्ठ पर है जहां अनुक्रमणिका खोज होती है। यदि कोई तालिका बड़ी है, तो क्लस्टर्ड इंडेक्स आर्किटेक्चर अक्सर डिस्क I/O ऑपरेशन को सहेजता है, जब उन स्टोरेज संगठनों की तुलना में जो इंडेक्स रिकॉर्ड से अलग पेज का उपयोग करके पंक्ति डेटा स्टोर करते हैं। उदाहरण के लिए, MyISAM एक फ़ाइल का उपयोग डेटा पंक्तियों के लिए और दूसरा अनुक्रमणिका रिकॉर्ड के लिए करता है।
अधिक जानकारी यहाँ:
सरलीकृत innodb परिणाम तालिका
drop table if exists results_innodb;
create table results_innodb
(
rc tinyint unsigned not null,
df int unsigned not null default 0,
id int unsigned not null, -- cant auto_inc this !!
val double(10,4) not null default 0,
ts timestamp not null default now(),
primary key (rc, df, id) -- note clustered (innodb only !) composite PK
)
engine=innodb;
innodb के साथ एक समस्या यह है कि auto_increment फ़ील्ड का समर्थन नहीं करता है जो एक समग्र कुंजी का हिस्सा बनता है, इसलिए आपको अनुक्रम जनरेटर, ट्रिगर या किसी अन्य विधि का उपयोग करके स्वयं को वृद्धिशील कुंजी मान प्रदान करना होगा - शायद परिणाम तालिका को पॉप्युलेट करने वाले एप्लिकेशन में ही ??
फिर से, मैंने आपके उदाहरण में लगभग समान कार्डिनैलिटी वाले प्रमुख क्षेत्रों के साथ डेटा की 100M पंक्तियाँ उत्पन्न कीं। चिंता न करें अगर ये आंकड़े माईसम उदाहरण से मेल नहीं खाते हैं क्योंकि इनोडब कार्डिनैलिटीज का अनुमान लगाता है, इसलिए वे बिल्कुल समान नहीं होंगे। (लेकिन वे हैं - एक ही डेटासेट का इस्तेमाल किया गया)
show indexes from results_innodb;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Index_type
===== ========== ======== ============ =========== ========= =========== ==========
results_innodb 0 PRIMARY 1 rc A 18 BTREE
results_innodb 0 PRIMARY 2 df A 18 BTREE
results_innodb 0 PRIMARY 3 id A 100000294 BTREE
संग्रहीत प्रक्रिया
संग्रहीत कार्यविधि बिल्कुल ऊपर myisam उदाहरण के समान है, लेकिन इसके बजाय innodb तालिका से डेटा का चयन करती है।
declare v_result_cur cursor for select id from results_innodb where rc = p_rc and df > p_df;
परिणाम इस प्रकार हैं:
call process_results_innodb(0,60);
runtime 1 = 01:53.407 Query OK (1 mins 53 secs)
runtime 2 = 01:52.088 Query OK (1 mins 52 secs)
call process_results_innodb(1,60);
runtime 1 = 02:01.201 Query OK (2 mins 01 secs)
runtime 2 = 01:49.737 Query OK (1 mins 50 secs)
counter
========
23000002 (23 million rows processed in each case)
लगभग 2-3 मिनट तेज myisam इंजन कार्यान्वयन की तुलना में! (निर्दोष FTW)
फूट डालो और जीतो
सर्वर साइड संग्रहीत प्रक्रिया में परिणामों को संसाधित करना जो एक कर्सर का उपयोग करता है, एक इष्टतम समाधान नहीं हो सकता है, विशेष रूप से MySQL के पास ऐसी सरणियों और जटिल डेटा संरचनाओं के लिए समर्थन नहीं है जो 3GL भाषाओं जैसे C# आदि या यहां तक कि अन्य डेटाबेस में आसानी से उपलब्ध हैं। Oracle PL/SQL के रूप में।
तो यहां विचार डेटा के बैचों को एक एप्लिकेशन परत (सी # जो कुछ भी) पर वापस करना है जो परिणामों को संग्रह आधारित डेटा संरचना में जोड़ सकता है और फिर डेटा को आंतरिक रूप से संसाधित कर सकता है।
संग्रहीत प्रक्रिया
संग्रहीत कार्यविधि में 3 पैरामीटर rc, df_low और df_high लगते हैं जो आपको निम्न प्रकार से डेटा की एक श्रेणी का चयन करने की अनुमति देता है:
call list_results_innodb(0,1,1); -- df 1
call list_results_innodb(0,1,10); -- df between 1 and 10
call list_results_innodb(0,60,120); -- df between 60 and 120 etc...
स्पष्ट रूप से df रेंज जितनी अधिक होगी, आप उतना ही अधिक डेटा निकालेंगे।
drop procedure if exists list_results_innodb;
delimiter #
create procedure list_results_innodb
(
in p_rc tinyint unsigned,
in p_df_low int unsigned,
in p_df_high int unsigned
)
begin
select rc, df, id from results_innodb where rc = p_rc and df between p_df_low and p_df_high;
end #
delimiter ;
मैंने एक myisam संस्करण भी खटखटाया जो उपयोग की गई तालिका को छोड़कर समान है।
call list_results_1mregr_c_ew_f(0,1,1);
call list_results_1mregr_c_ew_f(0,1,10);
call list_results_1mregr_c_ew_f(0,60,120);
ऊपर दिए गए कर्सर उदाहरण के आधार पर मुझे उम्मीद है कि इनोडब संस्करण myisam वाले से बेहतर प्रदर्शन करेगा।
मैंने एक त्वरित और गंदा विकसित किया है बहु-थ्रेडेड सी # एप्लिकेशन जो संग्रहीत प्रक्रिया को कॉल करेगा और परिणामों को पोस्ट क्वेरी प्रोसेसिंग के लिए संग्रह में जोड़ देगा। आपको थ्रेड्स का उपयोग करने की आवश्यकता नहीं है, समान बैच की गई क्वेरी दृष्टिकोण प्रदर्शन के बहुत नुकसान के बिना क्रमिक रूप से किया जा सकता है।
प्रत्येक थ्रेड (क्वेरी थ्रेड) df डेटा की एक श्रेणी का चयन करता है, परिणामसेट को लूप करता है और परिणाम संग्रह में प्रत्येक परिणाम (पंक्ति) जोड़ता है।
class Program
{
static void Main(string[] args)
{
const int MAX_THREADS = 12;
const int MAX_RC = 120;
List<AutoResetEvent> signals = new List<AutoResetEvent>();
ResultDictionary results = new ResultDictionary(); // thread safe collection
DateTime startTime = DateTime.Now;
int step = (int)Math.Ceiling((double)MAX_RC / MAX_THREADS) -1;
int start = 1, end = 0;
for (int i = 0; i < MAX_THREADS; i++){
end = (i == MAX_THREADS - 1) ? MAX_RC : end + step;
signals.Add(new AutoResetEvent(false));
QueryThread st = new QueryThread(i,signals[i],results,0,start,end);
start = end + 1;
}
WaitHandle.WaitAll(signals.ToArray());
TimeSpan runTime = DateTime.Now - startTime;
Console.WriteLine("{0} results fetched and looped in {1} secs\nPress any key", results.Count, runTime.ToString());
Console.ReadKey();
}
}
रनटाइम इस प्रकार देखा गया:
Thread 04 done - 31580517
Thread 06 done - 44313475
Thread 07 done - 45776055
Thread 03 done - 46292196
Thread 00 done - 47008566
Thread 10 done - 47910554
Thread 02 done - 48194632
Thread 09 done - 48201782
Thread 05 done - 48253744
Thread 08 done - 48332639
Thread 01 done - 48496235
Thread 11 done - 50000000
50000000 results fetched and looped in 00:00:55.5731786 secs
Press any key
तो 50 मिलियन पंक्तियों को लाया गया और 60 सेकंड से कम समय में संग्रह में जोड़ा गया।
मैंने myisam संग्रहीत प्रक्रिया का उपयोग करके वही काम करने की कोशिश की जिसे पूरा होने में 2 मिनट लगे।
50000000 results fetched and looped in 00:01:59.2144880 secs
innodb में जाना
मेरी सरलीकृत प्रणाली में myisam तालिका बहुत खराब प्रदर्शन नहीं करती है, इसलिए यह innodb में माइग्रेट करने के लायक नहीं हो सकता है। यदि आपने अपने परिणाम डेटा को एक innodb तालिका में कॉपी करने का निर्णय लिया है तो इसे निम्नानुसार करें:
start transaction;
insert into results_innodb
select <fields...> from results_1mregr_c_ew_f order by <innodb primary key>;
commit;
लेन-देन में पूरी चीज़ डालने और लपेटने से पहले innodb PK द्वारा परिणाम का आदेश देना चीजों को गति देगा।
मुझे आशा है कि इनमें से कुछ मददगार साबित होंगे।
शुभकामनाएँ