一、COBOL多文件排序的“隐形坑”:SORT和MERGE的性能瓶颈拆解

很多用COBOL做金融、电信业务的开发者都有过这种经历:明明逻辑没毛病,跑批作业却慢得离谱,尤其是处理好几个大文件的排序时,等半天才能出结果。其实问题大概率出在SORT和MERGE这两个常用语句上——它们自带的默认参数根本没针对多文件场景优化,成了拖慢速度的“性能杀手”。

先给大家说清楚SORT和MERGE在多文件场景的核心逻辑:SORT是把一堆零散的文件先合并成一个临时大文件,再整体排序,最后输出结果;MERGE则是直接按指定规则把多个已经排好序的文件合并成一个有序文件,不用全量重排。但不管用哪个,默认配置下都会踩两个大坑:一个是内存缓冲区给得太少,另一个是中间文件读写太笨。

二、第一个性能杀手:内存缓冲区的“抠门”默认值

COBOL里SORT和MERGE的排序逻辑,本质是先把要处理的数据读到内存里,在内存里排好序再输出。但默认给的内存缓冲区特别小,就像给你一个只能装10颗糖的小口袋,要装1000颗糖,你得来回跑100次,速度能快才怪。

2.1 缓冲区大小的计算逻辑

COBOL的缓冲区是按“记录块”来算的,比如你每条记录是100字节,默认块大小是4096字节,那一个块只能装40条记录。多文件场景下,本来要处理的数据量就大,小缓冲区会导致排序时频繁和硬盘交互(也就是“换页”)——内存满了就把排好一部分的临时数据写到硬盘,空出内存再读新数据,写来写去的时间占了总耗时的80%以上。

2.2 缓冲区优化的具体方法

优化的核心就是给缓冲区“扩容”,但不能乱加,得按实际数据算。给大家举个完整的例子,先说明技术栈是COBOL(IBM大型机常用版本,也是COBOL的主流应用场景):

* 技术栈:COBOL(IBM大型机Z/OS环境)
* 优化前的SORT代码(默认缓冲区)
SORT SORT-FILE
  ON ASCENDING KEY CUST-ID CUST-AMT
  INPUT PROCEDURE IN-PROC
  OUTPUT PROCEDURE OUT-PROC.

* 优化后的SORT代码(自定义缓冲区)
* 步骤1:先定义缓冲区参数(在WORKING-STORAGE SECTION)
01  SORT-PARAMS.
    05  BUFFER-SIZE        PIC 9(10) VALUE 1048576.  * 1MB缓冲区,按100字节记录算,能装10240条
    05  BLOCK-SIZE         PIC 9(6) VALUE 8192.     * 块大小设为8KB(硬盘最小读写单位的倍数)

* 步骤2:用SORT语句的OPTIONS子句指定缓冲区
SORT SORT-FILE
  ON ASCENDING KEY CUST-ID CUST-AMT
  INPUT PROCEDURE IN-PROC
  OUTPUT PROCEDURE OUT-PROC
  OPTIONS BUFFER = BUFFER-SIZE BLOCK = BLOCK-SIZE. * 这里指定自定义的缓冲区和块大小

这里给大家算个账:原来默认缓冲区是256KB,现在改成1MB,同样处理100万条100字节的记录,换页次数从原来的3906次降到976次,硬盘读写时间直接减少75%。而且块大小一定要设成8KB的倍数(大型机硬盘的最小读写单位是8KB),不然会出现“碎块”,反而更慢。

三、第二个性能杀手:中间文件的“笨读写”策略

多文件排序时,SORT和MERGE默认会生成一堆临时的中间文件,这些文件的读写逻辑特别笨:比如SORT会把每个输入文件都读一遍,再写一遍临时合并文件,再写一遍排序后的临时文件,最后才写结果文件;MERGE虽然不用重排,但也会把每个输入文件的记录都转一遍临时缓冲。中间文件的读写占了总耗时的60%左右,绝对是“隐形杀手”。

3.1 中间文件的优化逻辑

优化的核心是“减少中间步骤”和“利用硬盘特性”,主要有两个方法:一个是用“内存合并”替代临时合并文件,另一个是把中间文件放在高速硬盘上。

