Mysql
 sql >> डेटाबेस >  >> RDS >> Mysql

MySQL अपडेट क्वेरी - क्या रेस कंडीशन और रो लॉकिंग पर 'कहां' कंडीशन का सम्मान किया जाएगा? (php, PDO, MySQL, InnoDB)

दौड़ की स्थिति के दौरान जहां स्थिति का सम्मान किया जाएगा, लेकिन आपको सावधान रहना चाहिए कि आप यह देखने के लिए कैसे जांच करते हैं कि किसने दौड़ जीती।

निम्नलिखित प्रदर्शन पर विचार करें कि यह कैसे काम करता है और आपको क्यों सावधान रहना है।

सबसे पहले, कुछ न्यूनतम टेबल सेट करें।

CREATE TABLE table1 (
`id` TINYINT UNSIGNED NOT NULL PRIMARY KEY,
`locked` TINYINT UNSIGNED NOT NULL,
`updated_by_connection_id` TINYINT UNSIGNED DEFAULT NULL
) ENGINE = InnoDB;

CREATE TABLE table2 (
`id` TINYINT UNSIGNED NOT NULL PRIMARY KEY
) ENGINE = InnoDB;

INSERT INTO table1
(`id`,`locked`)
VALUES
(1,0);

id id . की भूमिका निभाता है आपकी तालिका में, updated_by_connection_id assignedPhone . जैसा काम करता है , और locked जैसे reservationCompleted

अब रेस टेस्ट शुरू करते हैं। आपके पास 2 कमांडलाइन/टर्मिनल विंडो खुली होनी चाहिए, जो mysql से जुड़ी हो और उस डेटाबेस का उपयोग कर रही हो जहां आपने ये टेबल बनाई हैं।

कनेक्शन 1

start transaction;

कनेक्शन 2

start transaction;

कनेक्शन 1

UPDATE table1
SET locked = 1,
updated_by_connection_id = 1
WHERE id = 1
AND locked = 0;

कनेक्शन 2

UPDATE table1
SET locked = 1,
updated_by_connection_id = 2
WHERE id = 1
AND locked = 0;

कनेक्शन 2 अब प्रतीक्षा कर रहा है

कनेक्शन 1

SELECT * FROM table1 WHERE id = 1;
commit;

इस बिंदु पर, कनेक्शन 2 जारी रखने के लिए जारी किया जाता है और निम्नलिखित को आउटपुट करता है:

कनेक्शन 2

SELECT * FROM table1 WHERE id = 1;
commit;

सब कुछ ठीक लग रहा है। हम देखते हैं कि हाँ, दौड़ की स्थिति में WHERE क्लॉज का सम्मान किया गया था।

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

आइए निम्नलिखित के साथ डेटाबेस को रीसेट करें:

delete from table1;
INSERT INTO table1
(`id`,`locked`)
VALUES
(1,0);

और अब, इस स्थिति पर विचार करें, जहां अद्यतन से पहले एक चयन किया जाता है।

कनेक्शन 1

start transaction;

SELECT * FROM table2;

कनेक्शन 2

start transaction;

SELECT * FROM table2;

कनेक्शन 1

UPDATE table1
SET locked = 1,
updated_by_connection_id = 1
WHERE id = 1
AND locked = 0;

कनेक्शन 2

UPDATE table1
SET locked = 1,
updated_by_connection_id = 2
WHERE id = 1
AND locked = 0;

कनेक्शन 2 अब प्रतीक्षा कर रहा है

कनेक्शन 1

SELECT * FROM table1 WHERE id = 1;
SELECT * FROM table1 WHERE id = 1 FOR UPDATE;
commit;

इस बिंदु पर, कनेक्शन 2 जारी रखने के लिए जारी किया जाता है और निम्नलिखित को आउटपुट करता है:

ठीक है, देखते हैं कौन जीता:

कनेक्शन 2

SELECT * FROM table1 WHERE id = 1;

किसकी प्रतीक्षा? locked 0 और updated_by_connection_id शून्य??

यही वह सावधानी है जिसका मैंने उल्लेख किया है। अपराधी वास्तव में इस तथ्य के कारण है कि हमने शुरुआत में चयन किया था। सही परिणाम प्राप्त करने के लिए, हम निम्नलिखित चला सकते हैं:

SELECT * FROM table1 WHERE id = 1 FOR UPDATE;
commit;

SELECT... FOR UPDATE का उपयोग करके हम सही परिणाम प्राप्त कर सकते हैं। यह बहुत भ्रमित करने वाला हो सकता है (जैसा कि यह मेरे लिए था, मूल रूप से), एक चयन और एक चयन के रूप में ... अद्यतन के लिए दो अलग-अलग परिणाम दे रहे हैं।

ऐसा होने का कारण डिफ़ॉल्ट आइसोलेशन स्तर READ-REPEATABLE . है . जब पहला चयन किया जाता है, start transaction; , एक स्नैपशॉट बनाया जाता है। भविष्य के सभी गैर-अद्यतन पठन उस स्नैपशॉट से किए जाएंगे।

