MongDB學習筆記(一) 初遇篇

北冥有隻魚發表於2022-03-06

本週去見了高中同學, 還是蠻開心的。本來打算的是寫MySQL資料庫事務的實現或者MySQL優化系列的文章,但是還沒有想到如何組裝這些知識。

索性這周換個方向,寫寫NOSQL。考慮到有些同學對JSON還不是很瞭解,這裡我們先介紹MongDB的基本概念,再接著講述為什麼要引入MongDB,再講解該如何使用。

是什麼? what

MongoDB is a document database designed for ease of application development and scaling 《MongDB官網》

MongDB是一個為高擴充套件性、高可用性而設計的文件資料庫。

注意到MongDB是一個文件資料庫,文件資料庫與我們常見的關係型資料庫有什麼區別呢?我們首先來看這個文件,也就是document究竟是什麼含義?

MongoDB中的記錄是一個文件,它是由欄位和值對組成的資料結構。MongoDB文件類似於JSON物件。欄位的值可以包括其他文件,陣列和文件陣列。

MongDB Document

關於什麼是JSON? 參看我寫的文章: 傻傻弄不清楚的JSON?

所以MongDB的記錄是一個類似於一個JSON物件的文件,MongDB將文件儲存在集合中,集合類似於關聯式資料庫中的表。存取關係類似於下面的圖:

MongDB VS 關係型資料庫

上面我們提到MongDB的記錄是一個類似於JSON的文件,之所以是類似於,是因為MongDB採取的是BSON,BSON = Binary JSON, 也就是二進位制形式的JSON,但同時又擴充了JSON,具備JSON中不具備的資料型別,像日期型別(Date type)、二進位制資料型別(BinData type)。

為什麼要引入MongDB?

那為什麼要引入MongDB呢? 或者說MongDB相對於關係型資料庫有哪些優勢呢?

十年前,當 Dwight 和我開始這個後來成為 MongoDB 的專案的時候,我們絕對沒有想到它今天的樣子。我們只有一個信念:讓開發者更高效。MongoDB 誕生於在龐大複雜的業務部署中使用關係型資料庫給我們帶來的沮喪。我們著手建造一個我們自己想用的資料庫。這樣,每當開發者想寫一個應用時,他們可以專注於應用本身,而不用圍著資料庫轉。 MongoDB 的十年,一個創始人的回顧

所以就是在某些領域,MongDB可以讓開發者更高效。

知乎上有一個問題: MongoDB 等 NoSQL 與關係型資料庫相比,有什麼優缺點及適用場景?下面的有一個MongDB的開發者做了回答,這裡我簡單摘錄一下他的回答:

  • 文件(JSON)模型與物件導向的資料表達方式更相似更自然。與關係型資料庫中的表結構不同,文件(JSON)中可以嵌入陣列和子文件(JSON),就像程式中的陣列和成員變數一樣。這是關係型資料庫三正規化不允許。但實際中我們經常看到一對多和一對一的的資料。比如,一篇部落格文章的Tag列表作為文章的一部分非常直觀,而把Tag與文章的從屬關係單獨放一張表裡就不那麼自然。SQL語音可以很精確地形式化。然而三大正規化強調的資料沒有任何冗餘,並不是今天程式設計師們最關心的問題,他們用著方便不方便才是更重要的問題。
這裡我們就得出MongDB相對於關係型資料庫的第一個優勢,在某些場景下MongDB比關係型資料庫方便,注意是在某些場景下。
  • 效能

    三大正規化帶來的jojn,有的時候為了滿足一個查詢,不得不join多表,但join多表的代價是隨著資料的提升,查詢速度會變得很慢,更簡單的訪問模式可以讓開發者更容易理解資料庫的效能表現,舉一個典型的場景就是,我join了十張表,在這樣的情況下,我該如何加索引才能獲得最優對的查詢速度。

  • 靈活

    如果需要加欄位,從資料庫到應用層可能都需要改一遍,儘管有些工具可以把它自動化,但這仍然是一個複雜的工作。 MongDB沒有Schema,就不需要改動資料庫,只需要在應用層做必要的改動。(這句話可以這麼理解,仔細看上面畫的MongDB存取資料的圖,集合中現在存取的學生都有三個欄位,事實上MongDB允許文件擁有不同的欄位)

自定義屬性也是一個問題,比如LCD顯示器和LED顯示的產品特性就不一樣,資料的這種特性被稱為多型,放在關係型資料庫中,產品這個表應該怎麼設計? 一般來說最簡單的方案是將每個可能的屬性都變成單獨一列,但如果顯示器廠商每年推出一個新特性,那麼這個表就要經常的改動,擴充套件性不強。還有一種更加極端的場景是,一個通訊錄允許使用者隨意新增新的聯絡方式,一種方案是將這種自定義聯絡方式放在一列,用json儲存。

MongDB的靈活還體現在非結構化和半結構化的資料上, MongDB提供全文索引,也支援地理位置查詢和索引。例如一位使用者想知道方圓五公里哪裡有公共衛生間,這是「地理範圍查詢」。然後他搜尋最近的單車。摩拜單車正是使用 MongoDB 完成這樣的「距離排序查詢」。強烈建議使用地理位置索引加速查詢,因為我能理解這位使用者的心情。

  • 可擴充套件性

    MongDB原生自帶分片,對應的就是關係型資料庫的分庫分表。

  • 哪些場景下MongDB不適合
