सभी स्तंभों को अपडेट करने का ओवरहेड क्या है, यहां तक ​​कि जो नहीं बदले हैं [बंद]


17

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

फायदा यह है कि आप अपडेट स्टेटमेंट को आसानी से बैच सकते हैं UPDATE स्टेटमेंट वही है जो आपके द्वारा बदले जाने वाली इकाई विशेषता के अनुरूप नहीं है। अधिक, आप सर्वर-साइड और क्लाइंट-साइड स्टेटमेंट कैशिंग का भी उपयोग कर सकते हैं।

इसलिए, यदि मैं एक इकाई लोड करता हूं और केवल एक ही संपत्ति निर्धारित करता हूं:

Post post = entityManager.find(Post.class, 1L);
post.setScore(12);

सभी कॉलम बदलने जा रहे हैं:

UPDATE post
SET    score = 12,
       title = 'High-Performance Java Persistence'
WHERE  id = 1

अब, यह मानते हुए कि हमारे पास titleसंपत्ति पर भी एक सूचकांक है , क्या डीबी को यह एहसास नहीं होना चाहिए कि मूल्य वैसे भी नहीं बदला है?

में इस लेख , मार्कस Winand का कहना है:

सभी स्तंभों पर अपडेट उसी पैटर्न को दिखाता है जिसे हम पहले ही पिछले अनुभागों में देख चुके हैं: प्रत्येक अतिरिक्त सूचकांक के साथ प्रतिक्रिया समय बढ़ता है।

मुझे आश्चर्य है कि डेटाबेस से संबंधित डेटा पेज को मेमोरी में लोड करने के बाद से यह ओवरहेड क्यों है और इसलिए यह पता लगा सकता है कि कॉलम वैल्यू को बदलना है या नहीं।

अनुक्रमणिका के लिए भी, यह कुछ भी फिर से संतुलित नहीं करता है क्योंकि सूचकांक मान उन स्तंभों के लिए नहीं बदलते हैं जो बदल नहीं गए हैं, फिर भी उन्हें UPDATE में शामिल किया गया था।

क्या यह है कि निरर्थक अपरिवर्तित कॉलम से जुड़े बी + ट्री इंडेक्स को भी नेविगेट करने की आवश्यकता है, केवल डेटाबेस को यह महसूस करने के लिए कि पत्ती का मूल्य अभी भी समान है?

बेशक, कुछ ORM उपकरण आपको केवल परिवर्तित गुणों को अद्यतन करने की अनुमति देते हैं:

UPDATE post
SET    score = 12,
WHERE  id = 1

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


1
यदि डेटाबेस PostgreSQL (या MVCC का उपयोग करने वाले कुछ अन्य ) थे, तो एक UPDATEव्यावहारिक रूप से DELETE+ के बराबर है INSERT(क्योंकि आप वास्तव में पंक्ति का नया V ersion बनाते हैं )। ओवरहेड उच्च है, और अनुक्रमित की संख्या के साथ बढ़ता है , खासकर यदि उनमें से कई स्तंभ जो उन्हें शामिल करते हैं, वास्तव में अपडेट किए जाते हैं, और सूचकांक का प्रतिनिधित्व करने के लिए उपयोग किए जाने वाले पेड़ (या जो भी) को एक महत्वपूर्ण बदलाव की आवश्यकता होती है। यह उन कॉलमों की संख्या नहीं है जो अपडेट किए गए हैं जो प्रासंगिक हैं, लेकिन चाहे आप किसी इंडेक्स के कॉलम भाग को अपडेट करते हैं या नहीं।
जोनलो

@joanolo यह केवल MVCC के कार्यान्वयन के लिए सही होना चाहिए। MySQL, Oracle (और अन्य) एक अपडेट करते हैं और बदले हुए कॉलम को UNDO स्पेस में स्थानांतरित करते हैं।
मॉर्गन टॉकर

2
मुझे यह इंगित करना चाहिए कि एक अच्छा ओआरएम ट्रैक करना चाहिए कि कौन से कॉलम को अपडेट की आवश्यकता है, और डेटाबेस को भेजे गए बयान को अनुकूलित करें। यह प्रासंगिक है, यदि केवल DB के लिए प्रेषित डेटा की मात्रा के लिए, विशेष रूप से यदि कुछ कॉलम लंबे पाठ या BLOB हैं
जोनलो

