起因:有一個innoDB引擎的表Table,在一個大概3000次的foreach循環(huán)中執(zhí)行
INSERT INTO Table(columnA, columnB) VALUES (valueA, valueB)
結(jié)果居然超出了60S的php執(zhí)行限制(當(dāng)然這個限制可以在php.ini中修改),讓我很不解為何插入效率如此低下。
經(jīng)過查找資料以及摸索,得到以下優(yōu)化方法:
1、innoDB是mysql引擎中唯一支持事務(wù)transaction的引擎。默認(rèn)所有用戶行為都在事務(wù)內(nèi)發(fā)生。
默認(rèn)mysql建立新連接時,innoDB采用自動提交autocommit模式,每個SQL語句在它自己上形成一個單獨(dú)的事務(wù),即insert一次就commit了一次,InnoDB在該事務(wù)提交時必須刷新日志到磁盤,因此效率受限于磁盤讀寫效率。
你可以通過
mysql_query("SET AUTOCOMMIT = 0");
來關(guān)閉自動提交模式。
如果自動提交模式被關(guān)閉,那么我們可以認(rèn)為一個用戶總是有一個事務(wù)打開著。一個SQL COMMIT或ROLLBACK語句結(jié)束當(dāng)前事務(wù)并且一個新事務(wù)開始。兩個語句都釋放所有在當(dāng)前事務(wù)中被設(shè)置的InnoDB鎖定。一個COMMIT語句意味著在當(dāng)前事務(wù)中做的改變被生成為永久的,并且變成其它用戶可見的。一個ROLLBACK語句,在另一方面,撤銷所有當(dāng)前事務(wù)做的修改。
當(dāng)然如果是自動提交模式,通過用明確的START TRANSACTION或BEGIN語句來開始一個事務(wù),并用COMMIT或者ROLLBACK語句來結(jié)束它,這樣用戶仍舊可以執(zhí)行一個多重語句事務(wù)。
2、因此對于本例,在建立數(shù)據(jù)庫連接后,立即關(guān)閉自動提交,在foreach循環(huán)結(jié)束后,一次commit即可,效率大大提升。
mysql_query("SET AUTOCOMMIT = 0");
foreach(***)
INSERT INTO Table(columnA, columnB) VALUES (valueA, valueB)
mysql_query("commit");
3、對于多次insert行到同一表的需求,你還可以采用多行插入語法來減少客戶端和服務(wù)器之間的通訊開支。
即
INSERT INTO Table(columnA, columnB) VALUES (1,2), (5,5), (3,3), ...
4、如果你的表有索引,索引會拖慢insert速度。大量插入數(shù)據(jù)時,可以先關(guān)閉索引,然后再重建索引。
ALTER TABLE Table DISABLE KEYS;
INSERT INTO ***;
ALTER TABLE Table ENABLE KEYS;