2012-12-23 25 views
5

Tôi đang tìm cách chọn một hàng trong bảng một cách rõ ràng cho một chuỗi. Tôi đã viết một trình thu thập thông tin, hoạt động với khoảng 50 quy trình song song. Mỗi quá trình phải mất một hàng ra khỏi một bảng và xử lý nó.Chỉ chọn một hàng của bảng trên các kết nối song song cao

CREATE TABLE `crawler_queue` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`url` text NOT NULL, 
`class_id` tinyint(3) unsigned NOT NULL, 
`server_id` tinyint(3) unsigned NOT NULL, 
`proc_id` mediumint(8) unsigned NOT NULL, 
`prio` tinyint(3) unsigned NOT NULL, 
`inserted` int(10) unsigned NOT NULL, 
PRIMARY KEY (`id`), 
KEY `proc_id` (`proc_id`), 
KEY `app_id` (`app_id`), 
KEY `crawler` (`class_id`,`prio`,`proc_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 

Bây giờ quá trình tôi làm như sau:

  • giao dịch bắt đầu DB
  • làm một lựa chọn như SELECT * FROM crawler_queue WHERE class_id=2 AND prio=20 AND proc_id=0 ORDER BY id LIMIT 1 FOR UPDATE
  • sau đó cập nhật hàng này với UPDATE crawler_queue SET server_id=1,proc_id=1376 WHERE id=23892
  • cam kết giao dịch

Điều này sẽ giúp không có quá trình nào khác có thể lấy một hàng được xử lý. Làm một giải thích về các chương trình chọn

id select_type table   type possible_keys key  key_len ref rows Extra 
1 SIMPLE  crawler_queue ref proc_id,crawler proc_id 3  const 617609 Using where 

Nhưng các quá trình dường như gây ra song song quá cao, bởi vì đôi khi tôi có thể thấy hai loại lỗi/cảnh báo trong nhật ký của tôi (mỗi 5 phút hoặc lâu hơn):

mysqli::query(): (HY000/1205): Lock wait timeout exceeded; try restarting transaction (in /var/www/db.php l 
ine 81) 

mysqli::query(): (40001/1213): Deadlock found when trying to get lock; try restarting transaction (in /var/www/db.php line 81) 

Câu hỏi của tôi là: ai có thể chỉ cho tôi đúng hướng để giảm thiểu các vấn đề về khóa này không? (Trong tình trạng sản xuất, xử lý song song sẽ cao hơn bây giờ 3-4 lần, vì vậy tôi giả định, rằng sẽ có nhiều vấn đề khóa hơn)

EDIT 2012/12/29: Tôi sửa đổi SELECT sử dụng chỉ số crawler theo gợi ý USE INDEX(crawler). Vấn đề của tôi bây giờ là lockwait timeouts nữa (deadlocks biến mất).

EDIT 2012/12/31: EXPLAIN với USE INDEX() thấy bây giờ (không có hàng là cao hơn, vì bảng chứa dữ liệu hơn bây giờ.):

id select_type table   type possible_keys key  key_len ref    rows  Extra 
1 SIMPLE  crawler_queue ref proc_id,crawler crawler 5  const,const,const 5472426 Using where 

Trả lời

0

Một giải pháp tốt hơn là thực hiện cập nhật và bỏ qua lựa chọn hoàn toàn. Sau đó, bạn có thể sử dụng last_insert_id() để chọn mục được cập nhật. Điều này sẽ cho phép bạn bỏ qua khóa hoàn toàn, đồng thời thực hiện cập nhật cùng một lúc. Khi bản ghi được cập nhật, bạn có thể bắt đầu xử lý nó, vì nó sẽ không bao giờ được chọn lại bởi cùng một truy vấn, xem xét không phải tất cả các điều kiện ban đầu đều phù hợp nữa.

Tôi nghĩ điều này sẽ giúp bạn giảm bớt tất cả các vấn đề liên quan đến khóa và sẽ cho phép bạn chạy nhiều quy trình như bạn muốn song song.

PS: Chỉ cần làm rõ, tôi đang nói về update ... limit 1 để đảm bảo bạn chỉ cập nhật một hàng.

EDIT: Solution

là điều đúng như chỉ dưới đây.

+1

Ý tưởng hay, nhưng 'LAST_INSERT_ID()' sẽ trả về giá trị chỉ khi bạn 'INSERT' dữ liệu hoặc' UPDATE' tăng cột autoincrement: ** EDIT ** Tôi sẽ cung cấp http://stackoverflow.com/questions/ 1388025/how-to-get-id-of-the-last-cập nhật-hàng-in-mysql một thử – rabudde

+0

Đối với một số lý do tôi có một giá trị last_insert_id khi tôi thử nghiệm, nhưng nó lừa tôi (nó trông giống như một trong những chính xác, nhưng nó không phải). Tôi tin rằng giải pháp được mô tả trong câu hỏi SO đó là con đường để đi. Tôi cũng sẽ cập nhật câu trả lời của mình. – Xnoise

0

Từ những gì tôi có thể nói vấn đề mà bạn đang phải đối mặt là hai chủ đề là vyying cho cùng một hàng trong bảng và cả hai đều không thể có nó. Nhưng không có cách nào thanh lịch để cơ sở dữ liệu nói "không, bạn không thể có cái đó, tìm hàng khác" và do đó bạn gặp lỗi. Điều này được gọi là tranh chấp tài nguyên.

Khi bạn đang thực hiện công việc song song như vậy, đây là một trong những cách dễ nhất để giảm các vấn đề dựa trên tranh chấp là loại bỏ hoàn toàn tranh chấp bằng cách phát minh ra cách cho tất cả các chủ đề biết hàng nào mà chúng phải làm việc trước thời hạn. Sau đó, họ có thể khóa mà không cần phải tranh giành tài nguyên và cơ sở dữ liệu của bạn không phải giải quyết tranh chấp.

Cách tốt nhất để làm điều này? Thông thường mọi người chọn một số loại lược đồ thread-id và sử dụng số học modulo để xác định chủ đề nào nhận được hàng nào. Nếu bạn có 10 chủ đề thì chủ đề 0 nhận hàng 0, 10, 20, 30, v.v. Chủ đề 1 được 1, 11, 21, 31, v.v.

Nói chung nếu bạn có NUM_THREADS thì mỗi chủ đề của bạn sẽ chọn id là THREAD_ID + i * NUM_THREADS từ cơ sở dữ liệu và làm việc trên các cơ sở dữ liệu đó.

Chúng tôi đã giới thiệu một vấn đề trong chủ đề đó có thể ngừng hoặc chết, và bạn có thể kết thúc với các hàng trong cơ sở dữ liệu mà không bao giờ bị xúc động.Có một số giải pháp cho vấn đề đó, một trong số đó là chạy "dọn dẹp" một khi hầu hết tất cả các chủ đề của bạn đã hoàn tất, nơi tất cả các chủ đề thu thập bất kỳ hàng nào họ có thể và thu thập chúng cho đến khi không còn lại các URL chưa được thu thập thông tin. Bạn có thể tinh vi hơn và có một vài luồng dọn dẹp liên tục chạy, hoặc thỉnh thoảng mỗi luồng chỉ thực hiện các nhiệm vụ dọn dẹp, v.v.

3

Báo cáo GIẢI THÍCH cho thấy bạn chỉ sử dụng chỉ mục một cột proc_id và truy vấn có để kiểm tra hơn 600 nghìn hàng. Nó có lẽ sẽ tốt hơn nếu trình tối ưu hóa đã chọn chỉ mục crawler.

InnoDB có thể khóa tất cả 600K hàng, không chỉ các hàng khớp với điều kiện đầy đủ trong mệnh đề WHERE của bạn. InnoDB khóa tất cả các hàng được kiểm tra để đảm bảo các thay đổi đồng thời không được ghi vào binlog theo thứ tự sai.

Giải pháp là sử dụng chỉ mục để thu hẹp phạm vi của các hàng được kiểm tra. Điều này có thể sẽ giúp bạn không chỉ để tìm các hàng nhanh hơn, mà còn để tránh khóa các hàng lớn. Chỉ số crawler sẽ giúp ích ở đây, nhưng không rõ ngay lý do tại sao chỉ mục đó không sử dụng chỉ mục đó.

Bạn có thể phải ANALYZE TABLE để đảm bảo cập nhật thống kê bảng của InnoDB để biết về chỉ mục crawler trước khi sử dụng chỉ mục đó trong gói tối ưu hóa. BẢNG ANALYZE là một hoạt động không tốn kém.

Các tùy chọn khác là sử dụng một gợi ý chỉ số:

SELECT * FROM crawler_queue USE INDEX(crawler) ... 

này cho tôi ưu để sử dụng chỉ mục đó, và không xem xét các chỉ số khác cho truy vấn này. Tôi thích tránh gợi ý chỉ mục, vì trình tối ưu hóa thường có thể tự quyết định tốt và sử dụng gợi ý trong mã có nghĩa là tôi có thể buộc trình tối ưu hóa không xem xét chỉ mục mà tôi tạo trong tương lai, nếu không nó sẽ chọn .


Giải thích rõ hơn, bây giờ bạn đã rõ ràng bạn đang sử dụng RDBMS làm FIFO. Đây không phải là việc sử dụng hiệu quả RDBMS. Có các công nghệ xếp hàng đợi tin nhắn cho mục đích này.

Xem thêm:

+0

Hey Bill, đó là những gì tôi đã thực hiện (xin lỗi vì không cập nhật câu hỏi của tôi, sẽ cung cấp +1 mặc dù). Nhưng điều kỳ lạ là, giải thích đó đã cho thấy đôi khi việc sử dụng 'trình thu thập dữ liệu' thay vì' proc_id'. Nhưng bây giờ tôi buộc sử dụng chỉ mục 'trình thu thập dữ liệu'. Tôi cũng sẽ thử lệnh phân tích bảng. Cảm ơn – rabudde

+0

Kiểm tra trường 'rows' trong đầu ra EXPLAIN. Với chỉ số hợp chất, số hàng được kiểm tra nên thấp hơn, tôi hy vọng. –

+0

Không, nó không (xem ở trên) – rabudde

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