如何防止基于mysql innodb的php SessionHandler

时间:2016-03-09 11:11:06

标签: php mysql session innodb deadlock

我通过实现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)

0 个答案:

没有答案