1
SQL सर्वर dba.stackexchange.com/q/114360/3690 के लिए इस पर चर्चा करते हुए प्रश्न
मार्टिन स्मिथ

2
आप कौन से DBMS का उपयोग कर रहे हैं?
a_horse_with_no_name

जवाबों:


12

मुझे पता है कि आप ज्यादातर चिंतित हैं UPDATE प्रदर्शन के बारे और , लेकिन एक साथी "ओआरएम" अनुरक्षक के रूप में, मुझे आपको "परिवर्तित" , "अशक्त" , और "डिफ़ॉल्ट" के बीच अंतर करने की समस्या पर एक और परिप्रेक्ष्य देना चाहिए मानों के , जो हैं एसक्यूएल में तीन अलग-अलग चीजें, लेकिन संभवतः जावा और अधिकांश ओआरएम में केवल एक चीज:

अपने औचित्य का अनुवाद करना INSERTबयानों के

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

INSERT INTO t (a, b)    VALUES (1, 2);
INSERT INTO t (a, b, c) VALUES (1, 2, DEFAULT);

यह सच नहीं है UPDATE, जहां पहले दो शब्द समतुल्य हैं, और तीसरे में एक पूरी तरह से अलग अर्थ है:

-- These are the same
UPDATE t SET a = 1, b = 2;
UPDATE t SET a = 1, b = 2, c = c;

-- This is different!
UPDATE t SET a = 1, b = 2, c = DEFAULT;

अधिकांश डेटाबेस क्लाइंट API, जिसमें JDBC और परिणाम, JPA शामिल हैं, एक DEFAULTअभिव्यक्ति को बाइंड चर में बाँधने की अनुमति नहीं देते हैं - ज्यादातर क्योंकि सर्वर इसे भी अनुमति नहीं देते हैं। यदि आप एक ही एसक्यूएल स्टेटमेंट को पूर्वोक्त बैटचैबिलिटी और स्टेटमेंट कैचेबिलिटी कारणों के लिए फिर से उपयोग करना चाहते हैं, तो आप दोनों मामलों में निम्नलिखित स्टेटमेंट का उपयोग करेंगे (मानकर(a, b, c) सभी कॉलम हैं t):

INSERT INTO t (a, b, c) VALUES (?, ?, ?);

और चूंकि cसेट नहीं किया गया है, आप शायद जावा nullको तीसरे बाइंड चर में बाँध देंगे , क्योंकि कई ओआरएम भी NULLऔर DEFAULT( jOOQ) के बीच अंतर नहीं कर सकते हैं , के , उदाहरण के लिए यहाँ एक अपवाद है)। वे केवल जावा देखते हैं nullऔर यह नहीं जानते कि क्या इसका मतलब है NULL(जैसा कि अज्ञात मूल्य में) या DEFAULT(जैसा कि असमान मूल्य में)।

कई मामलों में, यह अंतर कोई मायने नहीं रखता है, लेकिन यदि आपका कॉलम c निम्नलिखित में से किसी भी सुविधा का उपयोग कर रहा है, तो कथन बस है गलत है :

  • इसका एक DEFAULTक्लॉज है
  • यह एक ट्रिगर द्वारा उत्पन्न किया जा सकता है

वापस UPDATE बयानों के लिए

यद्यपि उपरोक्त सभी डेटाबेसों के लिए सही है, मैं आपको आश्वासन दे सकता हूं कि ट्रिगर मुद्दा ओरेकल डेटाबेस के लिए भी सही है। निम्नलिखित एसक्यूएल पर विचार करें:

CREATE TABLE x (a INT PRIMARY KEY, b INT, c INT, d INT);

INSERT INTO x VALUES (1, 1, 1, 1);

CREATE OR REPLACE TRIGGER t
  BEFORE UPDATE OF c, d
  ON x
BEGIN
  IF updating('c') THEN
    dbms_output.put_line('Updating c');
  END IF;
  IF updating('d') THEN
    dbms_output.put_line('Updating d');
  END IF;
END;
/

SET SERVEROUTPUT ON
UPDATE x SET b = 1 WHERE a = 1;
UPDATE x SET c = 1 WHERE a = 1;
UPDATE x SET d = 1 WHERE a = 1;
UPDATE x SET b = 1, c = 1, d = 1 WHERE a = 1;

