2012-07-05 45 views
6

Tôi có một thủ tục lưu trữ tạo ra UID từ một bảng "vé", nhưng dưới tải tôi nhận được rất nhiều deadlocks. Tôi đang gọi thủ tục này nhiều lần từ nhiều kết nối đồng thời bất cứ khi nào nhiệm vụ của tôi cần một UID mới.MySQL deadlocks với thủ tục lưu trữ tạo ra UID

BEGIN 
    DECLARE a_uid BIGINT(20) UNSIGNED; 
    START TRANSACTION; 
    SELECT uid INTO a_uid FROM uid_data FOR UPDATE; # Lock 
    INSERT INTO uid_data (stub) VALUES ('a') ON DUPLICATE KEY UPDATE uid=uid+1; 
    SELECT a_uid+1 AS `uid`; 
    COMMIT; 
END 

tôi đã xem xét sử dụng:

BEGIN 
    REPLACE INTO uid_data (stub) VALUES ('a'); 
    SELECT LAST_INSERT_ID(); 
END 

Tuy nhiên tôi không chắc rằng sẽ được an toàn với kết nối đồng thời là không có khóa, không giống như các thủ tục đầu tiên với SELECT FOR UPDATE.

Dưới đây là bảng:

mysql> DESCRIBE uid_data; 
+-------+---------------------+------+-----+---------+----------------+ 
| Field | Type    | Null | Key | Default | Extra   | 
+-------+---------------------+------+-----+---------+----------------+ 
| uid | bigint(20) unsigned | NO | PRI | NULL | auto_increment | 
| stub | char(1)    | NO | UNI | NULL |    | 
+-------+---------------------+------+-----+---------+----------------+ 

tôi đã thiết lập cho cô lập giao dịch đọc cam kết:

mysql> SHOW VARIABLES LIKE 'tx_isolation'; 
+---------------+-----------------+ 
| Variable_name | Value   | 
+---------------+-----------------+ 
| tx_isolation | READ-COMMITTED | 
+---------------+-----------------+ 

Đây là những gì tôi nhận được lại từ SHOW ENGINE INNODB STATUS;

... 
... dozens and dozens of the following record locks... 

Record lock, heap no 1046 PHYSICAL RECORD: n_fields 2; compact format; info bits 32 
0: len 1; hex 61; asc a;; 
1: len 8; hex 00000000000335f2; asc  5 ;; 

Record lock, heap no 1047 PHYSICAL RECORD: n_fields 2; compact format; info bits 32 
0: len 1; hex 61; asc a;; 
1: len 8; hex 00000000000335f1; asc  5 ;; 

*** (2) WAITING FOR THIS LOCK TO BE GRANTED: 
RECORD LOCKS space id 13 page no 4 n bits 1120 index `stub` of table `my_db`.`uid_data` trx id 13AA89 lock_mode X waiting 
Record lock, heap no 583 PHYSICAL RECORD: n_fields 2; compact format; info bits 32 
0: len 1; hex 61; asc a;; 
1: len 8; hex 00000000000334a8; asc  4 ;; 

*** WE ROLL BACK TRANSACTION (1) 

tôi Tôi rất biết ơn nếu ai đó có thể giải thích những gì đang xảy ra và cách họ có thể tránh được.

+0

Để biết thông tin: bế tắc xảy ra ngay cả khi sử dụng trình tự đơn giản này: 'BẮT ĐẦU GIAO DỊCH; CHỌN uid TỪ uid_data CHO CẬP NHẬT; CẬP NHẬT uid_data SET uid = uid +1 [[deadlock có thể ở đây]]; COMMIT; '(do đó nó không liên quan gì đến mệnh đề' ON DUPLICATE'). Tuy nhiên, không có bế tắc nào xảy ra với mức cô lập 'REPEATABLE READ;'. Tôi vẫn không biết phải kết luận gì từ thời điểm này. – RandomSeed

Trả lời

0

Một bế tắc xảy ra trong kịch bản này:

giao dịch 1: yêu cầu một khóa (SELECT...FOR UPDATE) và mua lại nó

giao dịch 2: yêu cầu một khóa (SELECT...FOR UPDATE) và phải đợi

giao dịch 1: cố gắng để chèn, lượt truy cập một bản sao, do đó thông tin cập nhật (INSERT...ON DUPLICATE KEY UPDATE) => bế tắc

tôi không quá chắc chắn về việc tái ason, tôi nghi ngờ nó có liên quan đến ON DUPLICATE KEY UPDATE. Tôi vẫn đang điều tra và sẽ quay trở lại nếu tôi tìm ra.

[Chỉnh sửa] Một bế tắc xảy ra ngay cả với:

BEGIN 
    START TRANSACTION; 
    SELECT uid FROM uid_data FOR UPDATE; 
    UPDATE uid_data SET uid = uid +1; -- here, a deadlock would be detected in a blocked, concurrent connection 
    COMMIT; 
END 

gì về điều này:

BEGIN 
    START TRANSACTION;  
    UPDATE uid_data SET uid = uid +1; 
    SELECT uid FROM uid_data; 
    COMMIT; 
END 

Bạn có thể thả stub cột của bạn hoàn toàn. Hạn chế duy nhất là bạn phải khởi tạo uid_data của bạn với một hàng.

+0

Điều đó có sửa đổi thủ tục lưu trữ xử lý đồng thời với bất kỳ loại khóa nào không? – Sencha

+0

@Sencha Có, 'UPDATE' là nguyên tử, và nó cũng khóa (các) hàng cho đến khi kết thúc giao dịch. Tuy nhiên, tôi vẫn còn rất tò mò về lý do của khóa chết trong chuỗi ban đầu của bạn (xem thêm ý kiến ​​của tôi cho câu hỏi của bạn). – RandomSeed

0

Bạn có thể thử sử dụng

UPDATE uid_data SET uid = LAST_INSERT_ID(uid+1); 
SELECT LAST_INSERT_ID(); 

trên một bảng như

CREATE TABLE `uid_data` (
    `uid` BIGINT(20) UNSIGNED NOT NULL 
) 
COLLATE='utf8_general_ci' 
ENGINE=MyISAM; 

Đây là chủ đề an toàn và sẽ không khóa bàn nếu nó là MyISAM (ngoại trừ trong báo cáo cập nhật thực tế).

2

Làm điều này:

CREATE TABLE tickets 
(
    uid serial 
) 

Sau đó, để có được những uid tiếp theo:

BEGIN 
    INSERT INTO tickets VALUES (NULL); 
    SELECT LAST_INSERT_ID(); 
END 

uid nối tiếp tương đương với

uid BIGINT(20) UNSIGNED NOT NULL PRIMARY KEY auto_increment 

Bạn không nên gặp bất kỳ sự bế tắc với cách tiếp cận này và có thể ném bao nhiêu kết nối vào nó như bạn muốn.

+0

Tôi nên thêm cho rõ ràng, rằng LAST_INSERT_ID() là phạm vi bị hạn chế - ví dụ: nếu 1000 trong số các truy vấn này đang chạy đồng thời, sẽ không bao giờ có bất kỳ rủi ro nào nhận sai số cho một kết nối khác. –

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