क्या सम्मिलित तालिकाओं के स्तंभों द्वारा सॉर्टिंग को अनुकूलित करने का एक तरीका है?


10

यह मेरी धीमी क्वेरी है:

SELECT `products_counts`.`cid`
FROM
  `products_counts` `products_counts`

  LEFT OUTER JOIN `products` `products` ON (
  `products_counts`.`product_id` = `products`.`id`
  )
  LEFT OUTER JOIN `trademarks` `trademark` ON (
  `products`.`trademark_id` = `trademark`.`id`
  )
  LEFT OUTER JOIN `suppliers` `supplier` ON (
  `products_counts`.`supplier_id` = `supplier`.`id`
  )
WHERE
  `products_counts`.product_id IN
  (159, 572, 1075, 1102, 1145, 1162, 1660, 2355, 2356, 2357, 3236, 6471, 6472, 6473, 8779, 9043, 9095, 9336, 9337, 9338, 9445, 10198, 10966, 10967, 10974, 11124, 11168, 16387, 16689, 16827, 17689, 17920, 17938, 17946, 17957, 21341, 21352, 21420, 21421, 21429, 21544, 27944, 27988, 30194, 30196, 30230, 30278, 30699, 31306, 31340, 32625, 34021, 34047, 38043, 43743, 48639, 48720, 52453, 55667, 56847, 57478, 58034, 61477, 62301, 65983, 66013, 66181, 66197, 66204, 66407, 66844, 66879, 67308, 68637, 73944, 74037, 74060, 77502, 90963, 101630, 101900, 101977, 101985, 101987, 105906, 108112, 123839, 126316, 135156, 135184, 138903, 142755, 143046, 143193, 143247, 144054, 150164, 150406, 154001, 154546, 157998, 159896, 161695, 163367, 170173, 172257, 172732, 173581, 174001, 175126, 181900, 182168, 182342, 182858, 182976, 183706, 183902, 183936, 184939, 185744, 287831, 362832, 363923, 7083107, 7173092, 7342593, 7342594, 7342595, 7728766)
ORDER BY
  products_counts.inflow ASC,
  supplier.delivery_period ASC,
  trademark.sort DESC,
  trademark.name ASC
LIMIT
  0, 3;

मेरे डेटासेट पर औसत क्वेरी समय 4.5 s है और यह अस्वीकार्य है।

समाधान मैं देख रहा हूँ:

ऑर्डर क्लॉज से products_countsटेबल पर सभी कॉलम जोड़ें । लेकिन मेरे पास आवेदन में ~ 10 ऑर्डर प्रकार हैं, इसलिए मुझे बहुत सारे कॉलम और इंडेक्स बनाने चाहिए। प्लस products_countsमें बहुत ही गहन रूप से अपडेट / इंसर्ट / डिलीट हैं, इसलिए मुझे तुरंत सभी ऑर्डर-संबंधित कॉलम (ट्रिगर्स का उपयोग करते हुए?) अपडेट करने की आवश्यकता है।

क्या कोई दूसरा उपाय है?

के बारे में बताएं:

+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
| id | select_type | table           | type   | possible_keys                               | key                    | key_len | ref                              | rows | Extra                                        |
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | products_counts | range  | product_id_supplier_id,product_id,pid_count | product_id_supplier_id | 4       | NULL                             |  227 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | products        | eq_ref | PRIMARY                                     | PRIMARY                | 4       | uaot.products_counts.product_id  |    1 |                                              |
|  1 | SIMPLE      | trademark       | eq_ref | PRIMARY                                     | PRIMARY                | 4       | uaot.products.trademark_id       |    1 |                                              |
|  1 | SIMPLE      | supplier        | eq_ref | PRIMARY                                     | PRIMARY                | 4       | uaot.products_counts.supplier_id |    1 |                                              |
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+

टेबल्स संरचना:

