Как предотвратить взаимные блокировки в php SessionHandler, основанном на mysql innodb

Я реализовал свой собственный PHP SessionHandler на основе innodb, реализовав интерфейс SessionHandlerInterface. Поскольку много одновременных запросов может происходить довольно часто, я должен предотвратить гоночные условия.
Мой провайдер не поддерживает надежные блокировки мьютекса через SELECT GET LOCK..., Также я не хочу использовать блокировку на основе файловой системы.
Поэтому я решил поработать с блокировками на основе строк innodb.

Я приобрел замок в рамках реализованного SessionHandlerInterface::read() метод через SELECT ID FROM session FOR UPDATE (после запуска новой транзакции) и я снимаю блокировку COMMIT в SessionHandlerInterface::write() метод.

Дополнительно я делаю DELETE FROM session WHERE ID = ? в SessionHandlerInterface::destroy() метод и DELETE FROM session WHERE TIMESTAMPDIFF(SECOND, access, NOW()) > ? в SessionHandlerInterface::gc() метод.

DELETE в пределах SessionHandlerInterface::gc() Похоже, что метод является запросом, который производит взаимоблокировки — по крайней мере SHOW ENGINE INNODB STATUS вывод (пример 1).

Обобщено ([...] = некоторый другой код):

[...]

protected static function lock($ID)
{
$e = null;
$i = 0;
while($i < 10)
{
$e = null;

try
{
DB::startTransaction("START TRANSACTION");
DB::select("SELECT `ID` FROM `session` FOR UPDATE");
break;
}
catch(MySQLi_SQL_Exception $e)
{
if(in_array($e->getCode(), [DB::ER_LOCK_WAIT_TIMEOUT, DB::ER_LOCK_DEADLOCK], true))
{
usleep(10);
$i++;
}
else
throw $e;
}
}

if($e) throw $e;
}

protected static function unlock($ID)
{
DB::commit("COMMIT");
}

public function read($ID)
{
self::lock($ID);
$data = DB::select("SELECT `data` FROM `session` WHERE `ID` = ?", $ID, 'single', '');
[...]
}

public function write($ID, $data)
{
[...]
$insert = (bool) DB::insert("INSERT INTO `session` (`ID`, `data`, `creation`, `access`, `access_micros`, `userAgent`, `IP`) VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE `data` = ?, `access` = ?, `access_micros` = ?", $vals);

self::unlock($ID);
[...]
}

public function destroy($ID)
{
return (bool) DB::delete("DELETE FROM `session` WHERE `ID` = ?", [$ID]);
}

public function gc($maxlifetime)
{
DB::delete("DELETE FROM `session` WHERE TIMESTAMPDIFF(SECOND, `access`, NOW()) > ?", $maxlifetime);
return true;
}

Поскольку у меня мало опыта работы с транзакциями / взаимоблокировками, мне нужен совет, как я мог бы решить эту проблему. Я также не уверен, являются ли методы чтения и записи правильными местами для запуска и закрытия транзакции блокировки.

SHOW ENGINE INNODB STATUS вывод (пример 1):

