使用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 ...
随机推荐
- partTwo自动出题程序
需求介绍 一家软件公司程序员二柱的小孩上了小学二年级,老师让家长每天出30道四则运算题目给小学生做. 二柱立马就想到写一个小程序来做这件事. 这个事情可以用很多语言或者工具来实现: Excel, C/ ...
- 踹掉后端,前端导出Excel!
前言 导出Excel文件这个功能,通常都是在后端实现返回前端一个下载链接,但有时候我们只想导出前端页面上已经有了的数据,不想再调后端导出接口浪费服务器资源,学习本文demo例子,我们踹掉后端,直接在前 ...
- python相关知识理解
Python3 基础了解 编码 Python 3 源码文件以 UTF-8 编码,所有字符串都是 unicode 字符串 # -*- coding: cp-1252 -*- 标识符 · 第一个字符 ...
- 手撸一个虚拟DOM,不错
大家好,我是半夏,一个刚刚开始写文的沙雕程序员.如果喜欢我的文章,可以关注 点赞 加我微信:frontendpicker,一起学习交流前端,成为更优秀的工程师-关注公众号:搞前端的半夏,了解更多前端知 ...
- GAIA-IR: GraphScope 上的并行化图查询引擎
在本文中,我们将介绍 GraphScope 图交互式查询引擎 GAIA-IR,它支持高效的 Gremlin 语言表达的交互图查询,同时高度抽象了图上的查询计算,具有高可扩展性. 背景介绍 在海量数据的 ...
- .NET桌面程序应用WebView2组件集成网页开发3 WebView2的进程模型
系列目录 [已更新最新开发文章,点击查看详细] WebView2 运行时使用与 Microsoft Edge 浏览器相同的进程模型. WebView2 运行时中的进程 WebView2 进程组 ...
- Bugku CTF练习题---加密---聪明的小羊
Bugku CTF练习题---加密---聪明的小羊 flag:KEY{sad23jjdsa2} 解题步骤: 1.观察题目,发现其中的信息 2.经过题目判断,得知该题属于栅栏密码的一种,并且介绍中表明了 ...
- XCTF练习题---WEB---Training-WWW-Robots
XCTF练习题---WEB---Training-WWW-Robots flag:cyberpeace{e37180e3f5ad17b4ac71a131e2de1fcb} 解题步骤: 1.观察题目,打 ...
- ceph日常运维管理
点击关注上方"开源Linux", 后台回复"读书",有我为您特别筛选书籍资料~ 相关阅读: ceph分布式存储简介 常见问题 nearfull osd(s) o ...
- 记将一个大型客户端应用项目迁移到 dotnet 6 的经验和决策
在经过了两年的准备,以及迁移了几个应用项目积累了让我有信心的经验之后,我最近在开始将团队里面最大的一个项目,从 .NET Framework 4.5 迁移到 .NET 6 上.这是一个从 2016 时 ...