Deadlocks, innodb

Найч

Алгоритмик :-)
Deadlocks, innodb

Завязался интерсный спор с хорошим ДБА на тему - может ли одиночный апдейт по праймари ключу (соответственно затрагивает только 1 строку) быть участником дедлока? Ессно, никаких сложных транзакций с ним, один апдейт - одна транзакция. При этом "партнер" по дедлоку может быть каким угодно - хоть апдейт всей таблицы.
Ваше мнение?
 

iceman

говнокодер
а почему бы нет? одну запись апдейтят одновременно 2 юзера, или тригер и т.д.
 

Найч

Алгоритмик :-)
как, по-твоему, будет происходит лок одной строки, если 2 процесса одновременно захотят ее изменить? Где тут возможен дедлок?
 

MiksIr

miksir@home:~$
Апдейт с подзапросом?

-~{}~ 02.07.10 19:34:

А, есть еще кейс... чорт... вроде что-то связанное с тем, что апдейт лочит строку, которая нужна другому селекту, при этом этот селект заблокировал уже индекс. В итоге селект ждет разблокировки строки, а апдейт ждет пока ему не отдадут в эксклюзив индекс.
 

svetasmirnova

маленький монстрик
Да, может.

За примерами можно сходить сюда: http://bugs.mysql.com/search.php?search_for=+innodb++deadlock&boolean=on&bug_type[]=Any&status[]=Not+a+Bug&severity=all&priority=all&showstopper=&limit=All&order_by=id&direction=DESC&cmd=display&phpver=&os=0&os_details=&bug_age=0&tags=&similar=&target=&defect_class=all&workaround_viability=all&impact=all&fix_risk=all&fix_effort=all&triageneeded=&assign=&reviewer=&lead=&verifier=&qatester=&qareviewer=&actionby=&metrics_ref=&internal_order=&affectscustomer=&mysqlnetwork=&security_vulnerability=&qatestneeded=&qareviewneeded=

(ссылка никак не хочет красиво отображаться :( )

Например, http://bugs.mysql.com/bug.php?id=51140
 

Найч

Алгоритмик :-)
svetasmirnova
прочитал баг репорты, не нашел случая с одиночным апдейтом. По ссылке http://bugs.mysql.com/bug.php?id=51140 пример с инсертом нескольких строк в обратных порядках, что очевидно приводит к дедлоку.
Можешь объяснить более подробно, почему ты уверена, что одиночный апдейт может образовать дедлок?

да, и никаких подзапросов - обычный апдейт по праймари ключу
 

svetasmirnova

маленький монстрик
Найч
Сейчас у меня нет готового примера, демонстрирующего такое поведение при каждом повторении.

Но вот недавно был такой случай в версии 5.0.

Была большая таблица, PRIMARY KEY был на колонке [sql]char(20) character set latin1 collate latin1_general_cs NOT NULL[/sql]

В одном соединении делался UPDATE ...WHERE PK='XXX' и в другом - SELECT. Когда 2 потока одновременно сканируют таблицы, чтобы найти нужную строку, они временно блокируют строки, которые просматривают. Соответственно можно было получить DEADLOCK и заблокирована будет совсем не та строка, которую искали.
 

Найч

Алгоритмик :-)
ага, т.е. ты хочешь сказать, что апдейт при поиске строки по пк может лочить несколько строк?
 

svetasmirnova

маленький монстрик
> Это при каком уровне изоляции?

default, repeatable read

> ага, т.е. ты хочешь сказать, что апдейт при поиске строки по пк может лочить несколько строк?

Да, но по одной. Вообще гарантированно это воспроизводится в версии 5.0.

> PRIMARY KEY разве не создает индекс? Почему скан таблицы?

Не важно чего скан, строки просто блокируются пока mysqld решает нужная она или нет.
 

Найч

Алгоритмик :-)
svetasmirnova
есть неясный для меня момент - каким образом мускул "просматривает" более одной строки при поиске WHERE PK=XXX?
 

svetasmirnova

маленький монстрик
korchasa
My fault: нашла тот case, там было 2 UPDATE-a c WHERE "разное значение"

Найч
Чтобы найти значение пусть и в индексе, его (индекс) нужно просмотреть :)

Если я правильно поняла, то это в innobase/row/row0sel.c, row_search_for_mysql, loop rec_loop

И в дебаггере можно посмотреть:

Breakpoint 2, mysql_parse (thd=<incomplete type>, inBuf=0x4860628 "select * from t1 where f2='55'", length=30, found_semicolon=0xb03e8d94) at sql_parse.cc:6399
6399 DBUG_ENTER("mysql_parse");
(gdb) b row_search_for_mysql
Breakpoint 3 at 0x2828b1: file row0sel.c, line 3024.
(gdb) c
Continuing.

Breakpoint 3, row_search_for_mysql (buf=0x4864930 '#' <repeats 200 times>..., mode=2, prebuilt=0x4019660, match_mode=1, direction=0) at row0sel.c:3024
3024 dict_index_t* index = prebuilt->index;
(gdb) n
...
3486 rec = btr_pcur_get_rec(pcur);
(gdb) n
3498 if (page_rec_is_infimum(rec)) {
(gdb) n
3507 if (page_rec_is_supremum(rec)) {
(gdb) n
3540 if (comp) {
(gdb) n
...
4010 if (UNIV_UNLIKELY(!btr_pcur_move_to_next(pcur, &mtr))) {
(gdb) n
4032 goto rec_loop;
(gdb) n
3486 rec = btr_pcur_get_rec(pcur);
(gdb) n
3498 if (page_rec_is_infimum(rec)) {
(gdb) n
3507 if (page_rec_is_supremum(rec)) {
(gdb) n
3540 if (comp) {
...

[sql]
CREATE TABLE `t1` (
`f1` int(11) NOT NULL,
`f2` char(255) NOT NULL default '',
PRIMARY KEY (`f2`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
[/sql]

Запрос
[sql]select * from t1 where f2='55';[/sql]
 
Сверху