mongoclient和mongo类写性能差异原因分析

1 背景介绍

在运维mongodb数据库工作中,发现研发同学编写代码访问mongodb数据库时,经常遇到一个问题:在建立初始化mongodb连接时,选择mongoclient还是Mongo类,二者究竟有什么区别?

由于mongodb驱动本身逻辑复杂,本文不做过多描述,只针对该问题,从源代码层面分析MongoClient类和Mongo类写性能差异的原因。

首先,Mongo和MongoClient类均在mongodb驱动中定义的,我们结合php和java两种语言,分别介绍这两个类的却别。

2 原因分析

首先我们来分析下php-mongodb驱动源码。

说明:以下是基于php-mongodb-1.4.5驱动版本为例介绍。
我们分别看下Mongo()MongoClient()构造函数:

Mongo():

PHP_METHOD(Mongo, __construct)
{
         php_mongo_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}

MongoClient()

PHP_METHOD(MongoClient, __construct)
{
         php_mongo_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}

从构造函数来看,很明显,Mongo类(以下简称Mongo)和MongoClient类(以下简称MongoClient)构造函数,调用同一个函数:
php_mongo_ctor()

该函数原型为:

void php_mongo_ctor(INTERNAL_FUNCTION_PARAMETERS, int bc)
 

关于第二个参数的代码片段如下:

if (bc) {
 /* Default to WriteConcern=0 for Mongo */
 link->servers->options.default_w = 0; }
else {
 /* Default to WriteConcern=1 for MongoClient */
 link->servers->options.default_w = 1; 
}

default_w是结构体mongo_servers的一个属性,意思为:写策略
很显然,M
ongo默认写策略为0;MongoClient默认写策略为1
我们来看下写策略的详细介绍:


写策略

意义

描述

w=0

Unacknowledged

写操作不需要确认

w=1

Acknowledged

主库写操作需要确认

w=N

Replica Set Acknowledged

主库和N-1从库写操作需要确认

w=majority

Majority Acknowledged

集群半数以上节点写操作需要确认

w=

Replica Set Tag Set Acknowledged

集群所有及诶单写操作均需要确认

表格1 写策略设置

从以上表格来看,写策略的含义是当应用写操作后,是否需要得到确认(Acknowledged),然后再继续写操作。

很显然,写策略设置为0时,不需要确认写操作是否ok,不过,会返回诸如socket异常或网络错误信息给应用程序。这种情况下,写性能明显会比较高;而当写策略设置为1时,需要副本集主库节点或者单点确认写操作ok后,再继续处理(后续)写操作,因为这个“确认”环节,降低了写性能。当写策略设置为N时,除了主库节点需要确认写操作ok,还需要N-1哥从苦节点也需要“确认”写操作ok,以此类推。

在php-mongodb驱动里,Mongo()类默认写策略设置为0,MongoClient()类写策略默认设置为1.除此以外,二者没有区别。

到这里,我们就很容易理解了,应用程序使用Mongo(0类,比MongoClient()类,写性能要高的缘由了。

 

我们再分析下java-mongodb驱动源代码。

说明:以下是基于mongo-java-driver-2.13.1版本来分析二者区别。

MongoClient类存在多种构造函数,如下:

public MongoClient(final ServerAddress addr, final List credentialsList, final MongoClientOptions options) {

super(addr, credentialsList, options);

}

public MongoClient(final List seeds) {

    this(seeds, new MongoClientOptions.Builder().build());

  }

public MongoClient(final List seeds, final List credentialsList) {

     this(seeds, credentialsList, new MongoClientOptions.Builder().build());

   }

public MongoClient(final List seeds, final MongoClientOptions options) {

   super(seeds, options);

  }

public MongoClient(final List seeds, final List credentialsList, final MongoClientOptions options) {

   super(seeds, credentialsList, options);

  }

无论是哪一种构造函数,都存在一个参数该参数的原型是:

MongoClientOptions()

该类调用Builder类,其构造函数片段如下:

public static class Builder {

        private String description;

        private ReadPreference readPreference = ReadPreference.primary();

        private WriteConcern writeConcern = WriteConcern.ACKNOWLEDGED;

 

        ......

        }

在这里,可以看到,WriteConcern是写策略,默认定义为:ACKNOWLEDGED,

在WriteConcern.java中,WriteConcern的取值常量为:

    public static final WriteConcern ACKNOWLEDGED = new WriteConcern(1);

    public static final WriteConcern UNACKNOWLEDGED = new WriteConcern(0);

    public static final WriteConcern NORMAL = UNACKNOWLEDGED;

public static final WriteConcern SAFE = ACKNOWLEDGED;

很显然,写策略默认设置为1.

再来看Mongo()类的构造函数:

    public Mongo(final String host) {

        this(new ServerAddress(host), createLegacyOptions());

}

createLegacyOptions()函数定义如下:

private static MongoClientOptions createLegacyOptions() {

return MongoClientOptions.builder().legacyDefaults().build();

    }

legacyDefaults()函数定义如下:

public Builder legacyDefaults() {

            this.connectionsPerHost(10).writeConcern(WriteConcern.UNACKNOWLEDGED);

     return this;

        }

writeConcern()函数定义为:

public Builder writeConcern(final WriteConcern writeConcern) {

       this.writeConcern = notNull("writeConcern", writeConcern);

       return this;

   }

notNull()函数定义为:

public static T notNull(final String name, final T value) {

   if (value == null) {

       throw new IllegalArgumentException(name + " can not be null");

   }

   return value;

}

从以上函数调用关系看,Mongo()类写策略默认值为0

从java-mongodb驱动源码看,MongoClient()类写策略默认值为1,Mongo()的写策略默认值为0,和php-mongodb源码情况一样。

 

3 测试验证

当修改源代码中MongoClient默认写策略为0时,重新编译驱动,进行读写测试,发现Mongo和MongoClient在写操作上,性能基本一致。

这就验证了Mongo比MongoClient写性能高的原因就在于写策略不同

测试结果如下:

 

MongoClient(cost)

Mongo(cost)

Insert

5565.1779174805s

674.36003684998s

Update

28807.404994965s

20184.952020645s

Find

24.696826934814s

22.372961044312s

表格二 测试统计

以上是调用MongoClient类和Mongo类,分别操作10000次,耗时统计结果
很明显,写操作方面,M
ongo性能高于MongoClient类,尤其是insert操作,性能提高了7倍,update性能也提高了42%;读操作性能基本一致。

结论

 

从以上分析和验证结果来看,Mongo类和MongoClient类主要区别在于写策略的默认值不同;而驱动本身提供了w参数,可以手动进行设置。如果手动设置写策略为相同值,二者在写性能上基本一致。

官方文档和源代码均建议使用MongoClient类,而且,在不久的将来,会废弃Mongo类。

但是,由于目前的php-mongodb驱动和java-mongodb驱动,均保留了Mongo类,而且,java-mongodb驱动里,MongoClient类继承了Mongo类。个人认为,两者均可以使用,如果业务对写性能要求高,可以设置写策略为0;否则,建议设置为1;不建议设置其他值

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/30341463/viewspace-1758585/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/30341463/viewspace-1758585/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值