在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
- package net.csdn.blog.chaijunkun.entities;
- import org.bson.types.ObjectId;
- import com.google.code.morphia.annotations.Entity;
- import com.google.code.morphia.annotations.Id;
- public class CSDNData {
- private ObjectId id;
- private Integer idx;
- private String userName;
- private String password;
- private String email;
- public ObjectId getId() {
- return id;
- }
- public void setId(ObjectId id) {
- this.id = id;
- }
- public Integer getIdx() {
- return idx;
- }
- public void setIdx(Integer idx) {
- this.idx = idx;
- }
- public String getUserName() {
- return userName;
- }
- public void setUserName(String userName) {
- this.userName = userName;
- }
- public String getPassword() {
- return password;
- }
- public void setPassword(String password) {
- this.password = password;
- }
- public String getEmail() {
- return email;
- }
- public void setEmail(String email) {
- this.email = email;
- }
- }
在實體中我增加了用於標識文件序號的idx欄位。
接下來就是資料轉儲程式碼了:
TransformData.java
- package net.csdn.blog.chaijunkun;
- import java.net.UnknownHostException;
- import net.csdn.blog.chaijunkun.entities.CSDNData;
- import com.google.code.morphia.Datastore;
- import com.google.code.morphia.Key;
- import com.google.code.morphia.Morphia;
- import com.mongodb.Mongo;
- import com.mongodb.MongoException;
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.FileReader;
- import java.io.IOException;
- import java.io.UnsupportedEncodingException;
- public class TransformData {
- public static void main(String[] args) throws UnknownHostException, MongoException{
- Mongo connection= new Mongo("localhost", 27017);
- Morphia morphia= new Morphia();
- Datastore ds= morphia.createDatastore(connection, "csdn");
- File dataFile= new File("D:\\www.csdn.net.txt");
- FileReader fr;
- Integer idx=1;
- try {
- fr = new FileReader(dataFile);
- BufferedReader br=new BufferedReader(fr);
- String currentLine= null;
- try {
- while((currentLine= br.readLine())!=null){
- //讀取操作
- if (currentLine.trim().equals("")){
- continue;
- }
- String[] record= currentLine.split("#");
- if (record.length>=3){
- CSDNData csdnData= new CSDNData();
- csdnData.setIdx(idx);
- csdnData.setUserName(record[0].trim());
- csdnData.setPassword(record[1].trim());
- csdnData.setEmail(record[2].trim());
- Key<CSDNData> key= ds.save(csdnData);
- System.out.println("已存入:"+ key.getId() + ",文件序列:"+ idx);
- }else{
- System.out.println("文件序列"+ idx+ "發生錯誤:"+currentLine);
- break;
- }
- idx++;
- }
- } catch (UnsupportedEncodingException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
就這樣執行程式,密碼庫中的資料就開始向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條記錄時丟擲了異常:
- 已存入:4f2f7a4e5a2768e3dced269b,文件序列:5790001
- 已存入:4f2f7a4e5a2768e3dced269c,文件序列:5790002
- 已存入:4f2f7a4e5a2768e3dced269d,文件序列:5790003
- 已存入:4f2f7a4e5a2768e3dced269e,文件序列:5790004
- Exception in thread "main" com.mongodb.MongoException: can't map file memory - mongo requires 64 bit build for larger datasets
根據異常資訊我們也可以知道:無法對映記憶體檔案,MongoDB為大資料集需要使用64位構建。