------------------------
LATEST DETECTED DEADLOCK
------------------------
2016-03-08 23:47:51 20a4
*** (1) TRANSACTION:
TRANSACTION 2564667, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 1284, OS thread handle 0x7e8, query id 1028285 localhost 127.0.0.1 U2251792 statistics
SELECT `ID` FROM `session` WHERE `ID` = '76157b19ce3c89f0985b585d8019a5ab0fa6444c6e516a844bd8cea6f6eba357' FOR UPDATE
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 2533 page no 3 n bits 72 index `PRIMARY` of table `db2251792`.`session` trx id 2564667 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
0: len 30; hex 373631353762313963653363383966303938356235383564383031396135; asc 76157b19ce3c89f0985b585d8019a5; (total 64 bytes);
1: len 6; hex 000000272233; asc    '"3;;
2: len 7; hex 3b00000152219e; asc ;   R! ;;
3: len 0; hex ; asc ;;
4: len 5; hex 9998d17b78; asc    {x;;
5: len 5; hex 9998d17bf3; asc    { ;;
6: len 3; hex 0e6a93; asc  j ;;
7: len 30; hex 4d6f7a696c6c612f352e30202857696e646f7773204e542031302e303b20; asc Mozilla/5.0 (Windows NT 10.0; ; (total 110 bytes);
8: len 4; hex 7f000001; asc     ;;

*** (2) TRANSACTION:
TRANSACTION 2564660, ACTIVE 0 sec starting index read, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 360, 2 row lock(s)
MySQL thread id 1281, OS thread handle 0x20a4, query id 1028318 localhost 127.0.0.1 U2251792 updating
DELETE FROM `session` WHERE TIMESTAMPDIFF(SECOND, `access`, NOW()) > 3600
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 2533 page no 3 n bits 72 index `PRIMARY` of table `db2251792`.`session` trx id 2564660 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
0: len 30; hex 373631353762313963653363383966303938356235383564383031396135; asc 76157b19ce3c89f0985b585d8019a5; (total 64 bytes);
1: len 6; hex 000000272233; asc    '"3;;
2: len 7; hex 3b00000152219e; asc ;   R! ;;
3: len 0; hex ; asc ;;
4: len 5; hex 9998d17b78; asc    {x;;
5: len 5; hex 9998d17bf3; asc    { ;;
6: len 3; hex 0e6a93; asc  j ;;
7: len 30; hex 4d6f7a696c6c612f352e30202857696e646f7773204e542031302e303b20; asc Mozilla/5.0 (Windows NT 10.0; ; (total 110 bytes);
8: len 4; hex 7f000001; asc     ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 2533 page no 3 n bits 72 index `PRIMARY` of table `db2251792`.`session` trx id 2564660 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
0: len 30; hex 373631353762313963653363383966303938356235383564383031396135; asc 76157b19ce3c89f0985b585d8019a5; (total 64 bytes);
1: len 6; hex 000000272233; asc    '"3;;
2: len 7; hex 3b00000152219e; asc ;   R! ;;
3: len 0; hex ; asc ;;
4: len 5; hex 9998d17b78; asc    {x;;
5: len 5; hex 9998d17bf3; asc    { ;;
6: len 3; hex 0e6a93; asc  j ;;
7: len 30; hex 4d6f7a696c6c612f352e30202857696e646f7773204e542031302e303b20; asc Mozilla/5.0 (Windows NT 10.0; ; (total 110 bytes);
8: len 4; hex 7f000001; asc     ;;

*** WE ROLL BACK TRANSACTION (1)

ПОКАЗАТЬ ВЫХОД ДВИГАТЕЛЯ INNODB STATUS (пример 2, другой ПК):

------------------------
LATEST DETECTED DEADLOCK
------------------------
2016-03-07 17:47:37 1894
*** (1) TRANSACTION:
TRANSACTION 3925672, ACTIVE 0 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 312, 1 row lock(s)
MySQL thread id 25345, OS thread handle 0x3a40, query id 5247758 localhost 127.0.0.1 U2251792 Sending data
SELECT `ID` FROM `session` FOR UPDATE
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 3918 page no 3 n bits 104 index `PRIMARY` of table `db2251792`.`session` trx id 3925672 lock_mode X waiting
Record lock, heap no 33 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
0: len 30; hex 363039303961363661393234616563366263666232636562346562343162; asc 60909a66a924aec6bcfb2ceb4eb41b; (total 64 bytes);
1: len 6; hex 0000003be6a0; asc    ;  ;;
2: len 7; hex 1d000001ab02e9; asc        ;;
3: len 30; hex 61596d5a57532b752f336945486a645a586a4f45745539387170517a546d; asc aYmZWS+u/3iEHjdZXjOEtU98qpQzTm; (total 192 bytes);
4: len 5; hex 9998cf1bdf; asc      ;;
5: len 5; hex 9998cf1be4; asc      ;;
6: len 3; hex 00023e; asc   >;;
7: len 30; hex 4d6f7a696c6c612f352e30202857696e646f7773204e542031302e303b20; asc Mozilla/5.0 (Windows NT 10.0; ; (total 109 bytes);
8: len 4; hex 7f000001; asc     ;;

*** (2) TRANSACTION:
TRANSACTION 3925665, ACTIVE 1 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 312, 3 row lock(s)
MySQL thread id 25341, OS thread handle 0x1894, query id 5247805 localhost 127.0.0.1 U2251792 update
INSERT INTO `session` (`ID`, `data`, `creation`, `access`, `access_micros`, `userAgent`, `IP`) VALUES ('41e4530eb45871ee64277560290062949b18e6fdb6f8e9cddb097ecd3ccb7c12', '+w1R6dDO2Xx1bzYV4IVt4JV/LFTwb+HtrGvQNK+JgzBs4DQfrjlmUiPX46VjAG0ONjiIHBPiGUAbZXd3Nvy25Lm3pVInq0qkGRDj6GaLVgQ2MSQkjk2YGfY3VMOYRM/23YD6XvPr6DdUIz7120skEBX2SrFUuRX7lBzUZROBJxUVXQ+/mal+paHacMoYJfjF', '2016-03-07 17:47:37', '2016-03-07 17:47:37', 40240, 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36', '\0\0') ON DUPLICATE KEY UPDATE `data` = '+w1R6dDO2Xx1bzYV4IVt4JV/LFTwb+HtrGvQNK+JgzBs4DQfrjlmUiPX46VjAG0ONjiIHBPiGUAbZXd3Nvy25Lm3pVInq0qkGRDj6GaLVgQ2MSQkjk2YGfY3VMOYRM/23YD6XvPr6DdUIz7120skEBX2SrFUuRX7lBzUZROBJxUVXQ+/mal+paHacMoYJfjF', `access` = '2016-03-07 17:47:37', `access_micros` = 40240
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 3918 page no 3 n bits 104 index `PRIMARY` of table `db2251792`.`session` trx id 3925665 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;

Record lock, heap no 33 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
0: len 30; hex 363039303961363661393234616563366263666232636562346562343162; asc 60909a66a924aec6bcfb2ceb4eb41b; (total 64 bytes);
1: len 6; hex 0000003be6a0; asc    ;  ;;
2: len 7; hex 1d000001ab02e9; asc        ;;
3: len 30; hex 61596d5a57532b752f336945486a645a586a4f45745539387170517a546d; asc aYmZWS+u/3iEHjdZXjOEtU98qpQzTm; (total 192 bytes);
4: len 5; hex 9998cf1bdf; asc      ;;
5: len 5; hex 9998cf1be4; asc      ;;
6: len 3; hex 00023e; asc   >;;
7: len 30; hex 4d6f7a696c6c612f352e30202857696e646f7773204e542031302e303b20; asc Mozilla/5.0 (Windows NT 10.0; ; (total 109 bytes);
8: len 4; hex 7f000001; asc     ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 3918 page no 3 n bits 104 index `PRIMARY` of table `db2251792`.`session` trx id 3925665 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 33 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
0: len 30; hex 363039303961363661393234616563366263666232636562346562343162; asc 60909a66a924aec6bcfb2ceb4eb41b; (total 64 bytes);
1: len 6; hex 0000003be6a0; asc    ;  ;;
2: len 7; hex 1d000001ab02e9; asc        ;;
3: len 30; hex 61596d5a57532b752f336945486a645a586a4f45745539387170517a546d; asc aYmZWS+u/3iEHjdZXjOEtU98qpQzTm; (total 192 bytes);
4: len 5; hex 9998cf1bdf; asc      ;;
5: len 5; hex 9998cf1be4; asc      ;;
6: len 3; hex 00023e; asc   >;;
7: len 30; hex 4d6f7a696c6c612f352e30202857696e646f7773204e542031302e303b20; asc Mozilla/5.0 (Windows NT 10.0; ; (total 109 bytes);
8: len 4; hex 7f000001; asc     ;;

*** WE ROLL BACK TRANSACTION (1)

2

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]