使用MySqlBulkLoader批量插入数据
最近在项目中遇到插入几万、几十万、几百万的数据到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批量插入数据的更多相关文章
- 批量插入数据, 将DataTable里的数据批量写入数据库的方法
大量数据导入操作, 也就是直接将DataTable里的内容写入到数据库 通用方法: 拼接Insert语句, 好土鳖 1. MS Sql Server: 使用SqlBulkCopy 2. MySql ...
- C#批量插入数据到Sqlserver中的四种方式
我的新书ASP.NET MVC企业级实战预计明年2月份出版,感谢大家关注! 本篇,我将来讲解一下在Sqlserver中批量插入数据. 先创建一个用来测试的数据库和表,为了让插入数据更快,表中主键采用的 ...
- .Net批量插入数据到SQLServer数据库,System.Data.SqlClient.SqlBulkCopy类批量插入大数据到数据库
批量的的数据导入数据库中,尽量少的访问数据库,高性能的对数据库进行存储. 采用SqlBulkCopy来处理存储数据.SqlBulkCopy存储大批量的数据非常的高效,将内存中的数据表直接的一次性的存储 ...
- sql 中的Bulk和C# 中的SqlBulkCopy批量插入数据 ( 回顾 and 粗谈 )
通常,我们会对于一个文本文件数据导入到数据库中,不多说,上代码. 首先,表结构如下. 其次,在我当前D盘中有个文本文件名为2.txt的文件. 在数据库中,可以这样通过一句代码插入. Bulk in ...
- Java 批量插入数据(Oracle)
//批量添加20000条数据用时8秒. try { String url = "jdbc:oracle:thin:@IP:1521:orcl"; // orcl为数据库的SI ...
- 批量插入数据(基于Mybatis的实现-Oracle)
前言:做一个数据同步项目,要求:同步数据不丢失的情况下,提高插入性能. 项目DB框架:Mybatis.DataBase:Oracle. -------------------------------- ...
- C#批量插入数据到Sqlserver中的三种方式
本篇,我将来讲解一下在Sqlserver中批量插入数据. 先创建一个用来测试的数据库和表,为了让插入数据更快,表中主键采用的是GUID,表中没有创建任何索引.GUID必然是比自增长要快的,因为你生 成 ...
- SqlServer——批量插入数据
像Major表里面批量插入数据演示: 代码如下: Declare @I int Set @I= Begin Tran InsertData: Insert into Major values(@I,' ...
- mybatis批量插入数据到oracle
mybatis 批量插入数据到oracle报 ”java.sql.SQLException: ORA-00933: SQL 命令未正确结束“ 错误解决方法 oracle批量插入使用 insert a ...
随机推荐
- Redis为什么变慢了?透彻解读如何排查Redis性能问题
Redis 作为优秀的内存数据库,其拥有非常高的性能,单个实例的 OPS 能够达到 10W 左右.但也正因此如此,当我们在使用 Redis 时,如果发现操作延迟变大的情况,就会与我们的预期不符. 你也 ...
- 9.2 Linux硬盘分区和挂载
一块新的硬盘存储设备后,先需要分区,然后再格式化文件系统,最后才能挂载并正常使用. 分区:根据需求和硬盘大小划分空间 格式化:对分区安装文件系统 挂载:将设备文件与一个目录关联的动作叫挂载 硬盘分区格 ...
- SSM框架中返回的是字符串还是页面跳转的问题
如果你在控制器前的注解是@RestController的话,将返回controller方法指定的String值,@RestController注解相当于@ResponseBody和@Controlle ...
- nginx从入门到入坟
目录 1.nginx下载安装 2.启动命令 3.服务脚本 4.nginx目录说明 5.nginx配置文件 6.虚拟主机与域名配置和站点配置 6.1server_name配置规则 7.反向代理负载均衡配 ...
- python学习-Day17
目录 今日内容详细 生成器对象(自定义迭代器) 小总结 自定义range方法 通过生成器模拟range方法 先以两个参数的range方法为例 针对一个参数情况 针对三个参数情况 自定义的range方法 ...
- 让交互更加生动!有意思的鼠标跟随 3D 旋转动效
今天,群友问了这样一个问题,如下所示的鼠标跟随交互效果,如何实现: 简单分析一下,这个交互效果主要有两个核心: 借助了 CSS 3D 的能力 元素的旋转需要和鼠标的移动相结合 本文,就将讲述如何使用纯 ...
- 从零开始搭建GoLang语言开发环境
更多干货文章,更多最新文章,欢迎到作者主博客 菜鸟厚非 一.安装 GoLang 1.1 下载 首先访问 https://go.dev/dl/ 下载 GoLang,下载完成后双击安装即可.如下: 1.2 ...
- Event Loop 是什么?
Event Loop 是什么? 本文写于 2020 年 12 月 6 日 广义上来说 Event Loop 并不是 JavaScript 独有的概念,他是一个计算机的通用概念. 狭义上来说,只有 No ...
- Base64隐写
0x00: 前言 Base64编码的作用: 将一些特殊的字符转换成常见的字符.特殊的字符可能是不可见字符或者是大于ascii码127的,将其变成常见的字符(在base64中为a~z A~Z 0~9 + ...
- PCIe引脚PRSNT与热插拔
热插拔的基本目的是要让PCIe设备按照规定的顺序.原则,从系统中移除或插入到系统中来,并能正常的工作,且不影响系统的正常运行.事实上,PCIe"热插拔"的关键目的就是为前面面所提到 ...