本文出处:http://blog.csdn.net/chaijunkun/article/details/7236911,转载请注明。

随着互联网的变革,互联网的内容生成方式也逐渐地从网站生成转为用户生成。这种变化不仅仅是内容生成对象的转变那样简单的问题,随之带来的就是互联网数据的大爆炸(big bang)。社交网络的兴起也给互联网相关技术提出了挑战。

MongoDB应用广泛,作为一款无关系型数据库产品,它具有伸缩性、高性能、开源、模式开放和基于文档等特性。因此很值得研究。

通过本文,我将与你分享:

1. MongoDB如何申请磁盘空间,采用何种策略申请

2. 印证网上流传的32位平台下MongoDB数据库不能大于2GB的说法

既然MongoDB擅长的是海量数据处理,对它进行研究避免不了使用庞大的数据来做测试。好了,问题来了——上哪找庞大的数据呢?

公司里当然有那么多数据了,可是由于保密方面的要求,不能拿来做测试,更不能写出来。因此我想到了一个好东西——CSDN密码库。

这个200+M的小家伙在年前闹得沸沸扬扬,弄得人人自危,掀起了一场改密码风暴,反正也被公布出来了,拿它来做测试不是很好么?

至于从哪里得到的这个密码库,我就不说了,身为ITer的你一定有办法搞到手的。我的这个版本一共有6428632条数据,每条数据的结构都很简单:

用户名 # 密码 # 邮箱

分析的时候只需要一行行地读出来,然后按照“#”分割,最后对每一个字段都trim一下就可以了。

我做本次实验使用的平台如下:

Windows XP SP3(当然是32位版啦)

奔腾E5300 CPU

2G内存