3.2 具体优化示例

还是用之前的COBOL技术栈,给大家举个MERGE的优化例子,因为MERGE在多文件有序场景下用得最多:

* 技术栈:COBOL(IBM大型机Z/OS环境)
* 优化前的MERGE代码(默认中间文件)
MERGE MERGE-FILE
  ON ASCENDING KEY CUST-ID CUST-AMT
  USING FILE1 FILE2 FILE3 * 三个已经排好序的输入文件
  OUTPUT PROCEDURE OUT-PROC.

* 优化后的MERGE代码(自定义中间文件位置+内存合并)
* 步骤1:定义中间文件的位置(在FILE SECTION)
FD  MERGE-TEMP-FILE.
01  MERGE-TEMP-REC.
    05  CUST-ID         PIC 9(10).
    05  CUST-AMT        PIC 9(10)V99.
    05  FILLER          PIC X(70). * 凑够100字节的记录长度

* 步骤2:定义内存合并的参数(在WORKING-STORAGE SECTION)
01  MERGE-PARAMS.
    05  MEMORY-MERGE-FLAG PIC X VALUE 'Y'. * 开启内存合并,不生成临时合并文件
    05  TEMP-DISK-TYPE   PIC X(2) VALUE 'SSD'. * 指定中间文件放在SSD硬盘(高速硬盘)

* 步骤3:用OPTIONS子句指定中间文件策略
MERGE MERGE-FILE
  ON ASCENDING KEY CUST-ID CUST-AMT
  USING FILE1 FILE2 FILE3
  OUTPUT PROCEDURE OUT-PROC
  OPTIONS MEMORY-MERGE = MEMORY-MERGE-FLAG TEMP-DISK = TEMP-DISK-TYPE.

这里的“内存合并”是大型机COBOL的一个特性,开启后MERGE不会把三个输入文件先合并成一个临时文件,而是直接在内存里按顺序取每个文件的下一条记录,比大小后输出,省掉了一次临时文件的读写。而把中间文件放在SSD硬盘,读写速度比普通硬盘快5-10倍,中间步骤的耗时直接砍半。

四、优化的适用场景、优缺点和注意事项

4.1 适用场景

这些优化主要适用于以下几种场景:一是多文件排序,比如金融系统的日终跑批,要把10个分行的交易文件合并排序;二是大文件排序,比如单个文件超过1GB的场景;三是对速度要求高的场景,比如实时跑批、紧急对账等。

4.2 优化的优缺点

优点很明显:能把多文件排序的速度提升2-5倍,比如原来要跑1小时的作业,优化后可能只要15分钟到30分钟。但也有缺点:一是要占用更多的内存,比如原来给SORT分配256KB内存,现在要分配1MB,可能会影响其他作业的内存使用;二是要对数据有深入的了解,比如要知道每条记录的长度、总数据量,不然缓冲区设得不对反而会变慢;三是部分优化特性只有大型机的COBOL版本才有,小型COBOL(比如Windows上的)可能不支持。

4.3 注意事项

优化的时候有三个坑要避开:第一,缓冲区不能设得太大,比如设成10MB,可能会导致整个作业的内存不足,被系统杀掉;第二,中间文件不能放在普通硬盘上,不然“内存合并”的优势就发挥不出来;第三,MERGE只能用在输入文件已经排好序的场景,如果输入文件是乱的,必须用SORT,不能强行用MERGE。

五、总结

COBOL的SORT和MERGE语句本身不是慢,而是默认配置没针对多文件场景优化。只要从两个方面入手:一是给内存缓冲区扩容,按实际数据算好大小和块大小;二是优化中间文件的读写,用内存合并替代临时文件,把中间文件放在高速硬盘,就能大幅提升速度。

最后给大家一个优化的检查清单:1. 缓冲区大小是不是硬盘最小读写单位的倍数?2. 块大小是不是8KB的倍数?3. MERGE的输入文件是不是已经排好序的?4. 中间文件是不是放在高速硬盘上?只要这四个都对,多文件排序的速度肯定能提上来。