एक 1000 मीटर पंक्ति MySQL तालिका का निर्माण


18

यह प्रश्न स्टैक ओवरफ्लो से रिपॉजिट किया गया है जो टिप्पणियों में एक सुझाव पर आधारित है, दोहराव के लिए माफी।

प्रशन

प्रश्न 1: जैसे-जैसे डेटाबेस टेबल का आकार बड़ा होता जाता है, मैं LOAD DATA INFILE कॉल की गति बढ़ाने के लिए MySQL को कैसे ट्यून कर सकता हूं?

प्रश्न 2: विभिन्न सीएसवी फ़ाइलों को लोड करने, प्रदर्शन में सुधार करने या इसे मारने के लिए कंप्यूटर के एक क्लस्टर का उपयोग करना होगा? (यह लोड डेटा और बल्क आवेषण का उपयोग करने के लिए कल मेरी बेंच-मार्किंग का काम है)

लक्ष्य

हम छवि खोज के लिए फीचर डिटेक्टरों और क्लस्टरिंग मापदंडों के विभिन्न संयोजनों की कोशिश कर रहे हैं, जिसके परिणामस्वरूप हमें समय पर फैशन में बड़े डेटाबेस बनाने और सक्षम होने की आवश्यकता है।

मशीन की जानकारी

मशीन में 256 गिग रैम है और डेटाबेस को वितरित करके निर्माण समय में सुधार करने का एक तरीका है, तो राम की समान मात्रा के साथ अन्य 2 मशीनें उपलब्ध हैं?

टेबल स्कीमा

तालिका स्कीमा जैसा दिखता है

+---------------+------------------+------+-----+---------+----------------+
| Field         | Type             | Null | Key | Default | Extra          |
+---------------+------------------+------+-----+---------+----------------+
| match_index   | int(10) unsigned | NO   | PRI | NULL    |                |
| cluster_index | int(10) unsigned | NO   | PRI | NULL    |                |
| id            | int(11)          | NO   | PRI | NULL    | auto_increment |
| tfidf         | float            | NO   |     | 0       |                |
+---------------+------------------+------+-----+---------+----------------+

के साथ बनाया

