性能优化技巧 - 查找( 四 )


执行后,生成组表的索引文件:id_600m.ctx__id_idx

性能优化技巧 - 查找

文章插图
 
代码5.3
代码5.3,在组表的 icursor()这个函数中,使用索引 id_idx,以条件 A2.contain(id) 来过滤组表 。集算器会自动识别出 A2.contain(id) 这个条件可以使用索引,并会自动将 A2 的内容排序后从前向后查找 。
进阶使用
使用排序索引多线程查找时,按键值排序分组后扔给多个线程去查询,避免两个线程中有交叉内容 。同时,还可以设计成多个组表,把键值能平均分配到多个组表上并行查找 。
所谓多线程并行,就是把数据分成 N 份,用 N 个线程查询 。但如果只是随意地将数据分成 N 份,很可能无法真正地提高性能 。因为将要查询的键值集是未知的,所以理论上也无法确保希望查找的数据能够均匀分布在每一份组表文件中 。比较好的处理方式是先观察键值集的特征,从而尽可能地进行数据的均匀拆分 。
如果键值数据有比较明显的业务特征,我们可以考虑按照实际业务场景使用日期、部门之类的字段来处理文件拆分 。如:将属于部门 A 的 1000 条记录均分在 10 个文件中,每个文件就有 100 条记录 。在利用多线程查询属于部门 A 的记录时,每个线程就会从各自对应的文件中取数相应的这 100 条记录了 。
下面我们来看个实际的例子,已有数据文件multi_source.txt的结构如下:
性能优化技巧 - 查找

文章插图
 
其中 type 和 id 两个字段作为联合主键确定一条记录,其中部分数据如下:
性能优化技巧 - 查找

文章插图
 

性能优化技巧 - 查找

文章插图
 
代码5.4
代码5.4详解:
A1:type 的枚举值组成的序列 。在实际情况中,枚举列表可能来自文件或者数据库数据源 。。
A2:给枚举值序列中每个 type 一个 tid 。为后续的数字化主键合并做准备 。
A3~A6:从 multi_source.txt 文件中获取数据,并按照 A2 中的对应关系,把 type 列的枚举串变成数字,然后将 type 和 id 进行合并后,生成新的主键 nid 。
A7:使用循环函数,创建名为“键值名 _ 键值取 N 的余数 _T.ctx”的组表文件,其结构同为 (#nid,data) 。
A8:用循环函数将游标数据分别追加到 N 个原组表上 。比如当 N=1 时,拼出的 eval 函数参数为:channel(A4).select(nid%4==0).attach(A7(1).Append(~.cursor())) 。意思是对游标 A4 创建管道,将管道中记录按键值 nid 取 4 的余数,将余数值等于 0 的记录过滤出来 。attach 是对当前管道的附加运算,表示取和当前余数值对应的原组表,将当前管道中筛选过滤出的记录,以游标记录的方式追加到 A7(1),即第 1 个组表 。
A9:循环游标 A6,每次获取 50 万条记录,直至 A6 游标中的数据取完 。
执行后,产出 4(这时例子取 N=4)个独立的组表文件:
性能优化技巧 - 查找

文章插图
 

性能优化技巧 - 查找

文章插图
 
代码5.5
代码5.5,创建索引过程详解:
A1:列出满足 nid*T.ctx 的文件名(这里 * 为通配符),这里 @p 选项代表需要返回带有完整路径信息的文件名 。使用 fork 执行多线程时,需要注意环境中的并行限制数是否设置合理 。这里用了 4 个线程,设计器中对应的设置如下:
性能优化技巧 - 查找

文章插图
 
B1:每个线程为各个组表建立对应的索引文件,最终结果如下:
性能优化技巧 - 查找

文章插图
 

性能优化技巧 - 查找

文章插图
 
代码5.6
代码5.6,查询过程详解:
A1:从 keys.txt 获取查询键值序列,因为只有一列结果,使用 @i 选项,将结果返回成序列:
性能优化技巧 - 查找

文章插图
 
A2:把 A1 的序列按 4 的余数进行等值分组:
性能优化技巧 - 查找

文章插图
 
A3、B3~B5:用 fork 函数,按等值分组后的键值对各个组表分别并行查询 。这里的 fork 后面分别写了两个参数,第一个是循环函数 N.(~-1),第二个是 A2 。在接下来的 B3、B4 中分别使用 A3(2) 和 A3(1) 来获取 fork 后面这两个对应顺序的参数,B4:对组表文件进行根据 B3 中的键值集进行数据筛选,B5:返回游标 。由于 A3 中是多个线程返回的游标序列,所以 A6 中需要使用 conjx 对多个游标进行纵向连接 。


推荐阅读