MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理

MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写,一般生产上建议以共享分片的形式来部署。 但是MongoDB官方也提供了其它语言的客户端操作API。如下图所示:

提供了C、C++、C#、.net、GO、java、Node.js、PHP、python、scala等各种语言的版本,如下图所示:

MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理_第1张图片

MongoDB的操作分为同步操作和异步操作以及响应式编程操作
一、同步操作API

官方JAVA API的路径:https://docs.mongodb.com/ecosystem/drivers/java/  我们这里以3.11的java 版本为例。各个版本的API对MongoDB服务的支持情况。

MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理_第2张图片

使用API时,先引入maven依赖



    org.mongodb
    mongo-java-driver
    3.11.1

  

 1、关于MongoDB Client的初始化和关闭。

从官方介绍来看,一般建议Client只需要一个建立一个长连接实例,然后使用时,都使用这个实例就可以,也就是可以用java的单例模式来创建连接实例。

MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理_第3张图片

 

//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: 

MongoClient mongoClient = MongoClients.create("mongodb://host1:27017");

client连接到一个 Replica Set:

本文作者:张永清,转载请注明出处:MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理

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 listCollectionNames(String dataBaseName){
List
stringList = new ArrayList();
mongoClient.getDatabase(dataBaseName).listCollectionNames().forEach((Consumer
super String>) t->{ stringList.add(t); });
return stringList; }

public MongoCollection getCollectionByName(String dataBaseName, String collectionName){ return getDatabase(dataBaseName).getCollection(collectionName); }

 3、关于MongoDB 的查询操作

    //通过id(objectid)精确查询
	public FindIterable  findMongoDbDocById(String dataBaseName, String collectionName, String id){
        BasicDBObject searchDoc = new BasicDBObject().append("_id", id);
       return getCollectionByName(dataBaseName,collectionName).find(searchDoc);
    }
    //通过id(objectid)模糊查询
    public FindIterable  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  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 findMongoDbDoc(String dataBaseName, String collectionName,BasicDBObject basicDBObject){
        return getCollectionByName(dataBaseName,collectionName).find(basicDBObject);
    }
	//限制查询返回的条数
    public FindIterable findMongoDbDoc(String dataBaseName, String collectionName,BasicDBObject basicDBObject,Integer limitNum){
        return findMongoDbDoc(dataBaseName,collectionName,basicDBObject).limit(limitNum) ;
    }
    public FindIterable  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  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  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  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  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 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 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 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);

  

8、关于MongoDB 的bulkWrite操作 (批量写入),对于数据很多时,效率很高

    public BulkWriteResult bulkWrite(String dataBaseName, String collectionName, List> 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 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);
    }

  

二、异步操作API  

 mongodb异步驱动程序提供了异步api,可以利用netty或java 7的asynchronoussocketchannel实现快速、无阻塞的i/o,maven依赖



org.mongodb
mongodb-driver-async
3.11.1

官方地址:http://mongodb.github.io/mongo-java-driver/3.11/driver-async/getting-started/installation/

异步操作必然会涉及到回调,回调时采用ResultCallback

本文作者:张永清,转载请注明出处:MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理

SingleResultCallback callbackPrintDocuments = new SingleResultCallback() {
   @Override
   public void onResult(final Document document, final Throwable t) {
       System.out.println(document.toJson());
   }
};

SingleResultCallback callbackWhenFinished = new SingleResultCallback() {
    @Override
    public void onResult(final Void result, final Throwable t) {
        System.out.println("Operation Finished!");
    }
};

  异步insert操作

collection.insertMany(documents, new SingleResultCallback() {
    @Override
    public void onResult(final Void result, final Throwable t) {
        System.out.println("Documents inserted!");
    }
});

  异步删除操作

