最近看了July的一些关于Java处理海量数据的问题研究,深有感触,链接:http://blog.csdn.net/v_july_v/article/details/6685962

感谢July ^_^

他用的是Java的Hash Map等方法做了处理,讲解的非常深刻入骨

我也一时兴起,想拿Python试试刀,看看Python对于海量数据的处理能力如何。无奈在百度和Google输入“Python 海量数据”都无果。可能是国内使用python的不多,用python处理海量数据的就更少了。不过这浇灭不了我的欲望,哈哈

打算拿July的其中一个问题来试验一下:

  1. <strong>海量日志数据,提取出某日访问百度次数最多的那个IP。</strong>

July给出的解决方案:

  1. 方案1:首先是这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有2^32个IP。同样可以采用映射的方法,比如模1000,把整个大文件映射为1000个小文件,再找出每个小文中出现频率最大的IP(可以采用hash_map进行频率统计,然后再找出频率最大的几个)及相应的频率。然后再在这1000个最大的IP中,找出那个频率最大的IP,即为所求。

下手吧!

(一)生成数据

我首先构造1亿个IP,这些IP前两段都是“10.197”,后两段为0-255的随机数

把这1亿个IP存入一个文本文件中

Python代码如下:

  1. __author__ = "Wally Yu (dayu.ebay@gmail.com)"
  2. __date__ = "$Date: 2012/04/09 $"
  3. def generateRandom(rangeFrom, rangeTo):
  4. import random
  5. return random.randint(rangeFrom,rangeTo)
  6. def generageMassiveIPAddr(fileLocation,numberOfLines):
  7. IP = []
  8. file_handler = open(fileLocation, 'a+')
  9. for i in range(numberOfLines):
  10. IP.append('10.197.' + str(generateRandom(0,255))+'.'+ str(generateRandom(0,255)) + '\n')
  11. file_handler.writelines(IP)
  12. file_handler.close()
  13. if __name__ == '__main__':
  14. from time import ctime
  15. print ctime()
  16. for i in range(10):
  17. print '  ' + str(i) + ": " + ctime()
  18. generageMassiveIPAddr('d:\\massiveIP.txt', 10000000)
  19. print ctime()

这里插一下,我的软件硬件环境:

硬件:

- ThinkPad T420(CPU: i7, 内存16G)

软件:

-OS: WinXP32位 (只认出3.6G内存)

- Python:2.7

从Python的print日志中基本可以看出,生成一千万条IP地址大概需要1分钟,生成1亿条记录需要10分钟

占据硬盘空间:1.4G

日志:

  1. Mon Apr 09 16:52:28 2012
  2. 0: Mon Apr 09 16:52:28 2012
  3. 1: Mon Apr 09 16:53:28 2012
  4. 2: Mon Apr 09 16:54:29 2012
  5. 3: Mon Apr 09 16:55:30 2012
  6. 4: Mon Apr 09 16:56:32 2012
  7. 5: Mon Apr 09 16:57:33 2012
  8. 6: Mon Apr 09 16:58:36 2012
  9. 7: Mon Apr 09 16:59:36 2012
  10. 8: Mon Apr 09 17:00:36 2012
  11. 9: Mon Apr 09 17:01:35 2012
  12. Mon Apr 09 17:02:36 2012

(二)处理思路

假设现在可用内存仅为128M,而每行IP经计算需要14Byte

因为数据太大,把1亿条数据载入内存,再做排序会导致内存溢出。July的思想:“以大化小,分而治之”非常合适,我转化后的操作思路:

1. 每行IP需要14B空间,那么128M内存最多可以处理 128M / 14B = 9142857个IP

把每36571429个IP拆成一个小文件保存起来,每个小文件的大小小于等于128M,共将生成11个文件

2. 对每个小文件用Hash Table处理,Python有自己非常高效的Hash Table:Dictionary!

具体处理如下:

1). 构建名为“Result”的Dictionary,“key”为IP地址,“value”为该IP地址出现的次数,用来记录11个文件每一个的最多出现的IP

2). 构建名为“IP”的Dictionary,“key”为IP地址,“value”为该IP地址出现的次数,用来记录每一个小文件的所有IP地址

3). 读入每一条IP地址到“IP” Dictionary,如果该IP地址出现过,把相应的value的值加1;如果该IP地址没有出现过,则key=IP地址,value=1

4). 对“IP” Dictionary进行内排序,返回最大的IP地址(如果有若干个最大值是一样的,就都返回)

5). 将返回值存入“Result” Dictionary

