我爱编程

MongoDB使用中问题汇总

2015-12-27  本文已影响9806人  白熊

如何集成MongoDB驱动包

推荐使用Maven管理包依赖关系:

<dependency>
  <groupId>org.mongodb</groupId>
  <artifactId>mongo-java-driver</artifactId>
  <version>2.13.2</version>
</dependency>

如何连接

主要有两种连接方式

示例代码

    //单机直连
    MongoClient mongoClient = new MongoClient( "localhost" , 27017 );
    
    //Replica Set连接
    MongoClientOptions options = MongoClientOptions.builder().autoConnectRetry(true).connectTimeout(60000).build();
    MongoCredential credential = MongoCredential.createMongoCRCredential("username", "dbname", "password".toCharArray());
    MongoClient mongoClient = new MongoClient(
                Arrays.asList(
                        new ServerAddress("mongoserver1", 34001),
                        new ServerAddress("mongoserver2", 34001),
                        new ServerAddress("mongoserver3", 34001)
                ), Arrays.asList(credential), options);

    //Replica Set连接 uri写法
    String connectionString = "mongodb://username:password@mongoserver1:34001,mongoserver2:34001,mongoserver3:34001/dbname?AutoConnectRetry=true";
    MongoClientURI mongoClientURI = new MongoClientURI(connectionString);
    MongoClient mongoClient = new MongoClient(mongoClientURI);

    //spring boot 请在properties里边使用uri方式进行连接
    spring.data.mongodb.uri=mongodb://username:password@mongoserver1:34001,mongoserver2:34001,mongoserver3:34001/dbname?AutoConnectRetry=true
    spring.data.mongodb.repositories.enabled=true

MongoDB Update的正确用法

通常一个文档只会有一小部分需要更新,如果我们把新文档做为update方法的参数显得很啰嗦很麻烦,特别是文档比较复杂的时候。而利用原子的更新修改器可以使得这种部分的更新极为方便高效。
更新修改器是种特殊的键,用来指定复杂的更新操作,比如调整,增加或者删除键,还可能是操作数组或者内嵌文档
$set用来指定一个键的值.如果这个键存在,就修改它;不存在,就创建它。

> db.name.find()
{ "_id" : ObjectId("505a5925f67c1b9a341caefb"), "fname" : "jeff", "lname" : "jiang" }
> db.name.update({"_id" : ObjectId("505a5925f67c1b9a341caefb")},{$set:{"fname" : "jeffery"}})
> db.name.find()
{ "_id" : ObjectId("505a5925f67c1b9a341caefb"), "fname" : "jeffery", "lname" : "jiang" }
# 可以看到,原文档的"fname"是存在的,所以$set修改器只修改了它的值("jeff"-->"jeffery")
> db.name.update({"_id" : ObjectId("505a5925f67c1b9a341caefb")},{$set:{age:23}})
> db.name.find()
{ "_id" : ObjectId("505a5925f67c1b9a341caefb"), "age" : 23, "fname" : "jeffery", "lname" : "jiang" }

示例代码

db.getCollection("restaurants").updateOne(new Document("name", "Juni"), new Document("$set", new Document("cuisine", "American (New)")) .append("$currentDate", new Document("lastModified", true)))

如何开启读写分离

默认情况下驱动是从Replica Set 集群中的 Primary 上进行读写的,应用可以在读多写少的场景下开启读写分离,提高效率。

这里介绍下 readPreference 这个参数:###

示例代码

//uri写法
mongodb://username:password@mongoserver1:34001,mongoserver2:34001,mongoserver3:34001/dbname?AutoConnectRetry=true&readPreference=secondaryPreferred

//java写法
MongoClientOptions options = MongoClientOptions.builder().readPreference(ReadPreference.secondaryPreferred()).build();
MongoClient mongoClient = new MongoClient(
                Arrays.asList(
                        new ServerAddress("mongoserver1", 34001),
                        new ServerAddress("mongoserver2", 34001),
                        new ServerAddress("mongoserver3", 34001)
                ), Arrays.asList(credential), options);

在Mongodb中最多能创建多少集合?

默认情况下,MongoDB 的每个数据库的命名空间保存在一个 16MB 的 .ns 文件中,平均每个命名占用约 628 字节,也即整个数据库的命名空间的上限约为 24000。
每一个集合、索引都将占用一个命名空间。所以,如果每个集合有一个索引(比如默认的 _id 索引),那么最多可以创建 12000 个集合。如果索引数更多,则可创建的集合数就更少了。同时,如果集合数太多,一些操作也会变慢。甚至使得MongoDB集群无法服务的情况发生!

MongoDB有传统数据库的事务和事务回滚么?

没有,请不要把它当成关系型数据库来使用,对于MongoDB集群来说,默认情况下数据也不是强一致性的,而是最终一致性。如果对数据一致性比较敏感建议更改WriteConcern级别,但后果是降低了性能,请酌情考虑。

