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

来这里找志同道合的小伙伴!



正文


背   景


很多用户在使用阿里云云数据库sql Server时,为了加快插入速度,都尝试使用大容量插入的方式,大家都知道,对于完整恢复模式下的数据库,大容量导入执行的所有行插入操作都会完整地记录在事务日志中。如果使用完整恢复模式,大型数据导入会导致填充事务日志的速度很快。相反,对于简单恢复模式或大容量日志恢复模式,大容量导入操作的按最小方式记录日志减少了大容量导入操作填满日志空间的可能性。另外,按最小方式记录日志的效率也比按完整方式记录日志高 。


但实际上,当大容量导入与数据库镜像共存时,会出现镜像 Suspend的情况,这个情况是由于微软在2008 R2上的BUG导致,详细你可以了解 https://support.microsoft.com/en-us/kb/2700641,微软已经明确表示在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  {

......

}catch{

.......

}

}


(ps:由于微信字数限制,欲查看完整代码,请点击“阅读原文”,进行查看。)


通过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的属性 ,如下图:




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



特别说明


不能在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来实现大容量导入,具体请参见https://msdn.microsoft.com/en-us/library/ms403302.aspx。希望这些对大家有用,特别是阿里云云数据库使用用户


本文为云栖社区文章,如需转载,请注明出处,并附上云栖社区微信公众号:yunqiinsight。


点击“阅读原文”可查看原文。



想和这群聪明人共事吗?加入阿里云云栖社区(全职/兼职):yqeditor@list.alibaba-inc.com


投稿或入驻云栖社区,请联系:yqeditor@list.alibaba-inc.com


2016,为了实现更多技术梦想,云栖社区与你携手并行。

yunqiinsight

长按二维码,一网打尽所有深度技术文章


戳原文,更有料!

相关文章

SELECT a.*,b.dp_name,c.pa_name,fm_name=(CASE WHEN a.fm_n...
if not exists(select name from syscolumns where name=&am...
select a.*,pano=a.pa_no,b.pa_name,f.dp_name,e.fw_state_n...
要在 SQL Server 2019 中设置定时自动重启,可以使用 Window...
您收到的错误消息表明数据库 'EastRiver' 的...
首先我需要查询出需要使用SQL Server Profiler跟踪的数据库标...