SQLServer · 最佳实践 · 数据库实现大容量插入的几种方式

本文涉及的产品
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介:

背景

很多用户在使用阿里云云数据库SQL Server时,为了加快插入速度,都尝试使用大容量插入的方式,大家都知道,对于完整恢复模式下的数据库,大容量导入执行的所有行插入操作都会完整地记录在事务日志中。如果使用完整恢复模式,大型数据导入会导致填充事务日志的速度很快。相反,对于简单恢复模式或大容量日志恢复模式,大容量导入操作的按最小方式记录日志减少了大容量导入操作填满日志空间的可能性。另外,按最小方式记录日志的效率也比按完整方式记录日志高 。
但实际上,当大容量导入与数据库镜像共存时,会出现镜像 Suspend的情况,这个情况是由于微软在2008 R2上的BUG导致,微软已经明确表示在2008 R2不会FIXED,那么如何正确在RDS使用大容量导入并避免镜像异常,下面介绍几种方式.

实现方法

  • 通过ADO.NET SQLBulkCopy 方式
  • 只需要将SqlBulkCopy 指定SqlBulkCopyOptions.CheckConstraints就好,即:SqlBulkCopy blkcpy = new SqlBulkCopy(desConnString, SqlBulkCopyOptions.CheckConstraints)
    例如:将本地的一个大表通过SQLBulkCopy方式导入到RDS的实例中
    
    static void Main()
    {
      string srcConnString = "Data Source=(local);Integrated Security=true;
      Initial Catalog=testdb";
      string desConnString = "Data Source=****.sqlserver.rds.aliyuncs.com,3433;
      UserID=**;Password=**;Initial Catalog=testdb";
    
      SqlConnection srcConnection = new SqlConnection();
      SqlConnection desConnection = new SqlConnection();
    
      SqlCommand sqlcmd = new SqlCommand();
      SqlDataAdapter da = new SqlDataAdapter();
      DataTable dt = new DataTable();
    
      srcConnection.ConnectionString = srcConnString;
      desConnection.ConnectionString = desConnString;
      sqlcmd.Connection = srcConnection;
    
      sqlcmd.CommandText = @"
      SELECT top 1000000 [PersonType],[NameStyle],[Title],[FirstName],[MiddleName],
      [LastName] ,[Suffix],[EmailPromotion],[AdditionalContactInfo],[Demographics],NULL 
      as rowguid,[ModifiedDate] FROM [testdb].[dbo].[Person]";
    
      sqlcmd.CommandType = CommandType.Text;
      sqlcmd.Connection.Open();
      da.SelectCommand = sqlcmd;
      da.Fill(dt);
    
    
      using (SqlBulkCopy blkcpy = 
      new  SqlBulkCopy(desConnString,SqlBulkCopyOptions.CheckConstraints))
      // using (SqlBulkCopy blkcpy = 
      // new SqlBulkCopy(desConnString, SqlBulkCopyOptions.Default))
      {
        blkcpy.BatchSize = 2000;
        blkcpy.BulkCopyTimeout = 5000;
        blkcpy.SqlRowsCopied += new SqlRowsCopiedEventHandler(OnSqlRowsCopied);
        blkcpy.NotifyAfter = 2000;
    
        foreach (DataColumn dc in dt.Columns)
        {
          blkcpy.ColumnMappings.Add(dc.ColumnName, dc.ColumnName);
        }
    
        try
        {
          blkcpy.DestinationTableName = "Person";
          blkcpy.WriteToServer(dt);
        }
        catch (Exception ex)
        {
          Console.WriteLine(ex.Message);
        }
        finally
        {
          sqlcmd.Clone();
          srcConnection.Close();
          desConnection.Close();
        }
      }
    }
    
    private static void OnSqlRowsCopied(
        object sender, SqlRowsCopiedEventArgs e)
    {
      Console.WriteLine("Copied {0} so far...", e.RowsCopied);
    }
    
    

  • 通过JDBC SQLServerBulkCopy 方式

  • 同样的道理,需要在copyOptions指定检查约束性
    SQLServerBulkCopyOptions copyOptions = new SQLServerBulkCopyOptions();  
    copyOptions.setCheckConstraints(true);
    测试时,请用Microsoft JDBC Drivers 6.0 的sqljdbc41.jar,sqljdbc4.jar及更老版本没有SQLServerBulkCopy 实现。
    
    例如: 将本地的一个大表通过SQLServerBulkCopy方式导入到RDS的实例中
    
    
    import java.sql.*;
    import com.microsoft.sqlserver.jdbc.SQLServerBulkCopy;
    import com.microsoft.sqlserver.jdbc.SQLServerBulkCopyOptions;
    
    public class Program {
      public static void main(String[] args)  
      {  
        String sourceConnectionString  = "jdbc:sqlserver://localhost:1433;" +  
                "databaseName=testdb;user=****;password=****";  
        String destConnectionString  = "jdbc:sqlserver://*****.sqlserver.rds.aliyuncs.com:3433;" +  
                "databaseName=testdb;user=****;password=**** ";  
        try  
        {  
          Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");  
          try (Connection sourceConnection =
               DriverManager.getConnection(sourceConnectionString))  
          {  
            try (Statement stmt = sourceConnection.createStatement())  
            {  
              try (ResultSet rsSourceData = stmt.executeQuery(  
                    " SELECT top 1000000 " +
                    "[PersonType],[NameStyle],[Title],[FirstName],[MiddleName],[LastName] ," +
                    "[Suffix],[EmailPromotion],[AdditionalContactInfo]," +
                    "[Demographics],NULL as rowguid,[ModifiedDate] " +
                    "FROM [testdb].[dbo].[Person]"))  
              {   
                try (Connection destinationConnection =  DriverManager.getConnection(destConnectionString))  
                {  
                  Statement stmt1 = destinationConnection.createStatement();
                  			
                  long countStart = 0;  
                  try (ResultSet rsRowCount = stmt1.executeQuery(  
                          "SELECT COUNT(*) FROM dbo.Person;"))  
                  {  
                    rsRowCount.next();  
                    countStart = rsRowCount.getInt(1);  
                    System.out.println("Starting row count = " + countStart);  
                  }  
                     
                  try (SQLServerBulkCopy bulkCopy =   new SQLServerBulkCopy(destinationConnection))  
                  {  
                    SQLServerBulkCopyOptions copyOptions = new SQLServerBulkCopyOptions();  
                    copyOptions.setKeepIdentity(true);  
                    copyOptions.setBatchSize(2000);
                    copyOptions.setBulkCopyTimeout(5000);
                    //this is importance setting
                    copyOptions.setCheckConstraints(true);
                    bulkCopy.setBulkCopyOptions(copyOptions);
                    bulkCopy.setDestinationTableName("dbo.Person");  
                    bulkCopy.addColumnMapping("PersonType", "PersonType");  
                    bulkCopy.addColumnMapping("NameStyle", "NameStyle");  
                    bulkCopy.addColumnMapping("Title", "Title");  
                    bulkCopy.addColumnMapping("FirstName", "FirstName");  
                    bulkCopy.addColumnMapping("MiddleName", "MiddleName");  
                    bulkCopy.addColumnMapping("LastName", "LastName");  
                    bulkCopy.addColumnMapping("Suffix", "Suffix");  
                    bulkCopy.addColumnMapping("EmailPromotion", "EmailPromotion");  
                    bulkCopy.addColumnMapping("AdditionalContactInfo", "AdditionalContactInfo");  
                    bulkCopy.addColumnMapping("Demographics", "Demographics");  
                    bulkCopy.addColumnMapping("rowguid", "rowguid");  
                    bulkCopy.addColumnMapping("ModifiedDate", "ModifiedDate");  
                    try  
                    {  
                      bulkCopy.writeToServer(rsSourceData);  
                    }  
                    catch (Exception e)  
                    {  
                      e.printStackTrace();  
                    }  
                    
                    try (ResultSet rsRowCount = stmt1.executeQuery(  
                          "SELECT COUNT(*) FROM dbo.Person;"))  
                    {  
                      rsRowCount.next();  
                      long countEnd = rsRowCount.getInt(1);  
                      System.out.println("Ending row count = " + countEnd);  
                      System.out.println((countEnd - countStart) + " rows were added.");  
                    }  
                  }  
                }  
              }  
            }  
          }  
        }  
        catch (Exception e)  
        {  
            e.printStackTrace();  
        }  
      }  
    }


  • 通过BCP方式

