如何优化Php mysql查询以加快执行速度

时间:2017-06-01 19:22:13

标签: php mysql optimization

有一个php脚本,用于将数据从一个数据库复制到另一个数据库。它工作正常,但需要花费很多时间。每分钟插入大约1000行意味着100,000行需要2小时才能被复制!可以通过优化下面的代码来减少这个时间吗?试图删除子查询并放左连接,但它不起作用。

编辑 - 脚本根据单词或其组合从一个数据库db1中选择(选择)数据。它使用explode / implode进行匹配。然后,它在其他数据库db2中创建该名称的新表。然后将所选数据插入新数据库db2。现在,它从旧数据库的2个表中选择数据。它正在使用子查询。使用左连接会加快执行速度吗?

我的代码

<?php
ini_set('max_execution_time', 30000); 
ini_set('default_charset',"utf-8");

include('config_collect.php');
header('Content-Type: text/html; charset=utf-8');

$keywordId = $_GET['id'];
require_once("config_duplicate.php");
?>

<?php 

try
{
$kewyQ = $db->query("SELECT * FROM collections where id = 
$keyId"); 
$kVal = $kewyQ->fetch(PDO::FETCH_ASSOC);
$table = trim($kVal['words']);

//Create table
$Q = "CREATE TABLE IF NOT EXISTS `$table` (
`id` INT AUTO_INCREMENT NOT NULL,
`t_id` bigint(20) NOT NULL,
`t_text` TEXT,
`user_name` varchar(100),
`place` varchar(100),
`time` datetime,
`description` TEXT,
PRIMARY KEY (`id`)) 
CHARACTER SET utf8 COLLATE utf8_general_ci";
$db2->query($Q);

// Count all records based on words
if (preg_match('/\s/',$table)) {
$keySpace = explode(" ", $table);
if(count($keySpace) > 1){
    $squery = "SELECT * FROM t WHERE t_text LIKE 
 '%".implode("%' AND t_text LIKE '%", $keySpace)."%' ";
 }else{
    $keyval = $keySpace[0];
    $squery = "SELECT * FROM t WHERE t_text LIKE '%$keyval%' ";
 }
 }else{
$squery = "SELECT * FROM t WHERE t_text LIKE '%$tablename%'";
}

则...

$allOBJ = $db->query($squery);

while($row = $allOBJ->fetch(PDO::FETCH_ASSOC)){
    $tId = $row['t_id'];
    $t_text = addslashes($row['t_text']);

    //Get user name and location
    $description = "";
    $place = "";

    $name = addslashes($row['user_name']);
    $time = $row['time'];
    $userj = $db->query("SELECT description,place FROM users WHERE 
    user_name='".$row['user_name']."'");
    if($row2 = $userj->fetch(PDO::FETCH_ASSOC)){
        $description = addslashes($row2['description']);
        $place = addslashes($row2['place']);

    } 

    $chKQ = $db2->query("SELECT COUNT(id) FROM `$tablename` WHERE t_id = 
   '$tId' "); 
    $countg = $chKQ->fetch(PDO::FETCH_NUM);
    if($countg[0]==0){
    $db2->query("insert into `$table`(t_id, t_text, user_name, place, 
    time, description) VALUES ('$tId','$t_text', '$name', 
    '$place','$time', '$description' )");
    }
}
} catch (PDOException $e) {
die("ERROR: " . $e->getMessage());
}

?>
<div>Duplication success</div>

(SQL示例,来自评论:)

SELECT  *
    FROM  collections
    where  id =30 ;
SELECT  *
    FROM  t
    WHERE  t_text LIKE '%red%'
      AND  t_text LIKE '%car%' ;
SELECT  description,place
    FROM  users
    WHERE  user_name='Rick';
INSERT
     into  $table(t_id, t_text, user_name, place, time, description)
    VALUES  ('30','Nice red
              and  big car', 'Rick', 'California','2017/06/03', 'developer' 
            )

2 个答案:

答案 0 :(得分:0)

在我公司的最后两个项目中,我不得不处理类似案件。 我们不得不在网络上复制类似10M行数据的内容,而第一个想到的解决方案是使用php。这种方法显然失败了。

相反,我用这段代码替换它,因为它是纯粹的mysql

,它的工作速度非常快
$this->logger->info('Dumping the data');
    $biDbUser = $this->getContainer()->getParameter('bi_database_user');
    $biDbPass = $this->getContainer()->getParameter('bi_database_password');
    $biDbHost = $this->getContainer()->getParameter('bi_database_host');
    $biDbName = $this->getContainer()->getParameter('bi_database_name');
    $dumpFilePath = rtrim(sys_get_temp_dir(), '/').'/'.uniqid('init_dump_bi').'.sql';

    exec(sprintf(
        'mysqldump -u %s -p%s -h %s --single-transaction --compress %s %s > %s',
        $biDbUser,
        $biDbPass,
        $biDbHost,
        $biDbName,
        $contextConfig->getBiTableLast(),
        $dumpFilePath
    ));
    $this->logger->info('Dump completed. Now loading it to our db.');

    $dpDbUser = $this->getContainer()->getParameter('dp_database_user');
    $dpDbPass = $this->getContainer()->getParameter('dp_database_password');
    $dpDbHost = $this->getContainer()->getParameter('dp_database_host');
    $dpDbName = $this->getContainer()->getParameter('dp_database_name');
    exec(sprintf(
        'cat %s | mysql -u %s -p%s -h %s %s',
        $dumpFilePath,
        $dpDbUser,
        $dpDbPass,
        $dpDbHost,
        $dpDbName
    ));


    unlink($dumpFilePath);

    $columns = array_diff($contextConfig->getColumns(), ['timestamp']);
    $query = sprintf(
        'INSERT INTO %s (%s) SELECT %s FROM %s',
        $contextConfig->getInvoicingTable(),
        implode(',', $columns),
        implode(',', $columns),
        $contextConfig->getBiTableLast()
    );

    $this->logger->info('Data moved to our db. Now inserting from bi to our table.');
    exec(sprintf('mysql -u %s -p%s -h %s %s -e "%s"', $dpDbUser, $dpDbPass, $dpDbHost, $dpDbName, $query));
    $this->logger->info('Insert done.');


    $query = 'DROP TABLE '.$contextConfig->getBiTableLast();
    exec(sprintf('mysql -u %s -p%s -h %s %s -e "%s"', $dpDbUser, $dpDbPass, $dpDbHost, $dpDbName, $query));

    $this->logger->info('Finished synchronisation BI->DP.', [
        'context' => $contextConfig->getContextCode()
    ]);

显然有很多变量我不会解释,但这是这个脚本的主要部分。

exec(sprintf(
        'mysqldump -u %s -p%s -h %s --single-transaction --compress %s %s > %s',
        $biDbUser,
        $biDbPass,
        $biDbHost,
        $biDbName,
        $contextConfig->getBiTableLast(),
        $dumpFilePath
    ));

我从symfony获得连接配置(在你的情况下你应该从你的连接中获得它)并调用mysqldump。我使用--single-transaction,因为用户具有只读访问权限。同时使用--compress可以避免对数据中心网络施加太大压力。

exec(sprintf(
        'cat %s | mysql -u %s -p%s -h %s %s',
        $dumpFilePath,
        $dpDbUser,
        $dpDbPass,
        $dpDbHost,
        $dpDbName
    ));

使用此命令完成转储后,您将把转储的数据放入目标数据库。 最后

$query = sprintf(
        'INSERT INTO %s (%s) SELECT %s FROM %s',
        $contextConfig->getInvoicingTable(),
        implode(',', $columns),
        implode(',', $columns),
        $contextConfig->getBiTableLast()
    );

    $this->logger->info('Data moved to our db. Now inserting from bi to our table.');
    exec(sprintf('mysql -u %s -p%s -h %s %s -e "%s"', $dpDbUser, $dpDbPass, $dpDbHost, $dpDbName, $query));

我使用mysql insert select query将数据从新的转储表传递给我们的表。 我尝试使用pdo连接进行最后一次调用,但我想有一个延迟,直到该表全局可用于所有mysql会话。所以PDO一直说无法找到这张桌子。

希望有所帮助。

答案 1 :(得分:0)

计划A:

将所有行从一个表复制到另一个表的最快方法是在单个查询中执行此操作:

INSERT INTO new (a,b,c)
    SELECT a,b,c FROM old;

没有行计数,没有循环,MySQL和PHP之间没有来回等等。

我不确定究竟在做什么,但看看你是否可以从这个目标向后工作。

计划B:

由于你似乎走过了一堆桌子,也许这会更好:

在PHP之外,做

mysqldump ... old_db  |  mysql ... new_db

注意:这也可以为new_db提供相同的CREATE TABLE

计划C:

这是上述两种的组合。它会为传输创建一个CSV文件。

SELECT a,b,c FROM old_tbl INTO OUTFILE ...;
LOAD DATA INFILE ...;

更多(在看到示例SQL之后)

是否有多个相同的&#39;由$table表示的表格?如果是这样,那么在架构设计中就是一个大红旗;请解释一下你的意图。

好吧,我不会使用wordscollections,但这会将插入内容与两个选项结合起来:

INSERT INTO  $table
      ( t_id, t_text, user_name, time,
        description, place ) 
SELECT  t.t_id, t.t_text, t.user_name, t.time,
        u.description, u.place
    FROM  t
    JOIN  users u  ON u.user_name = t.user_name
    WHERE  t_text LIKE '%blue%'
      AND  t_text LIKE '%car%' ;