【问题标题】:Mysqli connection designMysqli 连接设计
【发布时间】:2013-08-09 02:32:09
【问题描述】:

我将创建一个网站,该网站每天可以收到多达 6,000 个警报(这些警报包含最多 140 个字符,大约是 30 个单词令牌-)。我的问题是,如果我有一个函数来验证每个令牌是否已经在数据库中,它是假的,什么都不做,如果它是插入,等等,但是对于每个单词它必须遍历整个数据库进行检查,我应该如何处理连接?每次我需要检查单词时打开连接很糟糕?

 function insertTag($tag){
    $db = "test";
$user = "Eduardo";
$pass = "weaponx";
$host = "localhost";
$con = new mysqli($host, $user, $pass, $db);


    $noInsert = false;
$result = $con->query("select TAG from TAGS");
$num_tags = $result->num_rows;  

for($c=0; $c < $num_tags; $c++){
    $row = $result->fetch_assoc();
    echo "tag ". ($c+1) .": ". $row['TAG'] ."<br/>";

    if ($fila['TAG'] === $tag){
        echo "$tag: already exist.<br />";  
        $noInsert = true; 
        return;
    }
    else{
        $noInsert = false;                      
    }   
}

if (!$noInsert){
    $result2 = $con->query("insert into TAGS(TAG) values('$tag')");
    echo "token $tag: inserted<br />";
}
}

$tags = "danger in detroit";
// insert word in the BD, only if new
for($i=0; $i < count($tags); $i++){
 insertTag($tags[$i]);  
}

我应该使用 mysqli 版本的持久连接吗?如果有怎么办?

【问题讨论】:

  • 这是一种非常低效的查看标签是否存在的方法。你不能至少在你的 SELECT 中添加一个 WHERE 子句吗?
  • 我使用单词标签是因为他们告诉我,但实际上我保存了应用程序获得的所有可能值,这就是为什么我不使用 where 子句,这就像创建字典,我能想到的唯一改进是检查单词的第一个字母并搜索它们,但我不知道如何
  • 使用mysqli时,必须使用bind_paramproperly escape your values,避免严重的SQL injection bugs。您直接在查询中替换$tag,对此没有任何顾虑。
  • @WeaponX - 为每个单词执行不同的 SELECT 会更有效。一旦您的字典达到合适的大小,您每次都会检索数千个结果并循环遍历所有结果。您将进行更多查询,但每个查询最多只返回 1 行。
  • 伙计们根本不需要做任何选择。

标签: php mysql mysqli database-connection


【解决方案1】:

不不,不要那样做:)几点:

1) 在脚本的开头打开 mysql 连接,并根据需要执行尽可能多的查询,而无需为每个查询打开新连接。 (所以打开连接需要在你的功能之外)

2)您需要使用索引并将数据库中的标签值标记为唯一,在这种情况下,您每次都可以插入,如果重复则不会插入。为了为你的表创建索引,你需要使用你最喜欢的 sql manager,或者只是做一个查询,你可以在这里阅读更多:

http://dev.mysql.com/doc/refman/5.0/en/create-index.html

但简而言之,您需要使代表您的标签名称的列是唯一的(“TAGS”表中的“TAG”),并且您需要将您的 con->query 语句放在 try catch 块中,因为它会可能会在重复时引发异常,您需要处理它。

3)我认为您将 wring 值作为标签插入,您使用的是字母而不是单词,应该是这样吗?

4) 你的查询看起来不正确,它有效吗?

做这样的事情(显然是在你为你的表添加了唯一索引之后):

function insertTag($con, $tag){
    try {
        $con->query("INSERT INTO `TAGS` (TAG`) values('$tag')");
        echo "token $tag: inserted<br />";
    } catch (Exception $e) {
        echo "token $tag: NOT inserted<br />";
    }
}

$tags = "danger in detroit";

// will create array of words
$tags = explode(' ', $tags);

$db = "test";
$user = "Eduardo";
$pass = "weaponx";
$host = "localhost";
$con = new mysqli($host, $user, $pass, $db);

// insert word in the DB, only if new
for($i=0; $i < count($tags); $i++) {
    insertTag($con, $tags[$i]);  
}

另外,我认为这甚至可以通过对所有标签的 ONE INSERT 查询来完成,但我不是 100% 确定,需要检查。

【讨论】:

  • 在第一个问题上阅读我的评论,如果我将 $con 留在函数之外,它会变得未定义
  • 抱歉,编辑了代码,只需将 $con 作为附加参数传递给您的函数。 (还有其他方法,通过将 $con 声明为全局内部函数,但最好传递它)
  • 那行得通,唯一的问题是 id_tag 通过这样做跳转数字,我的代码全部按 1-12 的顺序排列,以此类推,但没有选择从 id_tag 10 跳转到 20 和然后是 23,但其余的工作很好,代码更少,但我需要添加 sql 注入代码
  • 还有 catch 块永远不会被执行!,我想我需要抛出异常
  • 你按照我说的把索引放到数据库里了吗?
【解决方案2】:

从数据库连接和断开连接是昂贵的;为了性能,您希望避免连接“搅动”。

(在我们的 J2EE Web 服务器中,我们实现了连接池,它是经过验证的数据库会话的集合。应用程序可以“搅动”连接池,检索和返回连接,但实际的数据库会话保持连接。)

所以,回答您的问题,通过数据库连接搅动是一个糟糕的设计。更好的方法是在进程开始时连接一次,将该数据库连接的句柄传递给需要它的函数,然后在进程结束时断开连接。


Avetis Zakharyan 的回答提供了一个很好的方法。我完全同意他的观点,即不需要运行单独的 SELECT 语句。让 INSERT 语句检查值是否存在会更有效。

这种方法减少了到数据库的往返次数,并且它也适用于多个并发会话,其中两个(或更多)会话可能正在运行 SELECT 以检查标签的存在,而两个会话都找不到它,并且当两个会话插入相同的标签时。列上的 UNIQUE 约束将避免重复;使用 INSERT IGNORE 可以避免抛出异常,但仍然...

我的首选方法是使用一条 SQL 语句,该语句仅在匹配行不存在时才尝试插入一行:

INSERT INTO tags (tag)
SELECT v.tagval
  FROM (SELECT :tag AS tagval) v
  LEFT JOIN tags d ON d.tag = v.tagval 
 WHERE d.tag IS NULL 

这不依赖于tag 列上的唯一约束;并且它不依赖于任何索引的存在,但是对于提高性能而言,需要具有前导列 tag 的索引。

(SELECT可以单独运行,用于测试。)

查询是一个经典的反连接...“从 v 中返回在 d 中没有匹配的行”。

对于 PDO,使用绑定参数,而不是在 SQL 文本中包含值。

$sql = "INSERT INTO tags (tag)
        SELECT v.tagval
          FROM (SELECT :tag AS tagval) v
          LEFT JOIN tags d ON d.tag = v.tagval 
         WHERE d.tag IS NULL ";
$stmt=$conn->prepare($sql);
$stmt->bindParam(":tag",$tag);
$stmt->execute();

(注意:如果客户端的字符集与目标列的字符集不同,可能需要显式转换。例如,如果客户端字符集是UTF8,列是latin1,那么:

      FROM (SELECT CONVERT(:tag AS latin1) AS tagval) v

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-29
    • 2013-11-29
    • 1970-01-01
    • 1970-01-01
    • 2018-05-08
    • 1970-01-01
    相关资源
    最近更新 更多