Control Flow 和 Data Flow,是SSIS Design中主要用到的两个Tab,理解这两个Tab的作用,对设计更高效的package十分重要。
一,Control Flow
在Control Flow中,Task是最小的单元,Task通过Precedence Constraint来保持同步,在对后续Task进行处理之前,必须完成前面Task(成功,失败或者完成)。
1,Control Flow 不能在组件之间传递数据,用于串行或并行执行任务,担当Task的调度者。
如果两个Task之间没有设置precedence constraint,那么这两个Task是并发执行的。在design package时,最大限度地提高task的并发处理能力,能够充分利用server的资源,有助于减少ETL执行的时间。
2,Control Flow 通过Precedence Constraint保持同步
在Task之间设置Precedence Constraint,只有在上游Task执行完成之后,才会执行下游Task;在上游Task执行完成之前,下游Task不会执行。因此,Task是同步的。
Task的同步表现在两个方面:
- Task之间:一个Task在将操作移交之前必须完成(成功,失败或完成),一个Task要想运行,其所有上游Task必须全部完成,否则不会执行。Task的执行顺序由Precedence Constraint定义。
- 单个Task:Task在执行完成之前,不会释放SSIS的执行线程,即执行SSIS Task对Server的线程资源是独占的,直至Task 完成,否则不会释放线程资源。
二,Data Flow
Data Flow 由三部分组成:源,转换和目的,用于处理数据,对数据进行转移(源提取数据,目标加载数据)和转换。Data Flow的核心功能就是:将数据提取到Server内存中,转换数据并将数据写入到另一个目的,将数据提取到内存,使SSIS能更快速地处理数据。
数据流具有流的特性,数据的提取,转换和加载是同时进行的。SSIS Engine以流的形式对数据进行并发处理,这意味着数据不是一次性全部加载,而是划分为不重复的多个部分,组成一个流,源源不断地从上游组件流向下游组件。在数据流动的过程中,Data Flow的所有组件同时对Data进行处理。上游组件处理完一批Data之后,交接给下游组件继续处理,同时,上游组件继续处理下一批数据。数据在组件之间流动,各个组件同时处理数据的不同部分,直到全部数据处理完成。
Data Flow具有反馈和自动调节的功能,如果下游组件的处理速度存在压力,那么SSIS将会向上游组件施加反向压力,SSIS Engine感受到压力,将启动自动调节机制,使上游组件的数据流动速度减缓,从而达到动态平衡。
1,内存缓冲区结构
数据流使用内存暂时存储数据流中的数据,这意味着,将数据从源提取到SSIS Engine时,Data会存储在预先分配好的内存缓冲区中,可以根据data row的宽度(一个row中所有column的字节数),设置DefaultBufferMaxRows属性来调整缓冲区的大小,或直接设置DefaultBufferSize属性。
Data Flow Task的属性
Data Flow的缓冲区可以理解为一个二维表,每行有固定的长度,每一行的Column位置都是固定的。
SSIS Engine根据Server的资源和压力,会预先分配一组缓冲区,每一个缓冲区存储完整数据集的一个不重复子集。
当对数据流进行转换处理时,SSIS Engine 后台使用一种更为有效的方式:对同一个缓冲区,逐个应用转换组件的业务逻辑,这比将转换更新后的数据复制到另一个缓冲区,然后应用下一个转换组件的业务逻辑更为有效。不过,有些情况下,SSIS Engine需要复制缓冲区,甚至需要拦截数据流,对整个数据集进行转换处理,例如,聚合和排序。
SSIS Engine处理数据最理想的情况是:所有的转换施加在同一个缓冲区中,按照转换定义的业务逻辑就地进行数据更新。但是,现实情况不总是理想的,有些转换组件需要复制缓冲区,才能将数据向下游传递。
数据流通过转换组件时,SSIS Engine是否需要复制缓冲区,跟转换的阻塞特性 和 通信机制有关。
2,转换的阻塞特性:非阻塞(流),半阻塞和阻塞
- 非阻塞:数据是立即从管道中传递到下游
- 半阻塞:将数据增加到一定数量后再向下游传递
- 阻塞:接收完全部数据后再向下游传递
大多数转换都是流性的,这意味着在转换逻辑应用到某一行时,不会阻止数据移动到下一个转换。
排序转换和聚合转换时阻塞的,这意味着每种转换在向一个转换传递数据之前,需要使用完整的数据集,在转换结束之前,不会向下游组件释放任何数据。通过在数据流镇南关使用一个阻塞转换组件,数据流将被拦截,所有行都将停留在阻塞转换组件中,直到最后一行流进转换组件。
阻塞转换是非常低效的和资源消耗大的,因为所有数据都被拦截,所以server必须使用大量的内存来存储数据,或者如果Server没有足够的内存,那么Server会将部分数据暂存在磁盘上,从而导致IO开销;因为要对大量的数据进行排序,聚合等操作,需要消耗大量的CPU资源和内存资源。
3,转换组件的通信机制
转换组件的通信是指转换组件将数据传递给下一个转换组件,即转换的输入和输出,每一种转换组件都有输入和输出。
如果输入所使用的缓冲区和输出所使用的缓冲区不一样,那么转换的输出就是异步的,换句话说,异步转换组件不能够既执行指定操作又维护缓存区(行数或者行的顺序),所以必须复制数据,以实现所期望的结果。异步转换组件接收上游组件的输出缓冲区,然后创建新的缓冲区,输出到下一个组件。
如果输入所使用的缓冲区和输出所使用的缓冲区相同,那么转换的输出是同步的,同步转换组件不会创建新的缓冲区,而是复用缓冲区。
缓冲区的生命周期,是从创建缓冲区开始,到异步转换组件截止。由于异步转换组件需要创建额外的缓冲区,消耗Server的内存资源,降低Server处理数据的性能,因此理想的Package Design是一个Data Flow 只使用一个缓冲区完成所有的转换处理操作,所有的转换都是同步输出的。在实际的工作中,我们可以尽量不适用异步转换组件,或是减少缓冲区创建的数量,尽量延长缓冲区的生命周期。
4,识别转换的同步输出和异步输出
Data Flow的缓冲区可以理解为一个二维表,每行有固定的长度,每一行的Column位置都是固定的,因此一个column在同一个缓冲区中的标识符是相同的。如果该column复制到新的缓冲区中,那么,该column的标识符发生变化。SSIS Engine使用LineageID属性来标识缓冲区中的每一个column。
在转换组件的Advanced Editor中,每个column存在一个LineageID属性,这是一个指向缓冲区的指针,标识该列在缓冲区中的位置。
Specifies the lineage ID of the output column when this item was first placed in the data flow.
如果转换输出是同步的,那么同一个column的LineageID属性在转换的输出和输入中是相同的,如果转换输出是异步的,那么同一个column的LineageID属性在转换的输出和输入中是不同的。
例如,Column1 的初始LineageID值是193,如果转换输出是同步的,在转换的Input和Output中,可以看到Column1的LineageID是193.
5,数据流缓冲区的申请和释放
Data source 组件会创建新的缓冲区,并为响应的Column分配LineageID值。
当缓冲区数据被加载到destination之后,destination会释放缓冲区。
异步转换组件会终止输入缓冲区,新建输出缓冲区。
同步转换组件复用输入缓冲区,并作为输出缓冲区,传递给下一个转换组件。
三,异步转换输出示例
异步转换输出是指,转换的输入缓冲区和输出缓冲区不同。
1,Data Flow
2,Data Source组件
Data Source 具有External columns和 output columns。
External columns直接来源于数据源,Data Source 组件创建缓冲区,将数据复制到缓冲区中,分配LineageID,这个缓冲区就是Data Source 组件的输出。
3,Sort 组件
Sort组件的Input Columns,ID 列的LineageID是197,和Data Source 输出缓冲区中ID 列的LineageID属性相同,这说明,Sort 转换直接使用Data Source的输出缓冲区。
Sort 转的输出,ID列的LineageID属性值是 288。相同的列之所以存在不同的LineageID值,是因为Sort转换的输出是异步的,输入缓冲区和输出缓冲区不同,因此输出需要一个新的列标识符。
SortColumnID属性值是197,是输出列的源列。SortColumnID属性 Specifies the lineage identifier of the input column that is associated with this output column。
所有具有半阻塞性和阻塞性的转换都是异步输出的,这些转换不会直接向下游传递输入缓冲区,因为需要拦截数据以进行处理和重组。
四,同步转换输出示例
属性IdentificationString用于标识组件的数据流,其值是有特定格式的字符串,例如: Conditional Split.Inputs[conditional Split input]
属性SynchronousInPutID 用于标识组件的输出流的IdentificationString,如果组件的属性SynchronousInPutID为None,说明组件会创建新的输出流,转换输出是异步的,如果组件的属性SynchronousInPutID 是一个IdentificationString,说明组件的输出和输入使用相同的缓冲区。
1,Data Flow
2,Conditional Split组件
查看Conditional Split组件的输出,发现case1 没有Output Columns
点击Case1,查看属性SynchronousInPutID,值是Conditional Split.Inputs[conditional Split input]。
查看 Conditional Split的 输入流,查看属性IdentificationString的值,和组件的输出流Case1的SynchronousInPutID属性值相同。
3,验证LineageID值
查看Sort组件的Input Columns,ID的LineageID是 197,和DataSource组件的输出流,Conditional Split的输入流都相同,说明Conditional Split没有创建新的数据缓冲区,使用的是Data source 创建的缓冲区来进行数据转换。
结论:在同步转换输出中,当完成了转换逻辑之后,会立即将缓冲区传递到下游的转换,即转换输入和转换输出使用相同的缓冲区,避免复制缓冲区到输出,因此相同列的LineageID是相同的。
五, 识别同步转换组件和异步转换组件
通过Advanced Editor,查看Output的SynchronousInPutID属性,Specifies the input ID of rows in this output
如果SynchronousInPutID属性值为None,那么该组件输出是异步的,如果该值不是None,而是IdentificationString 格式的字符串,那么该转换输出是同步的。