第一步:需要将数据BCP到本地

BCP testdb.dbo.person Out "bcp_data" /t  /N /U **** /P *** /S "****.sqlserver.rds.aliyuncs.com,3433"    

第二步:将导出的文件直接导入到RDS的实例中,但需要指定提示:/h “CHECK_CONSTRAINTS”

BCP testdb.dbo.person In "bcp_data" /C /N /q /k /h "CHECK_CONSTRAINTS" /U *** /P *** /b 500 /S  "***.sqlserver.rds.aliyuncs.com,3433"  

  • 通过DTS/SSIS方式

第一种:import/export data方式需要先保存SSIS包,然后修改Connection Manager的属性 ,如下图
1111111

第二种:直接使用SQL Server Business Intelligence Development Stuidio新建 SSIS包:

222222

  • 特别说明

不能在RDS通过下列两种方式进行大容量插入: 原因是基于安全考虑不提供上传文件到RDS 数据库服务器。

第一种:

BULK INSERT testdb.dbo.person_in
FROM N'D:\trace\bcp.txt'
WITH
(
 CHECK_CONSTRAINTS 
);  
第二种:

INSERT ... SELECT * FROM OPENROWSET(BULK...)


  • 总结

大容量导入数据会带来更快的插入,解决了用户在有大量数据导入缓慢困惑,在阿里云数据库中,你可以使用五种方式来实现业务场景,但是基于镜像的主备关系,需要特别加入一个检查约束的选项,这是写这个最佳实践的目的,一旦镜像SUSPEND,不断有DUMP文件产生,一来需要时间来修正,二来DUMP文件也会不断占用空间,但不会影响用户的可用性和可靠性。有两种方式在RDS中不能实现,另外,还可以通过ODBC来实现大容量导入,具体请参见。希望这些对大家有用,特别是阿里云云数据库使用用户。


