You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel手动运行正常的函数,定时任务执行时出现PDO驱动缺失错误

问题描述
  • 环境:Debian系统部署的Laravel应用,主数据库为PostgreSQL,需连接多台MSSQL服务器同步库存数据
  • 异常现象:手动调用auto_update_branch_stocks()函数一切正常,但每15分钟执行的定时任务(通过Kernel.php触发)会抛出错误:PDOException: could not find driver in ...vendor/laravel/framework/src/Illuminate/Database/Connectors/Connector.php:65
  • 触发节点:将Laravel从v10.13.5升级到v10.48.9后出现此问题,升级前定时任务运行正常

关联代码:

function auto_update_branch_stocks()
{
    $branches = Branch::orderBy('branch_code', 'asc')->where('active', "1")->get();
    $servers = '';

    foreach ($branches as $branch) {
        DB::purge('company_branch');
        Config::set('database.connections.company_branch', [
            'driver' => 'sqlsrv',
            'url' => '',
            'host' => $branch->host,
            'port' => $branch->port,
            'database' => $branch->database,
            'username' => config('database.connections.dripsvr.username'),
            'password' => config('database.connections.dripsvr.password'),
            'charset' => 'utf8',
            'prefix' => '',
            'prefix_indexes' => true,
        ]);
        
        try {
            $branch_stock = DB::connection('company_branch')
            ->table('dbo.StkItem')
            ->select('dbo.StkItem.Code as item_code', 'dbo.StkItem.Description_1 as item_description', 'dbo.StkItem.StockLink as stock_id', 'dbo._etblStockQtys.WhseID as warehouse_id', 'dbo._etblStockQtys.QtyOnHand as qty', 'dbo._etblStockCosts.AverageCost as avg_cost', 'dbo._etblStockCosts.LatestCost as last_cost', 'dbo._etblPriceListPrices.fExclPrice as sp_excl', 'dbo._etblStockDetails.GroupID as group_id', 'dbo.GrpTbl.StGroup as group', 'dbo.GrpTbl.Description as group_description')
            ->leftJoin('dbo._etblStockQtys', 'dbo.StkItem.StockLink', '=', 'dbo._etblStockQtys.StockID')
            ->leftJoin('dbo._etblStockCosts', 'dbo.StkItem.StockLink', '=', 'dbo._etblStockCosts.StockID')
            ->leftJoin('dbo._etblPriceListPrices', 'dbo.StkItem.StockLink', '=', 'dbo._etblPriceListPrices.iStockID')
            ->leftJoin('dbo._etblStockDetails', 'dbo.StkItem.StockLink', '=', 'dbo._etblStockDetails.StockID')
            ->leftJoin('dbo.GrpTbl', 'dbo._etblStockDetails.GroupID', '=', 'dbo.GrpTbl.idGrpTbl')
            ->where('dbo._etblStockQtys.WhseID', '=', $branch->warehouse_id)
            ->where('dbo._etblStockCosts.WhseID', '=', $branch->warehouse_id)
            ->where('dbo._etblPriceListPrices.iWarehouseID', '=', 1)
            ->where('dbo._etblPriceListPrices.iPriceListNameID', '=', 1)
            ->where('dbo._etblStockDetails.WhseID', '=', $branch->warehouse_id)
            ->get()->map(function($stock, $key) {
                return [
                    'item_code' => (string)$stock->item_code,
                    'item_description' => (string)$stock->item_description,
                    'stock_id' => (int)$stock->stock_id,
                    'warehouse_id' => (int)$stock->warehouse_id,
                    'qty' => (int)$stock->qty,
                    'avg_cost' => $stock->avg_cost,
                    'last_cost' => $stock->last_cost,
                    'sp_excl' => $stock->sp_excl,
                    'group_id' => (int)$stock->group_id,
                    'group' => (string)$stock->group,
                    'group_description' => (string)$stock->group_description
                ];
            })->all();
            Stock::where('warehouse_id', $branch->warehouse_id)->delete();
            Stock::insert($branch_stock);
        } 
        
        catch (\Exception $e) {
            Log::info('Error for ' . $branch->name . ', ' . $e);
            $servers = $servers . ', ' . $branch->name;
        }

    }
}
排查与解决方案

核心矛盾是CLI定时任务环境与Web手动调用环境的差异,结合Laravel升级后的变化,按以下方向排查:

  1. 检查CLI环境的MSSQL驱动是否缺失

    • Debian系统中,Web环境和CLI环境的PHP配置文件通常分离,执行php -m查看CLI已加载的扩展,确认包含pdo_sqlsrv和sqlsrv
    • 若缺失驱动,重新安装:
      sudo pecl install sqlsrv pdo_sqlsrv
      
      并在CLI对应的php.ini文件中添加:
      extension=pdo_sqlsrv.so
      extension=sqlsrv.so
      
    • 对比php -i和网页phpinfo()的输出,确保两者的扩展加载状态一致
  2. Laravel升级后的依赖约束变化

    • Laravel 10.x后期版本可能对数据库驱动的版本要求升级,执行composer show illuminate/database查看依赖约束,确认系统安装的pdo_sqlsrv扩展版本满足要求
    • 若版本不兼容,升级扩展至对应版本后重新运行定时任务
  3. 定时任务的环境变量与权限

    • Cron任务的执行环境变量可能与当前用户不同,导致PHP无法找到扩展路径,在Cron命令中指定完整PHP路径(如/usr/bin/php)
    • 确保执行Cron的用户拥有读取扩展文件的权限,可在Cron任务前添加环境变量声明:
      PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
      */15 * * * * cd /path/to/your/project && php artisan schedule:run >> /dev/null 2>&1
      
  4. 动态连接的缓存处理优化

    • Laravel升级后对数据库连接缓存的逻辑可能调整,在设置动态连接后强制重新初始化:
      DB::purge('company_branch');
      Config::set('database.connections.company_branch', [...]);
      // 新增:强制重新建立连接
      DB::reconnect('company_branch');
      
代码改进建议
  • 批量操作优化:将删除后插入的逻辑改为upsert,减少数据库IO:
    // 替换原有的delete + insert
    Stock::upsert($branch_stock, ['warehouse_id', 'item_code'], ['qty', 'avg_cost', 'last_cost', 'sp_excl']);
    
  • 错误日志与变量优化:用数组存储失败分支,避免字符串拼接;改用Log::error提升错误级别:
    $failedBranches = [];
    // catch块内
    Log::error('Branch sync failed: ' . $branch->name, ['error' => $e->getMessage()]);
    $failedBranches[] = $branch->name;
    // 循环结束后
    if (!empty($failedBranches)) {
        Log::error('Failed sync branches: ' . implode(', ', $failedBranches));
    }
    
  • 查询性能优化:为多表联查的关联字段添加索引;若数据量较大,改用分批次查询避免内存溢出:
    DB::connection('company_branch')
      ->table('dbo.StkItem')
      // ... 原有查询逻辑 ...
      ->chunk(1000, function($chunk) use ($branch) {
          $processed = $chunk->map(...)->all();
          Stock::upsert($processed, ['warehouse_id', 'item_code'], [...]);
      });
    

内容的提问来源于stack exchange,提问作者nj167252

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 05:26:11