इसलिए, यदि आप अपडेट करने के बाद बस भोलेपन से SELECT करते हैं, तो यह उस मूल स्नैपशॉट से जानकारी खींच लेगा, जो कि पहले है पंक्ति अद्यतन किया गया है। SELECT... FOR UPDATE करके आप इसे सही जानकारी प्राप्त करने के लिए बाध्य करते हैं।

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

इसके बजाय, आप एक अलग ट्रैक लेना चाहेंगे। यहां आपके पास कई विकल्प हैं।

एक, यह सुनिश्चित करना है कि अद्यतन पूरा होने के बाद आप लेन-देन करें। ज्यादातर मामलों में, यह शायद सबसे अच्छा, सरल विकल्प है।

एक अन्य विकल्प परिणाम निर्धारित करने के लिए SELECT का उपयोग करने का प्रयास नहीं करना है। इसके बजाय, आप प्रभावित पंक्तियों को पढ़ने में सक्षम हो सकते हैं, और इसका उपयोग कर सकते हैं (1 पंक्ति अद्यतन बनाम 0 पंक्ति अद्यतन) यह निर्धारित करने के लिए कि अद्यतन सफल था या नहीं।

एक अन्य विकल्प, और एक जिसे मैं अक्सर उपयोग करता हूं, जैसा कि मैं एक ही अनुरोध (जैसे एक HTTP अनुरोध) को पूरी तरह से एक लेनदेन में लपेटकर रखना चाहता हूं, यह सुनिश्चित करना है कि लेनदेन में निष्पादित पहला कथन या तो अद्यतन है या एक चुनें ... अद्यतन के लिए . इससे स्नैपशॉट तब तक नहीं लिया जाएगा जब तक कि कनेक्शन को आगे बढ़ने की अनुमति नहीं दी जाती।

आइए अपने परीक्षण डेटाबेस को फिर से रीसेट करें और देखें कि यह कैसे काम करता है।

delete from table1;
INSERT INTO table1
(`id`,`locked`)
VALUES
(1,0);

कनेक्शन 1

start transaction;

SELECT * FROM table1 WHERE id = 1 FOR UPDATE;

कनेक्शन 2

start transaction;

SELECT * FROM table1 WHERE id = 1 FOR UPDATE;

कनेक्शन 2 अब प्रतीक्षा कर रहा है।

कनेक्शन 1

UPDATE table1
SET locked = 1,
updated_by_connection_id = 1
WHERE id = 1
AND locked = 0;
SELECT * FROM table1 WHERE id = 1;
SELECT * FROM table1 WHERE id = 1 FOR UPDATE;
commit;

कनेक्शन 2 अब जारी किया गया है।

कनेक्शन 2

+----+--------+--------------------------+
| id | locked | updated_by_connection_id |
+----+--------+--------------------------+
|  1 |      1 |                        1 |
+----+--------+--------------------------+

यहां आप वास्तव में अपने सर्वर साइड कोड को इस चयन के परिणामों की जांच कर सकते हैं और जान सकते हैं कि यह सटीक है, और अगले चरणों के साथ भी जारी नहीं है। लेकिन, पूर्णता के लिए, मैं पहले की तरह समाप्त करूँगा।

UPDATE table1
SET locked = 1,
updated_by_connection_id = 2
WHERE id = 1
AND locked = 0;
SELECT * FROM table1 WHERE id = 1;
SELECT * FROM table1 WHERE id = 1 FOR UPDATE;
commit;

अब आप देख सकते हैं कि कनेक्शन 2 में SELECT और SELECT... FOR UPDATE एक ही परिणाम देते हैं। ऐसा इसलिए है क्योंकि स्नैपशॉट जिसे SELECT से पढ़ता है, कनेक्शन 1 के प्रतिबद्ध होने के बाद तक नहीं बनाया गया था।

तो, अपने मूल प्रश्न पर वापस जाएं:हां, सभी मामलों में, अद्यतन विवरण द्वारा WHERE क्लॉज की जाँच की जाती है। हालांकि, आपको उस अद्यतन के परिणाम को गलत तरीके से निर्धारित करने से बचने के लिए, आपके द्वारा किए जा रहे किसी भी चयन से सावधान रहना होगा।

(हां एक अन्य विकल्प लेन-देन अलगाव स्तर को बदलना है। हालांकि, मुझे वास्तव में इसका अनुभव नहीं है और कोई भी गोच्या मौजूद हो सकता है, इसलिए मैं इसमें नहीं जा रहा हूं।)



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. डोकर कंटेनर में चल रहे Intellij से mySql से कनेक्ट करने में असमर्थ - निर्दिष्ट डेटाबेस उपयोगकर्ता/पासवर्ड संयोजन अस्वीकार कर दिया गया है

  2. रेल में महीने और साल दोनों के हिसाब से ग्रुप रिकॉर्ड्स

  3. mysql/php में id/parent_id मॉडल का उपयोग करके रिकॉर्ड के सभी माता-पिता को प्राप्त करने का सबसे आसान तरीका क्या है?

  4. MySQL GROUP BY NULL और EMPTY

  5. आप सामान्य योजना पर डेटा चर विचरण को कैसे मॉडल करेंगे? एसक्यूएल