यह एक बड़ा सवाल है। InnoDB एक पंक्ति स्तर लॉकिंग इंजन है, लेकिन इसे बाइनरी लॉग के साथ सुरक्षा सुनिश्चित करने के लिए अतिरिक्त लॉक सेट करना पड़ता है (प्रतिकृति के लिए उपयोग किया जाता है; पॉइंट इन टाइम रिकवरी)। इसकी व्याख्या शुरू करने के लिए, निम्नलिखित (बेवकूफ) उदाहरण पर विचार करें:
session1> START TRANSACTION;
session1> DELETE FROM users WHERE is_deleted = 1; # 1 row matches (user_id 10), deleted.
session2> START TRANSACTION;
session2> UPDATE users SET is_deleted = 1 WHERE user_id = 5; # 1 row matches.
session2> COMMIT;
session1> COMMIT;
चूंकि बयान केवल एक बार किए गए बाइनरी लॉग में लिखे जाते हैं, दास सत्र # 2 पर पहले लागू होगा, और एक अलग परिणाम उत्पन्न करेगा, डेटा भ्रष्टाचार के लिए अग्रणी ।
तो InnoDB क्या करता है, अतिरिक्त ताले सेट करता है। अगर is_deleted
. है अनुक्रमित किया जाता है, फिर सत्र 1 के कमिट होने से पहले कोई अन्य या श्रेणी में सम्मिलित करने में सक्षम नहीं होगा उन रिकॉर्ड्स की संख्या जहां is_deleted=1
. अगर is_deleted
. पर कोई अनुक्रमणिका नहीं है , तो इनो डीबी को यह सुनिश्चित करने के लिए पूरी तालिका में प्रत्येक पंक्ति को लॉक करने की आवश्यकता है कि रीप्ले उसी क्रम में है। आप इसे अंतराल को बंद करना . के रूप में सोच सकते हैं , जो सीधे पंक्ति-स्तरीय लॉकिंग से अलग अवधारणा है ।
आपके मामले में उस ORDER BY position ASC
. के साथ , InnoDB को यह सुनिश्चित करने की आवश्यकता है कि न्यूनतम कुंजी मान और "विशेष" न्यूनतम संभव मान के बीच कोई नई पंक्तियों को संशोधित नहीं किया जा सकता है। यदि आपने कुछ ऐसा किया है जैसे ORDER BY position DESC
.. ठीक है, तो कोई भी इस श्रेणी में सम्मिलित नहीं हो सका।
तो समाधान यहां आता है:
-
कथन आधारित बाइनरी लॉगिंग बेकार है। मैं वास्तव में एक ऐसे भविष्य की प्रतीक्षा कर रहा हूं जहां हम सभी पंक्ति पर स्विच करें। आधारित बाइनरी लॉगिंग (MySQL 5.1 से उपलब्ध है, लेकिन डिफ़ॉल्ट रूप से चालू नहीं है)।
-
पंक्ति-आधारित प्रतिकृति के साथ, यदि आप आइसोलेशन स्तर को रीड-कमिटेड में बदलते हैं, तो मेल खाने वाली केवल एक पंक्ति को लॉक करने की आवश्यकता होती है।
-
यदि आप एक मर्दवादी बनना चाहते हैं, तो आप innodb_locks_unsafe_for_binlog कथन-आधारित प्रतिकृति के साथ।
अपडेट 22 अप्रैल :अपने टेस्टकेस के मेरे बेहतर संस्करण को कॉपी + पेस्ट करने के लिए (यह 'अंतराल में' नहीं खोज रहा था):
session1> CREATE TABLE test (id int not null primary key auto_increment, data1 int, data2 int, INDEX(data1)) engine=innodb;
Query OK, 0 rows affected (0.00 sec)
session1> INSERT INTO test VALUES (NULL, 1, 2), (NULL, 2, 1), (5, 2, 2), (6, 3, 3), (3, 3, 4), (4, 4, 3);
Query OK, 6 rows affected (0.00 sec)
Records: 6 Duplicates: 0 Warnings: 0
session1> start transaction;
Query OK, 0 rows affected (0.00 sec)
session1> SELECT id FROM test ORDER BY data1 LIMIT 1 FOR UPDATE;
+----+
| id |
+----+
| 1 |
+----+
1 row in set (0.00 sec)
session2> INSERT INTO test values (NULL, 0, 99); # blocks - 0 is in the gap between the lowest value found (1) and the "special" lowest value.
# At the same time, from information_schema:
localhost information_schema> select * from innodb_locks\G
*************************** 1. row ***************************
lock_id: 151A1C:1735:4:2
lock_trx_id: 151A1C
lock_mode: X,GAP
lock_type: RECORD
lock_table: `so5694658`.`test`
lock_index: `data1`
lock_space: 1735
lock_page: 4
lock_rec: 2
lock_data: 1, 1
*************************** 2. row ***************************
lock_id: 151A1A:1735:4:2
lock_trx_id: 151A1A
lock_mode: X
lock_type: RECORD
lock_table: `so5694658`.`test`
lock_index: `data1`
lock_space: 1735
lock_page: 4
lock_rec: 2
lock_data: 1, 1
2 rows in set (0.00 sec)
# Another example:
select * from test where id < 1 for update; # blocks