mehr-it/lara-mysql-locks

基于 MySQL 5.7.5 及以上版本的分布式锁,用于 Laravel

2.5.0 2023-01-04 01:00 UTC

This package is auto-updated.

Last update: 2024-09-04 04:30:16 UTC


README

此包实现了基于 MySQL 的分布式锁,具有以下功能:

  • 无需预先初始化的命名日志
  • 获取锁的等待超时
  • 锁的 TTL(生存时间)
  • 当锁过期并由其他进程接管时,数据库会话将被终止
  • 如果未释放,进程结束或意外终止时,锁将自动释放
  • 等待锁使用阻塞数据库请求,而不是轮询
  • 在 TTL 之前释放的锁可以立即被其他进程获取

要求

  • PHP >= 7.1
  • MySQL >= 5.7.5(在 5.7.5 之前,每个连接只能获取一个命名锁)

此包仅适用于 MySQL 数据库连接!

安装

composer require mehr-it/lara-mysql-locks

此包使用 Laravel 的包自动发现,因此服务提供者和别名将被自动加载。

用法

$lock = DbLock::lock('my-lock', 5, 10);

// do some work

$lock->release();

lock() 方法期望锁名称、等待时间(在超时之前)和锁的最大生存时间(均为秒)。

如果无法在给定超时内获取锁,则会抛出 DbLockTimeoutException

release() 方法释放锁。如果数据库事务丢失或锁的 TTL 已过期且另一个进程获取了锁,则会失败。在这种情况下,将抛出 DbLockReleaseException,表示锁已不再获取。

使用回调

应始终调用 release() 方法。因此,通常应使用 withLock() 方法获取锁并传递回调。 withLock() 确保即使抛出错误,也会释放锁

$return = DbLock::withLock(
		function($lock) {
		
		// do some work
			
		}, 'my-lock', 5, 10
	);

withLock() 方法在数据库事务中执行回调。 任何抛出或锁错误都会回滚事务。

您可以通过将连接名称传递给 withLock 来创建锁和事务,并使用不同的数据库连接

DbLock::withLock(function($lock) { }, 'my-lock', 5, 10, 'my-connection');

其他连接必须针对同一个 MySQL 实例,就像默认连接一样!否则,锁将立即释放!

锁和数据库连接

锁始终绑定到数据库连接。如果没有在创建锁时传递其他连接,则它们绑定到默认连接。

当锁的 TTL 过期并由另一个进程获取时,将终止绑定到锁的数据库连接!

在处理事务时必须考虑这一点。因为任何在事务中“锁丢失”之后的 SQL 查询都将失败。通常情况下(您通常不希望在锁 TTL 超过时提交任何内容),可能会出现不希望出现这种行为的情况。在这种情况下,您必须传递另一个数据库连接来创建锁

DbLock::lock('my-lock', 5, 10, 'other-connection');

此其他连接必须与默认连接针对同一个 MySQL 实例!否则,锁将立即释放!

如果不处理事务,Laravel 将优雅地重新连接到数据库,并且在这些情况下您的程序将按预期继续运行。

验证是否已获取锁

有时您可能需要检查是否已获取锁。这通常发生在执行影响其他资源(这些资源不受数据库事务覆盖)的操作时。

以下示例检查锁是否至少保持5秒钟。如果没有,将抛出DbLockRemainingTTLException异常

$lock->assertAcquiredFor(5);

如果您不希望抛出异常,可以使用remainsAcquiredFor()

if (!$lock->remainsAcquiredFor(5)) {
	// handle lock timeout
}

限制条件

MySQL对锁名称的长度有限制。因此,您不应使用超过50个字符的锁名称。