詳解MySqlBulkLoader的使用
mysql數(shù)據(jù)庫(kù):最近要寫(xiě)一個(gè)服務(wù),跨庫(kù)數(shù)據(jù)同步,目前數(shù)據(jù)量大約一萬(wàn),以后會(huì)越來(lái)越多,考慮到擴(kuò)展性,數(shù)據(jù)的插入操作就采用了MySqlBulkLoader。本文分兩部分來(lái)寫(xiě),第一部分寫(xiě)一下MySqlBulkLoader的使用,第二部分記錄使用過(guò)程中出現(xiàn)的問(wèn)題。
一、MySqlBulkLoader的使用
我們先來(lái)定義個(gè)數(shù)據(jù)表student,表結(jié)構(gòu)如下:

創(chuàng)建一個(gè)core控制臺(tái)項(xiàng)目,相關(guān)代碼如下:
入口代碼:
using System;
using System.Collections.Generic;
namespace MySqlBulkLoaderDemo
{
class Program
{
static void Main(string[] args)
{
//裝載30個(gè)數(shù)據(jù)
List<Student> stuList = new List<Student>();
for (int i = 0; i < 30; i++)
{
stuList.Add(
new Student
{
Guid = Guid.NewGuid().ToString(),
Name = "QXH",
Age = new Random().Next(1, 30)
});
}
//調(diào)用MySqlBulkLoader,往student表中插入stuList
int insertCount = MySqlBulkLoaderHelper.BulkInsert<Student>(stuList, "student");
Console.WriteLine($"成功插入{insertCount}條數(shù)據(jù)");
Console.ReadKey();
}
}
}定義一個(gè)Student映射類(lèi):
using System;
using System.Collections.Generic;
using System.Text;
namespace MySqlBulkLoaderDemo
{
public class Student
{
public string Guid { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}定義一個(gè)MySqlBulkLoaderHelper類(lèi),用于存放相關(guān)方法:
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
namespace MySqlBulkLoaderDemo
{
public class MySqlBulkLoaderHelper
{
const string ConnectionString = "server=localhost;port=3306;user=root;password=123456;database=mysql;SslMode = none;AllowLoadLocalInfile=true";
public static int BulkInsert<T>(List<T> entities, string tableName)
{
DataTable dt = entities.ToDataTable();
using (MySqlConnection conn = new MySqlConnection())
{
conn.ConnectionString = ConnectionString;
if (conn.State != ConnectionState.Open)
{
conn.Open();
}
if (tableName.IsNullOrEmpty())
{
var tableAttribute = typeof(T).GetCustomAttributes(typeof(TableAttribute), true).FirstOrDefault();
if (tableAttribute != null)
tableName = ((TableAttribute)tableAttribute).Name;
else
tableName = typeof(T).Name;
}
int insertCount = 0;
string tmpPath = Path.Combine(Path.GetTempPath(), DateTime.Now.Ticks.ToString() + "_" + Guid.NewGuid().ToString() + ".tmp");
string csv = dt.ToCsvStr();
File.WriteAllText(tmpPath, csv, Encoding.UTF8);
using (MySqlTransaction tran = conn.BeginTransaction())
{
MySqlBulkLoader bulk = new MySqlBulkLoader(conn)
{
FieldTerminator = ",",
FieldQuotationCharacter = '"',
EscapeCharacter = '"',
LineTerminator = "\r\n",
FileName = tmpPath,
Local = true,
NumberOfLinesToSkip = 0,
TableName = tableName,
CharacterSet = "utf8"
};
try
{
bulk.Columns.AddRange(dt.Columns.Cast<DataColumn>().Select(colum => colum.ColumnName).ToList());
insertCount = bulk.Load();
tran.Commit();
}
catch (MySqlException ex)
{
if (tran != null)
tran.Rollback();
throw ex;
}
}
File.Delete(tmpPath);
return insertCount;
}
}
}
}定義一個(gè)幫助類(lèi)ExtentionHelper,主要是擴(kuò)展方法:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
namespace MySqlBulkLoaderDemo
{
public static class ExtentionHelper
{
/// <summary>
/// 將對(duì)象序列化成Json字符串
/// </summary>
/// <param name="obj">需要序列化的對(duì)象</param>
/// <returns></returns>
public static string ToJson(this object obj)
{
return JsonConvert.SerializeObject(obj);
}
/// <summary>
/// 將Json字符串轉(zhuǎn)為DataTable
/// </summary>
/// <param name="jsonStr">Json字符串</param>
/// <returns></returns>
public static DataTable ToDataTable(this string jsonStr)
{
return jsonStr == null ? null : JsonConvert.DeserializeObject<DataTable>(jsonStr);
}
/// <summary>
/// 將IEnumerable'T'轉(zhuǎn)為對(duì)應(yīng)的DataTable
/// </summary>
/// <typeparam name="T">數(shù)據(jù)模型</typeparam>
/// <param name="iEnumberable">數(shù)據(jù)源</param>
/// <returns>DataTable</returns>
public static DataTable ToDataTable<T>(this IEnumerable<T> iEnumberable)
{
return iEnumberable.ToJson().ToDataTable();
}
/// <summary>
/// 判斷是否為Null或者空
/// </summary>
/// <param name="obj">對(duì)象</param>
/// <returns></returns>
public static bool IsNullOrEmpty(this object obj)
{
if (obj == null)
return true;
else
{
string objStr = obj.ToString();
return string.IsNullOrEmpty(objStr);
}
}
/// <summary>
///將DataTable轉(zhuǎn)換為標(biāo)準(zhǔn)的CSV字符串
/// </summary>
/// <param name="dt">數(shù)據(jù)表</param>
/// <returns>返回標(biāo)準(zhǔn)的CSV</returns>
public static string ToCsvStr(this DataTable dt)
{
//以半角逗號(hào)(即,)作分隔符,列為空也要表達(dá)其存在。
//列內(nèi)容如存在半角逗號(hào)(即,)則用半角引號(hào)(即"")將該字段值包含起來(lái)。
//列內(nèi)容如存在半角引號(hào)(即")則應(yīng)替換成半角雙引號(hào)("")轉(zhuǎn)義,并用半角引號(hào)(即"")將該字段值包含起來(lái)。
StringBuilder sb = new StringBuilder();
DataColumn colum;
foreach (DataRow row in dt.Rows)
{
for (int i = 0; i < dt.Columns.Count; i++)
{
colum = dt.Columns[i];
if (i != 0) sb.Append(",");
if (colum.DataType == typeof(string) && row[colum].ToString().Contains(","))
{
sb.Append("\"" + row[colum].ToString().Replace("\"", "\"\"") + "\"");
}
else sb.Append(row[colum].ToString());
}
sb.AppendLine();
}
return sb.ToString();
}
}
}完整項(xiàng)目:MySqlBulkLoaderDemo
運(yùn)行結(jié)果如下:


二、MySqlBulkLoader使用過(guò)程中出現(xiàn)的問(wèn)題
上邊已經(jīng)完整了介紹了MySqlBulkLoader的使用,但是在使用過(guò)程中出現(xiàn)了很多問(wèn)題,主要集中在兩方面,第一個(gè)方面是Mysql數(shù)據(jù)庫(kù)不支持加載本地文件數(shù)據(jù);第二個(gè)方面是我的數(shù)據(jù)庫(kù)在阿里云服務(wù)器上,而代碼在本地,換句話說(shuō)數(shù)據(jù)庫(kù)和項(xiàng)目是分別放在不同服務(wù)器上的。
1、Mysql數(shù)據(jù)庫(kù)不支持加載本地文件數(shù)據(jù)
(1)MySQLBulkLoader原理?
我們結(jié)合SQLBulkCopy來(lái)說(shuō),用過(guò)SqlServer數(shù)據(jù)庫(kù)的都熟悉SQLBulkCopy,很方便,可以直接將datatable中的數(shù)據(jù)批量導(dǎo)入到數(shù)據(jù)庫(kù)。與SQLBulkCopy不同,MySQLBulkLoader也稱(chēng)為L(zhǎng)OAD DATA INFILE,他要從文件讀取數(shù)據(jù),所以我們需要將我們的數(shù)據(jù)集(如上邊的List<Student>)保存到文件,然后再?gòu)奈募锩孀x取。而對(duì)于Mysql來(lái)說(shuō),為了數(shù)據(jù)庫(kù)的安全,本地導(dǎo)入文件的配置沒(méi)有開(kāi)啟,所以使用MySQLBulkLoader批量導(dǎo)入數(shù)據(jù)庫(kù),就需要mysql數(shù)據(jù)庫(kù)支持本地導(dǎo)入文件。否則會(huì)出現(xiàn)以下錯(cuò)誤:
The used command is not allowed with this MySQL version

(2)解決方案
mysql數(shù)據(jù)庫(kù)開(kāi)啟允許本地導(dǎo)入數(shù)據(jù)的配置,命令如下:
SET GLOBAL local_infile=1;//1表示開(kāi)啟,0表示關(guān)閉
查看該配置的狀態(tài)命令如下:
SHOW VARIABLES LIKE '%local%';

在項(xiàng)目里面的數(shù)據(jù)庫(kù)連接字符串做設(shè)置
數(shù)據(jù)庫(kù)連接字符串要加上”AllowLoadLocalInfile=true“,如下:
const string ConnectionString = "server=localhost;port=3306;user=root;password=123456;database=mysql;SslMode = none;AllowLoadLocalInfile=true";
2、數(shù)據(jù)庫(kù)和項(xiàng)目是分別放在不同服務(wù)器上
(1)問(wèn)題描述
數(shù)據(jù)庫(kù)和項(xiàng)目是分別放在不同服務(wù)器上,會(huì)造成以下問(wèn)題:
System.NotSupportedException HResult=0x80131515 Message=To use MySqlBulkLoader.Local=true, set AllowLoadLocalInfile=true in the connection string. See https://fl.vu/mysql-load-data

(2)原因
因?yàn)轫?xiàng)目中將數(shù)據(jù)集生成的文件保存在了項(xiàng)目所在的服務(wù)器,另一個(gè)服務(wù)器上的數(shù)據(jù)庫(kù)在插入數(shù)據(jù)操作時(shí),找不到數(shù)據(jù)集文件,導(dǎo)致的錯(cuò)誤
(3)解決方法
方法很簡(jiǎn)單,因?yàn)閿?shù)據(jù)庫(kù)并不在項(xiàng)目所在的服務(wù)器,所以MySqlBulkLoader中要設(shè)置Local = true讀取本地文件,進(jìn)行導(dǎo)入。具體代碼如下:

