在 Laravel 中当 MySQL 异常宕机时强制返回空数据

起因

  • 之前线上遇到一个问题, 就是当MySQL挂了, 然后导致整个服务崩塌, Redis在前面完全没分担任何压力.
  • 业务常规的查询逻辑如下:
    1. 从redis中获取数据, 有则返回
    2. 当第一步redis无数据, 去MySQL查询数据
    3. 把第二步查询到的数据写入redis
    4. 返回数据

问题分析

  • redis当然不会有问题, 问题是在第二步的时候
  • MySQL查询数据,数据库服务已经宕机, 这时候请求阻塞住
  • 阻塞超时,然后抛出异常,导致无法走到第三步
  • 下一次请求来, 又继续去连接MySQL,无限阻塞,把业务服务器也拖垮

解决方案

  • 这是我们的解决方案, 不一定适合所有业务. 当MySQL宕机强制缓存空数据到redis,允许部分页面为空.而不是无法提供服务

解决思路

  • 设置好合理的MySQL连接超时时间
    • mysqlnd.net_read_timeout = 3
    • 当数据库连接超时之后, 抛出异常
  • 新建一个基础模型BaseModel, 其它所有模型继承这个模型, 并重写newEloquentBuilder方法
<?php

namespace App\Models;


use Illuminate\Database\Eloquent\Model;

class BaseModel extends Model
{
    public function newEloquentBuilder($query)
    {
        return new MysqlCustomBuilder($query);
    }
}
  • 新建一个查询构造器类MysqlCustomBuilder
<?php

namespace App\Models\Database;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class MysqlCustomBuilder extends Builder
{
    public function get($columns = ['*'])
    {
        try {
            return parent::get($columns);
        } catch (\Exception $e) {

            // 根据 laravel 重连的错误码
            $message = $e->getMessage();
            if (Str::contains($message, [
                'server has gone away',
                'no connection to the server',
                'Lost connection',
                'is dead or not enabled',
                'Error while sending',
                'decryption failed or bad record mac',
                'server closed the connection unexpectedly',
                'SSL connection has been closed unexpectedly',
                'Error writing data to the connection',
                'Resource deadlock avoided',
            ])) {
                // 记录日志, 通知xxx
                // Log::error($e);
                // 强制返回空集合
                return Collection::make();
            }

            // 如果不在处理的范围内, 继续抛出异常
            throw $e;
        }
    }
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
当神不再是我们的信仰,那么信仰自己吧,努力让自己变好,不辜负自己的信仰!
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 2
sanders

我建议改一下,做个热备的数据库,如果出现这些错误且存在热备数据库配置就直接通过脚本把环境变量一改,php artisan config:cache 下就切过去了。

10个月前 评论
seth-shi (楼主) 10个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
39
粉丝
156
喜欢
701
收藏
333
排名:31
访问:22.0 万
私信
所有博文
社区赞助商