जब आप ऊपर चलाते हैं, तो आपको निम्न आउटपुट दिखाई देगा:

table X created.
1 rows inserted.
TRIGGER T compiled
1 rows updated.
1 rows updated.
Updating c

1 rows updated.
Updating d

1 rows updated.
Updating c
Updating d

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

दूसरे शब्दों में:

हाइबरनेट का वर्तमान व्यवहार जिसे आप वर्णन कर रहे हैं वह अधूरा है और यहां तक ​​कि ट्रिगर्स (और शायद अन्य उपकरण) की उपस्थिति में भी इसे गलत माना जा सकता है।

मुझे व्यक्तिगत रूप से लगता है कि गतिशील SQL के मामले में आपके क्वेरी कैश ऑप्टिमाइज़ेशन तर्क को ओवरराइड किया गया है। निश्चित रूप से, इस तरह के कैश में कुछ और प्रश्न होंगे, और थोड़ा और पार्स करने का काम किया जाएगा, लेकिन यह आमतौर पर डायनेमिक UPDATEस्टेटमेंट के लिए समस्या नहीं है, जिसकी तुलना में बहुत कम है SELECT

बैचिंग निश्चित रूप से एक मुद्दा है, लेकिन मेरी राय में, सभी कॉलमों को अपडेट करने के लिए एक एकल अद्यतन को सामान्यीकृत नहीं किया जाना चाहिए, क्योंकि बयान के बल्लेबाजी करने की थोड़ी संभावना है। संभावना है, ORM लगातार समान बयानों के उप-बैचों को इकट्ठा कर सकते हैं और "पूरे बैच" के बजाय बैच कर सकते हैं (यदि ORM "परिवर्तित" , "अशक्त" और "डिफ़ॉल्ट" के बीच अंतर को ट्रैक करने में भी सक्षम है)


DEFAULTउपयोग के मामले ने संबोधित किया जा सकता है @DynamicInsert। TRIGGER स्थिति को चेक के उपयोग से भी संबोधित किया जा सकता है जैसे WHEN (NEW.b <> OLD.b)या बस स्विच करना @DynamicUpdate
व्लाद मिहालसी

हां, चीजों को संबोधित किया जा सकता है, लेकिन आप मूल रूप से प्रदर्शन के बारे में पूछ रहे थे और आपका वर्कअराउंड ओवरहेड भी जोड़ता है।
लुकास ईडर

मुझे लगता है कि मॉर्गन ने इसे सबसे अच्छा कहा: यह जटिल है
व्लाद मिहालसी

मुझे लगता है कि यह सरल है। एक फ्रेमवर्क परिप्रेक्ष्य से, गतिशील एसक्यूएल को डिफ़ॉल्ट करने के पक्ष में अधिक तर्क हैं। उपयोगकर्ता-परिप्रेक्ष्य से, हाँ, यह जटिल है।
लुकास एडर

9

मुझे लगता है कि जवाब है - यह जटिल है । मैंने longtextMySQL में एक कॉलम का उपयोग करके एक त्वरित प्रमाण लिखने की कोशिश की , लेकिन जवाब थोड़ा अनिर्णायक है। पहला सबूत:

# in advance:
set global max_allowed_packet=1024*1024*1024;

