2011-01-31 28 views
5

Tôi có ứng dụng PHP/5.2 được điều khiển sử dụng giao dịch dưới MySQL/5.1 để có thể khôi phục nhiều lần chèn nếu gặp phải điều kiện lỗi. Tôi có các chức năng tái sử dụng khác nhau để chèn các loại mục khác nhau. Càng xa càng tốt.Giao dịch Rollback với LOCK BẢNG

Bây giờ tôi cần sử dụng khóa bảng cho một số phụ trang. Vì hướng dẫn chính thức đề xuất, tôi đang sử dụng SET autocommit=0 thay vì START TRANSACTION vì vậy LOCK TABLES không đưa ra cam kết ngầm định. Và, như tài liệu, mở khóa bàn ngầm cam kết bất kỳ giao dịch tích cực:

Và đây nằm vấn đề: nếu tôi chỉ đơn giản là tránh UNLOCK TABLES, nó sẽ xảy ra rằng cuộc gọi thứ hai để LOCK TABLES cam kết thay đổi cấp phát!

Dường như cách duy nhất là thực hiện tất cả LOCK TABLES cần thiết trong một tuyên bố đơn lẻ. Đó là một cơn ác mộng duy trì.

Vấn đề này có giải pháp hợp lý không?

Dưới đây là một kịch bản thử nghiệm nhỏ:

DROP TABLE IF EXISTS test; 

CREATE TABLE test (
    test_id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, 
    random_number INT(10) UNSIGNED NOT NULL, 
    PRIMARY KEY (test_id) 
) 
COLLATE='utf8_spanish_ci' 
ENGINE=InnoDB; 


-- No table locking: everything's fine 
START TRANSACTION; 
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND())); 
SELECT * FROM TEST ORDER BY test_id; 
ROLLBACK; 
SELECT * FROM TEST ORDER BY test_id; 



-- Table locking: everything's fine if I avoid START TRANSACTION 
SET autocommit=0; 
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND())); 
SELECT * FROM TEST ORDER BY test_id; 
ROLLBACK; 
SELECT * FROM TEST ORDER BY test_id; 
SET autocommit=1; 



-- Table locking: I cannot nest LOCK/UNLOCK blocks 
SET autocommit=0; 
LOCK TABLES test WRITE; 
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND())); 
SELECT * FROM TEST ORDER BY test_id; 
ROLLBACK; 
UNLOCK TABLES; -- Implicit commit 
SELECT * FROM TEST ORDER BY test_id; 
SET autocommit=1; 


-- Table locking: I cannot chain LOCK calls ether 
SET autocommit=0; 
LOCK TABLES test WRITE; 
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND())); 
SELECT * FROM TEST ORDER BY test_id; 
-- UNLOCK TABLES; 
LOCK TABLES test WRITE; -- Implicit commit 
INSERT INTO test (random_number) VALUES (ROUND(10000* RAND())); 
SELECT * FROM TEST ORDER BY test_id; 
-- UNLOCK TABLES; 
ROLLBACK; 
SELECT * FROM TEST ORDER BY test_id; 
SET autocommit=1; 
+0

Tại sao bạn không cần khóa? Vấn đề thực sự là gì? –

+0

Tôi cần khóa để đảm bảo rằng chỉ có một quy trình có thể sử dụng số thứ tự cho năm hiện tại và không còn thiếu sót nào trong chuỗi. Vấn đề thực sự là MySQL cam kết các tập dữ liệu không được xác nhận tự động khi bạn cố gắng sử dụng một tính năng không nhận biết giao dịch như khóa bảng, do đó đánh bại toàn bộ điểm của việc sử dụng các giao dịch. –

+1

Bạn không thể sử dụng SELECT .... FOR UPDATE; ? Hoạt động tốt trong giao dịch, không có vấn đề gì cả. Sử dụng một bản ghi duy nhất cho chuỗi và cập nhật bản ghi này mỗi lần. –

Trả lời

4

Rõ ràng, LOCK TABLES không thể được cố định để chơi tốt với các giao dịch. Giải pháp thay thế là thay thế bằng SELECT .... FOR UPDATE. Bạn không cần bất kỳ cú pháp đặc biệt (bạn có thể sử dụng thường xuyên START TRANSACTION) và nó hoạt động như mong đợi:

START TRANSACTION; 
SELECT COUNT(*) FROM foo FOR UPDATE; -- Lock issued 
INSERT INTO foo (foo_name) VALUES ('John'); 
SELECT COUNT(*) FROM bar FOR UPDATE; -- Lock issued, no side effects 
ROLLBACK; -- Rollback works as expected 

Xin lưu ý rằng COUNT(*) chỉ là một ví dụ, bạn thường có thể sử dụng câu lệnh SELECT để lấy dữ liệu bạn thực sự cần ;-)

(thông tin này được cung cấp bởi Frank Heikens.)

+2

Xin lưu ý rằng CHỌN ... CHO CẬP NHẬT vẫn cho phép các phiên cơ sở dữ liệu khác đọc từ bảng. Tôi hy vọng điều này sẽ giúp ai đó trước khi họ dựa vào chức năng này. –

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