MongDB的開發者周思遠舉了一個場景,他在一個專案中需要地鐵列車時刻表,官方提供的是一個滿足行業標準的SQL格式的資料,也就是按照三大正規化分成了好幾張表。然後他花了一晚上把資料庫匯入到了MongDB中。但是他說如果再來一次,他可能會直接選取關係型資料庫,因為如果源資料格式就是SQL資料,沒法控制,資料量小;交叉引用關係豐富,查詢模式豐富,應用又不需要高效能,那麼關係型資料頁也是一個務實的選擇。

我對這句話的理解是站在建模應用資料的角度不同,在某些場景下,關係型資料庫提供的SQL在統計資料、查詢、分析方面仍然具備強大的優勢。

怎麼用?

有了,what和why之後,我們就可以開始用起來了,也就是開始安裝使用,比較幸運的是MongDB有個國人維護的中文操作手冊:

MongDB操作手冊

介紹的相當詳細,也有安裝教程,也有免費試用:

免費試用

所以對於我這種我選擇了面試試用,點進去,我選擇的開發場景是開發微服務,你也可以選擇在本地安裝,這篇手冊上的安裝教程寫的還是蠻詳細的,這裡我就放個地址,不做詳細介紹了。https://docs.mongoing.com/ins...

但是這種在雲端的資料庫,安裝MongDB的shell可能稍微比較麻煩一點,我在windows上安裝了好久感覺都沒成功,這裡可以用MongDB的GUI,下載地址如下:

https://downloads.mongodb.com...

MongDB GUI

增刪改查

會填JSON就行

Java 操縱MongDB

現在我們就嘗試用起來,我在初遇篇講的就是一些基本特性,CRUD之類的。 首先連線資料庫肯定需要驅動:

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.12.5</version>
</dependency>

增刪改查,跟資料庫打交道的基本步驟一般都是獲取連線,然後傳送語句,在MongDB的java驅動中,增刪改查都和Document、Bson這兩個類有關係:

Document的UML圖

Document的方法結構圖

我們新增物件的時候就是new Document, 像裡面丟值。Filter則用來構造條件物件,用於查詢:

Filters.ne 是不等於
Filters.gt 大於
Filters.and(Filters.eq("age",2222)),Filters.eq("name","aaa") 連等於
Filters.gte 大於等於
Filters.lt 小於
Filters.lte 小於等於
Filters.in()
Filters.nin() not in

Filters 本質上來說就是來幫你傳遞了操作符,我們藉助於Document也能實現也能實現一樣的效果:

 new Document("name","張三") = Filters.eq("name","張三")
 new Document("age",new Document("$eq",24))  = Filters.eq("name","張三")
 new Document("age",new Document("$ne",24))  = Filters.ne("name","張三")    

我們主要藉助於MongoCollection來完成對MongDB中的增刪查改,增主要和Document有關係。改 刪 查可以使用Filters和Document來實現。

@Test
void contextLoads() throws Exception {
    ConnectionString connectionString = new ConnectionString("mongodb+srv://study:a872455@cluster0.exlk2.mongodb.net/myFirstDatabase?retryWrites=true&w=majority");
    MongoClientSettings settings = MongoClientSettings.builder()
            .applyConnectionString(connectionString)
            .build();
    MongoClient mongoClient = MongoClients.create(settings);
    insertDocumentDemo(mongoClient);
}

public void insertDocumentDemo(MongoClient mongoClient) throws  Exception {
    MongoDatabase database = mongoClient.getDatabase("test");
    database.getCollection("study");
    MongoCollection<Document> study = database.getCollection("study");
    // study 這張表的存的有 number name id, 如果是關係型資料庫,我們要新增屬性,需要先在表中加欄位,
    // 現在 在MongDB中我們直接新增
    Map<String,Object> map = new HashMap<>();
    map.put("number","bbb");
    map.put("name","aaa");
    map.put("id","1111");
    map.put("age",2222);
    Document document = new Document(map);
    study.insertOne(document);
}

結果: 新增結果

public void deleteDocument(MongoClient mongoClient){
        MongoDatabase database = mongoClient.getDatabase("test");
        database.getCollection("study");
        MongoCollection<Document> study = database.getCollection("study");
        // eq 是等於運算子,Filters.eq("age",2222) 表示查出定位的是age = 2222的物件
        study.deleteOne(Filters.eq("age",2222));
}
  • public void updateDocument(MongoClient mongoClient){
            MongoDatabase database = mongoClient.getDatabase("test");
            database.getCollection("study");
            MongoCollection<Document> study = database.getCollection("study");
            study.updateOne(Filters.eq("age",2222),new Document("$set",new Document("name","22222")));
     }
public void selectDocument(MongoClient mongoClient){
        MongoDatabase dataBase = mongoClient.getDatabase("test");
        MongoCollection<Document> studyTable = dataBase.getCollection("study");
        // age 可以是一個陣列, 所有的值都得是2222
        Bson condition = Filters.all("age", 2222);
        FindIterable<Document> documentList = studyTable.find(condition);
}

最後總結一下

程式運算元據

某種程度上我們的程度都是從現實資料採取資料經過處理之後送入資料庫,然後根據使用者請求再將資料庫中的資料取出,但是現實世界的資料結構是形形色色的,關係型資料庫也有力不能逮的場景,像是我們在上文討論過的為為什麼引入MongDB,還有一種場景就是描述資料之間的聯絡,比如組織架構,人際關係圖,在資料量比較大的時候,關聯式資料庫就面臨著查詢速度緩慢的問題,為了描述這種非線性的關係,圖資料庫應運而生。不同的資料庫都是在應對不同的資料描述場景。

參考資料

相關文章