Poszukuję sposobu wybrania jednego wiersza tabeli jawnie dla jednego wątku. Napisałem robota, który działa z około 50 równoległymi procesami. Każdy proces musi zająć jeden wiersz ze stołu i przetworzyć go.Wybierz tylko jeden wiersz tabeli na wysokim równoległym połączeniach
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
Teraz moje procesy wykonaj następujące czynności:
- transakcja początek DB
- zrobić select jak
SELECT * FROM crawler_queue WHERE class_id=2 AND prio=20 AND proc_id=0 ORDER BY id LIMIT 1 FOR UPDATE
- następnie zaktualizować ten wiersz z
UPDATE crawler_queue SET server_id=1,proc_id=1376 WHERE id=23892
- popełnić transakcja
Powinno to pomóc, aby żaden inny proces nie mógł pobrać wiersza, który jest jeszcze przetwarzany. Robi Wyjaśnij na wybranych pokazach
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
Ale procesy wydają się powodować zbyt wysoką równoległości, bo czasami widzę dwa rodzaje błędów/ostrzeżeń w moim dzienniku (co 5 minut lub tak):
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)
Moje pytanie brzmi: czy ktokolwiek może wskazać mi właściwy kierunek, aby zminimalizować te problemy z blokowaniem? (W stanie produkcyjnej, równoległość będzie 3-4 razy większa niż obecnie, więc zakładam, że nie będzie problemów znacznie bardziej blokujące)
EDIT 29.12.2012: I zmodyfikowane SELECT
użyć indeksu crawler
podpowiedź USE INDEX(crawler)
. Moim problemem są teraz limity czasu blokady (zakleszczenia znikają).
EDIT 2012-12-31: EXPLAIN
z USE INDEX()
pokazuje teraz (liczba wierszy jest wyższa, ponieważ tabela zawiera więcej danych teraz.):
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
Przyjemny pomysł, ale 'LAST_INSERT_ID()' zwróci wartość tylko wtedy, gdy dane "WSTAW" lub "AKTUALIZACJA" zwiększą kolumnę autoinkrementacji: ** EDYTUJ ** Podaję http://stackoverflow.com/questions/ 1388025/how-to-get-of-the-last-updated-row-in-mysql a spróbuj – rabudde
Z jakiegoś powodu uzyskałem wartość last_insert_id, kiedy testowałem, ale to mnie oszukało (wyglądało to jak poprawne, ale tak nie było). Uważam, że rozwiązanie opisane w tym pytaniu dotyczącym SO jest drogą do zrobienia. Zaktualizuję też odpowiedź. – Xnoise