最近在项目中遇到插入几万、几十万、几百万的数据到MYSQL数据库,使用EF插入会发现插入速度非常慢的场景,

数据量非常大时EF插入需要几十分钟,甚至几个小时,这样子的速度肯定不是我们所期望的。

后面经过了解与研究发现MySqlBulkLoader,可以批量将数据插入到数据库并且速度上面远远优于EF。

MySqlBulkLoader主要的实现方式:将需要插入的数据转成DataTable,转成一个CSV文件,将CSV文件使用批量导入的形式导入到数据库里面去。

注意:

1.数据库连接地址需要添加配置AllowLoadLocalInfile=true,允许本地文件导入;

Data Source = 数据库地址; Port = 端口; Initial Catalog = 数据库名; User Id = 用户名; Password = 密码;AllowLoadLocalInfile=true;

2.插入的时候会返回插入行数,但是检查所有的数据都正确,也没有报异常,却返回了插入数量为0,可以检查表是否有唯一索引,插入的数据是否违反了唯一索引。

(以下分块展示了代码,如果需要看完整的代码直接看 5.完整的代码)

1.将List转化为DataTable

   /// <summary>
/// 将List转化为DataTable
/// </summary>
/// <returns></returns>
public DataTable ListToDataTable<T>(List<T> data)
{
#region 创建一个DataTable,以实体名称作为DataTable名称 var tableName = typeof(T).Name;
tableName = tableName.ToSnakeCase(); /*实体名称与表名进行转化,主要根据各项目的规定进行转化,不一定就是我这些写的这种转换方式*/
DataTable dt = new DataTable
{
TableName = tableName
}; #endregion #region 拿取列名,以实体的属性名作为列名 var properties = typeof(T).GetProperties();
foreach (var item in properties)
{
var curFileName = item.Name;
curFileName = curFileName.ToSnakeCase();/*列名与表名进行转化,主要根据各项目的规定进行转化,不一定就是我这些写的这种转换方式*/
dt.Columns.Add(curFileName);
} #endregion #region 列赋值
foreach (var item in data)
{
DataRow dr = dt.NewRow();
var columns = dt.Columns; var curPropertyList = item.GetType().GetProperties();
foreach (var p in curPropertyList)
{
var name = p.Name;
name = name.ToSnakeCase();/*列名与表名进行转化,主要根据各项目的规定进行转化,不一定就是我这些写的这种转换方式*/
var curValue = p.GetValue(item); int i = columns.IndexOf(name);
dr[i] = curValue;
} dt.Rows.Add(dr);
} #endregion return dt;
}

2.将DataTable转换为标准的CSV文件

  /// <summary>
/// csv扩展
/// </summary>
public static class CSVEx
{
/// <summary>
///将DataTable转换为标准的CSV文件
/// </summary>
/// <param name="table">数据表</param>
/// <param name="tmpPath">文件地址</param>
/// <returns>返回标准的CSV</returns>
public static void ToCsv(this DataTable table, string tmpPath)
{
//以半角逗号(即,)作分隔符,列为空也要表达其存在。
//列内容如存在半角逗号(即,)则用半角引号(即"")将该字段值包含起来。
//列内容如存在半角引号(即")则应替换成半角双引号("")转义,并用半角引号(即"")将该字段值包含起来。
StringBuilder sb = new StringBuilder();
DataColumn colum;
foreach (DataRow row in table.Rows)
{
for (int i = 0; i < table.Columns.Count; i++)
{
Type _datatype = typeof(DateTime);
colum = table.Columns[i];
if (i != 0) sb.Append("\t");
//if (colum.DataType == typeof(string) && row[colum].ToString().Contains(","))
//{
// sb.Append("\"" + row[colum].ToString().Replace("\"", "\"\"") + "\"");
//}
if (colum.DataType == _datatype)
{
sb.Append(((DateTime)row[colum]).ToString("yyyy/MM/dd HH:mm:ss"));
}
else sb.Append(row[colum].ToString());
}
sb.Append("\r\n");
}
StreamWriter sw = new StreamWriter(tmpPath, false, UTF8Encoding.UTF8);
sw.Write(sb.ToString());
sw.Close();
} }