CREATE TABLE test 
(
  match_index INT UNSIGNED NOT NULL,
  cluster_index INT UNSIGNED NOT NULL, 
  id INT NOT NULL AUTO_INCREMENT,
  tfidf FLOAT NOT NULL DEFAULT 0,
  UNIQUE KEY (id),
  PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;

अब तक की बेंचमार्किंग

पहला कदम एक बाइनरी फ़ाइल से खाली तालिका में थोक आवेषण बनाम लोडिंग की तुलना करना था।

It took:  0:09:12.394571  to do  4,000  inserts with 5,000 rows per insert
It took: 0:03:11.368320 seconds to load 20,000,000 rows from a csv file

प्रदर्शन में अंतर को देखते हुए, मैं एक बाइनरी सीएसवी फ़ाइल से डेटा लोड करने के साथ चला गया हूं, पहले मैंने नीचे दिए गए कॉल का उपयोग करके 100K, 1M, 20M, 200M पंक्तियों से युक्त बाइनरी फ़ाइलों को लोड किया था।

LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test;

मैंने 2 घंटे के बाद 200M पंक्ति बाइनरी फ़ाइल (~ 3 जीबी सीएसवी फ़ाइल) लोड को मार दिया।

इसलिए मैंने तालिका बनाने के लिए एक स्क्रिप्ट चलाई, और एक बाइनरी फ़ाइल से अलग-अलग पंक्तियों को सम्मिलित किया, फिर तालिका को गिरा दिया, नीचे दिया गया ग्राफ़ देखें।

यहाँ छवि विवरण दर्ज करें

बाइनरी फ़ाइल से 1M पंक्तियों को सम्मिलित करने में लगभग 7 सेकंड का समय लगा। आगे मैंने एक समय में 1M पंक्तियों को बेंचमार्क करने का फैसला किया, यह देखने के लिए कि क्या किसी विशेष डेटाबेस आकार में एक अड़चन होने वाली है। एक बार जब डेटाबेस ने लगभग 59M पंक्तियों को मारा तो औसत डालने का समय लगभग 5,000 / सेकंड हो गया

यहाँ छवि विवरण दर्ज करें

वैश्विक key_buffer_size = 4294967296 सेट करने से छोटी बाइनरी फ़ाइलों को डालने के लिए गति में थोड़ा सुधार हुआ। नीचे दिया गया ग्राफ विभिन्न नंबरों की पंक्तियों के लिए गति दिखाता है

यहाँ छवि विवरण दर्ज करें

हालाँकि 1M पंक्तियों को सम्मिलित करने के कारण इसमें प्रदर्शन में सुधार नहीं हुआ।

पंक्तियाँ: 1,000,000 समय: 0: 04: 13.761428 आवेषण / सेकंड: 3,940

एक खाली डेटाबेस के लिए बनाम

पंक्तियाँ: 1,000,000 समय: 0: 00: 6.339295 आवेषण / सेक: 315,492

अपडेट करें

निम्न डेटा बनाम लोड डेटा कमांड का उपयोग करके लोड डेटा करना

SET autocommit=0;
SET foreign_key_checks=0;
SET unique_checks=0;
LOAD DATA INFILE '/mnt/imagesearch/tests/eggs.csv' INTO TABLE test_ClusterMatches;
SET foreign_key_checks=1;
SET unique_checks=1;
COMMIT;
यहाँ छवि विवरण दर्ज करें

तो यह डेटाबेस आकार के उत्पन्न होने के मामले में काफी आशाजनक लगता है, लेकिन अन्य सेटिंग्स लोड डेटा उल्लंघन कॉल के प्रदर्शन को प्रभावित करने के लिए प्रकट नहीं होती हैं।

मैंने तब विभिन्न मशीनों से कई फाइलें लोड करने की कोशिश की, लेकिन लोड डेटा इनफाइल कमांड टेबल को लॉक कर देता है, फाइलों के बड़े आकार के कारण अन्य मशीनों के साथ समय समाप्त हो जाता है

ERROR 1205 (HY000) at line 1: Lock wait timeout exceeded; try restarting transaction

बाइनरी फ़ाइल में पंक्तियों की संख्या बढ़ाना

rows:  10,000,000  seconds rows:  0:01:36.545094  inserts/sec:  103578.541236
rows:  20,000,000  seconds rows:  0:03:14.230782  inserts/sec:  102970.29026
rows:  30,000,000  seconds rows:  0:05:07.792266  inserts/sec:  97468.3359978
rows:  40,000,000  seconds rows:  0:06:53.465898  inserts/sec:  96743.1659866
rows:  50,000,000  seconds rows:  0:08:48.721011  inserts/sec:  94567.8324859
rows:  60,000,000  seconds rows:  0:10:32.888930  inserts/sec:  94803.3646283

समाधान: ऑटो इंक्रीमेंट का उपयोग करने के बजाय MySQL के बाहर आईडी को प्रीकंप्यूट करना

के साथ तालिका का निर्माण

CREATE TABLE test (
  match_index INT UNSIGNED NOT NULL,
  cluster_index INT UNSIGNED NOT NULL, 
  id INT NOT NULL ,
  tfidf FLOAT NOT NULL DEFAULT 0,
  PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;

SQL के साथ

LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';"

यहाँ छवि विवरण दर्ज करें

जैसे ही डेटाबेस आकार में बढ़ता है, स्क्रिप्ट को पूर्व-गणना करने के लिए अनुक्रमणिका प्रदर्शन हिट को हटा देती है।

अद्यतन 2 - मेमोरी टेबल का उपयोग करना

मोटे तौर पर 3 गुना तेजी से, बिना इन-मेमोरी टेबल को डिस्क-आधारित तालिका में स्थानांतरित करने की लागत को ध्यान में रखते हुए।

rows:  0  seconds rows:  0:00:26.661321  inserts/sec:  375075.18851
rows:  10000000  time:  0:00:32.765095  inserts/sec:  305202.83857
rows:  20000000  time:  0:00:38.937946  inserts/sec:  256818.888187
rows:  30000000  time:  0:00:35.170084  inserts/sec:  284332.559456
rows:  40000000  time:  0:00:33.371274  inserts/sec:  299658.922222
rows:  50000000  time:  0:00:39.396904  inserts/sec:  253827.051994
rows:  60000000  time:  0:00:37.719409  inserts/sec:  265115.500617
rows:  70000000  time:  0:00:32.993904  inserts/sec:  303086.291334
rows:  80000000  time:  0:00:33.818471  inserts/sec:  295696.396209
rows:  90000000  time:  0:00:33.534934  inserts/sec:  298196.501594

एक स्मृति आधारित तालिका में डेटा लोड हो रहा है और फिर एक डिस्क आधारित तालिका में कॉपी करके मात्रा में 10 मिनट 59.71 सेकंड का एक ओवरहेड क्वेरी के साथ 107,356,741 पंक्तियां कॉपी करने के लिए किया था

insert into test Select * from test2;

जो 100M पंक्तियों को लोड करने के लिए लगभग 15 मिनट बनाता है, जो लगभग एक डिस्क आधारित तालिका में सीधे डालने के समान है।


1
I think changing the primary key to just id should be faster. (Although I think you are not looking for this)
DavidEG

Hi David, thanks for the comment, unfortunately without the key the queries we need to do aren't fast enough ( the logic behind the primary key selection is outlined in this post stackoverflow.com/questions/4282526/mysql-group-by-optimization )
Ben

1
Is this just for testing? You might want to look at the MySQL MEMORY engine: dev.mysql.com/doc/refman/5.0/en/memory-storage-engine.html If you are planning to deploy this as an architecture I'm curious how you plan to recover from failures, it seems like something that would be better handled by MapReduce/Hadoop.
polynomial

Hi polynomial, thanks for the tip, at the moment we are just testing different feature detectors at different scales, once the database is generated it wont change much (in the current spec anyway)
Ben

जवाबों:


4

Good question - well explained.

how can I tune MySQL to increase the speed of the LOAD DATA INFILE call?

You've already got a high(ish) setting for the key buffer - but is it enough? I'm assuming this is a 64-bit installation (if not then the first thing you need to do is upgrade) and not running on MSNT. Have a look at the output of mysqltuner.pl after running a few tests.

In order to use the cache to best effect, you may find benefits in batching/pre-sorting the input data (most recent versions of the 'sort' command have a lot of functionality for sorting large datasets). Also if you generate the ID numbers outside of MySQL, then it may be more efficient.

would using a cluster of computers to load different csv files

Assuming (again) that you want to have the output set behave as a single table, then the only benefits you'll get are by distributing the work of sorting and generating ids - which you don't need more databases for. OTOH using a database cluster, you will get problems with contention (which you shouldn't see other than as performance problems).

If you can shard the data and handle the resulting datasets independently, then yes, you will get performance benefits - but this does not negate the the need to tune each node.

Check you've got at least 4 Gb for the sort_buffer_size.

Beyond that, the limiting factor on performance is all about disk I/O. There's lots of ways to address this - but you should probably be considering a mirrored set of striped datasets on SSDs for optimal performance.


1
  • Consider your limiting factor. It's almost certainly single-threaded CPU processing.
  • You've already determined that load data... is faster than insert, so use that.
  • You've already determined that really large files (by row number) slow things down a lot; you want to break them up into pieces.
  • Using non-overlapping primary keys, queue up at least N*CPU sets, using no more than one million rows... probably less (benchmark).
  • Use sequential blocks of primary keys in each file.

If you want to be really spiffy, you can create a multi-threaded program to feed a single file to a collection of named pipes and manage the insert instances.

In summary, you don't tune MySQL for this so much as you tune your workload to MySQL.


-1

I do not remember exactly the syntacx but if it's inno db you can turn off foreign key check.

Also you can create the index after the import, it's can be a really performance gain.


Defering the index rebuild only improves performance where the number of rows already in the table is significantly smaller than the number of rows you are adding.
symcbean
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.