1. 程式人生 > 實用技巧 >在32位系統下使用MongoDB的一點心得

在32位系統下使用MongoDB的一點心得

本文出處:http://blog.csdn.net/chaijunkun/article/details/7236911,轉載請註明。由於本人不定期會整理相關博文,會對相應內容作出完善。因此強烈建議在原始出處檢視此文。

https://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中的自動分片演算法開始起作用:

C:\MongoDB\csdn>dir
 Volume in drive C has no label.
 Volume Serial Number is F474-EC39

 Directory of C:\MongoDB\csdn

2012-02-06  14:59    <DIR>          .
2012-02-06  14:59    <DIR>          ..
2012-02-06  14:40        16,777,216 csdn.0
2012-02-06  14:40        33,554,432 csdn.1
2012-02-06  14:41        67,108,864 csdn.2
2012-02-06  14:42       134,217,728 csdn.3
2012-02-06  14:45       268,435,456 csdn.4
2012-02-06  14:49       536,608,768 csdn.5
2012-02-06  14:59       536,608,768 csdn.6
2012-02-06  14:40        16,777,216 csdn.ns
               8 File(s)  1,610,088,448 bytes
               2 Dir(s)  20,669,665,280 bytes free

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中有如下一段話:

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

翻譯如下:

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位構建。