MongoDB


MongoDB 是一款開源的面向文檔的數據庫(document database), NoSQL 中一種,同樣使用文檔存儲實現 NoSQL 的 DB 還有 MarkLogic、OrientDB、CouchDB 等等。

安裝

Mac 用戶可以直接使用 Homebrew 安裝,命令如下:

$ sudo brew install mongodb

也可以自己到 MongoDB 的下載中心 下載對應的系統和版本,如果是 Linux 的話可以使用 wget 下載:

wget "https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.4.tgz"

並配置環境變量,如下:

$ export PATH={MONGODB_DIR}/bin:$PATH

啟動

# {mongo_db_file_path} 為指定的數據庫文件存放位置,不支持~符號。如果使用默認位置 /data/db ,也需要先手動創建。
$ mongod --dbpath={mongo_db_file_path} --bind_ip_all --fork --logpath ./mongo.log

終端連接

(1)本地連接

$ mongo

(2)遠程連接

$  mongo 172.2.0.3:27017

基本概念

BSON

MongoDB 的文件存儲格式為 BSON,所謂 BSON,即是 Binary JSON,為 JSON 文檔對象的二進制編碼格式,其擴展了 JSON 的數據類型,支持浮點數和整數的區分,支持日期類型,支持直接存儲 JS 的正則表達式,支持 32 位和 64 位數字的區分,支持直接存儲 JS 函數等等。看起來 BSON 對於 JS 還是挺友好的呵。

注意:從 Shell 終端中輸入的數值都會被存儲為 64 位浮點類型。

文檔(Document)

一個文檔就相當於關系型數據庫中的的概念,由多個鍵值有序組成,格式為 BSON。示例如下:

{ "_id" : ObjectId("5c05e74a65a27abc9a619f8a"), "a_key" : "a_value" }

其中 _id 是系統自動生成的鍵,當然也可以在創建時自定義值。

集合(Collection)

一個集合就相當於關系型數據庫中的的概念,由多個文檔組成。集合中的默認索引為 _id ,可以新建其他鍵的索引來優化查詢,MongoDB 支持單字段索引(Single Field Index)、復合索引(Compound Index)以及多鍵索引(Multikey Index)等等,可以根據需求進行選用。

數據庫(Database)

多個集合組成一個數據庫,不同的數據庫之間文件是隔離的。單個 MongoDB 實例可以容納多個獨立數據庫。默認系統存在以下的保留數據庫:

  1. admin:用戶權限相關
  2. local:存儲限於本地的集合
  3. config:分片配置相關

Shell 操作

database 級別

# 列出所有的數據庫
> show dbs

# 查看當前使用的數據庫
> db

# 切換當前使用的數據庫
> use a_db

# 創建數據庫
> use new_db

# 刪除數據庫
> db.dropDatabase()

collection 級別

# 顯示數據庫中的所有 collection
> show collections

# 列出 collection 中的所有列
> db.a_collection.find()

# 刪除 collection
> db.a_collection.drop()

# 新建 collection
> db.createCollection("new_collection")

# 重命名 collection
> db.a_collection.renameCollection("new_name")

# 清空 collection 中數據
> db.a_collection.drop({})

docuement 級別

# 插入文檔
> db.a_collection.insert({
    "a_key": "a_value",
    "b_key": 100,
    "c_key": true
})

# 列出所有文檔,並美化
> db.a_collection.find().pretty()

# 查詢記錄條數
> db.a_collection.find().count()

# 略過前100條
> db.a_collection.find().skip(100)

# MongoDB AND 且過濾器
> db.a_collection.find({
    "a_key": "a_value",
    "c_key": true
})
# MongoDB OR 或過濾器
> db.a_collection.find({
   $or:[
       { "a_key": "a_value"},
       { "a_key": "another_value" }
   ]
})
# MongoDB 投影,只返回指定的字段
> db.a_collection.find({},{"a_key", "c_key"})
# 查詢存在某字段的文檔
> db.a_collection.find({"a_key",{"$exists":true}})


