你的位置:首页 > 数据库

[数据库]SQL Server Integration Services的10大最佳实践


原文出处:https://blogs.msdn.microsoft.com/sqlcat/2013/09/16/top-10-sql-server-integration-services-best-practices/

 

译:

多少人听说过“SQL Server Integration Services(译注:以下简称SSIS)不能扩展”的传说?我们要反问的是“你的系统是否有必要扩展到每秒能够处理超过450万行交易记录?”SSIS是可以适应最极端环境的高性能ETL平台。并且正如在SSIS ETL性能世界记录所记载,SSIS可以处理每秒450万规模的销售交易记录。

1.SSIS是基于内存的数据管道,因此请确保所有的转换发生在内存中。

在SQL Server的功能中加入SSIS的目的是提供一个有弹性且稳定的数据管道,以便于在内存中高效地执行逐行计算和数据校验。

SSIS本身的数据转换应该在内存中处理,然而数据抽取和载入阶段却会涉及磁盘(会产生相应的读写)。一旦数据转换操作溢出到磁盘上,ETL性能将会有显著下降。为了确保所有的转换都能够在内存中处理,请仔细构建包以分割并过滤数据。

确认包是否驻留在内存中的一个好办法是检查SSIS缓存池相关的性能计数器,它的初始值是0,超过0就意味着SSIS引擎开始切换到磁盘上。更多详情,请参考SSIS性能计数器二三事。

2.根据资源使用的情况规划容量

SSIS的设计目标是在内存中高速逐行处理大量数据。因此,理解资源使用情况是非常重要的,比如CPU,内存,I/O,以及网络消耗。

CPU限制

当SSIS正在运行的时候,请设法搞清楚SSIS用了多少CPU,SQL Server总共用了多少CPU。如果SQL Server和SSIS安装在同一台机器上,那么后者就显得十分重要。因为一旦两者之间存在资源竞争,SQL Server往往会胜出——这会导致SSIS的数据转换操作溢出到磁盘上,而这会降低数据转换性能。

你感兴趣的性能计数器应该是Process / % Processor Time (Total)。衡量sqlservr.exe和dtexec.exe的这个计数器值。如果SSIS无法驱使CPU达到100%负载,这可能表明:

  • 应用程序竞争:例如,SQL Server占用了更多CPU资源,使CPU不能为SSIS所用。
  • 硬件竞争:一个常见的场景是你的磁盘I/O不够好或者没有足够的内存,无法应付要处理的数据量。
  • 设计上的限制:你的SSIS包没有利用到并行设计,并且/或者包使用了太多单线程任务。

网络限制

SSIS会尽你网络所能地传输数据。因此,搞清楚你的网络拓扑结构是很重要的,同样重要的是确保从源到目标之间的路径具有低延迟和高吞吐。

以下性能计数器可以帮你调整你的网络拓扑结构:

  • Network Interface / Current Bandwidth: 这个计数器会估算当前带宽。
  • Network Interface / Bytes Total / sec:这是每个网络适配器上发送和接收的字节数。
  • Network Interface / Transfers / sec:显示每秒发生多少网络传输操作。如果这个值接近每秒40000IO,那么请考虑增加网卡并在网卡间使用teaming技术。

这些计数器可以让你分析你的系统是否充分利用带宽。理解这点可以让你通过使用千兆网卡,增加网卡数量,或者新增网络地址以恰当地规划网络容量。

I/O限制

如果你能确保SSIS已经最小化磁盘写入操作,SSIS只会在从源读取数据以及写入数据时接触到磁盘。而如果你的I/O性能太慢,读写(特别是写入)操作将成为性能瓶颈。

由于优化I/O超出了本文的范畴,请参考前期部署最佳实践。请注意,I/O系统的规格不只有它的大小(例如“我需要10TB”)——也包括了它的速度(例如“我需要达到每秒20000IO”)。

内存限制

