2017-07-06 19 views
7

Tôi có bảng rất đơn giản:Giải pháp cho Chèn Khóa Intention trong MySQL

CREATE TABLE `d` (
    `id` int(11) DEFAULT NULL, 
    UNIQUE KEY `id` (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

không có hồ sơ:

select * from d; 
Empty set (0,01 sec) 

Sau đó, tôi cố gắng mở hai giao dịch trong phiên khác nhau:

phiên # 1:

begin; 
Query OK, 0 rows affected (0,00 sec) 

select * from d where id = 100 for update; 
Empty set (0,00 sec) 

phiên # 2:

begin; 
Query OK, 0 rows affected (0,00 sec) 

select * from d where id = 700 for update; 
Empty set (0,00 sec) 

Bây giờ tôi cố gắng chèn kỷ lục mới trong phiên # 2 và phiên "đóng băng":

insert into d values (700); 

Và khi tôi cố gắng làm như vậy (có trường id khác) trong Phiên # 1 lỗi:

insert into d values (100); --> ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction in Session #1 
insert into d values (700); --> Query OK, 1 row affected (4,08 sec) in Session #2 

Làm cách nào để khắc phục bế tắc? tình trạng InnoDB là:

------------------------ 
LATEST DETECTED DEADLOCK 
------------------------ 
2017-07-06 15:59:25 0x70000350d000 
*** (1) TRANSACTION: 
TRANSACTION 43567, ACTIVE 15 sec inserting 
mysql tables in use 1, locked 1 
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1 
MySQL thread id 4, OS thread handle 123145358217216, query id 89 localhost root update 
insert into d values (700) 
*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 126 page no 4 n bits 72 index id of table `trx`.`d` trx id 43567 lock_mode X insert intention waiting 
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 
0: len 8; hex 73757072656d756d; asc supremum;; 

*** (2) TRANSACTION: 
TRANSACTION 43568, ACTIVE 7 sec inserting 
mysql tables in use 1, locked 1 
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1 
MySQL thread id 3, OS thread handle 123145357938688, query id 90 localhost root update 
insert into d values (100) 
*** (2) HOLDS THE LOCK(S): 
RECORD LOCKS space id 126 page no 4 n bits 72 index id of table `trx`.`d` trx id 43568 lock_mode X 
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 
0: len 8; hex 73757072656d756d; asc supremum;; 

*** (2) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 126 page no 4 n bits 72 index id of table `trx`.`d` trx id 43568 lock_mode X insert intention waiting 
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 
0: len 8; hex 73757072656d756d; asc supremum;; 

*** WE ROLL BACK TRANSACTION (2) 
+0

Bạn đang cố gắng giải quyết vấn đề gì :)? Điều này tự hỏi tại sao mysql đang làm những gì nó đang làm? hoặc có vấn đề cụ thể nào mà bạn đang cố gắng giải quyết không? – dm03514

+0

Tôi cố gắng để giải quyết deadlocks trong ứng dụng của tôi (tất nhiên bảng d là không thực tế). Đã thêm câu hỏi này cho câu hỏi rõ ràng hơn. –

Trả lời

1

Tôi nghi ngờ bế tắc xảy ra vì InnoDB bảo thủ khi xử lý "khoảng trống". Lưu ý rằng 100 và 700 là cả hai trong cùng một khu vực hoang sơ của đất hoang sơ. InnoDB không thể (hoặc ít nhất là không) đối phó với thực tế là "100" và "700" là khác nhau. InnoDB muốn gắn thẻ các hàng riêng lẻ nhưng không có hàng nào trong bảng có các id đó.

Giao dịch 2 có thể sẽ hết thời gian chờ (xem innodb_lock_wait_timeout). Khi bạn chọc vào giao dịch 1 lần thứ hai với # 2 vẫn muốn một khóa, InnoDB cúi đầu và bỏ cuộc.

Dòng dưới cùng: Trực tiếp với deadlocks. Khi chúng xảy ra, hãy bắt đầu lại tại số BEGIN. Bạn đã tìm thấy một trường hợp tối nghĩa khác mà một bế tắc không cần thiết xảy ra.

Hơn nữa, tôi nghi ngờ rằng việc sửa mã cho trường hợp này sẽ làm chậm hầu hết các trường hợp khác và dẫn đến một loạt lỗi sẽ mất nhiều bản phát hành để khám phá và khắc phục.

0

Lưu ý rằng bắt đầu với MySQL 8.0.1, giản đồ biểu diễn cho thấy ổ khóa dữ liệu InnoDB.

Xem https://dev.mysql.com/doc/refman/8.0/en/data-locks-table.html

Xem https://dev.mysql.com/doc/refman/8.0/en/data-lock-waits-table.html

Trong ví dụ này, sau khi chọn đầu tiên trong phiên 1 mình khóa là:

mysql> select * from performance_schema.data_locks \G 
*************************** 1. row ***************************                            
       ENGINE: INNODB                                    
     ENGINE_LOCK_ID: 1808:76                                    
ENGINE_TRANSACTION_ID: 1808                                     
      THREAD_ID: 35                                     
      EVENT_ID: 13081                                     
     OBJECT_SCHEMA: test                                     
      OBJECT_NAME: d                                      
     PARTITION_NAME: NULL                                     
    SUBPARTITION_NAME: NULL 
      INDEX_NAME: NULL 
OBJECT_INSTANCE_BEGIN: 139756088373592 
      LOCK_TYPE: TABLE 
      LOCK_MODE: IX 
      LOCK_STATUS: GRANTED 
      LOCK_DATA: NULL 
*************************** 2. row *************************** 
       ENGINE: INNODB 
     ENGINE_LOCK_ID: 1808:2:5:1 
ENGINE_TRANSACTION_ID: 1808 
      THREAD_ID: 35 
      EVENT_ID: 13111 
     OBJECT_SCHEMA: test 
      OBJECT_NAME: d 
     PARTITION_NAME: NULL 
    SUBPARTITION_NAME: NULL 
      INDEX_NAME: id 
OBJECT_INSTANCE_BEGIN: 139756088370552 
      LOCK_TYPE: RECORD 
      LOCK_MODE: X 
      LOCK_STATUS: GRANTED 
      LOCK_DATA: supremum pseudo-record <--- HERE 
2 rows in set (0.00 sec) 

Đây không phải là một giải pháp cho bế tắc chính nó, nhưng có khả năng hiển thị trên ổ khóa được thực hiện đi một chặng đường dài để hiểu vấn đề.

Ở đây, cả SELECT FOR UPDATE đều khóa bản ghi "suprenum", vì id 100 và 700 lớn hơn ID lớn nhất trong bảng (nó trống).

Khi có nhiều bản ghi hơn (nói tại ID = 500), cả hai truy vấn sẽ thực thi đồng thời, vì khoảng cách khác nhau trong ID sẽ bị khóa.

+0

Cảm ơn. Nhưng bây giờ tôi đã thử nghiệm nó trên MySQL 5.7 –

Các vấn đề liên quan