php+mysql模拟消息队列死循环问题?

我要解决什么问题?
数据库现在有五万条state=600的数据,每次取出1000条进行处理,我需要每五分钟跑一次crontab,将这五万条数据的state变为state=700

我是怎么做的?

while(true)
{
    //每次取出1000条进行处理
    $sql = 'select * from table_a  where state=600 limit 1000';
    $multi_row = $db->query($sql)->get_result();
    if(! $multi_row)
    {
        break;
    }
    //处理队列中的这1000条数据,1改变table_a中state的状态,2在table_b修改一些数据,
    //这两个操作有一个执行不成功我就需要回滚事务
    foreach($multi_row as  $val)
    {
        $db->star_trance();
        if(! $db->table_a->do_something(['state' => 700]))
        {
            $db->rollback();
        }
        if(! $db->table_b()->do_something())
        {
            $db->rollback();
        }
        $db->change()
        
        $db->commint()
    }
}

上述解决方案遇到的问题
我发现每当失败回滚后,数据库会每次查出这条状态没有修改成功的state=600的数据,这就导致了上面$multi_row永远不会空,进而导致整个while循环变成死循环,我又想到我可以统计大概的循环次数,一到循环次数就强制退出,可下次crontab依然会遇到这个问题,我该怎么办?

阅读 4.2k
3 个回答
$id = 0;
while(true)
{
    $sql = 'select * from table_a  where state=600 '. ($id ? " AND id < $id" : "") .' order by id desc limit 1000';
    $multi_row = $db->query($sql)->get_result();
    if(! $multi_row)
    {
        break;
    }
    foreach($multi_row as  $val)
    {
       $id = $val['id']; // $val->id??? 
    }
}

你这个不是在消耗队列啊,队列里消息在出栈之后就不会在消息里啦,其实你可以在消息取出来之后就直接把消息状态变更,如果处理失败,那就进入一个失败的队列,这样就不会有死循环。但是这个队列等级要区分一下啦。
这种队列的方式,比较生硬,缺少灵活性,如果可以的话,建议你加入一些别的服务,如果只是五万的数据量的话,httpsqs这个小服务是很好用的,在不是高并发的情况下,他都可以完全满足你的使用,而且减少数据库上的维护,对于你而言,代码上也可以简洁很多。

可以通过业务逻辑来规避,在一个crontab中处理失败的可以,重复尝试有限次,依然失败的则将600改为其他约定异常码,退出循环,以后也不会再进到循环中。事物为什么会重复失败,是不是代表异常或逻辑问题,这样的话可以再写个脚本来定时检查 state 为错误码的记录,并根据各种情况自动化处理,假若超出自动化实例则进行告警处理,及时抛出异常。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题