MongoDB 是一個基於分佈式文件存儲的數據庫。由 C++ 語言編寫,通常生產上建議以共享分片的形式來部署。 可是MongoDB官方也提供了其它語言的客戶端操做API。以下圖所示:html
提供了C、C++、C#、.net、GO、java、Node.js、PHP、python、scala等各類語言的版本,以下圖所示:java
MongoDB的操做分爲同步操做和異步操做以及響應式編程操做
1、同步操做APIpython
官方JAVA API的路徑:https://docs.mongodb.com/ecosystem/drivers/java/ 咱們這裏以3.11的java 版本爲例。各個版本的API對MongoDB服務的支持狀況。mysql
使用API時,先引入maven依賴react
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver --> <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>3.11.1</version> </dependency>
一、關於MongoDB Client的初始化和關閉。linux
從官方介紹來看,通常建議Client只須要一個創建一個長鏈接實例,而後使用時,都使用這個實例就能夠,也就是能夠用java的單例模式來建立鏈接實例。git
//mongoClient鏈接 protected static MongoClient mongoClient; public synchronized static MongodbClient getInstance(String mongodbUrl) { if (null == mongoClient) { mongoClient = MongoClients.create(mongodbUrl); if(null != mongoClient){ log.info("mongoClient init success!"); } else{ log.info("mongoClient init failed!"); } } return mongodbClient; }
直接經過mongodb的host和port來建立client: github
MongoClient mongoClient = MongoClients.create("mongodb://host1:27017");
client鏈接到一個 Replica Set:算法
本文做者:張永清,轉載請註明出處:MongoDB Java API操做很全的整理以及共享分片模式下的常見操做整理sql
MongoClient mongoClient = MongoClients.create("mongodb://host1:27017,host2:27017,host3:27017"); MongoClient mongoClient = MongoClients.create("mongodb://host1:27017,host2:27017,host3:27017/?replicaSet=myReplicaSet");
或者經過MongoClientSettings.builder() 來輔助生成鏈接字符串來建立client:
MongoClient mongoClient = MongoClients.create( MongoClientSettings.builder() .applyToClusterSettings(builder -> builder.hosts(Arrays.asList( new ServerAddress("host1", 27017), new ServerAddress("host2", 27017), new ServerAddress("host3", 27017)))) .build());
鏈接關閉:
public void close() { if(null!=mongoClient){ mongoClient.close(); mongoClient=null; } }
2、關於MongoDB 的基本操做
//建立Collection
public void createCollection(String dataBaseName,String collectionName){ getDatabase(dataBaseName).createCollection(collectionName); }
//查詢dataBaseName
public MongoDatabase getDatabase(String dataBaseName){ return mongoClient.getDatabase(dataBaseName); }
//查詢Collection
public List<String> listCollectionNames(String dataBaseName){
List<String> stringList = new ArrayList<String>();
mongoClient.getDatabase(dataBaseName).listCollectionNames().forEach((Consumer<? super String>) t->{ stringList.add(t); });
return stringList; }
public MongoCollection<Document> getCollectionByName(String dataBaseName, String collectionName){ return getDatabase(dataBaseName).getCollection(collectionName); }
3、關於MongoDB 的查詢操做
//經過id(objectid)精確查詢 public FindIterable<Document> findMongoDbDocById(String dataBaseName, String collectionName, String id){ BasicDBObject searchDoc = new BasicDBObject().append("_id", id); return getCollectionByName(dataBaseName,collectionName).find(searchDoc); } //經過id(objectid)模糊查詢 public FindIterable<Document> findMongoDbDocByIdRegex(String dataBaseName, String collectionName, String id){ BasicDBObject searchDoc = new BasicDBObject().append("_id", new BasicDBObject("$regex",id)); return getCollectionByName(dataBaseName,collectionName).find(searchDoc); } //經過開始id和結束id 查詢(根據objectId範圍查詢) public FindIterable<Document> findMongoDbDocById(String dataBaseName, String collectionName, String startId,String endId){ BasicDBObject searchDoc = new BasicDBObject().append("_id", new BasicDBObject("$gte", startId).append("$lte", endId)); return getCollectionByName(dataBaseName,collectionName).find(searchDoc); } public FindIterable<Document> findMongoDbDoc(String dataBaseName, String collectionName,BasicDBObject basicDBObject){ return getCollectionByName(dataBaseName,collectionName).find(basicDBObject); } //限制查詢返回的條數 public FindIterable<Document> findMongoDbDoc(String dataBaseName, String collectionName,BasicDBObject basicDBObject,Integer limitNum){ return findMongoDbDoc(dataBaseName,collectionName,basicDBObject).limit(limitNum) ; } public FindIterable<Document> findMongoDbDocById(String dataBaseName, String collectionName, String startId,String endId,Integer limitNum){ return findMongoDbDocById(dataBaseName,collectionName,startId,endId).limit(limitNum); } /** * 降序查詢(排序) * @param dataBaseName * @param collectionName * @param startId * @param endId * @param sortField 排序字段 * @return */ public FindIterable<Document> findMongoDbDocByIdDescSort(String dataBaseName, String collectionName, String startId,String endId,String sortField){ return findMongoDbDocById(dataBaseName,collectionName,startId,endId).sort(new Document().append(sortField, -1)); } public FindIterable<Document> findMongoDbDocByIdDescSort(String dataBaseName, String collectionName, String startId,String endId,String sortField,Integer limitNum){ return findMongoDbDocByIdDescSort(dataBaseName,collectionName,startId,endId,sortField).limit(limitNum); } /** * 降序查詢(排序) * @param dataBaseName * @param collectionName * @param startId * @param endId * @param sortField 排序字段 * @return */ public FindIterable<Document> findMongoDbDocByIdAscSort(String dataBaseName, String collectionName, String startId,String endId,String sortField){ return findMongoDbDocById(dataBaseName,collectionName,startId,endId).sort(new Document().append(sortField, 1)); } public FindIterable<Document> findMongoDbDocByIdAscSort(String dataBaseName, String collectionName, String startId,String endId,String sortField,Integer limitNum){ return findMongoDbDocByIdAscSort(dataBaseName,collectionName,startId,endId,sortField).limit(limitNum); }
4、關於MongoDB 的插入操做
//插入操做,注意插入時,若是數據已經存在會報錯,插入時必須數據不存在,不會自動進行覆蓋 //插入單條記錄 public void insertDoc(String dataBaseName, String collectionName, Document document){ getCollectionByName(dataBaseName,collectionName).insertOne(document); } //插入多條記錄 public void insertDoc(String dataBaseName, String collectionName,List<? extends Document> listData){ getCollectionByName(dataBaseName,collectionName).insertMany(listData); }
5、關於MongoDB 的更新操做
//更新單條 public void updateDoc(String dataBaseName, String collectionName, Bson var1, Bson var2){ getCollectionByName(dataBaseName,collectionName).updateOne(var1,var2); } public void updateDoc(String dataBaseName, String collectionName, Bson var1, List<? extends Bson> list){ getCollectionByName(dataBaseName,collectionName).updateOne(var1,list); } //批量更新 public void updateDocs(String dataBaseName, String collectionName, Bson var1, Bson var2){ getCollectionByName(dataBaseName,collectionName).updateMany(var1,var2); } public void updateDocs(String dataBaseName, String collectionName, Bson var1, List<? extends Bson> list){ getCollectionByName(dataBaseName,collectionName).updateMany(var1,list); }
6、關於MongoDB 的刪除操做
//單條刪除 public DeleteResult deleteDoc(String dataBaseName, String collectionName, Bson var1){ return getCollectionByName(dataBaseName,collectionName).deleteOne(var1); } //批量刪除 public DeleteResult deleteDocs(String dataBaseName, String collectionName,Bson var1){ return getCollectionByName(dataBaseName,collectionName).deleteMany(var1); }
7、關於MongoDB 的替換操做
本文做者:張永清,轉載請註明出處:MongoDB Java API操做很全的整理以及共享分片模式下的常見操做整理
//存在就替換,不存在的話就插入 public UpdateResult replaceDoc(String dataBaseName, String collectionName, Bson var1, Document var2,ReplaceOptions var3){ return getCollectionByName(dataBaseName,collectionName).replaceOne(var1,var2,var3); } public UpdateResult replaceDoc(String dataBaseName, String collectionName, Bson var1, Document var2){ return getCollectionByName(dataBaseName,collectionName).replaceOne(var1,var2); } //調用示例(設置存在就替換,不存在的話就插入) Document documentQuery = new Document("_id", docId); Document document = new Document("_id", docId); ReplaceOptions replaceOptions = new ReplaceOptions(); replaceOptions.upsert(true); String dataBaseName="zhangyonqing"; String collectionName="zhangyonqing"; replaceDoc(dataBaseName,collectionName,documentQuery,document,replaceOptions);
八、關於MongoDB 的bulkWrite操做 (批量寫入),對於數據不少時,效率很高
public BulkWriteResult bulkWrite(String dataBaseName, String collectionName, List<? extends WriteModel<? extends Document>> listData){ return getCollectionByName(dataBaseName,collectionName).bulkWrite(listData); }
9、關於MongoDB 的分頁查詢
mongodb的分頁查詢能夠有多種思路來實現。
思路一:採用相似mysql的limit start end 的這種。
獲取到總的數量:
//查詢總數 public long countDocs(String dataBaseName, String collectionName,Bson var1){ if(null==var1){ return getCollectionByName(dataBaseName,collectionName).countDocuments(); } return getCollectionByName(dataBaseName,collectionName).countDocuments(var1); }
// 分頁查詢,採用skip+limit的方式,在用了總數後,就能夠分頁了,skip的意思是前面跳過多少數據。可是這種方式在數據量大的時候效率不高,由於skip會致使全表掃描。
public FindIterable<Document> findMongoDbDoc(String dataBaseName, String collectionName,BasicDBObject basicDBObject,Integer skip,Integer limit){ return getCollectionByName(dataBaseName,collectionName).find(basicDBObject).skip(skip).limit(limit); }
思路二:利用limit 以及排序的方式,獲取分頁的上一頁的最後一條記錄的objectId,而後使用排序+$gte操做(大於)+limit 來獲取當頁的數據。找到一個能夠排序的字段,好比objectId或者時間字段均可以排序。這個也是mongodb官方推薦的方式,這種作飯能夠避免全表掃描。
思路三:在數據量不大的時候,使用代碼進行分頁。好比從mongodb中查詢出一個list對象後,對list對象作代碼分頁。
public class ListUtil { public static List getPagingList(List list,Integer start,Integer length){ start = start<0?0:start; //默認爲10 length = length<=0?10:length; Integer size = list.size(); if(start>size){ start = size; } Integer toIndex = (start+length-1)>=size?size:(start+length-1); if(toIndex<=0){ toIndex = size; } return list.subList(start,toIndex); }
2、異步操做API
mongodb異步驅動程序提供了異步api,能夠利用netty或java 7的asynchronoussocketchannel實現快速、無阻塞的i/o,maven依賴
<dependencies> <dependency> <groupId>org.mongodb</groupId> <artifactId>mongodb-driver-async</artifactId> <version>3.11.1</version> </dependency> </dependencies>
官方地址:http://mongodb.github.io/mongo-java-driver/3.11/driver-async/getting-started/installation/
異步操做必然會涉及到回調,回調時採用ResultCallback<Document>
本文做者:張永清,轉載請註明出處:MongoDB Java API操做很全的整理以及共享分片模式下的常見操做整理
SingleResultCallback<Document> callbackPrintDocuments = new SingleResultCallback<Document>() { @Override public void onResult(final Document document, final Throwable t) { System.out.println(document.toJson()); } }; SingleResultCallback<Void> callbackWhenFinished = new SingleResultCallback<Void>() { @Override public void onResult(final Void result, final Throwable t) { System.out.println("Operation Finished!"); } };
異步insert操做
collection.insertMany(documents, new SingleResultCallback<Void>() { @Override public void onResult(final Void result, final Throwable t) { System.out.println("Documents inserted!"); } });
異步刪除操做
collection.deleteMany(gte("i", 100), new SingleResultCallback<DeleteResult>() { @Override public void onResult(final DeleteResult result, final Throwable t) { System.out.println(result.getDeletedCount()); } });
異步更新操做
collection.updateMany(lt("i", 100), inc("i", 100), new SingleResultCallback<UpdateResult>() { @Override public void onResult(final UpdateResult result, final Throwable t) { System.out.println(result.getModifiedCount()); } });
異步統計操做
collection.countDocuments( new SingleResultCallback<Long>() { @Override public void onResult(final Long count, final Throwable t) { System.out.println(count); } });
3、MongoDB Reactive Streams 操做API
官方的MongoDB reactive streams Java驅動程序,爲MongoDB提供異步流處理和無阻塞處理。
徹底實現reactive streams api,以提供與jvm生態系統中其餘reactive streams的互操做,通常適合於大數據的處理,好比spark,flink,storm等。
<dependencies> <dependency> <groupId>org.mongodb</groupId> <artifactId>mongodb-driver-reactivestreams</artifactId> <version>1.12.0</version> </dependency> </dependencies>
官方地址:http://mongodb.github.io/mongo-java-driver-reactivestreams/
會包含以下三部分:
API問的地址:http://mongodb.github.io/mongo-java-driver-reactivestreams/1.12/javadoc/
代碼示例:
//創建鏈接 MongoClient mongoClient = MongoClients.create(mongodbUrl); //得到數據庫對象 MongoDatabase database = client.getDatabase(databaseName); //得到集合 MongoCollection collection = database.getCollection(collectionName); //異步返回Publisher FindPublisher publisher = collection.find(); //訂閱實現 publisher.subscribe(new Subscriber() { @Override public void onSubscribe(Subscription str) { System.out.println("start..."); //執行請求 str.request(Integer.MAX_VALUE); } @Override public void onNext(Document document) { //得到文檔 System.out.println("Document:" + document.toJson()); } @Override public void onError(Throwable t) { System.out.println("error occurs."); } @Override public void onComplete() { System.out.println("finished."); } });
四、MongoDB 共享分片模式安裝
這裏以mongodb4.2.0版本和操做系統CentOS Linux release 7.6.1810 (Core) 爲例:
一、從官網下載mongodb-linux-x86_64-rhel7的安裝包。
分片模式安裝包括三部分:shard、config、router
MongoDB分片模式下的架構圖以下:
(1)mongos :數據路由,和客戶端打交道的模塊。mongos自己沒有任何數據,他也不知道該怎麼處理這數據,去找config server
(2)config server:全部存、取數據的方式,全部shard節點的信息,分片功能的一些配置信息。能夠理解爲真實數據的元數據。
(3)shard:真正的數據存儲位置,以chunk爲單位存數據。
Mongos自己並不持久化數據,Sharded cluster全部的元數據都會存儲到Config Server,而用戶的數據會分散存儲到各個shard。Mongos啓動後,會從配置服務器加載元數據,開始提供服務,將用戶的請求正確路由到對應的碎片。
Mongos的路由功能
當數據寫入時,MongoDB Cluster根據分片鍵設計寫入數據。
當外部語句發起數據查詢時,MongoDB根據數據分佈自動路由至指定節點返回數據。
分片的主要目的:
高數據量和吞吐量的數據庫應用會對單機的性能形成較大壓力,大的查詢量會將單機的CPU耗盡,大的數據量對單機的存儲壓力較大,最終會耗盡系統的內存而將壓力轉移到磁盤IO上。
爲了解決這些問題,有兩個基本的方法: 垂直擴展和水平擴展。
垂直擴展:增長更多的CPU和存儲資源來擴展容量。
水平擴展:將數據集分佈在多個服務器上。水平擴展即分片。
分片爲應對高吞吐量與大數據量提供了方法。使用分片減小了每一個分片須要處理的請求數,所以,經過水平擴展,集羣能夠提升本身的存儲容量和吞吐量。舉例來講,當插入一條數據時,應用只須要訪問存儲這條數據的分片,使用分片減小了每一個分片存儲的數據。
分片的好處:
1.對集羣進行抽象,讓集羣「不可見」:
MongoDB自帶了一個叫作mongos的專有路由進程。mongos就是掌握統一路口的路由器,其會將客戶端發來的請求準確無誤的路由到集羣中的一個或者一組服務器上,同時會把接收到的響應拼裝起來發回到客戶端。
2.保證集羣老是可讀寫:
MongoDB經過多種途徑來確保集羣的可用性和可靠性。將MongoDB的分片和複製功能結合使用,在確保數據分片到多臺服務器的同時,也確保了每分數據都有相應的備份,這樣就能夠確保有服務器換掉時,其餘的從庫能夠當即接替壞掉的部分繼續工做。
3.使集羣易於擴展:
當系統須要更多的空間和資源的時候,MongoDB使咱們能夠按需方便的擴充系統容量。
二、部署shard,這裏咱們部署3個shard
建立shard1.config 配置文件,文件內容:
#數據路徑
dbpath=/data3/mongodb/data/shard1
#日誌路徑
logpath=/opt/mongodb/mongodb-linux-x86_64-rhel70-4.2.0/shard/logs/shard1.log
port=37017
logappend=true
#是否後臺運行
fork=true
quiet=true
journal=true
shardsvr=true
replSet=shard1RS/10.100.xx.xx:37017
bind_ip=0.0.0.0
建立shard2.config 配置文件,文件內容:
dbpath=/data1/mongodb/data/shard2
logpath=/opt/mongodb/mongodb-linux-x86_64-rhel70-4.2.0/shard/logs/shard2.log
port=47017
logappend=true
fork=true
quiet=true
journal=true
shardsvr=true
replSet=shard2RS/10.100.xx.xx:47017
bind_ip=0.0.0.0
建立shard3.config 配置文件,文件內容:
dbpath=/data1/mongodb/data/shard3
logpath=/opt/mongodb/mongodb-linux-x86_64-rhel70-4.2.0/shard/logs/shard3.log
port=57017
logappend=true
fork=true
quiet=true
journal=true
shardsvr=true
replSet=shard3RS/10.100.xx.xx:57017
bind_ip=0.0.0.0
分別啓動上面的3個共享分片
啓動方式:mongod -f 配置文件對應的路徑
mongod -f /opt/mongodb/mongodb-linux-x86_64-rhel70-4.2.0/shard/shard1.config
mongod -f /opt/mongodb/mongodb-linux-x86_64-rhel70-4.2.0/shard/shard2.config
mongod -f /opt/mongodb/mongodb-linux-x86_64-rhel70-4.2.0/shard/shard3.config
若是須要限制內存的大小,能夠在啓動參數後面增長--wiredTigerCacheSizeGB 0.2 ,這裏的0.2 表明緩存的大小。
關於MongoDB緩存的介紹:
With WiredTiger, MongoDB utilizes both the WiredTiger internal cache and the filesystem cache.
Starting in MongoDB 3.4, the default WiredTiger internal cache size is the larger of either:
For example, on a system with a total of 4GB of RAM the WiredTiger cache will use 1.5GB of RAM (0.5 * (4 GB - 1 GB) = 1.5 GB
). Conversely, a system with a total of 1.25 GB of RAM will allocate 256 MB to the WiredTiger cache because that is more than half of the total RAM minus one gigabyte (0.5 * (1.25 GB - 1 GB) = 128 MB < 256 MB
).
By default, WiredTiger uses Snappy block compression for all collections and prefix compression for all indexes. Compression defaults are configurable at a global level and can also be set on a per-collection and per-index basis during collection and index creation.
Different representations are used for data in the WiredTiger internal cache versus the on-disk format:
Via the filesystem cache, MongoDB automatically uses all free memory that is not used by the WiredTiger cache or by other processes.
To adjust the size of the WiredTiger internal cache, see storage.wiredTiger.engineConfig.cacheSizeGB
and --wiredTigerCacheSizeGB
. Avoid increasing the WiredTiger internal cache size above its default value.
NOTE
The storage.wiredTiger.engineConfig.cacheSizeGB
limits the size of the WiredTiger internal cache. The operating system will use the available free memory for filesystem cache, which allows the compressed MongoDB data files to stay in memory. In addition, the operating system will use any free RAM to buffer file system blocks and file system cache.
To accommodate the additional consumers of RAM, you may have to decrease WiredTiger internal cache size.
The default WiredTiger internal cache size value assumes that there is a single mongod
instance per machine. If a single machine contains multiple MongoDB instances, then you should decrease the setting to accommodate the other mongod
instances.
If you run mongod
in a container (e.g. lxc
, cgroups
, Docker, etc.) that does not have access to all of the RAM available in a system, you must set storage.wiredTiger.engineConfig.cacheSizeGB
to a value less than the amount of RAM available in the container. The exact amount depends on the other processes running in the container. See memLimitMB
.
To view statistics on the cache and eviction rate, see the wiredTiger.cache
field returned from the serverStatus
command.
更多信息,能夠參考http://docs.mongodb.com/manual/faq/diagnostics/#memory-diagnostics-for-the-wiredtiger-storage-engine
三、部署config,這裏咱們部署1個config
建立mongo.config配置文件,文件內容:
dbpath=/data3/mongodb/config/data
logpath=/opt/mongodb/mongodb-linux-x86_64-rhel70-4.2.0/config/logs/mongoconfig.log
port=27017
logappend=true
fork=true
quiet=true
journal=true
configsvr=true
replSet=configRS/10.100.xx.xx:27017
bind_ip=0.0.0.0
maxConns=100
啓動config :mongod -f 配置文件對應的路徑
mongod -f /opt/mongodb/mongodb-linux-x86_64-rhel70-4.2.0/config/mongo.config
四、部署router,這裏咱們部署1個router
建立router.config配置文件,文件內容:
logpath=/opt/mongodb/mongodb-linux-x86_64-rhel70-4.2.0/router/logs/mongorouter.log
port=17017
logappend=true
fork=true
quiet=true
configdb=configRS/10.100.xx.xx:27017
bind_ip=0.0.0.0
啓動router:mongos -f 配置文件對應的路徑
mongos -f /opt/mongodb/mongodb-linux-x86_64-rhel70-4.2.0/router/router.config
五、 初始化shard server,配置每一個shard的副本集
鏈接到每一臺共享分片
mongo 10.100.xx.xx:37017
備註有每一個分片配置幾個副本集是能夠本身選擇的,下面這個是配置1個副本集
rs.initiate({_id:"shard1RS",members:[{_id:1,host:"10.100.xx.xx:37017",priority:2},{_id:2,host:"192.168.xx.xx:37017"}]})
mongo 10.100.xx.xx:47017
rs.initiate({_id:"shard2RS",members:[{_id:1,host:"10.100.xx.xx:47017",priority:2},{_id:2,host:"192.168.xx.xx:47017"}]})
mongo 10.100.xx.xx:57017
rs.initiate({_id:"shard2RS",members:[{_id:1,host:"10.100.xx.xx:57017",priority:2},{_id:2,host:"192.168.xx.xx:57017"}]})
六、 配置分片
經過路由鏈接到mongo:mongo 10.100.xx.xx:17017
鏈接成功後切換到admin模式:use admin
添加對應的3個分片
db.runCommand({"addShard":"shard1RS/10.100.xx.xx:37017" ,"maxsize":1024})
db.runCommand({"addShard":"shard2RS/10.100.xx.xx2:47017" ,"maxsize":1024})
db.runCommand({"addShard":"shard3RS/10.100.xx.xx:57017" ,"maxsize":1024})
判斷當前是不是shard集羣:db.runCommand({isdbgrid:1});
查看分片的狀態信息:可用命令db.runCommand({listshards:1})
七、 其餘操做
刪除分片:
use admin
db.runCommand( { removeShard: "shard1RS" } )
給集羣新增用戶:
首先使用帶有「userAdmin」角色的用戶登陸集羣,執行以下命令
use admin
db.createUser(
{
"user" : "backupUser",
"pwd" : "123",
roles: [{role:"backup", db:"admin"}]
}
)
db.auth("backupUser","123") //使新增的用戶生效
至此,就完成了新增一個用戶備份整個集羣的用戶
給集羣用戶新增權限:
use admin
db.grantRolesToUser(
"pubUser",
[{role:"readWrite", db:"Philippines"},
{role:"readWrite", db:"Italy"},
{role:"readWrite", db:"India"},
{role:"readWrite", db:"Japan"}]
)
查詢全部DB的分片存儲信息,包括chunks數、shard key信息:
db.printShardingStatus()
獲取collection各個分片的數據存儲狀況:
db.collection.getShardDistribution()
顯示本mongos集羣全部DB的信息, 包含了Shard Key信息:
sh.status()
僅顯示分片:
use config
db.shards.find()
balancer是sharded集羣的負載均衡工具,新建集羣的時候默認開啓,除非你在config裏把它關閉掉:
db.settings.find()
手動啓動balancer:
sh.startBalancer()
判斷當前balancer是否在跑:
sh.isBalancerRunning()
五、MongoDB 分片模式下如何選擇分片鍵
MongoDB中數據的分片是以集合爲基本單位的,集合中的數據經過片鍵(Shard key)被分紅多部分。其實片鍵就是在集合中選一個鍵,用該鍵的值做爲數據拆分的依據。
因此一個好的片鍵對分片相當重要。片鍵必須是一個索引,經過sh.shardCollection加會自動建立索引(前提是此集合不存在的狀況下)。一個自增的片鍵對寫入和數據均勻分佈就不是很好,由於自增的片鍵總會在一個分片上寫入,後續達到某個閥值可能會寫到別的分片。可是按照片鍵查詢會很是高效。
隨機片鍵對數據的均勻分佈效果很好。注意儘可能避免在多個分片上進行查詢。在全部分片上查詢,mongos會對結果進行歸併排序。
對集合進行分片時,你須要選擇一個片鍵,片鍵是每條記錄都必須包含的,且創建了索引的單個字段或複合字段,MongoDB按照片鍵將數據劃分到不一樣的數據塊中,並將數據塊均衡地分佈到全部分片中。
爲了按照片鍵劃分數據塊,MongoDB使用基於範圍的分片方式或者 基於哈希的分片方式。
注意:
分片鍵是不可變。
分片鍵必須有索引。
分片鍵大小限制512bytes。
分片鍵用於路由查詢。
MongoDB不接受已進行collection級分片的collection上插入無分片
鍵的文檔(也不支持空值插入)
Sharded Cluster支持將單個集合的數據分散存儲在多shard上,用戶能夠指定根據集合內文檔的某個字段即shard key來進行範圍分片(range sharding)。
對於基於範圍的分片,MongoDB按照片鍵的範圍把數據分紅不一樣部分。
假設有一個數字的片鍵:想象一個從負無窮到正無窮的直線,每個片鍵的值都在直線上畫了一個點。MongoDB把這條直線劃分爲更短的不重疊的片斷,並稱之爲數據塊,每一個數據塊包含了片鍵在必定範圍內的數據。在使用片鍵作範圍劃分的系統中,擁有」相近」片鍵的文檔極可能存儲在同一個數據塊中,所以也會存儲在同一個分片中。
分片過程當中利用哈希索引做爲分片的單個鍵,且哈希分片的片鍵只能使用一個字段,而基於哈希片鍵最大的好處就是保證數據在各個節點分佈基本均勻。
對於基於哈希的分片,MongoDB計算一個字段的哈希值,並用這個哈希值來建立數據塊。在使用基於哈希分片的系統中,擁有」相近」片鍵的文檔極可能不會存儲在同一個數據塊中,所以數據的分離性更好一些。
Hash分片與範圍分片互補,能將文檔隨機的分散到各個chunk,充分的擴展寫能力,彌補了範圍分片的不足,但不能高效的服務範圍查詢,全部的範圍查詢要分發到後端全部的Shard才能找出知足條件的文檔。
1、遞增的sharding key
數據文件挪動小。(優點)
由於數據文件遞增,因此會把insert的寫IO永久放在最後一片上,形成最後一片的寫熱點。同時,隨着最後一片的數據量增大,將不斷的發生遷移至以前的片上。
2、隨機的sharding key
數據分佈均勻,insert的寫IO均勻分佈在多個片上。(優點)
大量的隨機IO,磁盤不堪重荷。
3、混合型key
大方向隨機遞增,小範圍隨機分佈。
爲了防止出現大量的chunk均衡遷移,可能形成的IO壓力。咱們須要設置合理分片使用策略(片鍵的選擇、分片算法(range、hash))
分片注意:
分片鍵是不可變、分片鍵必須有索引、分片鍵大小限制512bytes、分片鍵用於路由查詢。
MongoDB不接受已進行collection級分片的collection上插入無分片鍵的文檔(也不支持空值插入)、
數據庫開啓分片:
在共享分片模式下,建立完數據庫後,須要對數據庫開啓分片功能,並對數據庫下的表的字段指定分片算法
經過路由鏈接到mongoDb後,使用use admin 切換到admin模式。
開啓數據庫分片的命令:db.runCommand({"enablesharding":"數據庫名稱"}) ,例如對對庫hdctest開啓分片 :db.runCommand({"enablesharding":"hdctest"})
對庫hdctest下的表person按字段ID配置hash分片算法 :db.runCommand({"shardcollection":"hdctest.person","key":{_id:'hashed'}})
對庫hdctest下的表person按字段ID配置按照id升序或者降序配置分片算法(升序和降序 用1和-1表示):
db.runCommand({shardcollection:"hdctest.person",key:{_id:1}})
另外須要注意的是:對錶中按照字段進行分片時,須要預先建立索引才能配置分片算法(索引和分片算法保持一致,對id進行分片,那麼就對id建立索引)。
按照id升序索引:db.person.createIndex( {"_id": 1},{"name":'idx_id'})
按照createTime 降序索引:db.person.createIndex( {"createTime": -1 },{"name":'idx_createTime'})
六、MongoDB 如何建立索引
1.爲普通字段添加索引,而且爲索引命名
db.集合名.createIndex( {"字段名": 1 },{"name":'idx_字段名'})
說明: (1)索引命名規範:idx_<構成索引的字段名>。若是字段名字過長,可採用字段縮寫。
(2)字段值後面的 1 表明升序;如是 -1 表明 降序。
2.爲內嵌字段添加索引
db.集合名.createIndex({"字段名.內嵌字段名":1},{"name":'idx_字段名_內嵌字段名'})
3.經過後臺建立索引
db.集合名.createIndex({"字段名":1},{"name":'idx_字段名',background:true})
4:組合索引
db.集合名.createIndex({"字段名1":-1,"字段名2":1},{"name":'idx_字段名1_字段名2',background:true})
5.設置TTL 索引
db.集合名.createIndex( { "字段名": 1 },{ "name":'idx_字段名',expireAfterSeconds: 定義的時間,background:true} )
說明 :expireAfterSeconds爲過時時間(單位秒)
6.createIndex() 接收可選參數彙總
Parameter | Typ | Description |
background | Boolean | 建索引過程會阻塞其它數據庫操做,background可指定之後臺方式建立索引,即增長 "background" 可選參數。 "background" 默認值爲false。 |
unique | Boolean | 創建的索引是否惟一。指定爲true建立惟一索引。默認值爲false. |
name | string | 索引的名稱。若是未指定,MongoDB的經過鏈接索引的字段名和排序順序生成一個索引名稱。 |
sparse | Boolean | 對文檔中不存在的字段數據不啓用索引;這個參數須要特別注意,若是設置爲true的話,在索引字段中不會查詢出不包含對應字段的文檔.。默認值爲 false. |
expireAfterSeconds | integer | 指定一個以秒爲單位的數值,完成 TTL設定,設定集合的生存時間。 |
weights | document | 索引權重值,數值在 1 到 99,999 之間,表示該索引相對於其餘索引字段的得分權重。 |
default_language | string | 對於文本索引,該參數決定了停用詞及詞幹和詞器的規則的列表。 默認爲英語 |
原文出處:https://www.cnblogs.com/laoqing/p/11792578.html