collection.deleteMany(gte("i", 100), new SingleResultCallback() {
    @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() {
        @Override
        public void onResult(final UpdateResult result, final Throwable t) {
            System.out.println(result.getModifiedCount());
        }
    });

  异步统计操作

collection.countDocuments(
  new SingleResultCallback() {
      @Override
      public void onResult(final Long count, final Throwable t) {
          System.out.println(count);
      }
  });

  

三、MongoDB Reactive Streams 操作API

官方的MongoDB reactive streams Java驱动程序,为MongoDB提供异步流处理和无阻塞处理。

完全实现reactive streams api,以提供与jvm生态系统中其他reactive streams的互操作,一般适合于大数据的处理,比如spark,flink,storm等。


    
        org.mongodb
        mongodb-driver-reactivestreams
        1.12.0
    

  官方地址:http://mongodb.github.io/mongo-java-driver-reactivestreams/

会包含如下三部分:

  1. Publisher:Publisher 是数据的发布者。Publisher 接口只有一个方法 subscribe,用于添加数据的订阅者,也就是 Subscriber。
  2. Subscriber: 是数据的订阅者。Subscriber 接口有4个方法,都是作为不同事件的处理器。在订阅者成功订阅到发布者之后,其 onSubscribe(Subscription s) 方法会被调用。
  3. Subscription:表示的是当前的订阅关系。

API问的地址:http://mongodb.github.io/mongo-java-driver-reactivestreams/1.12/javadoc/

MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理_第4张图片

 

 

 

代码示例:

//建立连接
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) 为例:

1、从官网下载mongodb-linux-x86_64-rhel7的安装包。

分片模式安装包括三部分:shard、config、router 

MongoDB分片模式下的架构图如下:

MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理_第5张图片

 (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使我们可以按需方便的扩充系统容量。

2、部署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

3、部署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

4、部署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

5、 初始化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"}]})

6、 配置分片

通过路由连接到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})

7、 其他操作

删除分片:

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 分片模式下如何选择分片键

分片键shard key:

MongoDB中数据的分片是以集合为基本单位的,集合中的数据通过片键(Shard key)被分成多部分。其实片键就是在集合中选一个键,用该键的值作为数据拆分的依据。

  所以一个好的片键对分片至关重要。片键必须是一个索引,通过sh.shardCollection加会自动创建索引(前提是此集合不存在的情况下)。一个自增的片键对写入和数据均匀分布就不是很好,因为自增的片键总会在一个分片上写入,后续达到某个阀值可能会写到别的分片。但是按照片键查询会非常高效。

  随机片键对数据的均匀分布效果很好。注意尽量避免在多个分片上进行查询。在所有分片上查询,mongos会对结果进行归并排序。

  对集合进行分片时,你需要选择一个片键,片键是每条记录都必须包含的,且建立了索引的单个字段或复合字段,MongoDB按照片键将数据划分到不同的数据块中,并将数据块均衡地分布到所有分片中。

  为了按照片键划分数据块,MongoDB使用基于范围的分片方式或者 基于哈希的分片方式。

注意:

分片键是不可变。

分片键必须有索引。

分片键大小限制512bytes。

分片键用于路由查询。

MongoDB不接受已进行collection级分片的collection上插入无分片

键的文档(也不支持空值插入)

 

以范围为基础的分片Sharded Cluster:

Sharded Cluster支持将单个集合的数据分散存储在多shard上,用户可以指定根据集合内文档的某个字段即shard key来进行范围分片(range sharding)。

 MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理_第6张图片

 

  对于基于范围的分片,MongoDB按照片键的范围把数据分成不同部分。

  假设有一个数字的片键:想象一个从负无穷到正无穷的直线,每一个片键的值都在直线上画了一个点。MongoDB把这条直线划分为更短的不重叠的片段,并称之为数据块,每个数据块包含了片键在一定范围内的数据。在使用片键做范围划分的系统中,拥有”相近”片键的文档很可能存储在同一个数据块中,因此也会存储在同一个分片中。

基于哈希的分片:

分片过程中利用哈希索引作为分片的单个键,且哈希分片的片键只能使用一个字段,而基于哈希片键最大的好处就是保证数据在各个节点分布基本均匀。

 MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理_第7张图片

 

  对于基于哈希的分片,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 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语
本文作者:张永清,转载请注明出处: MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理   最近发现很多不同的网站转载作者的文章不注明出处,作者将追究法律责任。

你可能感兴趣的:(MongoDB Java API操作很全的整理以及共享分片模式下的常见操作整理)