我通过实现SessionHandlerInterface实现了我自己的基于innodb的PHP SessionHandler。由于许多同时发出的请求可能经常发生,我必须防止比赛条件。
我的提供程序不通过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)
SHOW ENGINE INNODB STATUS输出(例2,不同的PC):
------------------------
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)