1. 程式人生 > >恕我直言,牛逼哄哄的MongoDB你可能只會30%

恕我直言,牛逼哄哄的MongoDB你可能只會30%

MongoDB閃亮登場

自我介紹

MongoDB 是一個基於分散式檔案儲存的資料庫。由 C++ 語言編寫。旨在為 WEB 應用提供可擴充套件的高效能資料儲存解決方案。

MongoDB 是一個介於關係資料庫和非關係資料庫之間的產品,是非關係資料庫當中功能最豐富,最像關係資料庫的。

MongoDB最大的特點就是無Schema限制,靈活度很高。資料格式是BSON,BSON是一種類似JSON的二進位制形式的儲存格式,簡稱Binary JSON 它和JSON一樣,支援內嵌的文件物件和陣列物件。

跟關係型資料庫概念對比

Mysql MongoDB
Database(資料庫) Database(資料庫)
Table(表) Collection(集合)
Row(行) Document(文件)
Column(列) Field(欄位)

資料格式

MongoDB 將資料儲存為一個文件,BSON格式。由key 和 value組成。

{ 
    "_id" : ObjectId("5e141148473cce6a9ef349c7"),
    "title" : "批量更新", 
    "url" : "http://cxytiandi.com/blog/detail/8", 
    "author" : "yinjihuan", 
    "tags" : [
        "java", 
        "mongodb", 
        "spring"
    ], 
    "visit_count" : NumberLong(10), 
    "add_time" : ISODate("2019-02-11T07:10:32.936+0000")
}

使用場景

  • 大資料量儲存場景

MongoDB自帶副本集和分片,天生就適用於大數量場景,無需開發人員通過中介軟體去分庫分表,非常方便。

  • 操作日誌儲存

很多時候,我們需要儲存一些操作日誌,可能只需要儲存比如最近一個月的,一般的做法是定期去清理,在MongoDB中有固定集合的概念,我們在建立集合的時候可以指定大小,當資料量超過大小的時候會自動移除掉老資料。

  • 爬蟲資料儲存

爬下來的資料有網頁,也有Json格式的資料,一般都會按照表的格式去儲存,如果我們用了MongoDB就可以將抓下來的Json資料直接存入集合中,無格式限制。

  • 社交資料儲存

在社交場景中使用 MongoDB 儲存儲存使用者地址位置資訊,通過地理位置索引實現附近的人,附近的地點等。

  • 電商商品儲存

不同的商品有不同的屬性,常見的做法是抽出公共的屬性表,然後和SPU進行關聯,如果用MongoDB的話那麼SPU中直接就可以內嵌屬性。

自我陶醉

MongoDB的功能點很多,但是大部分場景下我們只用了最簡單的CRUD操作。下面隆重的介紹下MongoDB的功能點,就像你去相親一樣,不好好介紹自己的優點又怎能讓你對面的菇涼心動呢?

CRUD

CRUD也就是增刪改查,這是資料庫最基本的功能,查詢還支援全文檢索,GEO地理位置查詢等。

  • db.collection.insertOne()   

單個文件插入到集合中

  • db.collection.insertMany() 

多個文件插入到集合中

  • db.collection.insert() 

單個或者多個檔案插入到集合中

  • db.collection.find( )

查詢資料

  • db.inventory.updateOne()

更新單條

  • db.inventory.updateMany()

更新多條

  • db.inventory.deleteOne( )

刪除單條文件

  • db.inventory.deleteMany()

刪除多條文件

Aggregation

聚合操作用於資料統計方面,比如Mysql中會有count,sum,group by等功能,在MongoDB中相對應的就是Aggregation聚合操作。

聚合下面有兩種方式來實現我們需要對資料進行統計的需求,一個是aggregate,一個是MapReduce。

下圖展示了aggregate的執行原理:

聚合內建了很多函式,使用好了這些函式我們就可以統計出我們想要的資料。

$project:修改輸入文件的結構。可以用來重新命名、增加或刪除域,也可以用於建立計算結果以及巢狀文件。

$match:用於過濾資料,只輸出符合條件的文件。$match使用MongoDB的標準查詢操作。

