Странное поведение из MYSQL 5 (Изоляция базы данных) - PullRequest
4 голосов
/ 20 апреля 2011

Я открыл два командных окна для работы с моей базой данных (MySQL5).

Ниже приведена структура таблицы, с которой я работаю (Следует отметить, что я отключил автоматическую фиксацию, выполнив set autocommit=0;):

Структура таблицы:

CREATE TABLE  `ajax`.`zipcodes` (
  `ZIPCODE` varchar(5) NOT NULL,
  `CITY` varchar(50) DEFAULT NULL,
  `STATE` varchar(2) DEFAULT NULL,
  PRIMARY KEY (`ZIPCODE`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Ниже приведена последовательность действий:

Шаг 1: В окне команд 1, я выполнил нижекоманды, и вы также можете увидеть вывод:

mysql> insert into ajax.zipcodes values(5, 'Wil', 'AK');
Query OK, 1 row affected (0.00 sec)

Шаг 2 Во втором окне команд я запустил команду ниже, и она зависает (кажется, ожидание команды commit, чтобы быть проблемы из предыдущихокно)

mysql> update ajax.zipcodes set city='Dublin' where zipcode=5;

Шаг 3 Я пошел в командное окно # 1 и выполнил commit;вы могли видеть вывод ниже:

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

В то же время я мог видеть, что второе окно, которое было раньше зависало, также выполняло команду и печатало ниже вывод:

mysql> update ajax.zipcodes set city='Dublin' where zipcode=5;
Query OK, 1 row affected (3.63 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Шаг 4 Теперь я запускаю коммит во втором окне, чтобы гарантировать, что все изменения будут зафиксированы должным образом даже во втором сеансе:

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

Шаг 5 Теперь с фиксациибыло выдано из обоих окон, я думал, что все в порядке, и два сеанса также должны быть синхронизированы, поэтому я пошел в 1-е окно команд и выдает следующие команды:

mysql> select * from zipcodes where zipcode=5;
+---------+------+-------+
| ZIPCODE | CITY | STATE |
+---------+------+-------+
| 5       | Wil  | AK    |
+---------+------+-------+
1 row in set (0.00 sec)

Я был удивлен, потому что я быложидая, что City значение будет 'Dublin', потому что изменения из второго командного окна (то есть update) были зафиксированы в Step 4, но я все еще получаю Wil в столбце City.

Что я здесь не так делаю?

1 Ответ

4 голосов
/ 20 апреля 2011

Это связано с уровнями изоляции.Если вы повысите уровень изоляции до SERIALIZABLE (по умолчанию в MySQL - REPEATABLE READS), вы не получите "фантомные чтения".

Уровни изоляции и фантомные чтения описаны на странице Википедии.для изоляции транзакций базы данных .

Если я выполню эту процедуру, как вы, но с более высоким уровнем изоляции, я получу ожидаемый вами результат.

Сессия 1

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE  `ajax`.`zipcodes` (
    ->   `ZIPCODE` varchar(5) NOT NULL,
    ->   `CITY` varchar(50) DEFAULT NULL,
    ->   `STATE` varchar(2) DEFAULT NULL,
    ->   PRIMARY KEY (`ZIPCODE`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Query OK, 0 rows affected (0.07 sec)

mysql> insert into ajax.zipcodes values(5, 'Wil', 'AK');
Query OK, 1 row affected (0.00 sec)

Сессия 2

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Query OK, 0 rows affected (0.00 sec)

mysql> update ajax.zipcodes set city='Dublin' where zipcode=5;

Сессия 1

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

Сессия 2

/* continued from previous (was frozen) */
Query OK, 1 row affected (7.54 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from zipcodes;
+---------+--------+-------+
| ZIPCODE | CITY   | STATE |
+---------+--------+-------+
| 5       | Dublin | AK    |
+---------+--------+-------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

mysql> select * from zipcodes;
+---------+--------+-------+
| ZIPCODE | CITY   | STATE |
+---------+--------+-------+
| 5       | Dublin | AK    |
+---------+--------+-------+
1 row in set (0.00 sec)

Сессия 1

mysql> select * from zipcodes;
+---------+--------+-------+
| ZIPCODE | CITY   | STATE |
+---------+--------+-------+
| 5       | Dublin | AK    |
+---------+--------+-------+
1 row in set (0.00 sec)

NB: Это не обязательно означает, что вы всегда должны использовать SERIALIZABLE - есть компромиссы.Наиболее примечательным является то, что база данных получит блокировку диапазона при выполнении SELECT, и вы получите больше конфликтов на основе блокировок.

Обновление - явная обработка транзакций

Поскольку мыустановив в этих скриптах autocommit=0;, мы действительно должны обрабатывать транзакции явно, а не ожидать START TRANSACTION - хотя в большинстве случаев база данных ведет себя так, как вы ожидаете, если бы вы выполнили START TRANSACTION.

Однако, запустите исходный пример, пока явно запускает и заканчивает все транзакции (включая те, которые просто SELECT, и вы получите другой результат:

Сеанс 1

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE  `ajax`.`zipcodes` (
    ->   `ZIPCODE` varchar(5) NOT NULL,
    ->   `CITY` varchar(50) DEFAULT NULL,
    ->   `STATE` varchar(2) DEFAULT NULL,
    ->   PRIMARY KEY (`ZIPCODE`)
    -> ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Query OK, 0 rows affected (0.07 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into ajax.zipcodes values(5, 'Wil', 'AK');
Query OK, 1 row affected (0.00 sec)

Сессия 2

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Query OK, 0 rows affected (0.00 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> update ajax.zipcodes set city='Dublin' where zipcode=5;

Сессия 1

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

Сессия 2

/* continued from previous (was frozen) */
Query OK, 1 row affected (8.32 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from zipcodes;
+---------+--------+-------+
| ZIPCODE | CITY   | STATE |
+---------+--------+-------+
| 5       | Dublin | AK    |
+---------+--------+-------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from zipcodes;
+---------+--------+-------+
| ZIPCODE | CITY   | STATE |
+---------+--------+-------+
| 5       | Dublin | AK    |
+---------+--------+-------+
1 row in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

Сессия 1

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from zipcodes;
+---------+--------+-------+
| ZIPCODE | CITY   | STATE |
+---------+--------+-------+
| 5       | Dublin | AK    |
+---------+--------+-------+
1 row in set (0.00 sec)
...