PDO中的MySQL数据库连接管理

10

我对PHP/MySQL非常新手,一边学一边做。我最近学到的其中一个新事物是,每个用户名在数据库中能够建立的连接数是有上限的。当我开始用 Wordpress 构建我的网站时,我使用的是旧版的 mysql_query() 命令。因为 Wordpress 只要在网站上登录就会保持活动连接,所以我从未必须创建到 MySQL 数据库的连接。当我决定将所有 MySQL 查询转换为 PDO 扩展时,我不能再利用 Wordpress 的活动连接了,而是必须开始自己的数据库连接。我在一个单独的 PHP 配置文件中完成这个操作,并在运行脚本的每个页面中引入它。PHP 代码如下:

try {
    $DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
}
catch(PDOException $e) {  
    echo "Select DB Error: " .$e->getMessage(). "</br>";
}

很不幸,我经常遇到以下错误:

" SQLSTATE[42000] [1203] User already has more than 'max_user_connections' active connections"

通过一些在线研究,我尝试了各种不同的方法来解决这个问题。首先,我尝试在每个脚本结束时将数据库连接设置为null(尽管这应该是 PHP PDO 的默认设置?)。然后我尝试在每次查询数据库之后将每个语句处理程序设置为null(这是一个绝望的努力)。最后,我尝试使用持久连接,通过更改配置文件中的以下行:

$DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass, array(PDO::ATTR_PERSISTENT => true));

似乎这一切都不起作用,我仍然得到完全相同的错误。网站在准备好时需要能够处理每天100-200人输入各种不同的数据。即使我能弄清楚如何利用Wordpress的PDO活动连接,那也是一个好的开始。我会感激任何帮助。

更新:

我正在将SHOW FULL PROCESSLIST作为MySQL查询运行在我的代码中,但我得到非常奇怪的行为。这是我的脚本的第一部分(不包括SHOW FULL PROCESSLIST查询):

<?php

include 'config.php';

if(isset($_POST['submit']))
{
//Get Current User Login
global $current_user;
$current_user = wp_get_current_user();
$ulog = $current_user->user_login;
$tablename_cc = "cc_".$ulog;
$tablename_db = "db_".$ulog;
$tablename_misc = "misc_".$ulog;
$tablename_cash = "cash_".$ulog;

try {
    $DBH->exec("CREATE TABLE IF NOT EXISTS " .$tablename_cc. " (ID bigint(100) NOT NULL AUTO_INCREMENT, PRIMARY KEY(ID), accnt TEXT(20), cc_num TEXT(4), cc_amnt DECIMAL(8,2), cc_app TEXT(20), cc_date VARCHAR(10), cc_time VARCHAR(10))");            
    $DBH->exec("CREATE TABLE IF NOT EXISTS " .$tablename_db. " (ID bigint(100) NOT NULL AUTO_INCREMENT, PRIMARY KEY(ID), accnt TEXT(20), db_num TEXT(20), db_amnt DECIMAL(8,2), db_date VARCHAR(10), db_time VARCHAR(10))");        
    $DBH->exec("CREATE TABLE IF NOT EXISTS " .$tablename_misc. " (ID bigint(100) NOT NULL AUTO_INCREMENT, PRIMARY KEY(ID), accnt TEXT(20), misc_item TEXT(1000), misc_amnt DECIMAL(8,2), misc_date VARCHAR(10), misc_time VARCHAR(10))");       
    $DBH->exec("CREATE TABLE IF NOT EXISTS " .$tablename_cash. " (ID bigint(100) NOT NULL AUTO_INCREMENT, PRIMARY KEY(ID), accnt TEXT(20), cash_amnt DECIMAL(8,2), cash_time VARCHAR(10))");
}
catch(PDOException $e) {  
    echo "Create Tables Error: " .$e->getMessage();
}
} ?>
如果我尝试在if语句外部运行SHOW PROCESS LIST查询,则会得到一个结果,指出我有一个活动连接;但是,如果我尝试在if语句内部运行SHOW FULL PROCESSLIST查询,则会出现超过连接数的错误。基于此结果,我认为也许页面顶部的include行在用户提交表单时导致问题(实际上是尝试两次连接数据库),所以我将其移至if语句内部,但也没有任何区别。我对这种情况感到困惑。
更新/答案(仍有一些问题):
我找到了我的问题所在。在其中一个脚本完成后,有一个javascript命令弹出一个窗口,在那里另一个脚本将为他们打印发票。第二个脚本也尝试使用配置文件连接到数据库。用户使用的表单是动态的(他们可以输入尽可能少或多的数据集),因此当用户插入少量数据时,PDO查询非常快,并且连接数之间没有冲突;但是,如果用户输入大量数据,则需要更长时间,服务器达到最大连接数。问题实际上存在于服务器环境差和我的低效查询的组合中(还在学习PHP)。目前唯一的解决方案是在我的第二个脚本上放置更长的睡眠时间(已经使用了3秒的睡眠时间),以让其他脚本赶上,但是在某个时候,这将需要太长时间。我不确定您们是否有任何更好的建议?