$limit:用來限制MongoDB聚合管道返回的文件數。

$skip:在聚合管道中跳過指定數量的文件,並返回餘下的文件。

$group:將集合中的文件分組,可用於統計結果。

$sort:將輸入文件排序後輸出。

$geoNear:輸出接近某一地理位置的有序文件。

$unwind:將文件中的某一個數組型別欄位拆分成多條,每條包含陣列中的一個值。

下圖展示了MapReduce的執行原理:

總共4條資料,query指定了查詢條件,只處理status=A的資料。

map階段對資料進行分組聚合,也就是形成了第三部分的效果,根據cust_id去重統計。

reduce中的key也就是cust_id, values也就是彙總的amount集合。然後進行sum操作,最終的結果通過out輸出到一個集合中。

Transactions

MongoDB最開始是不支援事務的,在MongoDB中,對單個文件的操作是原子性操作。所以再設計的時候可以使用嵌入的文件和陣列來描述資料之間的關係,這樣就不用跨多個文件和集合進行操作,也就通過了單文件原子性消除了許多實際用例對多文件事務的需要。

任何事物都是有限制的,某些場景還是不能完全通過內嵌的方式來描述資料的關係,還是會存在多個集合,對於使用MongoDB的使用者來說,如果能支援事務就很方便了。

不負眾望,MongoDB 4.0 版本的釋出,為我們帶來了原生的事務操作。

Indexes

索引不用我多說了,作用大家都知道。單索引,組合索引,全文索引,Hash索引等。

db.collection.createIndex({user_id: 1, add_time: 1}, {background: true})

建立索引特別要注意的是將background設定為true,在建索引的過程會阻塞其它資料庫操作,background可指定以後臺方式建立索引,預設為false。這可是血的教訓呀,切記切記。

Security

MongoDB中的安全需要重視,目前啟動不知道有沒有強制的限制,以前啟動的時候可以不指定認證的方式,也就是不需要密碼即可訪問,然後很多人都直接用的預設埠,暴露在公網上,給不法分子有機可乘,出現了資料被刪,需要用比特幣來找回資料的案例比比皆是。

還是要開啟安全認證,內建了很多角色,不同的角色可操作的內容不一樣,控制的比較細。

Replication

副本集是一組相同資料集的MongoDB例項,同時在多個節點儲存資料,提高了可用性。主節點負責寫入,從節點負責讀取,提高整體效能。

副本集由下面的元件構成:

Primary:主節點接收所有的寫操作。

Secondaries:從節點會從主節點進行資料的複製,維護跟主節點相同的資料。用於查詢操作。

Arbiter:仲裁節點本身不儲存資料,只參與選舉。

Sharding

分片是MongoDB絕對的亮點,將資料水平拆分到多個節點。MongoDB的分片是全自動的,我們只需要配置好分片的規則,它就能自動維護資料並存儲到不同節點。MongoDB使用分片來支援大資料量的儲存和高吞吐量的操作。

下圖是Mongodb的分片叢集架構圖:

MongoDB分片叢集由以下元件夠成:

Shard:每個shard的資料都是獨立完整的一份。並且可以作為副本集部署。

mongos:mongos是查詢路由器,在客戶端和服務端中間的一層,請求會直接到mongos,由mongos路由到具體的Shard。

Config Servers:儲存叢集所有節點、分片資料路由資訊。

GridFS

GridFS是MongoDB的一個子模組,主要用於在MongoDB中儲存檔案,相當於MongoDB內建的一個分散式檔案系統。

本質上還是講檔案的資料分塊儲存在集合中,預設的檔案集合分為fs.files和fs.chunks。

fs.files是儲存檔案的基本資訊,比如檔名,大小,上傳時間,md5等。fs.chunks是儲存檔案真正資料的地方,一個檔案會被分割成多個chunk塊進行儲存,一般為256k/個。

如果你的專案中用到了MongoDB,那麼你可以使用GridFS來構建一個檔案系統,這樣就不用去購買第三方的儲存服務了。

GridFS的好處是你不用單獨去搭建一個檔案系統,直接使用Mongodb自帶的即可,備份,分片都依賴MongoDB,維護起來也方便。

