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