CREATE TABLE `products_counts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) unsigned NOT NULL,
  `supplier_id` int(11) unsigned NOT NULL,
  `count` int(11) unsigned NOT NULL,
  `cid` varchar(64) NOT NULL,
  `inflow` varchar(10) NOT NULL,
  `for_delete` tinyint(1) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `cid` (`cid`),
  UNIQUE KEY `product_id_supplier_id` (`product_id`,`supplier_id`),
  KEY `product_id` (`product_id`),
  KEY `count` (`count`),
  KEY `pid_count` (`product_id`,`count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `external_id` varchar(36) NOT NULL,
  `name` varchar(255) NOT NULL,
  `category_id` int(11) unsigned NOT NULL,
  `trademark_id` int(11) unsigned NOT NULL,
  `photo` varchar(255) NOT NULL,
  `sort` int(11) unsigned NOT NULL,
  `otech` tinyint(1) unsigned NOT NULL,
  `not_liquid` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `applicable` varchar(255) NOT NULL,
  `code_main` varchar(64) NOT NULL,
  `code_searchable` varchar(128) NOT NULL,
  `total` int(11) unsigned NOT NULL,
  `slider` int(11) unsigned NOT NULL,
  `slider_title` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `external_id` (`external_id`),
  KEY `category_id` (`category_id`),
  KEY `trademark_id` (`trademark_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `trademarks` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `external_id` varchar(36) NOT NULL,
  `name` varchar(255) NOT NULL,
  `country_id` int(11) NOT NULL,
  `sort` int(11) unsigned NOT NULL DEFAULT '0',
  `sort_list` int(10) unsigned NOT NULL DEFAULT '0',
  `is_featured` tinyint(1) unsigned NOT NULL,
  `is_direct` tinyint(1) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `external_id` (`external_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `suppliers` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `external_id` varchar(36) NOT NULL,
  `code` varchar(64) NOT NULL,
  `name` varchar(255) NOT NULL,
  `delivery_period` tinyint(1) unsigned NOT NULL,
  `is_default` tinyint(1) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `external_id` (`external_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

MySQL सर्वर जानकारी:

mysqld  Ver 5.5.45-1+deb.sury.org~trusty+1 for debian-linux-gnu on i686 ((Ubuntu))

3
क्या आप अनुक्रमणिका, तालिका स्कीमा और परीक्षण डेटा के साथ एक SQL फिडेल प्रदान कर सकते हैं? आपका लक्ष्य समय क्या है? क्या आप इसे 3 सेकंड, 1 सेकंड, 50 मिलीसेकंड में पूरा करना चाहते हैं? 1k, 100k, 100M के विभिन्न तालिकाओं में आपके कितने रिकॉर्ड हैं?
एरिक

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

क्या आपने एक इंडेक्स जोड़ने की कोशिश की है (inflow, product_id)?
ypercube y

सुनिश्चित करें कि आपके पास एक सभ्य है innodb_buffer_pool_size। आमतौर पर उपलब्ध रैम का लगभग 70% अच्छा होता है।
रिक जेम्स

जवाबों:


6

अपनी तालिका परिभाषाओं की समीक्षा करने से पता चलता है कि आपके पास शामिल तालिकाओं में मेल खाते हुए अनुक्रमित हैं। यह जुड़ने का कारण होना चाहिए MySQL'sतर्क जुड़ने की सीमा के भीतर जितनी जल्दी हो सके ।

हालांकि, कई तालिकाओं से छंटनी अधिक जटिल है।

2007 में सर्गेई पेट्रुनिया ने MySQLगति के क्रम में 3 सॉर्टिंग एल्गोरिदम का वर्णन किया MySQL: http://s.petrunia.net/blog/?m=201407

  1. आदेश-आधारित एक्सेस विधि का उपयोग करें जो ऑर्डर किए गए आउटपुट का उत्पादन करता है
  2. filesort()1 गैर-स्थिर तालिका पर उपयोग करें
  3. एक अस्थायी तालिका में शामिल होने के परिणाम डालें और filesort()उस पर उपयोग करें

ऊपर दिखाए गए तालिका परिभाषाओं और योगों से, आप देख सकते हैं कि आपको कभी भी सबसे तेज़ क्रम नहीं मिलेगा । इसका मतलब है कि आप filesort()जिस तरह के मानदंडों का उपयोग कर रहे हैं उसके लिए आप पर निर्भर होंगे ।

हालांकि, यदि आप एक भौतिकीकृत दृश्य का डिज़ाइन और उपयोग करते हैं, तो आप सबसे तेज़ सॉर्ट एल्गोरिथ्म का उपयोग करने में सक्षम होंगे

MySQL 5.5छँटाई विधियों के लिए परिभाषित विवरण देखने के लिए देखें: http://dev.mysql.com/doc/refman/5.5/en/order-by-optimization.html

के लिए MySQL 5.5(इस उदाहरण में) बढ़ाने के लिए ORDER BYगति अगर तुम नहीं मिल सकता है MySQLएक अतिरिक्त छंटाई चरण के बजाय अनुक्रमित उपयोग करने के लिए, निम्नलिखित रणनीतियों का प्रयास करें:

sort_buffer_sizeचर मान बढ़ाएँ ।

read_rnd_buffer_sizeचर मान बढ़ाएँ ।

• संग्रहीत किए जाने वाले वास्तविक मानों के लिए केवल बड़े रूप में स्तंभों की घोषणा करके कम रैम प्रति पंक्ति का उपयोग करें। [उदा। Varchar (256) को varchar (RealLongestString) में कम करें]

tmpdirबड़ी मात्रा में मुक्त स्थान के साथ एक समर्पित फ़ाइल सिस्टम को इंगित करने के लिए सिस्टम चर को बदलें । (अन्य विवरण उपरोक्त लिंक में दिए गए हैं।)

गति MySQL 5.7बढ़ाने के लिए प्रलेखन में अधिक विवरण दिया गया है ORDER, जिनमें से कुछ थोड़ा उन्नत व्यवहार हो सकते हैं:

http://dev.mysql.com/doc/refman/5.7/en/order-by-optimization.html

भौतिकवादी दृश्य - एक अलग दृष्टिकोण छाँटने में शामिल टेबल्स

आपने ट्रिगर्स का उपयोग करने के संदर्भ में अपने प्रश्न के साथ भौतिकवादी दृश्यों को प्रस्तुत किया। MySQL में Materialized View बनाने के लिए कोई कार्यक्षमता नहीं है, लेकिन आपके पास आवश्यक उपकरण हैं। लोड को फैलाने के लिए ट्रिगर्स का उपयोग करके आप पल भर में भौतिकीकृत दृश्य को बनाए रख सकते हैं।

Materialized देखें वास्तव में एक है तालिका जो आबादी है प्रक्रियात्मक कोड के माध्यम से निर्माण करने के लिए या पुनर्निर्माण materialized देखें और चलाता द्वारा बनाए रखा अप-टू-डेट डेटा रखने के लिए।

जब से आप एक तालिका बना रहे हैं जिसमें एक सूचकांक होगा , तब भौतिकीकृत दृश्य जब सबसे तेज़ क्रमबद्ध विधि का उपयोग कर सकते हैं : आदेश-आधारित एक्सेस विधि का उपयोग करें जो ऑर्डर किए गए आउटपुट का उत्पादन करता है

चूंकि मैटेरियलाइज्ड व्यूMySQL 5.5 को बनाए रखने के लिए ट्रिगर्स का उपयोग किया जाता है , इसलिए आपको प्रारंभिक मैटेरियलाइज्ड व्यू के निर्माण के लिए एक प्रक्रिया, स्क्रिप्ट या संग्रहीत प्रक्रिया की भी आवश्यकता होगी ।

लेकिन यह स्पष्ट रूप से बहुत भारी है कि बेस टेबलों के लिए प्रत्येक अद्यतन के बाद चलने के लिए एक प्रक्रिया है जहाँ आप डेटा का प्रबंधन करते हैं। यह वह जगह है जहाँ ट्रिगर डेटा में अप-टू-डेट रखने के लिए खेल में आते हैं क्योंकि परिवर्तन किए जाते हैं। इस तरह insert, प्रत्येक , updateऔर deleteअपने बदलावों का उपयोग करते हुए, अपने परिवर्तनों को भौतिककृत दृश्य में प्रचारित करेगा ।

Http://www.fromdual.com/ पर FROMDUAL संगठन के पास भौतिकीकृत दृश्य बनाए रखने के लिए नमूना कोड है । इसलिए, अपने स्वयं के नमूने लिखने के बजाय मैं आपको उनके नमूनों को इंगित करूंगा:

http://www.fromdual.com/mysql-materialized-views

उदाहरण 1: एक भौतिक दृश्य का निर्माण

DROP TABLE sales_mv;
CREATE TABLE sales_mv (
    product_name VARCHAR(128)  NOT NULL
  , price_sum    DECIMAL(10,2) NOT NULL
  , amount_sum   INT           NOT NULL
  , price_avg    FLOAT         NOT NULL
  , amount_avg   FLOAT         NOT NULL
  , sales_cnt    INT           NOT NULL
  , UNIQUE INDEX product (product_name)
);

INSERT INTO sales_mv
SELECT product_name
    , SUM(product_price), SUM(product_amount)
    , AVG(product_price), AVG(product_amount)
    , COUNT(*)
  FROM sales
GROUP BY product_name;

यह आपको रिफ्रेश के क्षण में मैटेरियलाइज्ड व्यू देता है । हालाँकि, चूंकि आपके पास तेज़ी से बढ़ने वाला डेटाबेस है, आप इस दृश्य को यथासंभव अद्यतित रखना चाहते हैं।

इसलिए आधार डेटा टेबल प्रभावित होने के लिए ट्रिगर्स को बेस टेबल से मैटेरियलाइज्ड व्यू टेबल में बदलने की जरूरत होती है । एक उदाहरण के रूप में:

उदाहरण 2: एक भौतिक दृश्य में नए डेटा को सम्मिलित करना

DELIMITER $$

CREATE TRIGGER sales_ins
AFTER INSERT ON sales
FOR EACH ROW
BEGIN

  SET @old_price_sum = 0;
  SET @old_amount_sum = 0;
  SET @old_price_avg = 0;
  SET @old_amount_avg = 0;
  SET @old_sales_cnt = 0;

  SELECT IFNULL(price_sum, 0), IFNULL(amount_sum, 0), IFNULL(price_avg, 0)
       , IFNULL(amount_avg, 0), IFNULL(sales_cnt, 0)
    FROM sales_mv
   WHERE product_name = NEW.product_name
    INTO @old_price_sum, @old_amount_sum, @old_price_avg
       , @old_amount_avg, @old_sales_cnt
  ;

  SET @new_price_sum = @old_price_sum + NEW.product_price;
  SET @new_amount_sum = @old_amount_sum + NEW.product_amount;
  SET @new_sales_cnt = @old_sales_cnt + 1;
  SET @new_price_avg = @new_price_sum / @new_sales_cnt;
  SET @new_amount_avg = @new_amount_sum / @new_sales_cnt;

  REPLACE INTO sales_mv
  VALUES(NEW.product_name, @new_price_sum, @new_amount_sum, @new_price_avg
       , @new_amount_avg, @new_sales_cnt)
  ;

END;
$$
DELIMITER ;

बेशक, आपको भौतिक रूप से हटाए गए डेटा और भौतिक दृष्टि में अपडेट डेटा को हटाने के लिए ट्रिगर की भी आवश्यकता होगी । इन ट्रिगर्स के लिए भी नमूने उपलब्ध हैं।

पिछले दिनों: कैसे बनाता है कि छँटाई में शामिल होने में तेजी से तालिकाओं?

Materialized देखें के रूप में अद्यतन इसे करने के लिए बना रहे हैं लगातार बनाया जा रहा है। इसलिए आप उस इंडेक्स (या इंडेक्स ) को परिभाषित कर सकते हैं जिसे आप भौतिकीकृत दृश्य या तालिका में डेटा को सॉर्ट करने के लिए उपयोग करना चाहते हैं ।

यदि डेटा को बनाए रखने का ओवरहेड बहुत भारी नहीं है, तो आप भौतिकीकृत दृश्य को बनाए रखने के लिए प्रत्येक प्रासंगिक डेटा परिवर्तन के लिए कुछ संसाधन (CPU / IO / etc) खर्च कर रहे हैं और इस तरह सूचकांक डेटा अद्यतित और आसानी से उपलब्ध है। इसलिए, चयन आपके बाद से तेज़ होगा:

  1. अपने SELECT के लिए डेटा तैयार करने के लिए पहले से ही वृद्धिशील CPU और IO खर्च किए।
  2. मैटेरियलाइज्ड व्यू पर सूचकांक MySQL के लिए उपलब्ध सबसे तेज़ छँटाई विधि का उपयोग कर सकते हैं , अर्थात् इंडेक्स-आधारित एक्सेस विधि का उपयोग करें जो ऑर्डर किए गए आउटपुट का उत्पादन करता है

आपकी परिस्थितियों पर और आप समग्र प्रक्रिया के बारे में कैसा महसूस करते हैं, इस पर निर्भर करते हुए, आप हर रात धीमे अवधि में भौतिकवादी दृश्यों का पुनर्निर्माण करना चाहते हैं ।

नोट: में Microsoft SQL Server materialized दृश्य में भेजा जाता है इंडेक्स्ड दृश्य और स्वचालित रूप से आधार पर अपडेट किया जाता है इंडेक्स्ड देखें के मेटाडाटा।


6

यहाँ पर जाने के लिए एक पूरी बहुत कुछ नहीं है, लेकिन मुझे लगता है कि प्राथमिक मुद्दा यह है कि आप हर बार डिस्क पर एक काफी बड़ी अस्थायी तालिका और सॉर्ट फ़ाइल बना रहे हैं। इसका कारण:

  1. आप UTF8 का उपयोग कर रहे हैं
  2. आप छंटाई के लिए कुछ बड़े varchar (255) फ़ील्ड का उपयोग कर रहे हैं

इसका मतलब है कि आपकी अस्थायी तालिका और सॉर्ट फ़ाइल काफी बड़ी हो सकती है, क्योंकि अस्थायी तालिका बनाते समय फ़ील्ड अधिकतम लंबाई में बनाए जाते हैं, और जब रिकॉर्ड सॉर्टिंग अधिकतम लंबाई में होती है (और UTF8 प्रति वर्ण 3 बाइट्स है)। इन-मेमोरी अस्थायी तालिका का उपयोग करने की संभावना भी है। अधिक जानकारी के लिए, आंतरिक अस्थायी तालिका विवरण देखें

लिमिट भी हमें यहां अच्छा नहीं करता है, क्योंकि हमें पहले 3 पंक्तियों के बारे में जानने से पहले पूरा परिणाम निर्धारित करने और आदेश देने की आवश्यकता है।

क्या आपने अपने tmpdir को tmpfs फाइल सिस्टम में ले जाने की कोशिश की है ? यदि / tmp पहले से ही tmpfs का उपयोग नहीं कर रहा है (MySQL tmpdir=/tmpडिफ़ॉल्ट रूप से * nix पर) का उपयोग करता है, तो आप सीधे / dev / shp का उपयोग कर सकते हैं। आपकी my.cnf फ़ाइल में:

[mysqld]
...
tmpdir=/dev/shm  

फिर आपको mysqld को पुनरारंभ करना होगा।

जो बहुत बड़ा बदलाव ला सकता था । यदि आपको सिस्टम पर मेमोरी प्रेशर के तहत आने की संभावना है, तो आप संभवतः साइज़ को कैप करना चाहते हैं (आमतौर पर linux डिस्ट्रोस कैप टैम्पफ़्स को डिफ़ॉल्ट रूप से कुल रैम के 50% पर), ताकि डिस्क से बाहर मेमोरी सेगमेंट से बचने के लिए या यहाँ तक कि एक OOM स्थिति बदतर । आप पंक्ति को संपादित करके ऐसा कर सकते हैं /etc/fstab:

tmpfs                   /dev/shm                tmpfs   rw,size=2G,noexec,nodev,noatime,nodiratime        0 0

आप इसे "ऑनलाइन" भी आकार दे सकते हैं। उदाहरण के लिए:

mount -o remount,size=2G,noexec,nodev,noatime,nodiratime /dev/shm

आप MySQL 5.6 में भी अपग्रेड कर सकते हैं - जिसमें प्रदर्शन करने वाली उपश्रेणियाँ और व्युत्पन्न टेबल हैं - और क्वेरी के साथ थोड़ा और अधिक खेल सकते हैं। मुझे नहीं लगता कि हम जो देखते हैं उससे बड़ी जीत को उस रास्ते पर जाते हुए देखेंगे।

सौभाग्य!


आपके उत्तर के लिए धन्यवाद। Tmpdir को tmpfs में ले जाने से एक अच्छा परफॉमेंस हासिल हुआ।
स्टानिस्लाव गमायुनोव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.