首先按照上一篇文章建立了本地的MongoDB服务(文章链接:http://blog.csdn.net/chaijunkun/article/details/7227967)

然后使用MongoDB-Driver操作MongoDB,使用Morphia做ORM。

下面是我写的数据迁移代码(从密码库txt文件存储至MongoDB)

CSDNData.java

  1. package net.csdn.blog.chaijunkun.entities;
  2. import org.bson.types.ObjectId;
  3. import com.google.code.morphia.annotations.Entity;
  4. import com.google.code.morphia.annotations.Id;
  5. @Entity(value="users", noClassnameStored=true)
  6. public class CSDNData {
  7. @Id
  8. private ObjectId id;
  9. private Integer idx;
  10. private String userName;
  11. private String password;
  12. private String email;
  13. public ObjectId getId() {
  14. return id;
  15. }
  16. public void setId(ObjectId id) {
  17. this.id = id;
  18. }
  19. public Integer getIdx() {
  20. return idx;
  21. }
  22. public void setIdx(Integer idx) {
  23. this.idx = idx;
  24. }
  25. public String getUserName() {
  26. return userName;
  27. }
  28. public void setUserName(String userName) {
  29. this.userName = userName;
  30. }
  31. public String getPassword() {
  32. return password;
  33. }
  34. public void setPassword(String password) {
  35. this.password = password;
  36. }
  37. public String getEmail() {
  38. return email;
  39. }
  40. public void setEmail(String email) {
  41. this.email = email;
  42. }
  43. }

在实体中我增加了用于标识文档序号的idx字段。

接下来就是数据转储代码了:

TransformData.java

  1. package net.csdn.blog.chaijunkun;
  2. import java.net.UnknownHostException;
  3. import net.csdn.blog.chaijunkun.entities.CSDNData;
  4. import com.google.code.morphia.Datastore;
  5. import com.google.code.morphia.Key;
  6. import com.google.code.morphia.Morphia;
  7. import com.mongodb.Mongo;
  8. import com.mongodb.MongoException;
  9. import java.io.BufferedReader;
  10. import java.io.File;
  11. import java.io.FileNotFoundException;
  12. import java.io.FileReader;
  13. import java.io.IOException;
  14. import java.io.UnsupportedEncodingException;
  15. public class TransformData {
  16. public static void main(String[] args) throws UnknownHostException, MongoException{
  17. Mongo connection= new Mongo("localhost", 27017);
  18. Morphia morphia= new Morphia();
  19. Datastore ds= morphia.createDatastore(connection, "csdn");
  20. File dataFile= new File("D:\\www.csdn.net.txt");
  21. FileReader fr;
  22. Integer idx=1;
  23. try {
  24. fr = new FileReader(dataFile);
  25. BufferedReader br=new BufferedReader(fr);
  26. String currentLine= null;
  27. try {
  28. while((currentLine= br.readLine())!=null){
  29. //读取操作
  30. if (currentLine.trim().equals("")){
  31. continue;
  32. }
  33. String[] record= currentLine.split("#");
  34. if (record.length>=3){
  35. CSDNData csdnData= new CSDNData();
  36. csdnData.setIdx(idx);
  37. csdnData.setUserName(record[0].trim());
  38. csdnData.setPassword(record[1].trim());
  39. csdnData.setEmail(record[2].trim());
  40. Key<CSDNData> key= ds.save(csdnData);
  41. System.out.println("已存入:"+ key.getId() + ",文档序列:"+ idx);
  42. }else{
  43. System.out.println("文档序列"+ idx+ "发生错误:"+currentLine);
  44. break;
  45. }
  46. idx++;
  47. }
  48. } catch (UnsupportedEncodingException e) {
  49. // TODO Auto-generated catch block
  50. e.printStackTrace();
  51. } catch (IOException e) {
  52. // TODO Auto-generated catch block
  53. e.printStackTrace();
  54. }
  55. } catch (FileNotFoundException e) {
  56. // TODO Auto-generated catch block
  57. e.printStackTrace();
  58. }
  59. }
  60. }

就这样运行程序,密码库中的数据就开始向MongoDB中传送了。

由于我启动MongoDB服务时使用了参数--directoryperdb,因此会在数据目录下建立csdn目录。

随着数据越来越多,MongoDB中的自动分片算法开始起作用:

  1. C:\MongoDB\csdn>dir
  2. Volume in drive C has no label.
  3. Volume Serial Number is F474-EC39
  4. Directory of C:\MongoDB\csdn
  5. 2012-02-06  14:59    <DIR>          .
  6. 2012-02-06  14:59    <DIR>          ..
  7. 2012-02-06  14:40        16,777,216 csdn.0
  8. 2012-02-06  14:40        33,554,432 csdn.1
  9. 2012-02-06  14:41        67,108,864 csdn.2
  10. 2012-02-06  14:42       134,217,728 csdn.3
  11. 2012-02-06  14:45       268,435,456 csdn.4
  12. 2012-02-06  14:49       536,608,768 csdn.5
  13. 2012-02-06  14:59       536,608,768 csdn.6
  14. 2012-02-06  14:40        16,777,216 csdn.ns
  15. 8 File(s)  1,610,088,448 bytes
  16. 2 Dir(s)  20,669,665,280 bytes free
  17. C:\MongoDB\csdn>

最早生成的文件分别是csdn.0和csdn.ns,当0分片存储满后建立1分片(csdn.1文件),可以仔细观察其文件大小

csdn.ns==>  16MB
csdn.0  ==>  16MB
csdn.1  ==>  32MB
csdn.2  ==>  64MB
csdn.3  ==>128MB
csdn.4  ==>256MB
csdn.5  ==>512MB

但是在第7个分片(csdn.6)时发生了问题,第7分片目前大小是512MB,和第6分片大小一致。这如何解释呢?

其实MongoDB在存储时并不是按照实际数据量来严格申请磁盘空间。它会随着当前数据量的多少(说白了就是判断当前到第几个分片了)来动态申请空间,一次申请多少完全取决于前一分片的大小。

例如csdn.0文件默认是16MB,当csdn.1刚出现时,它的大小也是16MB,也就是第一次申请磁盘空间16MB,随后16MB装满了,而该分片就会再次申请16MB空间。

总而言之就是当前分片所占的最大空间将由2次申请磁盘空间来实现的(第一分片除外,固定16MB),而每一次申请的大小都是前一分片的最大尺寸。

为什么在第7分片发生了问题呢?这个就是我要说的下一个问题了:

在MongoDB的README中有如下一段话:

  1. MongoDB uses memory mapped files.  If built as a 32 bit executable, you will
  2. not be able to work with large (multi-gigabyte) databases.  However, 32 bit
  3. builds work fine with small development databases.

翻译如下:

  1. MongoDB使用内存映射文件,如果构建成32位可执行程序,您将不能使其工作在大数据库(若干GB)方式下

我们来计算一下:

csdn.ns(16MB)+csdn.0(16MB)+csdn.1(32MB)+csdn.2(64MB)+csdn.3(128MB)+csdn.4(256MB)+csdn.5(512MB)+csdn.6(512MB)=1536MB,

也就是说在发生问题之前数据库的总大小已经到达了1536MB。这时候由于存储的需求,按照算法,将再次申请512MB空间(csdn.5的大小)给csdn.6。

试想一下,如果申请成功了,数据库的文件将达到多少?1536MB+512MB=2048,正好等于2GB。这与网上的”32位平台下,MongoDB最大不能超过2GB“的说法一致。

那么我是怎么知道出问题了呢?

很简单,代码在传送了5790004条记录后当要写入第5790005条记录时抛出了异常:

  1. 已存入:4f2f7a4e5a2768e3dced269b,文档序列:5790001
  2. 已存入:4f2f7a4e5a2768e3dced269c,文档序列:5790002
  3. 已存入:4f2f7a4e5a2768e3dced269d,文档序列:5790003
  4. 已存入:4f2f7a4e5a2768e3dced269e,文档序列:5790004
  5. Exception in thread "main" com.mongodb.MongoException: can't map file memory - mongo requires 64 bit build for larger datasets

根据异常信息我们也可以知道:无法映射内存文件,MongoDB为大数据集需要使用64位构建。

32位下操作mongodb心得的更多相关文章

  1. 32位Windows7上8G内存使用感受+xp 32位下使用8G内存 (转)

    32位Windows7上8G内存使用感受+xp 32位下使用8G内存 博客分类: Windows XPWindowsIE企业应用软件测试  我推荐做开发的朋友:赶快加入8G的行列吧....呵呵..超爽 ...

  2. VS2015配置内核WDK7600环境,32位下.

    VS2015配置内核WDK7600环境,32位下. 学习内核驱动的编写,就要会配置环境.不然总是用记事本编写.比较不方便. 环境配置如下. 1.首先下载WDK7600, 课堂资料代码中已经上传.链接: ...

  3. Ubuntu 12.04(32位)下PHP环境的搭建(LAMP)

    Ubuntu 12.04 32位 下默认安装为5.3.10  不是以下图文中的5.4 1.首先打开命令行,切换到root身份,获得最新的软件包 su root sudo apt-get install ...

  4. 【转】如何在Ubuntu11.10(32位)下编译Android4.0源码(图文)

    原文网址:http://blog.csdn.net/flydream0/article/details/7046612 关于如何下载Android4.0的源码请参考我的另一篇文章: http://bl ...

  5. 迁移32位下的旧代码到64位sever遇到过的两个很诡异的问题

    一个是GetHashCode,这个方法是返回一个int值,在32位系统里,都是正值,但在64位系统里会返回负值. 另一个问题是DataTable的Sort属性,在没有显示写明升序或降序的情况下,在32 ...

  6. Win7 32位下cocos2dx android开发调试环境

    1.使用环境 win7 32位 + vs2010 2.软件准备(下方绿色文字带链接) cocos2dx-v2.2.2 jdk7 android sdk android ndk adt bundle a ...

  7. windows7 64,32位下scrapy爬虫框架的环境搭建

    适用于python 2.7 64位安装 一.操作系统:WIN7 64位 二.python版本:2.7 64位(scrapy目前不支持3.x) 不确定位数的,看图 三.安装相关软件:(可以从我的百度网盘 ...

  8. windows32位下安装mongodb

    下载mongodb:http://downloads.mongodb.org/win32/mongodb-win32-i386-2.4.5.zip 给mongodb指定一个数据存放路径:这里我们放在m ...

  9. qt5.4.0在windows,32位下的编译, vs2010平台

    假设源代码入在D:\3DFrame\qt-everywhere-opensource-src-5.4.0\qt-everywhere-opensource-src-5.4.0 1:首先从官方网站下载源 ...

随机推荐

  1. vs nuget找不到包

    nuget.org https://api.nuget.org/v3/index.json

  2. 关于redis的几件小事(七)redis缓存雪崩与穿透

    1.缓存雪崩 (1)什么是缓存雪崩 缓存雪崩指的是在同一时刻,缓存大量失效,导致大量的请求直接到了数据库,数据库压力剧增,引起系统崩溃.可能出现的情况有: ①大量的key设置了相同的过期时间,导致在缓 ...

  3. 【Java面试题】解释内存中的栈(stack)、堆(heap)和静态存储区的用法

    Java面试题:解释内存中的栈(stack).堆(heap)和静态存储区的用法 堆区: 专门用来保存对象的实例(new 创建的对象和数组),实际上也只是保存对象实例的属性值,属性的类型和对象本身的类型 ...

  4. Ubuntu - 14.04下,GO语言的安装!

    一,下载GO语言的安装文件,我直接下载GO语言的安装包(64位),并不是源码: 下载地址: 64位:https://storage.googleapis.com/golang/go1.6.linux- ...

  5. 转载:Linux目录文件的权限查看与修改

    ########################################                                                             ...

  6. springboot访问出错,mapperScan导包错误java.lang.NoSuchMethodException: tk.mybatis.mapper.provider.base.BaseSelectProvider.<init>() at java.lang.Class.getConstructor0(Class.java:3082) ~[na:1.8.0_172] at java.

    2019-08-06 12:42:03.153 ERROR 10080 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Se ...

  7. js 继承的简单理解

    什么是继承 js中的继承就是获取存在对象已有属性和方法的一种方式. 继承一 属性拷贝 就是将对象的成员复制一份给需要继承的对象. // 创建父对象 var superObj = { name:'liy ...

  8. python、mysql三-1:存储引擎

    一 什么是存储引擎 mysql中建立的库===>文件夹 库中建立的表===>文件 现实生活中我们用来存储数据的文件有不同的类型,每种文件类型对应各自不同的处理机制:比如处理文本用txt类型 ...

  9. linux pip使用国内源

    最近在Linux里面使用pip安装应用的速度十分的慢,于是便上网找了一些国内的源. 清华大学:https://pypi.tuna.tsinghua.edu.cn/simple 阿里云:https:// ...

  10. mysql向表中某字段前后追加一段字符串 concat(), trim(), ltrim(), rtrim(), repeat()

    1.mysql向表中某字段后面追加一段字符串:update table_name set field=CONCAT(field, '分隔符', str);//'分隔符',可以为空,也可以省略updat ...