6). 对“Result”进行内排序,返回最大的IP地址(如果有若干个最大值是一样的,就都返回)

(三)实现

1)拆分成小文件

代码如下:

  1. __author__ = "Wally Yu (dayu.ebay@gmail.com)"
  2. __date__ = "$Date: 2012/04/10 $"
  3. from time import ctime
  4. def splitFile(fileLocation, targetFoler):
  5. file_handler = open(fileLocation, 'r')
  6. block_size = 9142857
  7. line = file_handler.readline()
  8. temp = []
  9. countFile = 1
  10. while line:
  11. for i in range(block_size):
  12. if i == (block_size-1):
  13. # write block to small files
  14. file_writer = open(targetFoler + "\\file_"+str(countFile)+".txt", 'a+')
  15. file_writer.writelines(temp)
  16. file_writer.close()
  17. temp = []
  18. print "  file " + str(countFile) + " generated at: " + str(ctime())
  19. countFile = countFile + 1
  20. else:
  21. temp.append(file_handler.readline())
  22. file_handler.close()
  23. if __name__ == '__main__':
  24. print "Start At: " + str(ctime())
  25. splitFile("d:\\massiveIP.txt", "d:\\massiveData")

运行后的log如下:

  1. Start At: Tue Apr 10 10:56:25 2012
  2. file 1 generated at: Tue Apr 10 10:56:37 2012
  3. file 2 generated at: Tue Apr 10 10:56:47 2012
  4. file 3 generated at: Tue Apr 10 10:57:00 2012
  5. file 4 generated at: Tue Apr 10 10:57:14 2012
  6. file 5 generated at: Tue Apr 10 10:57:26 2012
  7. file 6 generated at: Tue Apr 10 10:57:42 2012
  8. file 7 generated at: Tue Apr 10 10:57:53 2012
  9. file 8 generated at: Tue Apr 10 10:58:04 2012
  10. file 9 generated at: Tue Apr 10 10:58:16 2012
  11. file 10 generated at: Tue Apr 10 10:58:27 2012
  12. file 11 generated at: Tue Apr 10 10:58:38 2012

可见拆分一个文件需要费时10-15秒,拆分文件总共耗时2分14秒

2). 找出出现次数最大的IP:

代码如下:

  1. __author__ = "Wally Yu (dayu.ebay@gmail.com)"
  2. __date__ = "$Date: 2012/04/10 $"
  3. import os
  4. from time import ctime
  5. def findIP(targetFolder):
  6. Result = {}
  7. IP = {}
  8. for root, dirs, files in os.walk(targetFolder):
  9. for f in files:
  10. # read small files
  11. file_handler = open(os.path.join(targetFolder, f), 'r')
  12. lines = file_handler.readlines()
  13. file_handler.close()
  14. # get IP in file, store to IP Dictionary
  15. for line in lines:
  16. if line in IP:
  17. IP[line] = IP[line] + 1
  18. else:
  19. IP[line] = 1
  20. # sort Dictionary
  21. IP = sorted(IP.items(), key=lambda d: d[1])
  22. # get max item(s), store to Result Dictionary
  23. maxItem = IP[-1][1]
  24. print '  File ' + str(f) + ":"
  25. print "    maxItem: " + str(IP[-1])
  26. tempTuple = IP.pop()
  27. while tempTuple[1] == maxItem:
  28. if tempTuple[0] in Result:
  29. Result[tempTuple[0]] = Result[tempTuple[0]] + 1
  30. else:
  31. Result[tempTuple[0]] = tempTuple[1]
  32. tempTuple = IP.pop()
  33. IP = {}
  34. print '    Finished: ' + ctime()
  35. print sorted(Result.items(), key=lambda d: d[1])
  36. if __name__ == '__main__':
  37. print 'Start: ' + ctime()
  38. findIP("d:\\massiveData")
  39. print 'End: ' + ctime()

