天天看點

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

雲栖号: https://yqh.aliyun.com 第一手的上雲資訊,不同行業精選的上雲企業案例庫,基于衆多成功案例萃取而成的最佳實踐,助力您上雲決策!
恕我直言,牛逼哄哄的MongoDB你可能隻會30%

自我介紹

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

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

MongoDB 最大的特點就是無 Schema 限制,靈活度很高。資料格式是 BSON,BSON 是一種類似 JSON 的二進制形式的存儲格式,簡稱 Binary JSON 它和 JSON 一樣,支援内嵌的文檔對象和數組對象。

跟關系型資料庫概念對比

Mysql MongoDB
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 的執行原理:

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

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

$project:修改輸入文檔的結構。可以用來重命名、增加或删除域,也可以用于建立計算結果以及嵌套文檔。

match:用于過濾資料,隻輸出符合條件的文檔。

match 使用 MongoDB 的标準查詢操作。

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

$skip:在聚合管道中跳過指定數量的文檔,并傳回餘下的文檔。

$group:将集合中的文檔分組,可用于統計結果。

$sort:将輸入文檔排序後輸出。

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

$unwind:将文檔中的某一個數組類型字段拆分成多條,每條包含數組中的一個值。

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

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

總共 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:仲裁節點本身不存儲資料,隻參與選舉。

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

Sharding

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

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

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

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你可能隻會30%

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

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

知識點總結

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

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

工作必用

MongoDB 跟 Mysql 的文法對比

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

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[1]

用戶端推薦

下載下傳位址:

https://studio3t.com/download/[2]
恕我直言,牛逼哄哄的MongoDB你可能隻會30%

spring-boot-starter-mongodb-pool

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

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

參考資料

[1] mongodb:

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

[2] studio3t:

https://studio3t.com/download/

[3] spring-boot-starter-mongodb-pool:

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

原文釋出時間:2020-01-18

本文作者:尹吉歡

本文來自阿裡雲雲栖号合作夥伴“

清風Python

”,了解相關資訊可以關注“