CREATE TABLE `t2` (
  `a` int(11) NOT NULL AUTO_INCREMENT,
  `b` char(255) NOT NULL,
  `c` LONGTEXT,
  PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

mysql> insert into t2 (a, b, c) values (null, 'b', REPEAT('c', 1024*1024*1024));
Query OK, 1 row affected (38.81 sec)

mysql> UPDATE t2 SET b='new'; # fast
Query OK, 1 row affected (6.73 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql>  UPDATE t2 SET b='new'; # fast
Query OK, 0 rows affected (2.87 sec)
Rows matched: 1  Changed: 0  Warnings: 0

mysql> UPDATE t2 SET b='new'; # fast
Query OK, 0 rows affected (2.61 sec)
Rows matched: 1  Changed: 0  Warnings: 0

mysql> UPDATE t2 SET c= REPEAT('d', 1024*1024*1024); # slow (changed value)
Query OK, 1 row affected (22.38 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> UPDATE t2 SET c= REPEAT('d', 1024*1024*1024); # still slow (no change)
Query OK, 0 rows affected (14.06 sec)
Rows matched: 1  Changed: 0  Warnings: 0

तो धीमी गति से परिवर्तित मूल्य के बीच एक छोटा सा समय अंतर है, और धीमी + कोई परिवर्तित मूल्य नहीं है। इसलिए मैंने एक और मैट्रिक देखने का फैसला किया, जिसमें लिखा था पेज:

mysql> show global status like 'innodb_pages_written';
+----------------------+--------+
| Variable_name        | Value  |
+----------------------+--------+
| Innodb_pages_written | 198656 |
+----------------------+--------+
1 row in set (0.00 sec)

mysql> show global status like 'innodb_pages_written';
+----------------------+--------+
| Variable_name        | Value  |
+----------------------+--------+
| Innodb_pages_written | 198775 | <-- 119 pages changed in a "no change"
+----------------------+--------+
1 row in set (0.01 sec)

mysql> show global status like 'innodb_pages_written';
+----------------------+--------+
| Variable_name        | Value  |
+----------------------+--------+
| Innodb_pages_written | 322494 | <-- 123719 pages changed in a "change"!
+----------------------+--------+
1 row in set (0.00 sec)

इसलिए ऐसा लगता है कि समय बढ़ गया है क्योंकि इस बात की पुष्टि करने के लिए एक तुलना होनी चाहिए कि मूल्य को संशोधित नहीं किया गया है, जो कि 1G लॉन्गटेक्स्ट के मामले में समय लगता है (क्योंकि यह कई पृष्ठों में विभाजित होता है)। लेकिन संशोधन खुद को लाल लॉग के माध्यम से मंथन नहीं करता है।

मुझे संदेह है कि यदि मान नियमित कॉलम हैं जो इन-पेज हैं तो तुलना केवल थोड़ा उपरिव्यय जोड़ देती है। और एक ही अनुकूलन लागू होता है, जब यह अद्यतन की बात आती है तो ये नो-ऑप्स होते हैं।

दीर्घ उत्तर

मुझे वास्तव में लगता है कि ओआरएम को उन स्तंभों को समाप्त नहीं करना चाहिए जिन्हें संशोधित किया गया है ( लेकिन बदला नहीं गया है ), क्योंकि इस अनुकूलन के अजीब दुष्प्रभाव हैं।

छद्म कोड में निम्नलिखित पर विचार करें:

# Initial Data does not make sense
# should be either "Harvey Dent" or "Two Face"

id: 1, firstname: "Two Face", lastname: "Dent"

session1.start
session2.start

session1.firstname = "Two"
session1.lastname = "Face"
session1.save

session2.firstname = "Harvey"
session2.lastname = "Dent"
session2.save

यदि ओआरएम परिवर्तन के बिना "ऑप्टिमाइज़ आउट" संशोधन करने के लिए था:

id: 1, firstname: "Harvey", lastname: "Face"

यदि ORM सर्वर पर सभी संशोधन भेजता है तो परिणाम:

id: 1, firstname: "Harvey", lastname: "Dent"

यहां टेस्ट-केस repeatable-readआइसोलेशन (MySQL डिफॉल्ट) पर निर्भर करता है , लेकिन एक टाइम-विंडो भी read-committedआइसोलेशन के लिए मौजूद होती है, जहां सेशन 2 रीड सेशन से पहले होता है।

इसे दूसरे तरीके से रखने के लिए: अनुकूलन केवल तभी सुरक्षित है जब आप एक के SELECT .. FOR UPDATEबाद पंक्तियों को पढ़ने के लिए जारी करते हैं UPDATESELECT .. FOR UPDATEMVCC का उपयोग नहीं करता है और हमेशा पंक्तियों के नवीनतम संस्करण को पढ़ता है।


संपादित करें: सुनिश्चित करें कि परीक्षण केस डेटा सेट मेमोरी में 100% था। समायोजित समय परिणाम।


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

1
@VladMihalcea खबर है कि इसका जवाब MySQL के बारे में है। विभिन्न डीबीएमएस में निष्कर्ष समान नहीं हो सकते हैं।
ypercube y

@ypercube मैं इससे अवगत हूं। यह सब RDBMS पर निर्भर करता है।
व्लाद मिहालसी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.