运行后的log如下:

  1. Start: Thu Apr 12 10:20:01 2012
  2. File file_1.txt:
  3. maxItem: ('10.197.223.85\n', 190)
  4. Finished: Thu Apr 12 10:20:23 2012
  5. File file_10.txt:
  6. maxItem: ('10.197.44.215\n', 194)
  7. Finished: Thu Apr 12 10:20:37 2012
  8. File file_11.txt:
  9. maxItem: ('10.197.251.171\n', 181)
  10. Finished: Thu Apr 12 10:20:48 2012
  11. File file_2.txt:
  12. maxItem: ('10.197.181.190\n', 191)
  13. Finished: Thu Apr 12 10:21:00 2012
  14. File file_3.txt:
  15. maxItem: ('10.197.135.27\n', 193)
  16. Finished: Thu Apr 12 10:21:14 2012
  17. File file_4.txt:
  18. maxItem: ('10.197.208.113\n', 192)
  19. Finished: Thu Apr 12 10:21:24 2012
  20. File file_5.txt:
  21. maxItem: ('10.197.120.160\n', 190)
  22. Finished: Thu Apr 12 10:21:34 2012
  23. File file_6.txt:
  24. maxItem: ('10.197.69.155\n', 193)
  25. Finished: Thu Apr 12 10:21:44 2012
  26. File file_7.txt:
  27. maxItem: ('10.197.88.144\n', 192)
  28. Finished: Thu Apr 12 10:21:55 2012
  29. File file_8.txt:
  30. maxItem: ('10.197.103.234\n', 193)
  31. Finished: Thu Apr 12 10:22:08 2012
  32. File file_9.txt:
  33. maxItem: ('10.197.117.46\n', 192)
  34. Finished: Thu Apr 12 10:22:20 2012
  35. [('10.197.251.171\n', 181), ('10.197.120.160\n', 190), ('10.197.223.85\n', 190), ('10.197.181.190\n', 191), ('10.197.117.46\n', 192), ('10.197.208.113\n', 192), ('10.197.88.144\n', 192), ('10.197.147.29\n', 193), ('10.197.68.183\n', 193), ('10.197.69.155\n', 193), ('10.197.103.234\n', 193), ('10.197.135.27\n', 193), ('10.197.44.215\n', 194)]
  36. End: Thu Apr 12 10:22:21 2012

由此可见,最大的IP地址为:“10.197.44.215”,共出现194次!

而Python的计算时间为2分20秒,非常快

(四)引申测试

以上是在假设内存仅为128M下的计算时间,为了测试Python真正的执行效率,打算再写一算法,将所有1.4G的数据一次性导入内存,并作内排序,看看它的执行效率

代码如下:

  1. __author__ = "Wally Yu (dayu.ebay@gmail.com)"
  2. __date__ = "$Date: 2012/04/10 $"
  3. import os
  4. from time import ctime
  5. def findIPAtOnce(targetFile):
  6. print "Started At: " + ctime()
  7. Result = {}
  8. file_handler = open(targetFile, 'r')
  9. lines = file_handler.readlines()
  10. file_handler.close()
  11. print "File Read Finished At: " + ctime()
  12. for line in lines:
  13. if line in Result:
  14. Result[line] = Result[line] + 1
  15. else:
  16. Result[line] = 1
  17. print "Write to Dic Finished At: " + ctime()
  18. Result = sorted(Result.items(), key=lambda d: d[1])
  19. print "Sorting Finished At: " + ctime()
  20. print 'Result:'
  21. for i in range(10):
  22. print '  ' + str(Result.pop())
  23. if __name__ == '__main__':
  24. findIPAtOnce("d:\\massiveIP.txt")

最后得到了Memory Error:

  1. Traceback (most recent call last):
  2. File "C:/Documents and Settings/wally-yu/Desktop/findIPAtOnce.py", line 30, in <module>
  3. findIPAtOnce("d:\\massiveIP.txt")
  4. File "C:/Documents and Settings/wally-yu/Desktop/findIPAtOnce.py", line 11, in findIPAtOnce
  5. lines = file_handler.readlines()
  6. MemoryError

哈哈哈!

为了测试Python的处理速度,重新生成一个小一点的Txt文件,重新运行generageMassiveIPAddr函数,生成一千万个IP地址

  1. if __name__ == '__main__':
  2. from time import ctime
  3. print ctime()
  4. for i in range(1):
  5. print '  ' + str(i) + ": " + ctime()
  6. generageMassiveIPAddr('d:\\massiveIP_small.txt', 10000000)
  7. print ctime()

生成后的Txt占据144M的空间

再次运行

  1. if __name__ == '__main__':
  2. findIPAtOnce("d:\\massiveIP_small.txt")

得到Log如下:

  1. Started At: Thu Apr 12 11:03:35 2012
  2. File Read Finished At: Thu Apr 12 11:03:41 2012
  3. Write to Dic Finished At: Thu Apr 12 11:03:44 2012
  4. Sorting Finished At: Thu Apr 12 11:03:45 2012
  5. Result:
  6. ('10.197.222.105\n', 215)
  7. ('10.197.68.118\n', 210)
  8. ('10.197.229.152\n', 206)
  9. ('10.197.22.46\n', 202)
  10. ('10.197.98.83\n', 201)
  11. ('10.197.53.43\n', 201)
  12. ('10.197.169.65\n', 200)
  13. ('10.197.225.22\n', 200)
  14. ('10.197.146.78\n', 200)
  15. ('10.197.57.101\n', 200)

