通过 MySQL InnoDB 行锁的应用程序互斥锁

pep*_*_bg 4 mysql architecture mutex innodb

我的应用程序由几个 Apache 服务器组成,它们与一个常见的 MySQL 机器通信。该应用程序的一部分允许用户在未来创建一小时的约会。我需要一种机制来防止不同用户同时来自不同的 Apache 实例,预订相同的一小时预约时段。我已经看到在 Oracle 数据库上实现了类似的“系统间互斥锁”解决方案(基本上是“选择...更新”),但还没有处理对 MySQL 执行相同操作的详细信息。希望得到任何建议、代码或文档参考、最佳实践等。确实尝试过谷歌搜索,但主要是关于 MySQL 内部互斥锁的讨论。

这些是我认为相关的 MySQL 设置(我的代码将具有 try-catch 和所有内容,并且不应该在不解锁锁定的内容的情况下保释,但也必须考虑在这些情况下发生的情况):

mysql> show variables like 'innodb_rollback_on_timeout';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| innodb_rollback_on_timeout | OFF   | 
+----------------------------+-------+
1 row in set (0.00 sec)

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 100   | 
+--------------------------+-------+
1 row in set (0.00 sec)

mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
|            1 | 
+--------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

您可以推荐任何替代解决方案(MySQL 之外)?我也有一个 memcached 实例正在运行,但经常被刷新(并且不确定我是否想要 memcachedb 等以使其具有容错性)。

感谢你的帮助...

jac*_*ach 7

还可以使用 MySQL 和 MariaDB 的GET_LOCK(和RELEASE_LOCK)函数:

这些函数可用于实现问题中描述的行为。


获取锁my_app_lock_1

SELECT GET_LOCK('my_app_lock_1', 1000); -- lock's name 'my_app_lock_1', timeout 1000 ms
+---------------------------------+
| GET_LOCK('my_app_lock_1', 1000) |
+---------------------------------+
|                               1 |
+---------------------------------+
Run Code Online (Sandbox Code Playgroud)

释放锁:

DO RELEASE_LOCK('my_app_lock_1'); -- DO makes a result set ignored
Run Code Online (Sandbox Code Playgroud)

请注意(MariaDB 文档中的引用):

  • 名称在服务器范围内被锁定。如果某个名称已被一个客户端锁定,则GET_LOCK()阻止另一个客户端对具有相同名称的锁定的任何请求。这允许同意给定锁名称的客户端使用该名称来执行合作咨询锁定。但请注意,它还允许不在协作客户端组中的客户端无意或有意锁定名称,从而防止任何协作客户端锁定该名称。减少这种可能性的一种方法是使用特定于数据库或特定于应用程序的锁名称。例如,使用表单db_name.str或 的锁名称app_name.str

  • 获得的锁GET_LOCK()不与事务交互。


pep*_*_bg 1

在这里回答我自己的问题。我们最终会做的一个变体(在 PHP 中):

<?php
$conn = mysql_connect('localhost', 'name', 'pass');
if (!$conn) {
  echo "Unable to connect to DB: " . mysql_error();
  exit;
 }
if (!mysql_select_db("my_db")) {
  echo "Unable to select mydbname: " . mysql_error();
  exit;
 }
mysql_query('SET AUTOCOMMIT=0'); //very important! this makes FOR UPDATE work                                                                                           
mysql_query('START TRANSACTION');
$sql = "SELECT * from my_mutex_table where entity_id = 'my_mutex_key' FOR UPDATE";
$result = mysql_query($sql);
if (!$result) {
  echo "Could not successfully run query ($sql) from DB: " . mysql_error();
  exit;
 }
if (mysql_num_rows($result) == 0) {
  echo "No rows found, nothing to print so am exiting";
  exit;
 }

echo 'Locked. Hit Enter to unlock...';
$response = trim(fgets(STDIN));
mysql_free_result($result);
echo "Unlocked\n";
?>
Run Code Online (Sandbox Code Playgroud)

为了验证它是否可以从两个不同的控制台运行。时间性能比基于标准文件锁的互斥锁要差一些,但仍然可以接受。