你需要搞清楚一个很重要的问题“我的包到底需要多少内存?”

对此,关键的性能计数器有:

  • Process / Private Bytes (DTEXEC.exe):当前SSIS使用的内存大小。这部分内存不能与其它进程共享。
  • Process / Working Set (DTEXEC.exe):SSIS分配的总内存大小。
  • SQL Server: Memory Manager / Total Server Memory:SQL Server分配的总内存大小。由于SQL Server可以通过AWE API分配内存,只有这个性能计数器可以准确反映SQL Server使用的内存大小。要更好地理解SQL Server内存分配机制,请参考Slava Ok的博客。
  • Memory / Page Reads / sec:这代表系统的内存压力。如果这个指标持续在500以上,说明系统存在内存压力。

3.源系统抽取速度基线

你得搞清楚从源系统抽取数据的速度。毕竟,SSIS不可能优化得比源系统还快——比如说,你不可能比读取数据更快地进行数据转换。

你可以通过创建一个带“Row Count”数据流组件的包来测量源系统的速度。

 

你可以从命令行(DTEXEC)执行这个包并测量它的完成时间。也可以用SSIS日志输出来准确计算执行时间。你需要的速度,就是每秒传输了多少行:

Rows / sec = Rows Count / Time

根据这个基准速度,我们就知道可以多快地从源抽取数据——这也是你转换数据的速度极限。要计算出这个速度,你可以尝试以下方法:

  • 升级驱动和驱动配置:针对网络,数据源,和磁盘I/O,请确保你的驱动已经升级到最新版本。通常你的服务器默认网络驱动并未针对网络进行过精确的优化,当吞吐量大的时候这会导致性能下降。要注意的是,对于64位系统,你可能在设计时用的却是32位的驱动。请确保在运行时你用的是64位驱动。
  • 多重连接:为了克服驱动的限制,你可以尝试利用多重连接从数据源抽取数据。正如数据源可以处理并发连接一样,同时进行多重抽取可以提高吞量。如果并发导致了锁定或者阻塞问题,可以考虑分割数据源,并使你的包同时从多个分区抽取以分摊负载。
  • 多网卡:如果网络是性能性能瓶颈,而你已采用了千兆网卡和路由,那么一个可能的解决方案就是在每个服务器上都使用多块网卡。要注意的是多网卡环境需要仔细配置,否则有可能会发生网络冲突。

4.优化SQL Data Source,Lookup transformations,和Destination。

 

当你在SSIS中执行SQL语句(正如上图所示的Data access mode对话框),是否在查询数据源时实现Lookup逻辑,或者修改某些表,一些标准的优化手段可以显著地提高性能表现:

  • 使用NOLOCK或者TABLOCK表提示以避免锁定开销。
  • 为了优化内存消耗,只SELECT你确实需要的列。如果你SELECT一张表的所有列(例如,SELECT * FROM)你可能会浪费内存和带宽以存储和接收那些多余的列。
  • 尽量在源数据库或者目标数据库进行时间日期类型的转换,因为在SSIS实现的话成本太高。
  • SQL Server 2008 Integration Servcies有个新功能Shared Lookup Cache。当用到并行数据管道时(参见下文第8点和第10点),这个功能可以提供高速的共享缓存。
  • 如果SSIS和SQL Server运行在同一台服务器上,建议选择SQL Server Destination而不是OLE DB Destination以提高性能。
  • Commit Size为0的话在堆上进行批量操作是最快的,因为只提交了一个事务。如果不能为0,尽量提高这个值以减少多批次写入的开销。将数据插入平衡树结构时Commit Size为0则不是个好主意——因为所有新进的行都必须一次性排序以插入目标平衡树——并且如果你的内存有限,很有可能溢出到硬盘上。将数据插入堆时Batch Size为0是个比较理想的值。对于一个有索引的目标(表),我建议测试一下将Batch Size设在100000到1000000之间。
  • 插入数据时将Commit Size设为小于5000可以避免锁升级。要注意的是在SQL Server 2008现在你可以在对象级别启用/禁用锁升级,不过要灵活调整这个设置。
  • 对堆进行插入往往比聚集索引快。这意味着当目标表发生大量更改的时候你可能会删除并重建索引,你可以验证一下保留索引和删除并重建索引两种方法的插入性能。
  • 采用Partition和分区的SWITCH命令。例如,将数据载入只有单个分区的工作表,重建索引和约束后再将它SWITCH到主(目标)表。
  • 请参考SQL性能团队的另一个建议用Lookup优化性能。