3.CSV文件导入数据到数据库

    /// <summary>
/// 批量导入mysql帮助类
/// </summary>
public static class MySqlHelper
{
/// <summary>
/// MySqlBulkLoader批量导入
/// </summary>
/// <param name="_mySqlConnection">数据库连接地址</param>
/// <param name="table"></param>
/// <param name="csvName"></param>
/// <returns></returns>
public static int BulkLoad(MySqlConnection _mySqlConnection, DataTable table, string csvName)
{
var columns = table.Columns.Cast<DataColumn>().Select(colum => colum.ColumnName).ToList();
MySqlBulkLoader bulk = new MySqlBulkLoader(_mySqlConnection)
{
FieldTerminator = "\t",
FieldQuotationCharacter = '"',
EscapeCharacter = '"',
LineTerminator = "\r\n",
FileName = csvName,
NumberOfLinesToSkip = 0,
TableName = table.TableName, }; bulk.Columns.AddRange(columns);
return bulk.Load();
}
}

4.使用MySqlBulkLoader批量插入数据

        /// <summary>
/// 使用MySqlBulkLoader批量插入数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public int BulkLoaderData<T>(List<T> data)
{
if (data.Count <= 0) return 0; var connectString = "数据库连接地址";
using (MySqlConnection connection = new MySqlConnection(connectString))
{
MySqlTransaction sqlTransaction = null;
try
{
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
sqlTransaction = connection.BeginTransaction(); var dt = ListToDataTable<T>(data); //将List转成dataTable
string tmpPath = Path.GetTempFileName();
dt.ToCsv(tmpPath); //将DataTable转成CSV文件
var insertCount = MySqlHelper.BulkLoad(connection, dt, tmpPath); //使用MySqlBulkLoader插入数据
sqlTransaction.Commit(); try
{
if (File.Exists(tmpPath)) File.Delete(tmpPath);
}
catch (Exception)
{
//删除文件失败 }
return insertCount; //返回执行成功的条数
}
catch (Exception e)
{
if (sqlTransaction != null)
{
sqlTransaction.Rollback();
}
//执行异常
throw e;
}
} }

5.完整的代码:

namespace WebApplication1.BrantchInsert
{ /// <summary>
/// 批量插入
/// </summary>
public class BulkLoader
{ /// <summary>
/// 测试批量插入入口
/// </summary>
/// <returns></returns>
public int BrantchDataTest()
{ #region 模拟数据
var data = new List<CrmCouponTestDto>() {
new CrmCouponTestDto {
Id=1,
CouponCode="test001",
CouponId = 1,
MemberId=100,
IssueTime=Convert.ToDateTime("2022-06-27 14:00:00"),
UsageTime=Convert.ToDateTime("3000-12-31 00:00:00"),
UsageShopId=0,
UsageBillNo="",
EffectiveStart=Convert.ToDateTime("2022-06-27 14:00:00"),
EffectiveEnd=Convert.ToDateTime("2023-06-27 14:00:00"),
Status=0
},
new CrmCouponTestDto {
Id=2,
CouponCode="test002",
CouponId = 1,
MemberId=101,
IssueTime=Convert.ToDateTime("2022-06-27 14:00:00"),
UsageTime=Convert.ToDateTime("2022-06-27 14:30:00"),
UsageShopId=2,
UsageBillNo="CS202206271430001",
EffectiveStart=Convert.ToDateTime("2022-06-27 14:00:00"),
EffectiveEnd=Convert.ToDateTime("2023-06-27 14:00:00"),
Status=1
},
new CrmCouponTestDto {
Id=3,
CouponCode="test003",
CouponId = 1,
MemberId=102,
IssueTime=Convert.ToDateTime("2022-06-27 14:00:00"),
UsageTime=Convert.ToDateTime("3000-12-31 00:00:00"),
UsageShopId=0,
UsageBillNo="",
EffectiveStart=Convert.ToDateTime("2022-06-27 14:00:00"),
EffectiveEnd=Convert.ToDateTime("2023-06-27 14:00:00"),
Status=0
},
new CrmCouponTestDto {
Id=4,
CouponCode="test004",
CouponId = 1,
MemberId=103,
IssueTime=Convert.ToDateTime("2022-06-27 14:00:00"),
UsageTime=Convert.ToDateTime("3000-12-31 00:00:00"),
UsageShopId=0,
UsageBillNo="",
EffectiveStart=Convert.ToDateTime("2022-06-27 14:00:00"),
EffectiveEnd=Convert.ToDateTime("2023-06-27 14:00:00"),
Status=0
}
};
#endregion
var result = BulkLoaderData<CrmCouponTestDto>(data);
return result; } /// <summary>
/// 使用MySqlBulkLoader批量插入数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public int BulkLoaderData<T>(List<T> data)
{
if (data.Count <= 0) return 0; var connectString = "数据库连接地址";
using (MySqlConnection connection = new MySqlConnection(connectString))
{
MySqlTransaction sqlTransaction = null;
try
{
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
sqlTransaction = connection.BeginTransaction(); var dt = ListToDataTable<T>(data); //将List转成dataTable
string tmpPath = Path.GetTempFileName();
dt.ToCsv(tmpPath); //将DataTable转成CSV文件
var insertCount = MySqlHelper.BulkLoad(connection, dt, tmpPath); //使用MySqlBulkLoader插入数据
sqlTransaction.Commit(); try
{
if (File.Exists(tmpPath)) File.Delete(tmpPath);
}
catch (Exception)
{
//删除文件失败 }
return insertCount; //返回执行成功的条数
}
catch (Exception e)
{
if (sqlTransaction != null)
{
sqlTransaction.Rollback();
}
//执行异常
throw e;
}
} } /// <summary>
/// 将List转化为DataTable核心方法
/// </summary>
/// <returns></returns>
public DataTable ListToDataTable<T>(List<T> data)
{
#region 创建一个DataTable,以实体名称作为DataTable名称 var tableName = typeof(T).Name;
tableName = tableName.ToSnakeCase(); /*实体名称与表名进行转化,主要根据各项目的规定进行转化,不一定就是我这些写的这种转换方式*/
DataTable dt = new DataTable
{
TableName = tableName
}; #endregion #region 拿取列名,以实体的属性名作为列名 var properties = typeof(T).GetProperties();
foreach (var item in properties)
{
var curFileName = item.Name;
curFileName = curFileName.ToSnakeCase();/*列名与表名进行转化,主要根据各项目的规定进行转化,不一定就是我这些写的这种转换方式*/
dt.Columns.Add(curFileName);
} #endregion #region 列赋值
foreach (var item in data)
{
DataRow dr = dt.NewRow();
var columns = dt.Columns; var curPropertyList = item.GetType().GetProperties();
foreach (var p in curPropertyList)
{
var name = p.Name;
name = name.ToSnakeCase();/*列名与表名进行转化,主要根据各项目的规定进行转化,不一定就是我这些写的这种转换方式*/
var curValue = p.GetValue(item); int i = columns.IndexOf(name);
dr[i] = curValue;
} dt.Rows.Add(dr);
} #endregion return dt;
} } /// <summary>
/// 批量导入mysql帮助类
/// </summary>
public static class MySqlHelper
{
/// <summary>
/// MySqlBulkLoader批量导入
/// </summary>
/// <param name="_mySqlConnection">数据库连接地址</param>
/// <param name="table"></param>
/// <param name="csvName"></param>
/// <returns></returns>
public static int BulkLoad(MySqlConnection _mySqlConnection, DataTable table, string csvName)
{
var columns = table.Columns.Cast<DataColumn>().Select(colum => colum.ColumnName).ToList();
MySqlBulkLoader bulk = new MySqlBulkLoader(_mySqlConnection)
{
FieldTerminator = "\t",
FieldQuotationCharacter = '"',
EscapeCharacter = '"',
LineTerminator = "\r\n",
FileName = csvName,
NumberOfLinesToSkip = 0,
TableName = table.TableName, }; bulk.Columns.AddRange(columns);
return bulk.Load();
}
} /// <summary>
/// csv扩展
/// </summary>
public static class CSVEx
{
/// <summary>
///将DataTable转换为标准的CSV文件
/// </summary>
/// <param name="table">数据表</param>
/// <param name="tmpPath">文件地址</param>
/// <returns>返回标准的CSV</returns>
public static void ToCsv(this DataTable table, string tmpPath)
{
//以半角逗号(即,)作分隔符,列为空也要表达其存在。
//列内容如存在半角逗号(即,)则用半角引号(即"")将该字段值包含起来。
//列内容如存在半角引号(即")则应替换成半角双引号("")转义,并用半角引号(即"")将该字段值包含起来。
StringBuilder sb = new StringBuilder();
DataColumn colum;
foreach (DataRow row in table.Rows)
{
for (int i = 0; i < table.Columns.Count; i++)
{
Type _datatype = typeof(DateTime);
colum = table.Columns[i];
if (i != 0) sb.Append("\t");
//if (colum.DataType == typeof(string) && row[colum].ToString().Contains(","))
//{
// sb.Append("\"" + row[colum].ToString().Replace("\"", "\"\"") + "\"");
//}
if (colum.DataType == _datatype)
{
sb.Append(((DateTime)row[colum]).ToString("yyyy/MM/dd HH:mm:ss"));
}
else sb.Append(row[colum].ToString());
}
sb.Append("\r\n");
}
StreamWriter sw = new StreamWriter(tmpPath, false, UTF8Encoding.UTF8);
sw.Write(sb.ToString());
sw.Close();
} } /// <summary>
/// 字符串转化
/// </summary>
public static class StringExtensions
{
/// <summary>
/// 转换为 main_keys_id 这种形式的字符串方式
/// </summary>
public static string ToSnakeCase(this string input)
{
if (string.IsNullOrEmpty(input)) { return input; } var startUnderscores = Regex.Match(input, @"^_+");
return startUnderscores + Regex.Replace(input, @"([a-z0-9])([A-Z])", "$1_$2").ToLower();
}
} /// <summary>
/// 实体
/// </summary>
public class CrmCouponTestDto
{
/// <summary>
/// ID
/// </summary>
public long Id { get; set; } /// <summary>
/// 卡券号
/// </summary>
public string CouponCode { get; set; } /// <summary>
/// 卡券ID
/// </summary>
public int CouponId { get; set; } /// <summary>
/// 会员ID
/// </summary>
public int MemberId { get; set; } /// <summary>
/// 发放时间
/// </summary>
public DateTime IssueTime { get; set; } /// <summary>
/// 使用时间
/// </summary>
public DateTime UsageTime { get; set; } /// <summary>
/// 使用店铺ID
/// </summary> public int UsageShopId { get; set; } /// <summary>
/// 使用单号
/// </summary>
public string UsageBillNo { get; set; } /// <summary>
/// 有效开始时间
/// </summary>
public DateTime EffectiveStart { get; set; } /// <summary>
/// 有效结束时间
/// </summary>
public DateTime EffectiveEnd { get; set; } /// <summary>
/// 状态
/// CouponStatus 卡券状态:
/// -1:未领用
/// 0:未使用
/// 1:已使用
/// 2:已过期
///3:已作废
///4:转赠中
/// </summary> public Int16 Status { get; set; }
}
}

完整代码

使用MySqlBulkLoader批量插入数据的更多相关文章

  1. 批量插入数据, 将DataTable里的数据批量写入数据库的方法

    大量数据导入操作, 也就是直接将DataTable里的内容写入到数据库 通用方法: 拼接Insert语句, 好土鳖 1. MS Sql Server:   使用SqlBulkCopy 2. MySql ...

  2. C#批量插入数据到Sqlserver中的四种方式

    我的新书ASP.NET MVC企业级实战预计明年2月份出版,感谢大家关注! 本篇,我将来讲解一下在Sqlserver中批量插入数据. 先创建一个用来测试的数据库和表,为了让插入数据更快,表中主键采用的 ...

  3. .Net批量插入数据到SQLServer数据库,System.Data.SqlClient.SqlBulkCopy类批量插入大数据到数据库

    批量的的数据导入数据库中,尽量少的访问数据库,高性能的对数据库进行存储. 采用SqlBulkCopy来处理存储数据.SqlBulkCopy存储大批量的数据非常的高效,将内存中的数据表直接的一次性的存储 ...

  4. sql 中的Bulk和C# 中的SqlBulkCopy批量插入数据 ( 回顾 and 粗谈 )

    通常,我们会对于一个文本文件数据导入到数据库中,不多说,上代码. 首先,表结构如下.   其次,在我当前D盘中有个文本文件名为2.txt的文件. 在数据库中,可以这样通过一句代码插入. Bulk in ...

  5. Java 批量插入数据(Oracle)

    //批量添加20000条数据用时8秒. try {    String url = "jdbc:oracle:thin:@IP:1521:orcl"; // orcl为数据库的SI ...

  6. 批量插入数据(基于Mybatis的实现-Oracle)

    前言:做一个数据同步项目,要求:同步数据不丢失的情况下,提高插入性能. 项目DB框架:Mybatis.DataBase:Oracle. -------------------------------- ...

  7. C#批量插入数据到Sqlserver中的三种方式

    本篇,我将来讲解一下在Sqlserver中批量插入数据. 先创建一个用来测试的数据库和表,为了让插入数据更快,表中主键采用的是GUID,表中没有创建任何索引.GUID必然是比自增长要快的,因为你生 成 ...

  8. SqlServer——批量插入数据

    像Major表里面批量插入数据演示: 代码如下: Declare @I int Set @I= Begin Tran InsertData: Insert into Major values(@I,' ...

  9. mybatis批量插入数据到oracle

    mybatis 批量插入数据到oracle报 ”java.sql.SQLException: ORA-00933: SQL 命令未正确结束“  错误解决方法 oracle批量插入使用 insert a ...

随机推荐

  1. 安卓记账本开发学习day5之版本兼容问题

    安卓5.0以上版本想要隐藏DatePicker头布局的写法比较复杂,需要一层一层隐藏 int headerId = getContext().getResources().getIdentifier( ...

  2. Net程序崩溃了怎么去查找定位问题

    工具 这里用到两个工具分别为Procdump+Windbg Procdump:ProcDump是一个命令行实用工具,主要目的是监视应用程序,以便在管理员或开发人员可用于确定峰值的原因期间监视 CPU ...

  3. 听说Integer有bug?1000不等于1000?

    bug? 前几天有位朋友找我,说:"老哥,老哥,我好像发现了Integer一个bug,你帮我看看什么情况?",说完给了我两个很简单的demo,上代码. 100 == 100 100 ...

  4. 【转】python代码优化常见技巧

    https://blog.csdn.net/egefcxzo3ha1x4/article/details/97844631

  5. 你不知道的Python容器

    你不知道的Python容器 你不知道的Python容器 散列表 ChainMap MappingProxyType 线性表 堆 参考资料 昨天阅读了<Python Tricks: The Boo ...

  6. 基于.Net C# 通信开发-串口调试助手

    基于.Net C# 通信开发-串口调试助手 1.概述 串口调试助手,广泛应用于工控领域的数据监控.数据采集.数据分析等工作,可以帮助串口应用设计.开发.测试人员检查所开发的串口应用软硬件的数据收发状况 ...

  7. ZooKeeper 基本原理你懂了么?

    点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! 作者:阿凡卢来源:cnblogs.com/luxiaox ...

  8. 踩到一个关于分布式锁的非比寻常的BUG!

    你好呀,我是歪歪. 提到分布式锁,大家一般都会想到 Redis. 想到 Redis,一部分同学会说到 Redisson. 那么说到 Redisson,就不得不掰扯掰扯一下它的"看门狗&quo ...

  9. 手脱无名壳tslgame_rl

    1.使用Detect It Easy查壳,该壳未显示出壳信息,至于为何有壳,我们使用IDA打开,查看其的导入表,其中没有太多函数使用: 2.我们使用x32dbg打开,运行至入口点,此处没有pushad ...

  10. Java 17 新特性:switch的模式匹配(Preview)

    还记得Java 16中的instanceof增强吗? 通过下面这个例子再回忆一下: Map<String, Object> data = new HashMap<>(); da ...