可见时间消耗如下:

文件数据读取:6秒

写入Dictionary:3秒

排序:1秒

总共耗时不过10秒,而且大多时间都是I/O的开销!!!

 

(五)小节

由以上种种可见Python对于海量数据处理的高效率,也为Python的同行处理海量数据提供了一些思路

有兴趣的朋友可以拿其他语言做同样的测试,共同进步

(六)修改

注:

1. 以上完成于2012年4月10日,本节及以下完成于2012年4月18日

2. 六、七节的增加是由于lidguan兄发现的一个大漏洞而做的修改,非常感谢!具体评论见下:

4楼 lidguan昨天 16:23发表[回复]
我有个问题,请不吝赐教:
将一个大文件分割成许多的小文件,但楼主分割方式好像是根据文件大小来分割,然后分别排序,得出一系列的最大值,最后在最大值中比较,得出一个最后结果....

但每个ip可能在不同文件中都有记录,也许这个倒霉的ip是第一个文件的第二名,在第二个文件也是第二名,你用最大值进行比较,就会把这个倒霉的ip忽略掉,但其实这个ip才是真正的最大值..

我不懂python,当伪代码看的....如有不对的地方,请多多原谅

我确实也是考虑不周,才导致了以上算法的巨大漏洞,今天做如下修改:

思路:

1. 不对大文件进行拆分,否则会产生lidguan兄提到的问题

2. 假设这个一亿个IP地址的重复率比较高,重复后的IP可以一次性记录入Python Dictionary (Java Hash Map),那么就直接从大文件中一条一条读取IP地址,记录入Dictionary

3. 对Dictionary进行排序并输出

代码:

  1. __author__ = "Wally Yu (dayu.ebay@gmail.com)"
  2. __date__ = "$Date: 2012/04/18 $"
  3. import os
  4. from time import ctime
  5. def findIPAtOnce(targetFile):
  6. print "Started At: " + ctime()
  7. Result = {}
  8. file_handler = open(targetFile, 'r')
  9. for line in file_handler:
  10. if line in Result:
  11. Result[line] = Result[line] + 1
  12. else:
  13. Result[line] = 1
  14. print "Write to Dic Finished At: " + ctime()
  15. file_handler.close()
  16. Result = sorted(Result.items(), key=lambda d: d[1])
  17. print "Sorting Finished At: " + ctime()
  18. print 'Result:'
  19. for i in range(10):
  20. print '  ' + str(Result.pop())
  21. if __name__ == '__main__':
  22. findIPAtOnce("d:\\massiveIP.txt")

Log:

  1. >>>
  2. Started At: Wed Apr 18 13:20:34 2012
  3. Write to Dic Finished At: Wed Apr 18 13:21:34 2012
  4. Sorting Finished At: Wed Apr 18 13:21:34 2012
  5. Result:
  6. ('10.197.200.159\n', 1713)
  7. ('10.197.143.163\n', 1707)
  8. ('10.197.68.193\n', 1693)
  9. ('10.197.136.119\n', 1692)
  10. ('10.197.71.24\n', 1692)
  11. ('10.197.132.242\n', 1690)
  12. ('10.197.4.219\n', 1688)
  13. ('10.197.113.84\n', 1684)
  14. ('10.197.204.142\n', 1681)
  15. ('10.197.78.110\n', 1675)

由此可见,出现最多的IP为“10.197.200.159”,出现次数1713次!

执行时间:

  • 读取文件并写入Dictionary:60秒
  • 内排序:小于1秒!!!

经过这次的修改,运算结果相信是可靠的

(七)总结

1. 修改后的代码是在假设IP地址的重复性比较高,可以一次性导入内存中操作的前提下展开的;如果重复性低,或者更大量的数据,导致无法一次导入内存的话,就需要使用外排序来找出IP地址了

2. 希望大家多多探讨,也幸亏lidguan兄的指出,否则险些酿成大错

3. 最近在讨论的纯粹的QA是否有必要存在,我也来多嘴几句,我相信这些代码如果是经过了QA的测试后多多少少会降低风险,尤其是此类明显的逻辑性的错误是肯定可以避免的,所以QA人员的重要性不言而喻。说要消灭QA,鄙人觉得是软件工程思维的一种倒退

