IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> sqlserver:使用 SqlBulkCopy 批量插入数据 -> 正文阅读

[大数据]sqlserver:使用 SqlBulkCopy 批量插入数据

环境:

  • window 10
  • sqlserver 2014

参考:
《博文:SqlBulkCopy使用注意事项》

1. 问题场景

在批量迁移或导入数据时,我们可能会遇到插入大量数据的问题,比如:100万、500万、甚至几千万。
这个时候,如果我们再使用普通的insert插入的话就太慢了。

在sqlserver中,我们可以使用 SqlBulkCopy 进行大数据量的插入。

2. 简单示例

先看个示例:

var connString = "Data Source=192.168.252.129;Initial Catalog=test;User ID=sa;Password=123456;Encrypt=True; TrustServerCertificate=True;";
var conn = new SqlConnection(connString);
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = @"create table test(
id int primary key,
name varchar(50)
)";
cmd.ExecuteNonQuery();
var dt = new DataTable("test");
dt.Columns.Add("id");
dt.Columns.Add("name");
for (var i = 0; i < 5; i++)
{
    var row = dt.NewRow();
    row["id"] = i;
    row["name"] = "name" + i;
    dt.Rows.Add(row);
}

var sbc = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, null);
sbc.DestinationTableName = dt.TableName;

for (int i = 0; i < dt.Columns.Count; i++)
{
    sbc.ColumnMappings.Add(dt.Columns[i].ColumnName, dt.Columns[i].ColumnName);
}
sbc.WriteToServer(dt);
conn.Close();

插入的数据如下:
在这里插入图片描述

3. 注意点

上面是一个简单的示例,我们在使用的时候需要关注以下几种情况:

  • 如果上面表的id是自增的话,那么批量插入时是数据库自增还是使用自己指定的?(SqlBulkCopyOptions.KeepIdentity);
  • 如果上面表的某列有定义默认值,那么批量插入数据时是使用定义的默认值还是自己指定数据的null值?(SqlBulkCopyOptions.KeepNulls);
  • 如果上面表的某列有检查约束,那么批量插入时,是否进行检查以保证数据正确?(SqlBulkCopyOptions.CheckConstraints);
  • 如果上面表定义了insert触发器,那么批量插入数据时,是否触发?(SqlBulkCopyOptions.FireTrigger);
  • 插入时是否内部开启一个事务,以保证数据完整性?(SqlBulkCopyOptions.UseInternalTransaction);
  • 插入时是否获取这个表的表锁(默认是行锁)?(SqlBulkCopyOptions.TableLock);

上面的这些设置使用如下:

var sbc = new SqlBulkCopy(conn, SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.KeepNulls  null);

上面的配置项,建议取SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.KeepNulls
因为在批量插入数据时我们是希望将自己准备的树原封不动的插入进去!!!

另外,我们需要注意,当我们使用SqlBulkCopyOptions.UseInternalTransaction选项的时候,我们不能再手动指定事务,否则会报错:
在这里插入图片描述

除了上面的参数,还有一个需要关注的,就是执行超时时间,默认是30秒,如果数据量太大,可以设置的长一点,如下:

var sbc = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, null);
sbc.DestinationTableName = dt.TableName;
sbc.BulkCopyTimeout = 60 * 30;//半小时

4. 性能测试

下面以测试插入500万条数据耗时结束本次实验:

public static void Main(string[] args)
 {
     var connString = "Data Source=192.168.252.129;Initial Catalog=test;User ID=sa;Password=123456;Encrypt=True; TrustServerCertificate=True;";
     var conn = new SqlConnection(connString);
     conn.Open();
     var cmd = conn.CreateCommand();
     cmd.CommandText = @"create table test(
id int primary key,
name varchar(50),
age int,
addr varchar(50),
birth datetime,
avatar varchar(500),
sex int,
uno varchar(50),
remark varchar(50)
)";
     cmd.ExecuteNonQuery();

     var st = new Stopwatch();
     st.Start();
     var dt = new DataTable("test");
     dt.Columns.Add("id");
     dt.Columns.Add("name");
     dt.Columns.Add("addr");
     dt.Columns.Add("birth");
     dt.Columns.Add("avatar");
     dt.Columns.Add("sex");
     dt.Columns.Add("uno");
     dt.Columns.Add("remark");

     var count = 500 * 10000;
     for (var i = 0; i < count; i++)
     {
         var row = dt.NewRow();
         row["id"] = i;
         row["name"] = "name" + i;
         row["addr"] = "天明路" + i;
         row["birth"] = DateTime.Now.AddMilliseconds(i);
         row["avatar"] = $"http://www.qq.com/1234567878/{i + 1}.png";
         row["sex"] = i % 2;
         row["uno"] = Guid.NewGuid().ToString();
         row["remark"] = $"这是一个关于{i} 的描述!";
         dt.Rows.Add(row);
     }
     Console.WriteLine($"准备数据: 500万,花费: {st.ElapsedMilliseconds} 毫秒!");
     st.Restart();

     var sbc = new SqlBulkCopy(conn, SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.KeepNulls, null);
     sbc.DestinationTableName = dt.TableName;
     //向服务器传输,一次传输1万条
     sbc.BatchSize = 10000;
     sbc.SqlRowsCopied += (object sender, SqlRowsCopiedEventArgs e) =>
     {
         Console.WriteLine($"已拷贝: {e.RowsCopied} 行,进度: {(e.RowsCopied / (count + 0.0) * 100).ToString("0.00")}%");
     };
     //进度通知: 没传输1万条回调一次 SqlRowsCopied
     sbc.NotifyAfter = 10000;

     sbc.BulkCopyTimeout = 5 * 60;//5分钟超时

     for (int i = 0; i < dt.Columns.Count; i++)
     {
         sbc.ColumnMappings.Add(dt.Columns[i].ColumnName, dt.Columns[i].ColumnName);
     }
     sbc.WriteToServer(dt);
     conn.Close();
     st.Stop();

     Console.WriteLine($"插入数据: 500万,花费: {st.ElapsedMilliseconds} 毫秒!");
     Console.ReadLine();
 }

输出如下:
在这里插入图片描述

在这里插入图片描述

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-03-08 22:34:18  更:2022-03-08 22:35:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:45:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码