設計一個數據表如下:
create table optimization(
id INT NOT NULL AUTO_INCREMENT,
value VARCHAR(10) NOT NULL,
PRIMARY KEY(id)
);
現在有一個業務需求需要批量插入數據。
先來看看下面這一段代碼:
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'root';
$password = 'root';
try {
$dbh = new PDO($dsn, $user, $password);
} catch(PDOException $e) {
echo 'Connection failed: ' , $e->getMessage();
}
$begin = microtime(true) * 1000;
$count = 100;
$stmt = $dbh->prepare('INSERT INTO `optimization` (id, value) VALUES(:id, :value)');
$stmt->bindParam(':id', $id);
$stmt->bindParam(':value', $value);
for ($i = 0; $i < $count; $i++)
{
$id = '';
$value = $i;
$stmt->execute();
}
$end = microtime(true) * 1000;
echo 'excuted : ' , ($end - $begin) , ' ms';
經過測試,上面代碼運行結果如下:
1、excuted : 7601.4348144531 ms
2、excuted : 7476.4270019531 ms
3、excuted : 7674.4387207031 ms
平均:7584.100179036433 ms
再來看看第二段代碼:
<?php
$dsn = 'mysql:dbname=test;host=127.0.0.1';
$user = 'root';
$password = 'root';
try {
$dbh = new PDO($dsn, $user, $password);
} catch(PDOException $e) {
echo 'Connection failed: ' , $e->getMessage();
}
$begin = microtime(true) * 1000;
$dbh->beginTransaction();
try {
$count = 100;
$sql = 'INSERT INTO `optimization` (id, value) VALUES ';
$sql_arr = array();
$sql_str = '';
for ($i = 0; $i < $count; $i++)
{
$sql_arr[] = ("('', $i)");
}
$sql_str = implode(',', $sql_arr);
$sql .= $sql_str;
$stmt = $dbh->prepare($sql);
$stmt->execute();
$dbh->commit();
} catch(Exception $e) {
$dbh->rollBack();
echo $e->getMessage() . '<br>';
}
$end = microtime(true) * 1000;
echo 'excuted : ' , ($end - $begin) , ' ms';
上面這段代碼的運行結果如下:
1、excuted : 99.005859375 ms
2、excuted : 103.00610351562 ms
3、excuted : 68.00390625 ms
平均:90.00528971354 ms
##分析 可以看出,在第二段代碼中,使用了批量插入,此時的效率比第一段提高了84%。原因如下:
- 使用第一段代碼的時候,因為每一次循環里都執行了一個mysql語句,此時php需要與mysql獲得連接,然后再執行mysql語句,然后再斷開。這就是第一段代碼最主要的時間開銷–PHP與MySQL連接的網絡傳輸IO
- 第一段代碼SQL語句解析的次數更多
因此,在第二段代碼中,通過合並SQL語句來實現減少SQL語句解析的次數以及PHP與MySQL連接的次數來達到減少網絡傳輸IO的開銷。
注意: 1、SQL語句是有長度限制的,因此,在進行SQL語句合並時務必不能超過SQL長度限制,通過設置max_allowed_packet可以修改,默認是1M,測試時修改為8M。
##總結
在進行對數據庫的批量操作(如:插入、更新、修改)時,應當盡可能將SQL語句合並后再執行而不是在循環中依次執行。
記錄下最近在項目中犯下的一個比較大的錯誤,以后不能再犯了。以前一直都沒有注意到,直到現在真正參與到企業項目中,自己的代碼被老大指出錯誤后才發現自己的錯誤。學習了。