知識點總結

下圖是我自己總結的一些知識點,作為一個後端開發來說,能掌握下面的內容就已經不錯了,畢竟我們又不是要去搶DBA的飯碗,如果大家業餘時間要學習的話可以按照下面的點進行學習,幾年前我錄製了一套視訊,在我的網站上,大部分內容都覆蓋到了。

工作必用

MongoDB跟Mysql的語法對比


Spring Boot中整合MongoDB

加入MongoDB的依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

配置MongoDB的資訊:

spring.data.mongodb.database=test
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
// 使用者名稱,密碼省略.......

直接注入MongoTemplate就可以操作MongoDB:

@Autowired
private MongoTemplate mongoTemplate;

使用示列

建立一個實體類,對應MongoDB的集合

@Data
@Document(collection = "article_info")
public class Article {
    @Id
    @GeneratedValue
    private Long id;
    @Field("title")
    private String title;
    @Field("url")
    private String url;
    @Field("author")
    private String author;
    @Field("tags")
    private List<String> tags;
    @Field("visit_count")
    private Long visitCount;
    @Field("add_time")
    private Date addTime;
}

最終儲存到資料中的格式如下:

{ 
    "_id" : ObjectId("5e141148473cce6a9ef349c7"),
    "title" : "批量更新", 
    "url" : "http://cxytiandi.com/blog/detail/8", 
    "author" : "yinjihuan", 
    "tags" : [
        "java", 
        "mongodb", 
        "spring"
    ], 
    "visit_count" : NumberLong(10), 
    "add_time" : ISODate("2019-02-11T07:10:32.936+0000")
}

插入資料

Article article = new Article();
article.setTitle("MongoTemplate 的基本使用 ");
article.setAuthor("yinjihuan");
article.setUrl("http://cxytiandi.com/blog/detail/1");
article.setTags(Arrays.asList("java", "mongodb", "spring"));
article.setVisitCount(0L);
article.setAddTime(new Date());
mongoTemplate.save(article);

資料庫語法

db.article_info.save({
    "title": "批量更新",
    "url": "http://cxytiandi.com/blog/detail/8",
    "author": "yinjihuan",
    "tags": [
        "java",
        "mongodb",
        "spring"
    ],
    "visit_count": NumberLong(10),
    "add_time": ISODate("2019-02-11T07:10:32.936+0000")
})

更新資料

Query query = Query.query(Criteria.where("author").is("yinjihuan")); 
Update update = Update.update("title", "MongoTemplate")
                .set("visitCount", 10); 
mongoTemplate.updateMulti(query, update, Article.class);

資料庫語法

db.article_info.updateMany(
    {"author":"yinjihuan"}, 
    {"$set":
       {
         "title":"MongoTemplate", 
         "visit_count": NumberLong(10)
       }
    }
)

刪除資料

Query query = Query.query(Criteria.where("author").is("yinjihuan")); 
mongoTemplate.remove(query, Article.class);

資料庫語法

db.article_info.remove({"author":"yinjihuan"})

查詢資料

Query query = Query.query(Criteria.where("author").is("yinjihuan")); 
List<Article> articles = mongoTemplate.find(query, Article.class);

資料庫語法

db.article_info.find({"author":"yinjihuan"})

儲存檔案

File file = new File("/Users/yinjihuan/Downloads/logo.png");
InputStream content = new FileInputStream(file);
// 儲存檔案的額外資訊,比如使用者ID,後面要查詢某個使用者的所有檔案時就可以直接查詢
DBObject metadata = new BasicDBObject("userId", "1001");
ObjectId fileId = gridFsTemplate.store(content, file.getName(), "image/png", metadata);

原始碼參考

https://github.com/yinjihuan/spring-cloud/tree/master/Spring-Cloud-Book-Code-2/ch-17/mongodb

客戶端推薦

下載地址:

https://studio3t.com/download/

spring-boot-starter-mongodb-pool

最後推薦一個我自己寫的小框架:Spring Boot中增強Mongodb的配置,多資料來源,連線池

https://github.com/yinjihuan/spring-boot-starter-mongodb-pool