5.调整网络

一个关键的属性是连接的网络包大小。默认情况下这个值是4096字节。这意味着每4KB数据必须封装为一个新的网络包。正如SqlConnection.PacketSize Property中所述在.Net Framework类库中,增加网络包大小可以提高性能,因为传输大数据集的时候这意味着更少的网络读写操作。

如果你的系统天生就是事务性的,有很多小数据量的读/写,那么减小这个值会提高性能。

既然SSIS主要用于移动大量数据,你可能希望最小化网络开销。这意味着32767是最快的选项。也可以在服务器层面用sp_configure配置网络包大小,然而你不应该这么做。将网络包配置为32767以外的值,DBA可能有他们的考虑。作为替代方案,你可以在Connection Manager里如下覆盖服务器设置。

 

另一个网络优化技巧是在操作系统层面采用Network Affinity。在高吞量的情况下,有时你可以通过这个办法提高性能。

单从网络来看,你可能需要与你的网络管理员协作启用Jumbo Frames(巨型帧)以便将帧负载能力从1500字节提高到9000字节。采用这种方法,将极大地减少移动大数据集所需的网络操作。

6.数据类型——没错,回到数据类型!——理性点。

在本文的10点建议中,这可能是最显而易见的。然而,这是如此重要所以需要专门作为一点建议。请遵循以下准则:

  • 使数据类型尽可能小以节约内存。
  • 切勿执行代价高昂的类型转换——这只会降低性能。匹配源和目标的数据类型并显式指定必要的类型转换。
  • 当你用到money,float,或者decimal类型时留意精度问题。同时,要注意money比decimal更快,而且在精度方面money也比float更让人放心。

7.改变设计

SSIS比较擅长一些事情,而其它事情用别的工具更方便。你必须基于效率和对问题的了解来选择ETL工具。为辅助你的选择,请注意以下几点:

  • 除非必要,否则切勿在SSIS中排序。为了进行排序,SSIS会分配足以容纳整个(需要移动的)数据集大小的内存。请尽量在数据进入数据流之前就提前排好序。相对于在SSIS中排序,不如用带ORDER BY子句的SQL语句去排序大型数据集——然后将Data Source的输出标记为Sorted(有序)即可。
  • 有些时候用T-SQL操作会比在SSIS中处理数据更快。作为一般性原则,任何基于集合的操作在T-SQL中总是比较快,因为问题可以转化为SQL Server擅长解决的关系代数函数。并且,SQL Server查询优化器对基于集合的操作自动采用了高并行处理和内存管理机制——在SSIS中你需要手工去实现。典型的集合操作包括:
    • 基于集合的UPDATE语句——比逐行处理的OLE DB调用更高效。
    • 像GROUP BY和SUM这样的聚合计算。这用T-SQL计算通常会比SSIS在内存中的计算更快。
  • 分支检测是一种更改已有行而不是刷新整张表的技巧。为实现分支检测,你可以采用更改检测机制,例如SQL Server 2008的Change Data Capture(CDC)功能。如果无法采用这样的功能,你不得不比对源数据与目标数据以实现分支检测。这可能是个代价高昂的操作,需要维护特定的索引并计算校验和。通常,刷新整张表会比较快。一个经验是一旦某个表超过10%的行需要更改,那么通常简单地刷新这张表会比实现分支检测更快。

8.分割问题