【转贴】Python处理海量数据的实战研究的更多相关文章

  1. Python处理海量数据的实战研究

    最近看了July的一些关于Java处理海量数据的问题研究,深有感触,链接:http://blog.csdn.net/v_july_v/article/details/6685962 感谢July ^_ ...

  2. 【读书笔记与思考】《python数据分析与挖掘实战》-张良均

    [读书笔记与思考]<python数据分析与挖掘实战>-张良均 最近看一些机器学习相关书籍,主要是为了拓宽视野.在阅读这本书前最吸引我的地方是实战篇,我通读全书后给我印象最深的还是实战篇.基 ...

  3. 论文学习 - 《Hadoop平台下的海量数据存储技术研究》

    摘要 研究背景: 1. 互联网的图片数据急剧膨胀 2. Hadoop平台下的Hdfs分布式文件系统能够很好的处理海量数据 研究内容: 1. Hadoop平台工作原理 2. Hadoop平台下图片存储系 ...

  4. Python Django CMDB项目实战之-3创建form表单,并在前端页面上展示

    基于之前的项目代码 Python Django CMDB项目实战之-1如何开启一个Django-并设置base页.index页.文章页面 Python Django CMDB项目实战之-2创建APP. ...

  5. Python Django CMDB项目实战之-2创建APP、建模(models.py)、数据库同步、高级URL、前端页面展示数据库中数据

    基于之前的项目代码来编写 Python Django CMDB项目实战之-1如何开启一个Django-并设置base页index页文章页面 现在我们修改一个文章列表是从数据库中获取数据, 下面我们就需 ...

  6. Python Django CMDB项目实战之-1如何开启一个Django-并设置base页、index页、文章页面

    1.环境 win10 python 2.7.14 django 1.8.2 需要用到的依赖包:MySQLdb(数据库的接口包).PIL/pillow(处理图片的包) 安装命令: pip install ...

  7. Python数据可视化编程实战pdf

    Python数据可视化编程实战(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1vAvKwCry4P4QeofW-RqZ_A 提取码:9pcd 复制这段内容后打开百度 ...

  8. python内建的命名空间研究

    python内建的命名空间研究 说明: python内置模块的命名空间.python在启动的时候会自动为我们载入很多内置的函数.类,比如 dict,list,type,print,这些都位于 __bu ...

  9. Python NLP完整项目实战教程(1)

    一.前言 打算写一个系列的关于自然语言处理技术的文章<Python NLP完整项目实战>,本文算是系列文章的起始篇,为了能够有效集合实际应用场景,避免为了学习而学习,考虑结合一个具体的项目 ...

随机推荐

  1. erlang 虚机CPU 占用高排查

    -问题起因 近期线上一组服务中,个别节点服务器CPU使用率很低,只有其他1/4.排除业务不均,曾怀疑是系统top统计错误,从Erlang调度器的利用率调查 找到通过erlang:statistics( ...

  2. BeanUtils.populate(obj, map);

    public static void populate(Object bean, Map<String, ? extends Object> properties) throws Ille ...

  3. yum 保存下载包

    --- 1 --- $ sudo yum install yum-plugin-downloadonly $ sudo yum install --downloadonly --downloaddir ...

  4. gradle

    http://examples.javacodegeeks.com/core-java/gradle/gradle-hello-world-tutorial/

  5. zookeeper3.3.6 伪分布式安装

    下载地址(http://zookeeper.apache.org/releases.html#download)   一:下载zookeeper的安装包,解压,进入到zk的目录文件,进入conf目录 ...

  6. Linux更改计算机名称

    1.修改:vim /etc/hosts 2.修改:vim /etc/sysconfig/network 3.重启:reboot 如不重启可以输入:hostname  新改的计算机名称,然后su

  7. 使用Selector改变TextView的字体颜色textColor的方法

    先上Selector文件,名字为singer_fragment_top_text_style.xml, <?xml version="1.0" encoding=" ...

  8. zeromq rpc原型

    /* Asynchronous request-reply single-threaded server in Python that spawns a request handler each ti ...

  9. POJ 3669 Meteor Shower【BFS】

    POJ 3669 去看流星雨,不料流星掉下来会砸毁上下左右中五个点.每个流星掉下的位置和时间都不同,求能否活命,如果能活命,最短的逃跑时间是多少? 思路:对流星雨排序,然后将地图的每个点的值设为该点最 ...

  10. WebStorm 2016.2 破解方法

    http://www.jetbrains.com/ 以前的 http://idea.lanyus.com/ 不能激活了 有帖子说的 http://15.idea.lanyus.com/ http:// ...