相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情: https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
1月前
|
SQL IDE Java
Java连接SQL Server数据库的详细操作流程
Java连接SQL Server数据库的详细操作流程
|
11天前
|
SQL Oracle 关系型数据库
MySQL、SQL Server和Oracle数据库安装部署教程
数据库的安装部署教程因不同的数据库管理系统(DBMS)而异,以下将以MySQL、SQL Server和Oracle为例,分别概述其安装部署的基本步骤。请注意,由于软件版本和操作系统的不同,具体步骤可能会有所变化。
45 3
|
17天前
|
SQL 存储 安全
数据库数据恢复—SQL Server数据库出现逻辑错误的数据恢复案例
SQL Server数据库数据恢复环境: 某品牌服务器存储中有两组raid5磁盘阵列。操作系统层面跑着SQL Server数据库,SQL Server数据库存放在D盘分区中。 SQL Server数据库故障: 存放SQL Server数据库的D盘分区容量不足,管理员在E盘中生成了一个.ndf的文件并且将数据库路径指向E盘继续使用。数据库继续运行一段时间后出现故障并报错,连接失效,SqlServer数据库无法附加查询。管理员多次尝试恢复数据库数据但是没有成功。
|
22天前
|
SQL 存储 关系型数据库
关系型数据库SQL Server学习
【7月更文挑战第4天】
26 2
|
28天前
|
SQL 弹性计算 数据管理
数据管理DMS产品使用合集之sql server实例,已经创建了数据库,登录时提示实例已存在,该怎么处理
阿里云数据管理DMS提供了全面的数据管理、数据库运维、数据安全、数据迁移与同步等功能,助力企业高效、安全地进行数据库管理和运维工作。以下是DMS产品使用合集的详细介绍。
36 1
|
14天前
|
网络协议 关系型数据库 MySQL
【最佳实践】MySQL数据库迁移到PXC集群
借本次数据库迁移实践,再次总结一下MySQL数据库迁移到PXC的最佳操作路径。
24 0
|
1月前
|
SQL 存储 关系型数据库
关系型数据库中的SQL Server
【6月更文挑战第11天】
61 3
|
20天前
|
存储 关系型数据库 MySQL
探索MySQL:关系型数据库的基石
MySQL,作为全球最流行的开源关系型数据库管理系统(RDBMS)之一,广泛应用于各种Web应用、企业级应用和数据仓库中
|
19天前
|
缓存 运维 关系型数据库
数据库容灾 | MySQL MGR与阿里云PolarDB-X Paxos的深度对比
经过深入的技术剖析与性能对比,PolarDB-X DN凭借其自研的X-Paxos协议和一系列优化设计,在性能、正确性、可用性及资源开销等方面展现出对MySQL MGR的多项优势,但MGR在MySQL生态体系内也占据重要地位,但需要考虑备库宕机抖动、跨机房容灾性能波动、稳定性等各种情况,因此如果想用好MGR,必须配备专业的技术和运维团队的支持。 在面对大规模、高并发、高可用性需求时,PolarDB-X存储引擎以其独特的技术优势和优异的性能表现,相比于MGR在开箱即用的场景下,PolarDB-X基于DN的集中式(标准版)在功能和性能都做到了很好的平衡,成为了极具竞争力的数据库解决方案。
|
18天前
|
关系型数据库 MySQL 网络安全
Mysql 数据库主从复制
在MySQL主从复制环境中,配置了两台虚拟机:主VM拥有IP1,从VM有IP2。主VM的`my.cnf`设置server-id为1,启用二进制日志;从VM设置server-id为2,开启GTID模式。通过`find`命令查找配置文件,编辑`my.cnf`,在主服务器上创建复制用户,记录二进制日志信息,然后锁定表并备份数据。备份文件通过SCP传输到从服务器,恢复数据并配置复制源,启动复制。检查复制状态确认运行正常。最后解锁表,完成主从同步,新用户在从库中自动更新。
994 7
Mysql 数据库主从复制