可伸缩计算的一个原则就是分而治之。这使你可以更容易地处理问题的规模,利用并行处理以更快地解决问题。

对ETL设计来说,你可能会将源数据分割成大小相同的小数据块。这一点很重要,因为如果你的数据块大小有差异,最后你总得等待某个进程完成。比如说,如下图,当4个进程分别运行在大小相同的数据块,4个进程会同时完成2008年1月份数据的处理并开始处理2008年2月份的数据。但对于大小有差异的分割方式,前面的3个进程将同时完成数据处理然后等待第4个进程,这得花费更多时间。总的运行时间将取决于最大的数据块。

 

为创建相同大小的分区范围,可以考虑用时间区间和/或其它维度(例如地理信息)作为分割机制。如果你的主键是个自增列或者其它自增值,你可以用余函数。如果你没有比较合适的列用于分区,可以为每一行计算一个哈希值然后基于这个哈希值进行分区。

一些关于分区的提示:

  • 在目标表应用分区。这样你可以并行地运行一个包的多个版本,以便将数据插入同一张表的不同分区。用了分区,SWITH就是你的好朋友。它不仅增高并行载入的速度,也使你可以更快地传输数据。请参考SQL Server在线文档使用分区切换高效传输数据。
  • 如上文所暗示的,你的包需要接受一个参数以指定它要抽取哪个分区。这样一来,你可以以不同的参数同时并行执行同一个包,那么你可以从并行执行中获得更快的速度。
  • 从命令行,你可以用“START”命令多次执行包。你可以在SQL Server最佳实践找到一个简单的代码示例。

9.最小化日志操作

当你插入数据到SQL Server数据库,尽量最小化日志操作。当数据插入到完整日志模式的数据库,由于每一行都会先插入日志,日志会快速增长。

因此,设计SSIS包时,考虑以下几点:

  • 尝试以批量模式而不是逐行模式执行数据流。通过批量导入模式,你可以最小化插入日志的操作。这个调整也可以优化其它插入操作的底层磁盘I/O,并最小化日志写入造成的性能瓶颈。
  • 如果你需要执行删除操作,将你的数据构造成可以TRUNCATE的形式而不是DELETE。后者会在日志中为删除的每一行放一条记录。而前者会简单地移除全部数据同时在日志做很小的记录表明TRUNCATE发生过。与一般的观点相对地,TRUNCATE语句可以包含在事务中。
  • 利用SWITCH和分区。如果分区需要被移除,你可以用SWITCH语句(移入一个新分区或者移除最老的分区),这也是最小化日志操作的语句。
  • 使用DML语句的时候要谨慎,如果你将DML语句混在INSERT语句中,无法最小化日志操作。

10.正确地计划与分发

当你的问题被分割成大小可控的数据块之后,你得考虑一下什么时候以及在哪里抽取这些数据块。这是为了避免一个长时间运行的任务影响ETL数据流的总运行时间。

解决执行顺序的一个好办法就是创建一个优先级队列然后执行一个包的多个实例(分别带有不同的参数)。这个队列甚至可以简单到就是SQL Server的一张表。每个包都得在控制流包含一个循环模板:

1.  从队列中找出一个要抽取的数据块:

  1. 这意味着这个数据块还没有抽取,并且它依赖的数据块都已经抽取了。
  2. 如果队列没有返回任何项,退出包的执行。

2. 执行这个数据块所需的操作。

3. 在队列中将数据块标记为“已抽取”。

4. 返回循环的起点。

从队列中提取一个数据块并标记为“已抽取”(上面的第一和第三步)可以实现为存储过程。

这个队列扮演了一个控制中心的角色并起到协调的作用,它可以判断执行顺序并确保任意两个包不会工作在相同的数据块上。一旦这个队列就绪,你可以简单地启动多个DTEXEC副本以提高并行度。

点击加入SQL Server精英群SQLServer精英群

点击访问软件技术咨询中心