Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/273.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php “基于Laravel 5.5 MySQL”;“排队”;以高请求量锁定数据库_Php_Mysql_Laravel_Locking - Fatal编程技术网

Php “基于Laravel 5.5 MySQL”;“排队”;以高请求量锁定数据库

Php “基于Laravel 5.5 MySQL”;“排队”;以高请求量锁定数据库,php,mysql,laravel,locking,Php,Mysql,Laravel,Locking,关于MySQL数据库事务、锁定和Laravel雄辩的ORM,我遇到了一个超出我深度的问题。提前感谢您的帮助 拉威尔5.5 PHP 7.2.25 MySQL 8.0.13(InnoDB) 我的(简化的)问题是:我的MySQL数据库中有一个tasks表,一个用于检索下一个任务的端点,以及一个用于将任务标记为已完成或不活动的端点。tasks表的作用是充当一种队列,允许用户提取当前没有其他人正在处理的下一个可用任务(读取:task处于非活动状态)。如果任务被拉入,但在3小时内未进行更新,则检索端点应

关于MySQL数据库事务、锁定和Laravel雄辩的ORM,我遇到了一个超出我深度的问题。提前感谢您的帮助

  • 拉威尔5.5
  • PHP 7.2.25
  • MySQL 8.0.13(InnoDB)
我的(简化的)问题是:我的MySQL数据库中有一个
tasks
表,一个用于检索下一个任务的端点,以及一个用于将任务标记为已完成或不活动的端点。
tasks
表的作用是充当一种队列,允许用户提取当前没有其他人正在处理的下一个可用任务(读取:task处于非活动状态)。如果任务被拉入,但在3小时内未进行更新,则检索端点应再次可以看到该任务(以防用户意外退出应用程序等)

我的问题出现在任务检索端点上。我在这里使用
DB::transaction
lockForUpdate()
来防止并发问题(即两个用户在同一个任务被标记为活动之前获取该任务)。当每秒请求量相对较低时,它会按预期工作,为每个请求正确地获取一个唯一的任务。但是,在更高的请求频率下,整个
任务
表最终会锁定,请求开始超时,数据库在重新启动之前会变得无响应。这是我的密码:

// Select the next task in the queue
$task = DB::transaction(
    function () {
        $t = Task::where('finished', '=', false)
            ->where(function ($q) {
                // Task must not be active for another user
                $q->where('active', '=', false)
                    ->orWhere('updated_at', '<', DB::raw('DATE_SUB(NOW(), INTERVAL 3 HOUR)'));
            })
            // Prefer tasks that haven't been seen yet
            ->orderBy('attempts')
            ->lockForUpdate()
            ->first();

        if (null !== $t) {
            // Set the task as active and increment attempts
            $t->active = true;
            $t->attempts++;
            $t->save();
        }

        return $t;
    }
);
//选择队列中的下一个任务
$task=DB::事务(
函数(){
$t=Task::where('finished','=',false)
->其中(函数($q){
//对于其他用户,任务不能处于活动状态
$q->where('active','=',false)
->orWhere('updated_at'、'orderBy('truments'))
->lockForUpdate()
->第一个();
如果(空!==$t){
//将任务设置为活动和增量尝试
$t->active=true;
$t->尝试次数++;
$t->save();
}
返回$t;
}
);
其他一些背景:

  • 其他具有类似DB读取的更高频率的请求没有问题,因此这里的问题肯定与事务/锁定行为有关,而不是其他一些系统配置(MySQL连接、PHP线程等)
  • Task
    Eloquent模型具有标准的Laravel时间戳,因此
    updated_at
    自动设置为
    save()
    方法的一部分

我在这里是否有明显的遗漏,或者我是否完全偏离了基准?我正在尝试的是可能的,还是我需要从MySQL转移到更传统的排队系统(Redis、SQS等)?如果可以的话,我可以切换到原始SQL查询,而不是使用ORM。感谢您的指导!

我不确定这一点,但建议太长,无法发表评论

我认为
lockForUpdate
方法可能会导致该行被锁定,甚至在下一次搜索时也会考虑。这意味着在提交上一次更新之前,一次搜索无法完成。(即,在第一次搜索中,您选择非活动的行A,然后在第二次搜索中,您查找所有非活动行,其中行A仍然是一行,但在解锁行A之前,您无法完成查询)

要解决此问题,您可以尝试添加
跳过锁定的
(请参阅)。这将从第二次搜索中排除锁定的行A,并允许它在提交上一个事务之前返回结果


我在Laravel中找不到添加此项的方法,因此您需要使用原始查询生成器手动添加锁定子句,或者可能需要使用
->raw
在何处工作。

表运行在什么引擎上?如果您不知道如何检查,可以在Tinker中运行此项:
DB::select('show table status where name=?',['tasks'))
我询问的原因是InnoDB允许您一次锁定一行,而MyISAM要求您锁定整个表。如果我在这里出错,请纠正我,但我认为
lockForUpdate
仅锁定该查询,而不锁定
save()
query after。虽然这个名字很容易让人误解,但我认为它的工作方式与你想象的不一样。@lufc它是InnoDB;我会更新帖子,thanks@PtrTon我的理解是,它读取锁定行直到DB事务结束。很可能我错了,但如果是这样,我不确定如何正确使用它,或者alt是什么Ernactive会beAmazing--你完全正确。我以为查询是同时运行的,但你的断言是正确的,即每个查询都在等待前一个锁释放。我看到的问题一定是由于达到了数据库无法跟上的查询量。修复程序也比我预期的要简单得多o be:我刚刚不得不将
->lockForUpdate()
更改为
->lock('FOR UPDATE SKIP LOCKED')
。谢谢!