(4)總結(jié)
如果你的項(xiàng)目和數(shù)據(jù)庫(kù)在一臺(tái)服務(wù)器上,那么就不會(huì)出現(xiàn)該問(wèn)題。
相關(guān)文章
SQL查詢(xún)語(yǔ)句執(zhí)行的過(guò)程
這篇文章主要介紹了SQL查詢(xún)語(yǔ)句執(zhí)行的過(guò)程,文章圍繞主題展開(kāi)SQL查詢(xún)語(yǔ)句的相關(guān)資料,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05
MYSQL的binary解決mysql數(shù)據(jù)大小寫(xiě)敏感問(wèn)題的方法
BINARY不是函數(shù),是類(lèi)型轉(zhuǎn)換運(yùn)算符,它用來(lái)強(qiáng)制它后面的字符串為一個(gè)二進(jìn)制字符串,可以理解為在字符串比較的時(shí)候區(qū)分大小寫(xiě)2013-09-09
mysql通過(guò)find_in_set()函數(shù)實(shí)現(xiàn)where in()順序排序
這篇文章主要介紹了mysql通過(guò)find_in_set()函數(shù)實(shí)現(xiàn)where in()順序排序的相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10
Mysql數(shù)據(jù)庫(kù)雙機(jī)熱備難點(diǎn)分析
本文主要給大家介紹了在Mysql數(shù)據(jù)庫(kù)雙機(jī)熱備其中的難點(diǎn)分析以及重要環(huán)節(jié)的經(jīng)驗(yàn)心得,需要的朋友收藏分享下吧。2017-12-12
關(guān)于MySQL的sql_mode合理設(shè)置詳解
這篇文章主要給大家介紹了關(guān)于MySQL的sql_mode合理設(shè)置的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用MySQL具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
Kubernetes中實(shí)現(xiàn) MySQL 讀寫(xiě)分離的詳細(xì)步驟
Kubernetes中實(shí)現(xiàn)MySQL的讀寫(xiě)分離通過(guò)主從復(fù)制架構(gòu),利用Kubernetes部署MySQL主節(jié)點(diǎn)和從節(jié)點(diǎn),并通過(guò)Service實(shí)現(xiàn)讀寫(xiě)分離,提高數(shù)據(jù)庫(kù)性能和可維護(hù)性2024-11-11
mysql學(xué)習(xí)筆記之基礎(chǔ)知識(shí)
本文是MySQL學(xué)習(xí)筆記系列文章的第一篇,給大家簡(jiǎn)單講解下MySQL的一些檢查操作命令,希望大家能夠喜歡2017-02-02

