我的会话管理(Zebra会话)使用用户级锁来避免同一会话中两个请求之间的争用情况。要开始会话,请使用GET_LOCK。关闭会话后,将使用RELEASE_LOCK。
MariaDB [planner_20201026]> select GET_LOCK('session_ebe210e9b39f1ad3a409763be60efebff587aaaa', '5');
+-------------------------------------------------------------------+
| GET_LOCK('session_ebe210e9b39f1ad3a409763be60efebff587aaaa', '5') |
+-------------------------------------------------------------------+
| 1 |
+-------------------------------------------------------------------+
1 row in set (0.000 sec)
MariaDB [planner_20201026]> select RELEASE_LOCK('session_ebe210e9b39f1ad3a409763be60efebff587aaa');
+-----------------------------------------------------------------+
| RELEASE_LOCK('session_ebe210e9b39f1ad3a409763be60efebff587aaa') |
+-----------------------------------------------------------------+
| NULL |
+-----------------------------------------------------------------+
1 row in set (0.000 sec)
现在,由于某种原因,我处于一种情况,我不知道该锁在哪里没有正确释放。GET_LOCK由于超时而结束,RELEASE_LOCK告诉我它不能释放该锁,因为它(根据文档)是由另一个线程建立的:
MariaDB [xyz]> select GET_LOCK('session_ebe210e9b39f1ad3a409763be60efebff587ac8b', '5');
+-------------------------------------------------------------------+
| GET_LOCK('session_ebe210e9b39f1ad3a409763be60efebff587ac8b', '5') |
+-------------------------------------------------------------------+
| 0 |
+-------------------------------------------------------------------+
1 row in set (5.015 sec)
MariaDB [xyz]> select RELEASE_LOCK('session_ebe210e9b39f1ad3a409763be60efebff587ac8b');
+------------------------------------------------------------------+
| RELEASE_LOCK('session_ebe210e9b39f1ad3a409763be60efebff587ac8b') |
+------------------------------------------------------------------+
| 0 |
+------------------------------------------------------------------+
1 row in set (0.000 sec)
现在,该会话已或多或少被阻止了/没用/被删除了,每个请求都要花费TIMEOUT秒。
我有什么机会可以清除该锁定,尤其是在超时之后?
你只能使用RELEASE_LOCK()释放在同一线程中获取的锁。一个线程没有特权强制另一个线程放弃其锁定。
如果你可以获取锁,但是任何其他线程都可以单方面迫使你释放它,那将是一个非常无用的锁系统!
解决此问题的一种方法是调用IS_USED_LOCK()告诉你哪个线程持有该锁。它返回持有者的整数线程ID;如果没有任何人持有该锁,则返回NULL。
然后,如果你具有SUPER特权,则你的线程可以杀死该其他线程,这将迫使它释放其锁(以及断开该客户端的连接)。但这是一件很不礼貌的事情。
我感觉这是一个XY问题。你正在寻找一种强制释放其他线程持有的锁的解决方案,但这是一个糟糕的解决方案,因为它不能解决你的实际问题。
真正的问题是:
现在,由于某种原因,我处于一种情况,我不知道该锁在哪里没有正确释放。
你需要对此进行更认真的考虑,并设计一个不会丢失谁获得了锁的系统。
提示:GET_LOCK(name, 0)
可能有帮助。这将立即返回(即超时为零秒)。如果可以获取该锁,则将其获取,并且GET_LOCK的返回值为1。如果该锁已经由另一个线程持有,则GET_LOCK仍会立即返回,但返回值为0,告诉你不能获得。
比尔,谢谢。我使用IS_USED_LOCK(...),发现该锁由线程23610锁定,SHOW PROCESSLIST告诉我它正在睡眠,时间为7133,差不多两个小时了!然后,我终止了我的Web服务器,该服务器也应该自动关闭了数据库连接,没有进行任何更改。我运行了KILL 23610,线程不见了,GET_LOCK(...)再次起作用。
听起来您有一个客户端程序可能会在运行其他代码时获得一个锁并保持住该锁,甚至冻结。我建议进行代码审查,并确保您构造代码以立即释放锁定,并且不允许GET_LOCK()和RELEASE_LOCK()之间的任何代码冻结或在无限制的时间内运行。
好吧...从您的个人资料中,我看到您有点了解PHP。Zebra会话用数据库中的会话替换文件会话。其他解决方案完全忽略了竞争条件问题(在传统会话中使用文件锁解决)。Zebra Sessions使用锁(GET_LOCK和RELEASE_LOCK)来做到这一点。PHP应该始终自动回写会话,如果这种情况没有发生,则关闭连接最终应释放该锁。但是今天并没有发生。至少不是在生产中,而是在我的开发机上。我会注意的。
我不会在MySQL会话中使用MySQL。使用内存缓存服务器,例如Redis或Memcached。
您是否使用某种形式的“连接池”?也许它无法释放锁。我通常反对GET_LOCK,PHP会话和代码,在这些函数中我没有获取锁并在同一函数中释放它,并且两个步骤同时显示在屏幕上。(C ++在这里有一个优势-获取锁可以在构造函数中,释放锁可以在隐式析构函数中。一种永不丢失LOCK的优美方法。)