3.3 帐户有序存储
按 CustomerID 排序后,可以快速实现针对 CustomerID 的去重和分组运算,遍历过程中只需要和上一条记录对比即可,也不需要保留结果集,速度快且不会有内存溢出问题。
3.3.1 存量数据
A | |
---|---|
1 | =file(“ShipVia.btx”).import@bi() |
2 | =file(“Orders.txt”).cursor@mt(CustomerID:string, OrderDate:datetime, ProductID:string, Quantity:int, Unit:string, Price:decimal, Amount:decimal, EmployeeID:int,EmployeeName:string,ShipVia:string).run(OrderDate=days@o(OrderDate), ShipVia=A1.pos@b(ShipVia)).sortx(CustomerID,OrderDate;1000000) |
3 | =file(“Orders_Account.ctx”).create@py(#CustomerID,#OrderDate, ProductID, Quantity, Unit, Price, Amount, EmployeeID,EmployeeName,ShipVia) |
4 | =A3.append(A2) |
5 | >A3.close() |
A2 按 CustomerID 和 OrderDate 排序
A3 产生组表文件的结构,@p 选项表示组表将按第一个字段(这里是 CustomerID)为单位分段,在并行计算时将不会把 CustomerID 相同的记录分配给不同的线程,确保并行计算的正确性。无此选项时,将简单地按记录为单位分段,可能把同一个 CustomerID 的数据分配给两个线程,在某些有序运算时会出错。前面章节中没有用到有序算法,也没有特别的分段要求,不用 @p 也可以正确地并行计算。
3.3.2 增量数据
新增数据通常并不会按 CustomerID 有序,所以不能直接追加到有序数据的末尾, 而直接将有序数据和新增数据一起重新做常规大排序,会非常耗时。
组表有补表,会另外保存一份小规模的有序数据 (即补表)。新增数据仅和补表合并排序,原组表不变。经过一段时间后,补表积累到合适大小时,再和原组表合并排序。做有序计算时,将从原组表和补表中分别读取后再归并返回,这比只有一份有序数据时性能会下降一些,但仍能利用有序快速计算。
积累到何时再和原数据合并,与新增数据的周期有关。比如每天都有新增数据,通常可以每月做一次原组表和补表的全并。这样,补表中不会超过一个月的数据量,原组表存储一个月之前的所有数据。补表可能比原组表小很多,所以每天合并的数据量相对较小,很快就能完成数据追加。每个月才需要完成一次全量合并,耗时长一些也可以接受。
增量数据一般是单独一个文件,这里假设是 Orders_add.txt:
A | B | |
---|---|---|
1 | =file(“ShipVia.btx”).import@bi() | |
2 | =file(“Orders_add.txt”).cursor@t(CustomerID:string, OrderDate:datetime, ProductID:string, Quantity:int, Unit:string, Price:decimal, Amount:decimal, EmployeeID:int, EmployeeName:string, ShipVia:string).run(OrderDate=days@o(OrderDate), ShipVia=A1.pos@b(ShipVia)).sortx(CustomerID,OrderDate;1000000) | |
3 | =file(“Orders_Account.ctx”) | =A3.open() |
4 | =B3.append@a(A2) | |
5 | if day(now())==1 | >A3.reset() |
6 | >B3.close() |
A2 读 Orders 表的增量文件,按 CustomerID,OrderDate 排序
A4 把增量数据合并到组表文件的补表中,@a 选项表示以有序归并的方式追加
A5-B5 判断当前是否为每月 1 日,如果是,对组表和补表进行合并,补表清空,这个功能由 reset() 函数来完成
产生组表时使用了 @p 选项,增量数据追加到补表时,SPL 也会自动按正确的方式处理,保证后续的并行计算正确。