mysql批量更新及拆分
阿新 • • 發佈:2021-01-14
前陣子有個指令碼執行特別慢,根據一張表中查詢的資料來更新另一張表資料。每晚18:00開始執行,之前一般大概執行到21、22點左右,最近有幾次竟然直到凌晨才跑完,這就有點接受不了了。捋了一下指令碼,是每查出一條就去更新一條,每次更新時都得連庫、操作、然後關閉。就想著能不能一次更新多條呢,也就是寫個批量更新語句。
假設表結構如下:
CREATE TABLE `test_book` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `book_name` varchar(32) NOT NULL default '' COMMENT '書名', `uid` int unsigned NOT NULL DEFAULT 0 COMMENT '作者id', `u_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間', `c_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間', PRIMARY KEY (`id`), ) ENGINE=InnoDB DEFAULT CHARSET=utf8
如果想根據id批量更新book_name欄位,可以如下書寫:
UPDATE test_book SET
book_name = CASE id
WHEN 1 THEN '三國演義'
WHEN 2 THEN '紅樓夢'
WHEN 3 THEN '水滸傳'
END
WHERE id IN (1,2,3)
這樣一條語句即可更新三條記錄,對id=1的書名更新為三國演義,id=2的書名更新為紅樓夢,以此類推。如果是同時更新多個欄位,譬如書名和作者id:
UPDATE test_book SET book_name = CASE id WHEN 1 THEN '三國演義' WHEN 2 THEN '紅樓夢' WHEN 3 THEN '水滸傳' END, uid = CASE id WHEN 1 THEN 4 WHEN 2 THEN 5 WHEN 3 THEN 6 END WHERE id IN (1,2,3)
指令碼改成批量更新後,執行時間瞬間成為原來的10%,十幾分鐘頂多半小時就執行完了。
然而隨著批量更新的資料量越來越大,又報了另一個問題導致指令碼中斷了。
Communication link failure: 1153 Got a packet bigger than 'max_allowed_packet' bytes
檢視mysql該引數配置:
mysql> select @@max_allowed_packet; +----------------------+ | @@max_allowed_packet | +----------------------+ | 16777216 | +----------------------+ 1 row in set (0.01 sec)
也就是16M。解決方法有兩種,要麼聯絡dba擴大該引數值;要麼拆分資料。我這裡是兩種方法都採用了,一方面聯絡dba將該引數值調整為32M,一方面在程式碼層面做了修改,一次只更新20000條。
$total = count($res);
echo '共'. $total. "條資料\n";
$k = 0;
while ($k < $total) {
$temp = array_slice($res, $k, 20000);
$success = $order->batchUpdateOrderPayStatus($temp);
echo '成功修改'. $success. "\n";
$k += 20000;
}
至此,批量更新徹底解決。