大家有没有遇到多个应用中数据库连接错乱问题?

多个应用中数据库连接错乱#

事件背景#

同事无意中发现,在多个基于 Laravel 的 Web 应用中,当应用 A 进行一个长时间操作时(PHP 会运行超过 30s+),在这期间,在应用 B 中进行数据库操作时,B 应用会连接到 A 应用中的数据库,而非 B 的数据库。

下面的这段错误就是因为不知道为何连接到了 A 数据库而出现的:

2/2 QueryException in Connection.php line 655: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'database_a.member_user' doesn't exist (SQL: select * from member_user limit 1)

    in Connection.php line 655
    at Connection->runQueryCallback('select * from member_user limit 1', array(), object(Closure)) in Connection.php line 611
    at Connection->run('select * from member_user limit 1', array(), object(Closure)) in Connection.php line 324
    at Connection->select('select * from member_user limit 1')
    at call_user_func_array(array(object(MySqlConnection), 'select'), array('select * from member_user limit 1')) in DatabaseManager.php line 296
    at DatabaseManager->__call('select', array('select * from member_user limit 1')) in Facade.php line 216
    at DatabaseManager->select('select * from member_user limit 1') in Facade.php line 216
    at Facade::__callStatic('select', array('select * from member_user limit 1')) in IndexController.php line 35
    at DB::select('select * from member_user limit 1') in IndexController.php line 35
    at IndexController->index(object(ConfigService), object(WechatQrcodeService))
    at call_user_func_array(array(object(IndexController), 'index'), array(object(ConfigService), object(WechatQrcodeService))) in Controller.php line 256
    at Controller->callAction('index', array(object(ConfigService), object(WechatQrcodeService))) in ControllerDispatcher.php line 164
    at ControllerDispatcher->call(object(IndexController), object(Route), 'index') in ControllerDispatcher.php line 112
    at ControllerDispatcher->Illuminate\Routing\{closure}(object(Request))
    at call_user_func(object(Closure), object(Request)) in Pipeline.php line 139
    at Pipeline->Illuminate\Pipeline\{closure}(object(Request))
    at call_user_func(object(Closure), object(Request)) in Pipeline.php line 103
    at Pipeline->then(object(Closure)) in ControllerDispatcher.php line 114
    at ControllerDispatcher->callWithinStack(object(IndexController), object(Route), object(Request), 'index') in ControllerDispatcher.php line 69
    at ControllerDispatcher->dispatch(object(Route), object(Request), '\App\Controller\Wecolour\IndexController', 'index') in Route.php line 203

分析过程#

第一个猜想到的是不是在 B 应用连接数据库时,直接使用的是 A 应用中连接数据库的的资源 ID,但是感觉这种情况应该不会出现。

另外一种情况,就是读取数据库配置的时候出错了,读取到了 A 应用的数据库配置信息,但是这个感觉理论有点不太可能,因为两个应用完全在两个不同的位置(后来证实确实是第二点)。

Laravel 使用了 vlucas/phpdotenv 这个包来加载 .env 文件中的配置信息,具体的过程如下:

  1. .env 文件中读取配置信息;
  2. 调用 putenv() 方法将配置信息设置到 PHP 环境中;
  3. 调用 Laravel 封装的 env() 方法来读取配置信息(env() 中其实调用了 getenv() 方法来配置信息读取);

.env 文件主要功能是为了在不同的部署环境实现不同配置,这样不同的环境可以共用同一套代码;
putenv()getenv() 是 PHP4,PHP5,PHP7 支持的用来设置和获取环境变量(environment variable)的方法。

问题就出现在这里。

getenv()putenv() 不是一个线程安全的函数,意味着如果两个线程同时调用这个函数,就会出现问题。

而且服务器的环境正好是:

Apache + worker 模式,这种模式下,php 运行环境是以线程模式运行的,所以才出现了上述的问题。

解决办法#

  1. 启用 php 进程运行模式(参照 articles/dev/server/run_environments_for_php.md);
  2. 将配置单独放在 php 配置文件中,这个可能造成硬编码,不过不推荐;
  3. 等待 Laravel 修复这个问题。

或者大家有没有遇到过这个问题,交流一下解决办法。

参考资料#

原文#

https://git.oschina.net/edwin404/articles/...

本帖已被设为精华帖!
本帖由 Summer 于 8年前 加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 15
Summer

看你的描述我第一反应也是 .env 出现问题。

还有一个非常奇怪的 Crypt : The Mac is invalid 问题,相关帖子见这里:https://phphub.org/topics/2490#reply10

调查到最后也是因为 Windows 下的 .env 没加载上

8年前 评论

@Summer 环境也是 windows+apache(mpm 模式),没有报加载出现问题的错误,所以才感觉比较奇怪。

8年前 评论
Summer

@edwin404 所以还是江湖中那句老话,除非不得已,别用 win 开发。。。一堆奇奇怪怪的问题,直接 Homestead 省心多了

8年前 评论

分析很到位啊,以后再有人遇到问题就不用花时间纠结了,感谢!

8年前 评论

@Summer 很对,不过是历史遗留问题了,真是迫不得已 :joy:

8年前 评论

上次同事同样问题,原来是这样。有解了

8年前 评论

我也遇到过这个问题,数据库连接不上,跟我报 access denied at xxx@localhost ...
(然后我用的根本就不是 localhost 的数据库)

8年前 评论

咦?我之前遇到的是项目 A 建了一个任务插入队列,但是到了那个时间,处理任务的却是项目 B!
难道也是因为这个?

8年前 评论

@skys215 如果你的队列是使用的数据库作为驱动的话,很可能就是这个原因导致的。

8年前 评论

@skys215 那就同样的,A 应用是不是读到了 B 应用的 .env 配置信息。

8年前 评论

@edwin404 我也觉得可能是这样,但是网页方面又没什么问题。就这个队列有问题的样子。

8年前 评论
vance

妥妥的坑啊

6年前 评论

@skys215 如果是队列用的是同一个驱动是会这样的,我以前就有两个项目,一个是 app 接口用 lumen 开发的,一个是 web 用 laravel 开发的,lumen 的推送的队列我都是直接用 laravel 的监听处理

6年前 评论

:thumbsup:昨天碰到这个问题了,最后采取配置文件硬编码解决了 ,看了文章,理解了为什么出现这种情况

6年前 评论