最近在做一個數據導入功能,數據量爲360W,按照我們通常的比較笨的方法則的遍歷數據一條條的往數據庫插入;經實驗證明這個方法確實可以完成這個工作,但是它的插入數據的速度太慢,尤其當隨着數據庫的數據增加,速度會越來越慢。
所以有的人會說我可以使用事務的方式,確實使用事務的方式可以優化數據的插入速度,一般是可以滿足我們的要求;但是我們知道使用事務,是會導致鎖表的,當執行事務過程中,數據庫會鎖表,導致其它的連接無法操作此表,尤其在服務器端,支持多用戶的情況下,這是一個致命的缺陷;所以現在很多服務器數據庫都是採用分表分庫的方式,使得讀寫分離,可以提高服務器的工作效率。
如果不採用分表分庫的方式我們該怎樣來優化呢?
首先,我們知道一條條插入數據是比較慢的,插入數據批量插入數據才最快。
單條插入:INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....)
批量插入:INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....), (值1, 值2,....), (值1, 值2,....).....
當然批量插入數據有數據量限制,開始我插入360w數據把它們寫入了一條sql語句中,結果導致程序出現段錯誤;
所以我們要限定批量插入數據大小,單次插入多少數據合適,我也不知道,我沒有去查;
我們需要將數據切分成多段然後插入數據庫,切分的方法如下:
std::string AgentRelationNet::getValueListStr(AgentRelationNet mAgentRelationNet) {
std::ostringstream sql;
sql << "(\'" << mAgentRelationNet.id.toStdString() << "\',"
<< "\'" << mAgentRelationNet.origId << "\',"
<< "\'" << mAgentRelationNet.destId << "\')";
return sql.str();
}
std::list<std::string> AgentRelationNet::getBatchInsertSqlList(std::list<AgentRelationNet> &dataList, long maxLength)
{
std::list<std::string> sqls;
if (dataList.size() <= 0)
return sqls;
std::string pre = getInsertPre();
std::ostringstream sqlBuilder;
sqlBuilder << pre;
for each (AgentRelationNet mAgentRelationNet in dataList)
{
std::string valueStr = AgentRelationNet::getValueListStr(mAgentRelationNet);
sqlBuilder << valueStr << ",";
if (sqlBuilder.str().length() >= maxLength) {
std::string sql = sqlBuilder.str();
sql = sql.substr(0, sql.length() - 1);
sqls.push_back(sql);
sqlBuilder.str("");
sqlBuilder.clear();
sqlBuilder << pre;
}
}
if (sqlBuilder.str().length() > 1) { //處理最後一條或沒有達到最大值時
std::string sql = sqlBuilder.str();
sql = sql.substr(0, sql.length() - 1);
sqls.push_back(sql);
sqlBuilder.clear();
}
return sqls;
}
std::string AgentRelationNet::getInsertPre() {
std::ostringstream sql;
sql << "insert into agent_relation_net(id, orig_agent_id, dest_agent_id) values ";
return sql.str();
}
總之就是將數據分爲多個批量插入語句,在切分成多個sql語句後,我們可以分幾次插入數庫,在插入語句中我們可以休眠一段時間再執行下一次插入,爲的就是中間騰出一些時間給其它的連接操作。