【问题标题】:PHP/Mysql based web services API for mobile app running out of mysql connections用于移动应用程序的基于 PHP/Mysql 的 Web 服务 API 耗尽 mysql 连接
【发布时间】:2014-01-01 01:57:29
【问题描述】:

我的一个 Android 应用程序在使用基于 PHP/Mysql 的 Web 服务 API 时遇到问题。这是问题描述。

我在错误日志中收到以下错误。

用户 my-db-user 已经有超过 'max_user_connections' 个活动连接

我的代码用完了mysql database connections,我已经达到了我的主机可以提供的最大限制。我不能再增加max_connectionsmax_user_connections

这是我在 PHP 脚本中编写数据库访问的方法。我到处都遵循这种模式,所以在所有数据库访问类及其方法(如获取、插入、更新、删除等)中都是相同的。

DatabaseHelper.php

 <?php

 class DatabaseHelper {

      private $db_host = 'my-host';
      private $db_user = 'my-db-user';
      private $db_password = 'my-password';
      private $db_name = 'my-db-name';

      public function getConnection() {
           try {
           $con = new mysqli($this->db_host, $this->db_user, $this->db_password, $this->db_name);
           return $con;
           } catch (Exception $ex) {
                error_log('DB Error --> ' . $ex);
                return null;
           }
      }
 }

?>

这是我在数据库访问类中使用它的方式。

<?php

require_once (dirname(__FILE__) . DIRECTORY_SEPARATOR . ".." . DIRECTORY_SEPARATOR . "pathtofile" . DIRECTORY_SEPARATOR . 'DatabaseHelper.php');

class SomeDao {    

    public function get() {
        $result = array();
        $database = new DatabaseHelper();
        $mysqli = $database->getConnection();       
        if ($mysqli != null) {
            $stmt = $mysqli->stmt_init();
            if ($stmt->prepare("SELECT columns FROM some_table")) {
                $status = $stmt->execute();
                if ($status) {
                    $stmt->bind_result($columns);
                    $records = array();
                    while ($stmt->fetch()) {
                        // build record array
                        $record = array();
                        // add key value pairs to array
                        $record["columns"] = $columns;
                        // add $record to $records
                        array_push($records, $record);
                    }
                    $result["statusCode"] = Constants::$OK;
                    $result["statusMessage"] = "OK";
                    $result["records"] = $records;
                } else {
                    $result["statusCode"] = Constants::$SYSTEM_ERROR;
                    $result["statusMessage"] = "System Error." . $mysqli->error;
                }
                @$stmt->close();
            } else {
                $result["statusCode"] = Constants::$SYSTEM_ERROR;
                $result["statusMessage"] = "System Error." . $mysqli->error;
            }            
            @$mysqli->close();
        } else {
            $result["statusCode"] = Constants::$SYSTEM_ERROR;
            $result["statusMessage"] = "System Error.";
        }
        return $result;
    }   
}
?>

我相信会有很多人像我一样面对这个问题并成功解决它。必须有某种方法来优化mysql db connections 的使用,我想知道我该怎么做。我只需要可以用来克服这个问题的最佳方法。

希望我已经清楚地说明了我的问题。请帮忙。

问候

【问题讨论】:

  • 嗨@jagmohan,我希望你已经解决了这个问题。我们在一个 Android 应用程序中面临同样的问题,后端团队无法解决这个问题。你能告诉我你是如何解决这个问题的吗?

标签: php mysql mysqli mysqlconnection


【解决方案1】:

为每个查询打开和关闭连接非常浪费。

这意味着每次将火柴移入和移出桌子时,都要经历整个身份验证过程并创建不同的 TCP 连接。

您最终会遇到一堆垂死的 TCP 连接(有耗尽 TCP 端口的风险),并使用相当大一部分带宽与数据库服务器交换密码。

此外,如果您碰巧忘记关闭其中一个连接,它可能会一直保持到脚本结束,从而减少可用连接的数量。可以说是一种“连接泄漏”。

一个典型的会话应该是这样的:

open connection
    do some stuff
    do some stuff
    do some stuff
    do some stuff
close connection

而不是:

open / do stuff / close
open / do stuff / close
open / do stuff / close
open / do stuff / close

由于您已经有一个助手类,我建议您只打开一次连接(当您实例化助手时)并在每次需要访问数据库时从中获取连接句柄。仅当帮助程序被销毁时(即在最坏的情况下在脚本结束时,或者当帮助程序超出范围时)才应关闭连接。

另外一个好处是,它可以使您的代码更加紧凑。所有这些错误报告都比实际的处理代码大,因此很难发现错误。更好地在您的帮助对象中处理它们,以便代码可以专注于完成任务。

最后但同样重要的是,您可以在帮助对象中添加一致性检查。例如,计算静态类变量中打开的连接数,只是为了确保您不会无意中实例化更多帮助程序或创建比您想象的更多的连接。

如果您遵循此模式,则每个当前活动的请求最多应该有一个数据库连接,这应该会最大化您的响应能力。

如果您的页面一直在通过 Ajax 进行六次查询,那么会更快地达到限制。尝试通过分解您的请求来减少数据库使用量。

如果一些愚蠢的 SEO 机器人或精神病用户在您的网站上玩手提钻,您只能监控客户端 IP 并拒绝访问那些执行过于频繁的查询的人。

持久连接只会让您首先减轻做错事造成的损害,因为“关闭”它们实际上不会这样做(并且重新打开它们也不会对它们产生影响),前提是您设置了正确的选项。

【讨论】:

  • 非常感谢您对此的想法。如果我这样做this way,你有什么想法?另外,我将自己尝试并使用 Apache Jmeter 对其进行负载测试。
  • 不客气。您参考的教程可能有点过于关注单个应用程序,但它仍然很好地说明了帮助对象可以提高可靠性和可读性的方式。也许您可以为自己的应用程序设计一些更通用的东西?
  • 我正在考虑重新设计所有 Dao 类以扩展 mysqli 类并使用静态引用连接句柄。这样,在为每个新请求创建一个新的 Dao 类时,代码将使用静态 mysqli 句柄,从而减少数据库连接数。你怎么看?
  • 是的,我认为这是要走的路。我自己并没有那么多地使用 MySQL,所以我无法向您指出现有的代码,但您可能应该在设计自己的工具之前尝试找到一些示例。至少,看看 PHP/PDO 接口。即使您最终不使用它,它也是一个有趣且精心设计的设计。
  • 好的,我会找一些例子。
【解决方案2】:

您可以尝试使用持久连接。

在你的主机前加上 p: 像这样:而不是 私人 $db_host = '我的主机'; 尝试 私人 $db_host = 'p:my-host';

这仅适用于 php 版本 5.3 及更高版本。 它可能会或可能不会帮助您,但值得一试。 请务必阅读此文档 http://www.php.net/manual/en/mysqli.quickstart.connections.php

如果尚未设置,您可能必须设置 mysqli.allow_persistent。

【讨论】:

    【解决方案3】:

    尝试通过在$db_host前面添加p:来使用持久连接

    private $db_host = 'p:my-host';
    

    请务必阅读this documentation,以免引入任何不必要的副作用。

    【讨论】:

      猜你喜欢
      • 2021-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-16
      • 2023-03-10
      • 1970-01-01
      相关资源
      最近更新 更多