2
你能找出你的连接限制是多少吗?这很有帮助,因为如果它被设置为像10这样的低值,你可能需要将其更改。如果它是一个很高的值,那么我们可能在某个地方有失控的代码。 - Levi Morrison
如果有助于展示我的低效率所在,我可以提供如何利用数据库查询的示例代码。 - user1562781
你提到了一个Javascript弹出框。这个弹出框是做什么的?它是否会阻塞线程等待用户输入? - Malachi
更好的问题。你已经找到解决方案了吗? - Malachi
@user1562781 我强烈反对您长时间睡眠 PHP 脚本 - 通常同时执行的最大 PHP 脚本数量是有限制的 - 睡眠脚本仍然计算为执行。因此,它可能适用于一些用户,但对于同时使用更多用户的情况,这将是一个应用程序杀手。无论如何,请始终检查连接是否已经存在 => 1个脚本执行不应该有超过1个连接。 - jave.web
显示剩余3条评论
3个回答

2

如果您有其他工具可以连接到数据库,您可能想查看实际创建了多少个连接。在紧急情况下,phpmyadmin 可以做到这一点。

通常,您可以使用以下命令查看所有活动查询和连接:

SHOW PROCESSLIST

如果您在PDO连接例程中犯了错误,可能会为每个尝试运行的查询建立一个连接,而不是在请求期间建立一个连接。您可能还需要检查每个请求是否为您创建了大量未正确关闭的连接池。您可能需要尝试使用连接选项来解决这些问题。


我确实有phpmyadmin,但由于我目前甚至无法建立连接,因此连接状态显示为“null”。 - user1562781
这可能与您的PDO工作无关,而是发生在同一时间的其他事情。尝试禁用您的应用程序并查看是否清除,或者如果您可以以某种方式重新启动数据库服务器。 - tadman
我在我的代码中运行了SHOW FULL PROCESSLIST查询以查看结果,但是我得到了一些奇怪的行为。我已经更新了我的初始帖子,并附上了结果。也许你可以发表评论? - user1562781
我猜测 config.php 建立了一个连接,wp_get_current_user 引起 WordPress 在填充 WP_User 对象时出于某种原因建立了第二个连接,而您的主机一次只允许一个连接,所以当您尝试在那里运行查询时就会达到限制。这一切看起来像是一个过度限制的网络主机,在任何合理的环境中问题都会消失... - Dan Grossman
有两个原因让我怀疑这可能不是这种情况。1)我刚刚将WordPress wp_get_current_user()函数移出if语句以查看其行为,并且PROCESSLIST显示它仅正在进行一次连接。2)我今天早些时候成功使用了几次代码,它运行得很好。只有当我尝试输入几个大数组时,它才会崩溃。现在,无论我的数组多小,它都无法工作。 - user1562781
我找出了我的问题,但不确定如何解决它。我已经更新了原帖。如果你们有任何评论/建议,我将不胜感激。 - user1562781

1

在文件中使用include_once('config.php')require_once('config.php')而不是include 'config.php'可能是避免重复数据库连接的解决方案。


0
你需要的是一个叫做PDO“单例”类。这里有一个例子:

http://tonylandis.com/php/php5-pdo-singleton-class/

这不是最好的例子,因为构造函数需要太多参数。只需将 $dsn、..、args 硬编码到构造函数中,即 _construct(){...},您就可以在任何您原本会使用 PDO() 的地方使用 new sdb()。除了一次用于类的 include 或 require 语句外,无需任何其他语句。

$pdo 对象存储为类内的静态变量,因此始终可访问。


我认为在这个例子中"return self::$PDOInstance;"没有任何作用——这个想法是复制所有的类方法,而不是返回对象,我不认为你可以从__construct()返回任何东西。 - user1122069

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接