Защитная стратегия для блокировки операции или предотвращения одновременного манипулирования данными - PullRequest
0 голосов
/ 07 апреля 2011

У меня есть небольшой скрипт PHP, который работает с фиксированным числом строк на шаг в таблице создавать статистику, пока не будут обработаны все строки.

Интересно, какова лучшая стратегия, чтобы избежать одновременного запуска сценария? SQL / DML-операторы, если я

  1. только знаю, что это основной выпуск базы данных MySQL 5
  2. не знаю, какой движок используется (MyISAM или ...)
  3. может только гарантировать, что у пользователя базы данных есть разрешения SELECT, INSERT, UPDATE, DELETE

1 Ответ

1 голос
/ 07 апреля 2011

InnoDB работает автоматически:

В InnoDB вам вообще не нужно ничего блокировать, поскольку MySQL работает на уровне изоляции транзакции REPEATABLE READ.Когда вы начинаете транзакцию, а затем ВЫБИРАЕТЕ что-то, при первом запуске ВЫБРАТЬ транзакция запускается, и так как это ПОВТОРЯЕМОЕ ЧТЕНИЕ, ваше представление данных будет согласованным и неизменным до тех пор, пока вы не ПРИМИТЕ.

Это означает, что НАЧАТЬ,SELECT, SELECT, COMMIT в InnoDB REPEATABLE READ отличается от SELECT, SELECT.

MyISAM - это блокировки таблиц:

В MyISAM вы бы LOCK TABLES a, b, c READ, d WRITE установили блокировки чтения для таблиц, которые вы используете.блокирует чтение и чтение / запись таблиц, в которые вы пишете.Невозможно построить список блокировок постепенно с помощью LOCK TABLES, поэтому вы должны заблокировать все, к чему вы хотите прикоснуться, за один раз, и вы не можете касаться чего-либо, что не заблокировали, пока вы UNLOCK TABLES.LOCK TABLES требует дополнительной привилегии, и не имеет смысла предоставлять SELECT priv без LOCK priv в MyISAM.

Универсальная оптимистическая блокировка:

В MyISAM и в InnoDB без транзакций операторы выполняютсяатомарно.Этого достаточно для реализации оптимистической блокировки.При оптимистической блокировке предположим, что у вас есть строка с первичным ключом и данными.Добавьте к нему столбцы state и owner.

root@localhost [kris]> create table d ( id serial, d varchar(20), state enum('unclaimed', 'claimed', 'done') not null, owner integer unsigned not null);
Query OK, 0 rows affected (0.51 sec)

root@localhost [kris]> insert into d values (1, 'one', 'unclaimed', 0), (2, 'two','unclaimed', 0);
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

root@localhost [kris]> select * from d;
+----+------+-----------+-------+
| id | d    | state     | owner |
+----+------+-----------+-------+
|  1 | one  | unclaimed |     0 |
|  2 | two  | unclaimed |     0 |
+----+------+-----------+-------+
2 rows in set (0.00 sec)

Теперь вы можете запросить одну или несколько строк для вашего процессора:

root@localhost [kris]> update d 
    set owner = 1, state = 'claimed' 
where 
    state = 'unclaimed' 
order by id limit 1;
Query OK, 1 row affected, 1 warning (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Предупреждение утверждает, что этот оператор являетсяне безопасно для репликации, но ORDER BY должно сделать LIMIT безопасным для репликации.В результате мы запросили одну строку для владельца с идентификатором владельца 1. Это безопасно при параллельном доступе, потому что UPDATE выполняется атомарно:

root@localhost [kris]> select * from d;
+----+------+-----------+-------+
| id | d    | state     | owner |
+----+------+-----------+-------+
|  1 | one  | claimed   |     1 |
|  2 | two  | unclaimed |     0 |
+----+------+-----------+-------+
2 rows in set (0.00 sec)

Нам нужно выбрать то, что мы в данный моментработаем над ним и обрабатываем его:

root@localhost [kris]> select id, d from d where state = 'claimed' and owner = 1;
+----+------+
| id | d    |
+----+------+
|  1 | one  |
+----+------+
1 row in set (0.00 sec)

Нам также нужно сделать это, когда мы закончим:

root@localhost [kris]> update d set state = 'done', owner = 0 where id = 1;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0
...