# 更新單個文檔
db.a_collection.update({"a_key": "a_value"},{$set:{"a_key": "another_value"}})
# 更新多個文檔
db.a_collection.update({"a_key": "a_value"},{$set:{"a_key": "another_value"}},,{multi: true})

# 刪除文檔
db.a_collection.remove({"a_key": "a_value"})

# 后台執行創建單一復合索引操作
db.a_collection.createIndex({"a_key": 1,"c_key": -1},{unique: true,background: true})
# 查詢所有索引
db.a_collection.getIndexes()
# 刪除索引
db.a_collection.dropIndex({"a_key":1})

Java 操作

模塊划分

  1. bson:高性能的編碼解碼。
  2. mongodb-driver-core:核心庫,抽取出來主要是用於自定義 API。
  3. mongodb-driver-legacy:兼容舊的 API 的同步 Java Driver。
  4. mongodb-driver-sync:只包含 MongoCollection 泛型接口,服從一套新的跨 Driver 的 CRUD 規范。
  5. mongodb-driver:mongodb-driver-legacy + mongodb-driver-sync,新項目推薦使用它!
  6. mongodb-driver-async:新的異步 API,充分利用 Netty 或者 Java7 的 AsynchronousSocketChannel 已達到快而非阻塞的 IO。
  7. mongo-java-driver(uber-jar):包含 bson, mongodb-driver-core 和 mongodb-driver。

引入依賴

dependencies {
    compile 'org.mongodb:mongodb-driver-sync:3.9.1'
}

在 v3.6.4 使用 MongoURI

String mongoUri = ConfigManager.getInstance().getString(DistributedConfig.MONGODB_URI);
ConnectionString connectionString = new ConnectionString(mongoUri);
CodecRegistry pojoCodecRegistry = fromRegistries(MongoClient.getDefaultCodecRegistry(),
                                                 fromProviders(PojoCodecProvider.builder()
                                                               .automatic(true)
                                                               .build()));
MongoClient mongoClient = new MongoClient(new MongoClientURI(mongoUri,
                                                             MongoClientOptions.builder()
                                                             .codecRegistry(
                                                                 pojoCodecRegistry)));
String database = connectionString.getDatabase();
if (Strings.isNullOrEmpty(database)) {
    database = "my_db";
}
MongoDatabase mongoDatabase = mongoClient.getDatabase(database);

在 v3.9.1 使用 MongoURI

public class Mongo {

  private MongoDatabase mongoDatabase;

  private Mongo() {
    String mongoUri = ConfigManager.getInstance().getString(DistributedConfig.MONGODB_URI);
    ConnectionString connectionString = new ConnectionString(mongoUri);
    CodecRegistry pojoCodecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(),
                                                     fromProviders(PojoCodecProvider.builder()
                                                                                    .automatic(true)
                                                                                    .build()));
    MongoClientSettings settings = MongoClientSettings.builder()
                                                      .applyConnectionString(connectionString)
                                                      .codecRegistry(pojoCodecRegistry)
                                                      .build();
    MongoClient mongoClient = MongoClients.create(settings);
    String database = connectionString.getDatabase();
    if (Strings.isNullOrEmpty(database)) {
      database = "my_db";
    }
   MongoDatabase mongoDatabase = mongoClient.getDatabase(database);
  }

  public <T> MongoCollection<T> getCollection(Class<T> documentClass) {
    return mongoDatabase.getCollection(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE,
                                                                 documentClass.getSimpleName()),
                                       documentClass);
  }

}

事務支持

MongoDB 的事務支持始於 MongoDB 4.0,對應 Java Driver 版本為 3.8.0,對應 Python 版本為 3.7.0,詳情閱讀 Transactions and MongoDB Drivers - mongodb.com.

void runTransactionWithRetry(Runnable transactional) {
    while (true) {
        try {
            transactional.run();
            break;
        } catch (MongoException e) {
            System.out.println("Transaction aborted. Caught exception during transaction.");

            if (e.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL)) {
                System.out.println("TransientTransactionError, aborting transaction and retrying ...");
                continue;
            } else {
                throw e;
            }
        }
    }
}

