追梦人博客Dream.ren

追梦人 Dream.ren

Linux下几种去重方法的比较

博客又双叒叕一个多月没更了,今天(好吧,我承认这是之前的存稿)就来比较下Linux下对数据去重处理的几种方式,更具体一点讲就是比较sort -u、uniq、awk '!x[$0]++' 等方法的效果与效率。

输入文件为Nginx的日志文件,共100万行,文件名为nginx.log,其格式为“时间|用户IP|主机名|发送流量(字节)|下载流量(字节)|耗时(毫秒)”
具体内容截取如下(部分内容已被隐藏):

2018-03-03T20:00:52+08:00 | 116.237.***.205 | *****.********.com | 780 | 4319 | 1.495
2018-03-03T20:00:52+08:00 | 39.181.***.45 | *****.********.com | 3820 | 3777 | 1.480
2018-03-03T20:00:52+08:00 | 101.87.***.203 | *****.********.com | 617 | 4427 | 29.249
2018-03-03T20:00:52+08:00 | 183.206.***.72 | *****.********.com | 2481 | 5164 | 20.894
2018-03-03T20:00:52+08:00 | 59.56.***.98 | *****.********.com | 825 | 4425 | 1.679
2018-03-03T20:00:53+08:00 | 27.198.***.59 | www.******.**.** | 287 | 1034 | 240.426
2018-03-03T20:00:53+08:00 | 112.67.***.155 | *****.********.com | 2914 | 726 | 40.238
2018-03-03T20:00:53+08:00 | 153.34.***.97 | *****.********.com | 552 | 4420 | 185.736
2018-03-03T20:00:53+08:00 | 113.232.***.0 | www.******.com | 124 | 3221 | 1.022

接下来分别采用上面几种不同的方式去重提取出用户IP地址并比较其效率,每种方法各执行三次取消耗时间的平均值。注意,我这里仅仅保留了IP字段,请灵活运用!如保留整行的话请替换$2为$0。

第一种方式:sort+uniq:

提到文本去重,Linux下有个uniq工具,顾名思义就是用来取唯一值的嘛!uniq走起!但,但是,,,uniq命令只能对相邻的重复行去重,所以在使用uniq前需要先排序,使相同内容的行相邻,再去重才行,于是就有了下面的命令:

time awk 'BEGIN{FS="|"}{print $2}' nginx.log|sort|uniq >/dev/null

耗时:0m7.609s、0m7.538s、0m7.782s 平均:7.643s
注意:这种去重方式会改变待处理内容原本的行序!

第二种方式:sort -u:

uniq使用前还得保证相同行相邻,是不是觉得很鸡肋?sort命令其实是有取唯一值功能的,只需加个-u参数即可:

time awk 'BEGIN{FS="|"}{print $2}' nginx.log|sort -u >/dev/null

耗时:0m6.242s、 0m6.136s、0m5.920s 平均:6.099s
可以看到直接使用sort -u比sort后再uniq要节省一点时间!这种方式同样也是会改变原本的行序的。

第三种方式:awk:

哇!重量级工具出场了!awk是一个非常强大的文本分析处理工具,用起来非常爽!awk支持关联数组,下面三条命令原理和效果基本是一样的,都是把IP作为key进行处理,区别是前两条是直接把第一次出现的IP输出出来,非第一次出现的IP直接忽略;第三条是,遍历所有行后直接输出key实现去重操作。

#只保留第一次出现的字段,去除后续其他字段
time awk 'BEGIN{FS="|"}{print $2}' nginx.log|awk '!x[$0]++' >/dev/null
#只保留第一次出现的字段,去除后续其他字段
time awk 'BEGIN{FS="|"}{if(!x[$2]++){print $2}}' nginx.log >/dev/null
#乱序
time awk 'BEGIN{FS="|"}{a[$2]}END{for(i in a)print i }' nginx.log >/dev/null
#只保留最后一次出现的字段,去除之前其他字段(这条是后续加的,未测试耗时)
awk 'BEGIN{FS="|"}{a[$2]=NR;b[NR]=$2}END{n=asort(a);for(i=1;i<=n;i++)print b[a[i]]}' nginx.log

耗时:
第一条:0m2.362s、0m2.405s、0m2.052s 平均:2.273s
第二条:0m2.116s、0m2.130s、0m2.151s 平均:2.132s
第三条:0m2.238s、0m1.944s、0m2.251s 平均:2.144s
这种利用awk关联数组的去重方式无需进行耗时的排序操作,也就没有那么高的时间复杂度,当处理的内容较多时,效率优势就非常明显了,而且前两条命令是不会改变原内容出现先后顺序的,各行出现的顺序还是原来的!可以说是非常完美的一种去重方式了!注意这里的第一条的第二条完全是一个东西,只是写法稍微有点不同罢了,只保留最后出现的字段还可以使用tac反序再去重然后再反序的思路。

总结:当待去重的数据量非常大或者有保留原有行序需求的时候,应该选择使用awk对数据进行去重!其他情况下嘛!看心情随便选一个就行~~~

评论回复

  1. 回复 visij

    啊啊啊啊啊啊啊啊

  2. 回复 保密

    博主Pixiv是不支持了吗

  3. 回复 谢强

    :razz: :?: 你好

  4. 回复 谢强

    你好

  5. 回复 东方

    大神,请问怎么打赏?

  6. 回复 Woshiluo

    为什么不试试自己写一个cmp呢(划

  7. 回复 UC震惊部

    大哥。pureDNS能不能增加对http://cnpolitics.org/ 政见网的支持?

  8. 回复 叶子

    博主求教翻墙,我新萌,你们讲的专业术语我都不懂 :cry: ,你可以直接告诉我该干嘛么!!please

  9. 回复 鸟叔

    鸟叔看不懂,哈哈

  10. 回复 鸿基

    :mrgreen:多谢博主的dns

  11. 回复 樱花安逸

    大佬,现在北方是用不了的对吗?

    • 回复 Dreamer

      能用,就是目前宽带不够大家用,高峰时几秒解析不出来结果都正常,而南方节点目前使用人数才是满载人数的2%左右,强烈推荐只使用南方节点。

  12. 回复 Alexwang

    看不懂,但是赞一个!

  13. 回复 Aaron

    看标题还以为是论文去重那种去重,好像进入了我完全 不了解的领域啊 :neutral:

    • 回复 Dreamer

      @Aaron哇,论文去重。。。。太高级了,不会。仅仅是简单的文本按行去重而已……

  14. 回复 star

    看不懂,我就在下面喊喊666算了

  15. 回复 yz

    :oops: 看不懂.

6 + 5 =

回到顶部