C#在MySQL大量數(shù)據(jù)下的高效讀取、寫(xiě)入詳解
前言
C#操作MySQL大量數(shù)據(jù)最常見(jiàn)的操作便是 select 讀取數(shù)據(jù),然后在C#中對(duì)數(shù)據(jù)進(jìn)行處理, 完畢后再插入數(shù)據(jù)庫(kù)中。 簡(jiǎn)而言之就 select -> process -> insert 三個(gè)步驟。 對(duì)于數(shù)據(jù)量小的情況下(百萬(wàn)級(jí)別 or 幾百兆)可能
最多1個(gè)小時(shí)就處理完了。但是對(duì)于千萬(wàn)級(jí)數(shù)據(jù)可能幾天,甚至更多。 那么問(wèn)題來(lái)了,如何優(yōu)化??
第一步 解決讀取的問(wèn)題
跟數(shù)據(jù)庫(kù)打交道的方式有很多,我來(lái)列舉下吧:
1. 【重武器-坦克大炮】使用重型ORM框架,比如 EF,NHibernat 這樣的框架。
2. 【輕武器-AK47】 使用 Dapper,PetaPoco 之類(lèi),單cs文件。靈活高效,使用簡(jiǎn)單。居家越貨必備(我更喜歡PetaPoco :))
3. 【冷兵器?匕首?】使用原生的Connection、Command。 然后寫(xiě)原生的SQL語(yǔ)句。。
分析:
【重武器】在我們這里肯定直接被PASS, 他們應(yīng)該被用在大型項(xiàng)目中。
【輕武器】 Dapper,PetaPoco 看過(guò)源碼你會(huì)發(fā)現(xiàn)用到了反射,雖然使用 IL和緩存技術(shù) ,但是還是會(huì)影響讀取效率,PASS
好吧那就只有使用匕首, 原生SQL 走起, 利用 DataReader 進(jìn)行高效讀取,并且使用 索引 取數(shù)據(jù)(更快),而不是列名。
大概的代碼如下:
using (var conn = new MySqlConnection("Connection String..."))
{
conn.Open();
//此處設(shè)置讀取的超時(shí),不然在海量數(shù)據(jù)時(shí)很容易超時(shí)
var c = new MySqlCommand("set net_write_timeout=9999999; set net_read_timeout=9999999", conn);
c.ExecuteNonQuery();
MySqlCommand rcmd = new MySqlCommand();
rcmd.Connection = conn;
rcmd.CommandText = @"SELECT `f1`,`f2` FROM `table1`";
//設(shè)置命令的執(zhí)行超時(shí)
rcmd.CommandTimeout = 99999999;
var myData = rcmd.ExecuteReader();
while (myData.Read())
{
var f1= myData.GetInt32(0);
var f2= myData.GetString(1);
//這里做數(shù)據(jù)處理....
}
}
哈哈,怎么樣,代碼非常原始,還是使用索引來(lái)取數(shù)據(jù),很容易出錯(cuò)。 當(dāng)然一切為了性能咱都忍了
第二步 數(shù)據(jù)處理
其實(shí)這一步,根據(jù)你的業(yè)務(wù)需要,代碼肯定不一, 不過(guò)無(wú)非是一些 字符串處理 , 類(lèi)型轉(zhuǎn)換 的操作,這時(shí)候就是考驗(yàn)?zāi)愕腃#基礎(chǔ)功底的時(shí)候了。 以及如何高效編寫(xiě)正則表達(dá)式。。。
具體代碼也沒(méi)法寫(xiě)啊 ,先看完 CLR via C# 在來(lái)跟我討論吧 ,O(∩_∩)O哈哈哈~ 跳過(guò)。。。。
第三部 數(shù)據(jù)插入
如何批量插入才最高效呢? 有同學(xué)會(huì)說(shuō), 使用 事務(wù) 啊,BeginTransaction, 然后EndTransaction。 恩,這個(gè)的確可以提高插入效率。 但是還有更加高效的方法,那就是合并insert語(yǔ)句。
那么怎么合并呢?
insert into table (f1,f2) values(1,'sss'),values(2,'bbbb'),values(3,'cccc');
就是把values后面的全部用逗號(hào),鏈接起來(lái),然后一次性執(zhí)行 。
當(dāng)然不能一次性提交個(gè)100MB的SQL執(zhí)行,MySQL服務(wù)器對(duì)每次執(zhí)行命令的長(zhǎng)度是有限制的。 通過(guò) MySQL服務(wù)器端的 max_allowed_packet 屬性可以查看, 默認(rèn)是 1MB
咱們來(lái)看看偽代碼吧
//使用StringBuilder高效拼接字符串
var sqlBuilder = new StringBuilder();
//添加insert 語(yǔ)句的頭
string sqlHeader = "insert into table1 (`f1`,`f2`) values";
sqlBuilder.Append(sqlHeader);
using (var conn = new MySqlConnection("Connection String..."))
{
conn.Open();
//此處設(shè)置讀取的超時(shí),不然在海量數(shù)據(jù)時(shí)很容易超時(shí)
var c = new MySqlCommand("set net_write_timeout=9999999; set net_read_timeout=9999999", conn);
c.ExecuteNonQuery();
MySqlCommand rcmd = new MySqlCommand();
rcmd.Connection = conn;
rcmd.CommandText = @"SELECT `f1`,`f2` FROM `table1`";
//設(shè)置命令的執(zhí)行超時(shí)
rcmd.CommandTimeout = 99999999;
var myData = rcmd.ExecuteReader();
while (myData.Read())
{
var f1 = myData.GetInt32(0);
var f2 = myData.GetString(1);
//這里做數(shù)據(jù)處理....
sqlBuilder.AppendFormat("({0},'{1}'),", f1,AddSlash(f2));
if (sqlBuilder.Length >= 1024 * 1024 * 1024)//當(dāng)然這里的1MB length的字符串并不等于 1MB的Packet。。。我知道:)
{
insertCmd.Execute(sqlBuilder.Remove(sqlBuilder.Length-1,1).ToString())//移除逗號(hào),然后執(zhí)行
sqlBuilder.Clear();//清空
sqlBuilder.Append(sqlHeader);//在加上insert 頭
}
}
}
好了,到這里 大概的優(yōu)化后的高效查詢、插入就完成了。
總結(jié)
總結(jié)下來(lái),無(wú)非2個(gè)關(guān)鍵技術(shù)點(diǎn), DataReader、SQL合并, 都是一些老的技術(shù)啦。其實(shí),上面的代碼只能稱(chēng)得上高效, 但是, 卻非常的不優(yōu)雅。以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家能有所幫助,如果有疑問(wèn)大家可以留言交流。
- C#如何連接MySQL數(shù)據(jù)庫(kù)
- C#連接MySQL操作詳細(xì)教程
- C#實(shí)現(xiàn)MySQL命令行備份和恢復(fù)
- C#連接MySQL的兩個(gè)簡(jiǎn)單代碼示例
- C#連接mysql的方法【基于vs2010】
- C#如何在海量數(shù)據(jù)下的高效讀取寫(xiě)入MySQL
- c#幾種數(shù)據(jù)庫(kù)的大數(shù)據(jù)批量插入(SqlServer、Oracle、SQLite和MySql)
- C#中調(diào)用MySQL存儲(chǔ)過(guò)程的方法
- C#連接mysql數(shù)據(jù)庫(kù)完整實(shí)例
- C#實(shí)現(xiàn)操作MySql數(shù)據(jù)層類(lèi)MysqlHelper實(shí)例
- c# 向MySQL添加數(shù)據(jù)的兩種方法
相關(guān)文章
c# 通過(guò)代碼開(kāi)啟或關(guān)閉防火墻
這篇文章主要介紹了c# 通過(guò)代碼開(kāi)啟或關(guān)閉防火墻的示例,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下2020-10-10
C#修改及重置電腦密碼DirectoryEntry實(shí)現(xiàn)方法
這篇文章主要介紹了C#修改及重置電腦密碼DirectoryEntry實(shí)現(xiàn)方法,實(shí)例分析了C#修改及重置電腦密碼的相關(guān)技巧,需要的朋友可以參考下2015-05-05
winform實(shí)現(xiàn)限制及解除鼠標(biāo)移動(dòng)范圍的方法
這篇文章主要介紹了winform實(shí)現(xiàn)限制及解除鼠標(biāo)移動(dòng)范圍的方法,涉及C#控制WinForm鼠標(biāo)事件屬性的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09
淺談C#在網(wǎng)絡(luò)波動(dòng)時(shí)防重復(fù)提交的方法
這篇文章主要介紹了淺談C#在網(wǎng)絡(luò)波動(dòng)時(shí)防重復(fù)提交的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
基于C#編寫(xiě)一個(gè)遠(yuǎn)程桌面應(yīng)用
封閉環(huán)境無(wú)法拷貝外來(lái)的遠(yuǎn)程桌面軟件,所以這篇文章小編就來(lái)帶大家用C#編寫(xiě)一個(gè)簡(jiǎn)單的遠(yuǎn)程桌面應(yīng)用,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-10-10