void commitWithRetry(ClientSession clientSession) {
    while (true) {
        try {
            clientSession.commitTransaction();
            System.out.println("Transaction committed");
            break;
        } catch (MongoException e) {
            // can retry commit
            if (e.hasErrorLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)) {
                System.out.println("UnknownTransactionCommitResult, retrying commit operation ...");
                continue;
            } else {
                System.out.println("Exception during commit ...");
                throw e;
            }
        }
    }
}

void updateEmployeeInfo() {
    MongoCollection<Document> employeesCollection = client.getDatabase("hr").getCollection("employees");
    MongoCollection<Document> eventsCollection = client.getDatabase("hr").getCollection("events");

    try (ClientSession clientSession = client.startSession()) {
        clientSession.startTransaction();

        employeesCollection.updateOne(clientSession,
                Filters.eq("employee", 3),
                Updates.set("status", "Inactive"));
        eventsCollection.insertOne(clientSession,
                new Document("employee", 3).append("status", new Document("new", "Inactive").append("old", "Active")));

        commitWithRetry(clientSession);
    }
}


void updateEmployeeInfoWithRetry() {
    runTransactionWithRetry(this::updateEmployeeInfo);
}

備份與還原

使用 Mongo 安裝包 bin 目錄下的 mongodump 進行備份,mongorestore 進行還原。

備份

mongodump -h IP --port 端口 -u 用戶名 -p 密碼 -d 數據庫 -o 文件存在路徑 

還原

mongorestore -h IP --port 端口 -u 用戶名 -p 密碼 -d 數據庫 --drop 文件存在路徑

當為還原 bson 文件為

mongorestore -h IP --port 端口 -u 用戶名 -p 密碼 -d 數據庫 --drop bson文件路徑 -d 表名

數據庫遷移

db.copyDatabase("db_to_rename","db_renamed","localhost")

Q&A

一個服務中該使用一個還是多個 MongoClient?

通常一個服務應使用一個全局的 MongoClient,並且 MongoClient 中已經實現了一個連接池,最大值默認為 1000000 的連接限制,這相當於沒有限制。

參考:為什么 MongoDB 連接數被用滿了? - mongoing.com

Invalid BSON field name id

更新文檔時出現該錯誤,原因是使用了 updateOne 但是沒有 $set 字段,改為使用 replaceOne 就不用這么麻煩了。

readString can only be called when CurrentBSONType is STRING, not when CurrentBSONType is OBJECT_ID

給名為 id 的字段添加注解 @BsonProperty("id") 即可。

MongoWaitQueueFullException

錯誤日志:

com.mongodb.MongoWaitQueueFullException: Too many threads are already waiting for a connection. Max number of threads (maxWaitQueueSize) o
f 500 has been exceeded.
	at com.mongodb.internal.connection.DefaultConnectionPool.createWaitQueueFullException(DefaultConnectionPool.java:280)
	at com.mongodb.internal.connection.DefaultConnectionPool.get(DefaultConnectionPool.java:99)
	at com.mongodb.internal.connection.DefaultConnectionPool.get(DefaultConnectionPool.java:92)
	at com.mongodb.internal.connection.DefaultServer.getConnection(DefaultServer.java:85)
	at com.mongodb.binding.ClusterBinding$ClusterBindingConnectionSource.getConnection(ClusterBinding.java:115)
	at com.mongodb.operation.OperationHelper.withReleasableConnection(OperationHelper.java:424)
	at com.mongodb.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.java:192)
	at com.mongodb.operation.MixedBulkWriteOperation.execute(MixedBulkWriteOperation.java:67)
	at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.execute(MongoClientDelegate.java:193)
	at com.mongodb.client.internal.MongoCollectionImpl.executeBulkWrite(MongoCollectionImpl.java:467)
	at com.mongodb.client.internal.MongoCollectionImpl.bulkWrite(MongoCollectionImpl.java:447)
	at com.mongodb.client.internal.MongoCollectionImpl.bulkWrite(MongoCollectionImpl.java:442)

參考

  1. MongoDB Driver Quick Start - mongoDB
  2. MongoDB學習(二):數據類型和基本概念 - Hejin.Wang
  3. MongoDB索引原理 - mongoing.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM