排号键是SPL独特的数据类型,适合替代多层次、各层不连续的键值,比如×××号、合同编号、产品编号、组织机构代码等。排号键定位速度快,常用于优化内存索引查询和外键关联计算。
cardNormal.btx是集文件格式的×××信息表,数据量一百万条,字段为:cardNo(×××,主键),name(姓名),gender(性别),province(省份),email(电子邮件),mobile(移动电话),address(住址)。cardK.btx与cardNormal.btx在结构和数据上完全相同,唯一的区别在于cardNo字段是排号键。
本案例对cardNormal.btx和cardK.btx分别执行百万次索引查询,并比较两者性能。
其中cardNo是简化后的×××,格式如下:
位数 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
规则 | 行政区划 | 生日 | 流水 | 校验 | ||||||||||||
细则 | 省 | 地区 | 年 | 月 | 日 | 流水 | 校验 | |||||||||
范例 | 1 | 0 | 0 | 3 | 1 | 9 | 8 | 0 | 1 | 2 | 1 | 3 | 0 | 2 | 3 | x |
省、地区:各自取值范围为1-10。
生日:取值为"1980-06-01"至"1981-01-01"。
流水:取值为1-100。
校验:根据前15位计算出的冗余校验位,取值为0至10,其中10用x来表示。
将上述×××转为排号键,可采取如下思路:
1. 省、地区:排号键的每一层只支持1-255的整数,因此将省和地区分别转为整数,作为第1、2层排号键。
2. 生日:排号键以1为起始,可以取得更好的性能,因此算出原生日和1980-06-01的间隔天数,作为第3层排号键。
3. 流水:转为整数,作为第4层的排号键。
4. 校验位:冗余数据,可删除。
具体转换脚本如下:
k(int(mid(cardNo,1,2)), int(mid(cardNo,3,2)), interval("1980-06-01",date(mid(cardNo,5,8),"yyyyMMdd")), int(mid(cardNo,13,3)) ) |
下面对cardNormal.btx和cardK.btx分别执行百万次索引查询。
A | B | C | |
1 | =cardNormal=file("d:\\temp\\cardNormal.btx").import@b().keys(cardNo).index() | /将cardNormal.btx读入内存 | |
2 | =paramList=cardNormal.(cardNo).sort(rand()).to(100000) | /随机取1万个××× | |
3 | |||
4 | =now() | ||
5 | for 100 | =paramList.(cardNormal.find(~)) | /查询百万次 |
6 | =interval@ms(A4,now()) | /字符串键性能:5537ms | |
7 | |||
8 | =cardK=file("d:\\temp\\cardk.btx").import@b().keys(cardNo).index@s() | /将cardk.btx读入内存 | |
9 | =paramListK=paramList.(k(int(mid(~,1,2)), int(mid(~,3,2)), interval("1980-06-01",date(mid(~,5,8),"yyyyMMdd")), int(mid(~,13,3)) )) | /将字符串参数转为排号键参数 | |
10 | =now() | ||
11 | for 100 | =paramListK.(cardK.find(~)) | /查询百万次 |
12 | =interval@ms(A10,now()) | /排号键性能:1977ms |
A8:对排号键建立内存哈希索引,需使用函数选项@s。
可以看到,对字符串键建立索引,查询需耗费5547毫秒,而排号键只需1977毫秒,后者明显快。
taxNormal.btx是集文件格式的报税信息表,数据量一千万条,字段为:serial(主键,流水号),cardNo(外键,×××),tax(报税额),area(所属地区),declaretype(申报类型),unit(申报单位),declareTime(申报时间),network(办理网点)。taxK.btx 与taxNormal.btx在结构和数据上完全相同,唯一的区别在于cardNo字段是排号键。
本案例算法:
1. 对taxNormal.btx和cardNormal.btx进行外键关联计算,排除操作系统缓存和硬盘IO的影响,只取关联动作消耗的时间。
2. 对taxK.btx和cardK.btx进行外键关联计算,排除操作系统缓存和硬盘IO的影响,只取关联动作消耗的时间。
3. 比较1和2的差异。
具体算法如下:
A | B | C | |
1 | =file("d:\\temp\\taxNormal.btx").cursor@b() | for A1,10000 | /打开报税表,预遍历游标 |
2 | =A1.reset() | /重置游标到起点 | |
3 | =file("d:\\temp\\cardNormal.btx").import@b().keys(cardNo).index() | /打开×××表 | |
4 | =now() | ||
5 | for A1,10000 | /正式遍历游标 | |
6 | =interval@ms(A4,now()) | =A1.reset() | /遍历游标耗时3748ms |
7 | =A1.switch(cardNo,A3:cardNo) | for A1,10000 | /建立关联,预遍历游标 |
8 | =A1.reset() | /重置游标到起点 | |
9 | =now() | ||
10 | for A7,10000 | /正式遍历关联游标 | |
11 | =interval@ms(A9,now()) | =A11-A6 | / 建立关联耗时:7553ms |
12 | /上面:字符串键关联。下面:排号键关联 | ||
13 | =file("d:\\temp\\taxK.btx").cursor@b() | for A13,10000 | /打开报税表,预遍历游标 |
14 | =A13.reset() | /重置游标到起点 | |
15 | =file("d:\\temp\\cardK.btx").import@b().keys(cardNo).index@s() | /打开×××表 | |
16 | =now() | ||
17 | for A13,10000 | /正式遍历游标 | |
18 | =interval@ms(A16,now()) | =A13.reset() | /遍历游标耗时2884ms |
19 | =A13.switch(cardNo,A15:cardNo) | for A19,10000 | /建立关联,预遍历游标 |
20 | =A19.reset() | /重置游标到起点 | |
21 | =now() | ||
22 | for A19,10000 | /正式遍历关联游标 | |
23 | =interval@ms(A21,now()) | =A23-A18 | /建立关联耗时:966ms |
B2、B7、B13、B19:预遍历游标,避免操作系统缓存的影响。遍历之后需用reset函数重置游标,以便后续再次遍历。
B11、B23:用总时间减去遍历时间,获得关联动作消耗的时间。
可以看到,用普通键做外键关联计算,其关联动作耗时7553ms;排号键做外键关联计算时,只需耗时966ms,后者明显快。而且可以看出,使用排号键读出记录(fetch)的时间也少于使用字符串(其它字段和记录行数都相同),说明排号键对象的生成性能更好。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。