MongoDB有命名规范么?

MongoDB有系统保留库名么?

MongoDB有连接池么?

MongoDB驱动中其实已经是一个现成的连接池了,而且线程安全。这个内置的连接池默认初始了100个连接,每一个操作(增删改查等)都会获取一个连接,执行操作后释放连接。
【题外话】请务必记得关闭资源,并且设置合理的池子连接数和超时时间。

内置连接池有多个重要参数,分别是:###

连接池的MaximumPoolSize要有个合理值,否则这个值数据量的连接都被占用,后面再有新的连接创建时就要等待了,而不能超出池上限新建连接。除此之外还要设置合理的连接等待,连接超时时间,以防止一个连接占用时间过长,影响其它连接请求。

connectTimeout 和 socketTimeout 的区别:###

一次完整的请求包括三个阶段:

如果与服务器(这里指数据库)请求建立连接的时间超过ConnectTimeout,就会抛 ConnectionTimeOutException,即服务器连接超时,没有在规定的时间内建立连接。
如果与服务器连接成功,就开始数据传输了。
如果服务器处理数据用时过长,超过了SocketTimeOut,就会抛出SocketTimeOutExceptin,即服务器响应超时,服务器没有在规定的时间内返回给客户端数据。

所以这该死的超时该怎么配?

这里有一份国外写的关于超时的建议:
http://blog.mongolab.com/2013/10/do-you-want-a-timeout/
上文给出的通常情况下:connectTimeout=5000,socketTimeout=0

附录

http://api.mongodb.org/java/2.13/com/mongodb/MongoClientOptions.Builder.html
http://api.mongodb.org/java/2.13/com/mongodb/MongoClientURI.html

关于WriteConcern

MongoDB提供了一个配置参数:write concern 来让用户自己衡量性能和写安全。分布式数据库中这样的参数比较常见,记得Cassandra中也有一个类似参数,不过那个好像是要写入几个节点返回成功。其实道理都一样分布式的集群环境考虑到性能因素不能确保每个成员都写入后在返回成功,所以只能交给用户根据实际场景衡量。

附录

http://my.oschina.net/u/217548/blog/195995
http://docs.mongodb.org/manual/core/write-concern/

MongoDB的锁机制

MongoDB的锁机制和一般关系数据库如 MySQL(InnoDB), Oracle 有很大的差异,InnoDB 和 Oracle 能提供行级粒度锁,而 MongoDB v2 只能提供库级粒度锁,这意味着当 MongoDB 一个写锁处于占用状态时,其它的读写操作都得干等。

初看起来库级锁在大并发环境下有严重的问题,但是 MongoDB 依然能够保持大并发量和高性能,这是因为 MongoDB 的锁粒度虽然很粗放,但是在锁处理机制和关系数据库锁有很大差异,主要表现在:

通常不出问题不等于没有问题,如果数据操作不当,依然会导致长时间占用写锁,比如下面提到的前台建索引操作,当出现这种情况的时候,整个数据库就处于完全阻塞状态,无法进行任何读写操作,情况十分严重。

解决问题的方法,尽量避免长时间占用写锁操作,如果有一些集合操作实在难以避免,可以考虑把这个集合放到一个单独的 MongoDB 库里,因为 MongoDB 不同库锁是相互隔离的,分离集合可以避免某一个集合操作引发全局阻塞问题。

建索引导致数据库阻塞

上面提到了 MongoDB 库级锁的问题,建索引就是一个容易引起长时间写锁的问题,MongoDB 在前台建索引时需要占用一个写锁(而且不会临时放弃),如果集合的数据量很大,建索引通常要花比较长时间,特别容易引起问题。

解决的方法很简单,MongoDB 提供了两种建索引的访问,一种是 background 方式,不需要长时间占用写锁,另一种是非 background 方式,需要长时间占用锁。使用 background 方式就可以解决问题。
例如,为超大表 posts 建立索引

//千万不用使用
db.posts.ensureIndex({user_id: 1})
//而应该使用
db.posts.ensureIndex({user_id: 1}, {background: 1})

哪些操作会对数据库产生锁

操作 锁类型
Issue a query Read lock
Get more data from a cursor Read lock
Insert data Write lock
Remove data Write lock
Update data Write lock
Map-reduce Read lock and write lock, unless operations are specified as non-atomic. Portions of map-reduce jobs can run concurrently.
Create an index Building an index in the foreground, which is the default, locks the database for extended periods of time.
db.eval() Write lock. db.eval() blocks all other JavaScript processes.
eval Write lock. If used with the nolock lock option, the eval option does not take a write lock and cannot write data to the database.
aggregate() Read lock

附录

https://ruby-china.org/topics/20128
http://docs.mongodb.org/v2.6/faq/concurrency/
http://docs.mongodb.org/v2.6/reference/method/db.collection.ensureIndex/#db.collection.ensureIndex

上一篇下一篇

猜你喜欢

热点阅读