2012-06-29 15 views
6

Tôi cần khóa toàn bộ bảng (không phải một hàng) với học thuyết, tôi muốn thực hiện điều này mà không có truy vấn gốc nếu có thể.Làm thế nào để khóa toàn bộ bảng trong symfony2 bằng doctrine2?

Các tài liệu cho pessimistic locking chỉ mô tả làm thế nào để khóa các đơn vị cụ thể thông qua các phương pháp:

  • EntityManager # tìm
  • EntityManager # khóa
  • Query # setLockMode

Tôi có một giao dịch cần chèn một hàng có giá trị phụ thuộc vào giá trị của phần còn lại của các hàng trong bảng, vì vậy tôi cần phải ngăn hai giao dịch thực hiện đồng thời trên bàn đó.

Tôi đang sử dụng phân giới giao dịch rõ ràng, được cho là hoạt động tốt với khóa (theo tài liệu ở trên).

LƯU Ý: Khóa tối ưu không đủ tốt trong trường hợp này, tôi không thể đủ khả năng thử lại giao dịch. Bên cạnh đó truy vấn không được cho là chậm nên hiệu năng không phải là vấn đề.

EDIT: Tôi sẽ đưa ra một ví dụ. Hãy tưởng tượng bạn muốn tay xây dựng một auto_increment, và bạn phải chọn max() từ bảng để có được kết quả trước đó để chèn tiếp theo. Bạn phải đảm bảo rằng không có hai giao dịch nào cố gắng chèn cùng một giá trị trong trường hợp chúng chọn tối đa() cùng một lúc.

Tôi đang tìm giải pháp chung cho vấn đề này khi auto_increment không tốt, ví dụ với chuỗi hoặc nhiều cột, băm hoặc bất kỳ phép tính nào bạn phải thực hiện trên tập hợp hàng trước.

Khóa là giải pháp chắc chắn và không giống như khóa lạc quan, bạn không phải thử lại các lỗi.

Vì vậy, có cách nào để sử dụng tính năng khóa bảng trong học thuyết không?

+0

Imo, một lựa chọn tốt hơn là nên mở một giao dịch, tìm nạp dữ liệu từ bảng bạn cần thông qua truy vấn chọn, sau đó thực hiện cập nhật. – JamesHalsall

+0

Vấn đề là nếu một giao dịch thứ hai đến trước lần commit đầu tiên giao dịch thứ hai sẽ chèn một giá trị không hợp lệ. Kết quả của giao dịch thứ hai cũng phụ thuộc vào hàng được chèn bởi lệnh đầu tiên. Đó là lý do tại sao tôi cần khóa, giao dịch không được trùng lặp cho bảng đó. – Jens

Trả lời

1

Từ xem tài liệu trong Doctrine 2.x Tôi không nghĩ rằng có một cách được hỗ trợ để khóa toàn bộ bảng. Tất nhiên bạn có thể thử khóa tất cả các bản ghi thông qua Doctrine riêng lẻ, nhưng điều này sẽ rất cồng kềnh và không thực sự là một ý tưởng hay.

Thay vào đó, tôi sẽ sử dụng người quản lý thực thể thuyết để thực hiện SQL liệu trên cơ sở dữ liệu ...

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access

và sau đó sau khi bạn đang thực hiện với cập nhật của bạn ...

$em->getConnection()->exec('UNLOCK TABLES;');

EDIT:

Từ MySQL documentation trên ổ khóa bảng ...

  • Phiên giữ khóa có thể đọc và ghi bảng.

  • Chỉ phiên có khóa mới có thể truy cập vào bảng.Không có phiên nào khác có thể truy cập nó cho đến khi khóa được giải phóng.

  • Yêu cầu khóa cho bảng theo khối phiên khác trong khi khóa WRITE được giữ.

Tôi nghĩ rằng điểm thứ hai ở đây là chìa khóa, chỉ có phiên của bạn có thể đọc/ghi vào bảng đó.

+0

Ngoài ra, việc khóa các bản ghi riêng lẻ sẽ không ngăn cản việc chèn bản ghi mới. Câu lệnh OTOH the LOCK không giao dịch theo tài liệu và tôi không chắc cách học thuyết thực hiện giao dịch, nhưng nếu nó sử dụng BẮT ĐẦU GIAO DỊCH thì nó sẽ không hoạt động. – Jens

+0

'LOCK BẢNG [tbl_name]' sẽ khóa toàn bộ bảng để viết, xem chỉnh sửa của tôi – JamesHalsall

+0

Có, tôi biết, vấn đề duy nhất tôi có với LOCK là nó chỉ phát tốt khi bạn thực hiện giao dịch bằng 'autocommit = 0' và tôi 'không chắc chắn cách giáo lý xử lý các giao dịch. Tôi vẫn phải kiểm tra nó. Xem http://dev.mysql.com/doc/refman/5.0/en/lock-tables-and-transactions.html – Jens

1

Có thể nhân đôi bởi Doctrine2 ORM select for update

Dưới đây là một số mã có liên quan:

try { 
    $entity = $em->find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion); 

    // do the work 

    $em->flush(); 
} catch(OptimisticLockException $e) { 
    echo "Sorry, but someone else has already changed this entity. Please apply the changes again!"; 
} 

Các LockMode :: thông số lạc quan có thể cung cấp những gì bạn cần.

+0

Nói chung, việc chọn hàng để cập nhật không ngăn cản chèn đồng thời. –

2

Sau lời khuyên cho đến nay, tôi đã cố gắng này:

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access 

// calculate $new_number... 

// persist $new_number on table_name... 
$table_name->setCalculatedNumber($new_number); 
$em->persist($table_name); 
$em->flush(); 

$em->getConnection()->exec('UNLOCK TABLES;'); 

Tôi đã thử nghiệm nó với JMeter, và khóa không được làm việc với một tải nặng (16 yêu cầu/giây). Truy tìm cho thấy rằng các trường hợp khác có khóa trước khi nó đã được đưa ra một cách rõ ràng. Vấn đề (như đề xuất của Jens) là flush() ngầm bắt đầu với một GIAO DỊCH BẮT ĐẦU, làm giảm khóa bảng. Sử dụng một Cập nhật bản sửa lỗi sự cố cho tôi:

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access 

// calculate $new_number... 

// persist $new_number on table_name... 
$em->getConnection()->executeUpdate("UPDATE table_name set ...;");  

$em->getConnection()->exec('UNLOCK TABLES;'); 
$em->refresh($table_name); 

Các refresh dấu() là cần thiết để làm cho số tính toán có sẵn trong truy vấn tiếp theo kết quả

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