0

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

 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 版本的持久连接吗?如果是这样怎么办?

4

2 回答 2

0

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

(在我们的 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列上的 UNIQUE 约束;并且它不依赖于任何索引的存在,但是具有前导列的索引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

于 2013-08-07T18:13:07.877 回答
0

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

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

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

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% 确定,需要检查。

于 2013-08-07T16:26:08.677 回答