文档章节

MongoDB使用中的一些问题

Flyer_cao
 Flyer_cao
发布于 2017/01/15 16:39
字数 3859
阅读 75
收藏 0

从shell中更新/写入到文档的数字,会变为float类型

引用:“shell中的数字都被MongoDB当作是双精度数。这意味着如果你从数据库中获得的是一个32位整数,修改文档后,将文档存回数据库的时候,这个整数也就被换成了浮点数,即便保持这个整数原封不动也会这样的。”

restore数据到新DB时,不要去先建索引

把bson数据文件restore到另一个DB时,需要注意:不能先创建索引再restore数据,否则性能极差,mongorestore工具默认会在restore完数据时,根据dump出来的index信息创建索引,无须自己创建,如果是要更换索引,也应该在数据入库完之后再创建。

bson size不能超过16MB的限制

单个文档的BSON size不能超过16MB。find查询有时会遇到16MB的限制,譬如使用$in 查询的时候,in中的数组元素不能太多。对一些特殊的数据源做MapReduce,MapReduce中间会将数据组合为“KEY:[VALUE1、VALUE2]”这样的格式,当value特别多的时候,也可能会遇上16MB的限制。 限制无处不在,需要注意,”The issue is that the 16MB document limit applies to everything - documents you store, documents MapReduce tries to generate, documents aggregation tries to return, etc.

批量插入

批量插入可以减少数据往服务器的提交次数,提高性能,一般批量提交的BSON size不超过48MB,如果超过了,驱动程序自动修改为往mongos的多次提交。

安全写入介绍及其沿革

关键字:acknowledge、write concern。 在2012年11月之前,MongoDB驱动、shell客户端默认是不安全写入,也就是fire-and-forget,动作发出之后,不关心是否真的写入成功,如果这时候出现了_id重复、非UTF8字符等异常,客户端不会知道。在2012年11月之后,默认为安全写入,安全级别相当于参数w=1,客户端可以知道写入操作是否成功。如果代码使用Mongo或者Collection来连接数据库,则说明它是默认不安全写入的legacy代码,安全写入已经把连接数据库修改为MongoClient接口。 安全写入可以分为三个级别,

  • 第一级是默认的安全写入,确认数据写入到内存中就返回(w=N属于这一级);
  • 第二级是Journal save,数据在写入到DB磁盘文件之前,MongoDB会先把操作写入到Journal文件,这一级指的是确认写入了Journal文件就返回;
  • 第三级是fysnc,所有数据刷写到到DB磁盘文件才返回。

一般第一级就足够了,第二级是为了保证在机器异常断电的情况下也不会丢失数据。安全写入要付出性能的代码:不安全写入的性能大概是默认安全写入的3倍。使用fync参数则性能更差,一般不使用。如果是副本集(replica set),其w=N参数,N表示安全写入到多少个副本集才返回。

善用索引——可能跟你以为的不一样

使用组合索引的时候,如果有两组索引,在限量查询的情况下,可能跟常规的认识不同: 利用组合索引做的查询,在不同数量级下会有不同性能: 组合索引A: {"age": 1, "username": 1} 组合索引B: {"username": 1, "age": 1} 全量查询:db.user.find({"age": {"$gte": 21, "$lte":30}}).sort({"username":1}) ,使用索引A的性能优于索引B。 限量查询: db.user.find({"age": {"$gte": 21, "$lte": 30}}).sort({"username": 1}).limit(1000) ,使用索引B的性能优于索引A。 这两个查询在使用索引A的时候,是先根据age索引找到符合age的数据,然后再对这些结果做排序。使用索引B的时候,是遍历name,对应的数据判断age,然后得到的结果是name有序的。 优先使用sort key索引,在大多数应用上执行得很好。

查询时索引位置的无顺序性

做find的时候,并不要求索引一定要在前面, 譬如:db.test集合中对R有索引 db.test.find({R:"AA", "H": "BB"}).limit(100) db.test.find({"H":"BB", "R" : "AA"}).limit(100) 这两个查找性能一样,它都会使用R索引。只有当R属性不参与排序且R的位置不影响查询结果的条件下,才会满足索引的无顺序行。

使用组合索引做shard key可以大幅度提高集群性能

集群分片应遵循“整体分散,局部递增”的原则。“固定值+增量值” 两字段做组合索引可以有效的实现分布式集群中的分散多热点写入、读取。

在单个MongoDB实例上,最高效的写入是顺序写入,而MongoDB集群则要求写入能随机,以便平均分散到多个MongoDB实例。所以最高效的写入是有多个局部热点:在多个MongoDB实例之间是分散写入,在实例内部是顺序写入。 要实现这一点,我们采用组合索引。

怎么建索引更能提高查询性能?

在查询时,索引是否高效,要注意它的cardinality(cardinality越高表示该键可选择的值越多),在组合索引中,让cardinality高的放在前面。也就是说在创建组合索引的时候,应该把枚举类型、布尔类型等低cardinality的属性排在后面,避免给低cardinality属性创建独立索引。

index cardinality(索引散列程度),表示的是一个索引所对应到的值的多少,散列程度越低,则一个索引键对应的值越多,索引效果越差。在使用索引时,高散列程度的索引可以更多的排除不符合条件的文档,让后续的比较在一个更小的集合中执行,这更高效。所以一般选择高散列程度的键做索引,或者在组合索引中,把高散列程度的键放在前面。

非原地update,性能会很差

update文档时,如果新文档的空间占用大于旧文档加上它周围padding的空间,那么就会放弃原来的位置,把数据拷贝到新空间。

TTL索引

TTL代表"time to live",TTL是一种特殊的索引,可以将集合中过期的数据删除。使用expireAfterSeconds 选项创建索引即可。 推荐和限制:

  • 使用usePowerOf2Sizes标识可以更有效的防止磁盘碎片的产生。 db.runCommand( {collMod: "products", usePowerOf2Sizes : true }) db.runCommand( {collMod: "products", usePowerOf2Sizes : false })
  • TTL索引必须建立在date类型的字段上,如果不是date类型将不会被删除。
  • TTL索引不能建立在_id字段上
  • TTL索引不能是联合索引,否则会报错,不让建
  • 如果date类型中包含一个数组,比如time:['date1','date2'],那么TTL会按照一个最早的进行过滤。
  • TTL不能建立在固定集合上(capped collection),因为固定集合不能删除数据。 使用方法: db.log.ensureIndex( { "createDate": 1 }, { expireAfterSeconds: 3600 } )

无法在索引建立之后再去增加索引的过期时间

如果索引建立指定了过期时间,后续要update过期时间可以这样子:

db.runCommand({"collMod":"a", index:{keyPattern:{"_":-1}, expireAfterSeconds: 60}})。 注意,通过collMod能修改过期时间的前提是:这个索引有过期时间,如果这个索引之前没有设置过期时间,那么无法update,只能删了索引,重建索引并指定过期时间。

###  **paddingFactor是什么?**
它是存储空间冗余系数,1.0表示没有冗余,1.5表示50%的冗余空间,有了冗余空间,可以让后续引发size增加的操作更快(不会导致重新分配磁盘空间和文档迁移),一般是在1到4之间。可以通过db.collection.stats()看到collection的该值“paddingFactor”。
该值是MongoDB自己处理的,使用者无法设置paddingFactor。我们可以在compact的时候对已经有的文档指定该值,但这个paddingFactor值不影响后续新插入的文档。
repairDatabase跟compact类似,也能移除冗余减少存储空间,但冗余空间少了会导致后续增加文档size的update操作变慢。
虽然我们无法设置paddingFactor,但是可以使用usePowerOf2Sizes保证分配的空间是2的倍数,这样也可以起到作用(MongoDB2.6版本起默认启用usePowerOf2Size)。
或者手动实现padding:在插入文档的时候先用默认字符占用一块空间,等到真实数据写入时,再unset掉它。
###  **usePowerOf2Size是什么**
这是为更有效的复用磁盘空间而设置的参数:分配的磁盘空间是2的倍数,如果超过了4MB,则是距离计算值最近的且大于它的完整MB数。
可以通过db.collections.stats()看到该值“userFlags”。
MongoDB2.6之后默认开启usePowerOf2Size参数
使用后的效果可以看这里的PPT:http://www.slideshare.net/mongodb/use-powerof2sizes-27300759

###  **aggregate pipeline 指定运算完成输出文档跟MapReduce相比有不足**
(基于MongoDB2.6版本)MapReduce可以指定输出到特定的db.collection中,例如:out_put = bson.SON([("replace", "collection_name" ), ("db", "xx_db")])
aggregate pipeline只能指定collection名字,也就意味着数据只能写入到本db,同时结果不能写入到capped collection、shard collection中。
相比之下,aggregate pipeline限制是比较多的,如果我们需要把结果放到某个DB下,则需要再做一次迁移:
db.runCommand({renameCollection:"sourcedb.mycol",to:"targetdb.mycol"})
但是!!上面的这条命令要求在admin下执行,且只能迁移往同shard下的DB,且被迁移的collection不能是shard的。

###  **停止MongoD进程的几种方式**
> ###  进入到MongoD的命令行模式执行shutdown,
>$ mongo --port 10001
> use admin
> db.shutdownServer()
>###  1方式的简化:**
mongo admin --port 10001 --eval "db.shutdownServer()"
>###  使用MongoD命令行关闭,需要指定db路径:
mongod --dbpath ./data/db --shutdown

###  **集群的shard key慎重采用hash**
如果你的日志是有日期属性的,那么shard key不要使用hash,否则删除过期日志时无法成块删除;在更新日志的时候,也不能利用局部性原理,查找、更新、插入数据都会因此而变慢。一般来说,hash id应付小数据量时压力不大,但在数据量较大(热数据大于可用内存容量)时,CRUD性能极差,且会放大碎片对性能的影响:数据非常分散,当有过期日志被删除后,这些删除后的空间成为碎片,可能会因为磁盘预读策略被加载到内存中。另外,采用hash shard key还会浪费掉一个索引,浪费不少空间。

###  **副本数也不用太多**
如果你的副本数量超过了12个(MongoDB3.0.0超过了50个),那么就要选择使用 master-slave ,但这样会失去故障自恢复功能,主节点故障时,需要手动去切换到无故障节点。

###  **mongos的config server配置信息中不要使用localhost、127.0.0.1**
启动mongos时,config server的配置信息不得使用localhost、127.0.0.1,否则添加其它机器的shard时,会出现错误提示:
>
"can’t use localhost as a shard since all shards need to communicate. either use all shards and configdbs in localhost or all in actual IPs host: xxxxx isLocalHost"
以新的config server启动mongos,也需要重启config server,否则会有错误提示:
“could not verify config servers were active and reachable before write”
如果改完后面又出现 “mongos specified a different config database string”  错误,那么还需要重启mongod,
>修改了config server 几乎是要全部实例重启。另外,在配置replica set时也不得使用localhost、127.0.0.1。


###  **shard key的选择跟update性能紧密关联**
分布式MongoDB,shard key的选择跟update性能,甚至是update可用性有很大关系,需要注意。
1.在对文档个别字段update时,如果query部分没有带上shard key,性能会很差,因为mongos需要把这条update语句派发给所有的shard 实例。
2.update 的upsert参数为true时,query部分必须带上 shard key,否则语句执行出错,例子:
>db.test.update({"_id":".7269993106A92327A89ABCD70D46AD5"}, {"$set":{"P": "aaa"}, "$setOnInsert":{"TEST":"a"}}, true)
WriteResult({
"nMatched" : 0,
"nUpserted" : 0,
"nModified" : 0,
"writeError" : {
"code" : 61,
"errmsg" : "upsert { q: { _id: \".7269993106A92327A89ABCD70D46AD5\" }, u: { $set: { P: "aaa" }, $setOnInsert: { TEST: \"a\" } }, multi: false, upsert: true } does not contain shard key for pattern { _: 1.0, B: 1.0 }"
}
})
    
    这是因为如果没有shard key,mongos既不能在所有shard实例上执行这条语句(可能会导致每个shard都插入数据),也无法选择在某个shard上执行这条语句,于是出错了。
    另外,需要特别注意,如果使用pymongo引擎,它不会告诉你出错了,只是函数调用陷入不返回,在shell下执行才能看到错误信息。

###  **通过repairDatabase提高性能**
从db.stats()中可以看到几个跟碎片相关的关键字段,
>- dataSize,表示数据的大小,它包含了padding的空间;
>- storageSize,表示这些数据存储占用的空间,包含了dataSize和被删除数据所占空间,

    可以认为storageSize/dataSize就是磁盘碎片比例,当删除、update文档比较多后,它会变大,考虑做repairDatabase,以减少碎片让数据更紧凑。在实践中,这对提高CURD性能极其有用。
    repairDatabase时需要注意:它是把数据拷贝到新的地方,然后再做处理,所以repair之前在DB目录所在磁盘需要预留一倍的空闲磁盘空间,如果你发现磁盘空间不足,可以停止服务,然后增加一块新磁盘,再执行实例级别的repair,并指定--repairpath为新磁盘路径,
    >mongod --dbpath /path/to/corrupt/data --repair --repairpath    /media/external-hd/data/db,实例的数据会拷贝到/media/external-hd/data/db上做处理。


###  **索引字段的长度不能大于1024字节**
索引字段的长度不能大于1024字节,否则shell下会有插入错误提示:
>"errmsg" : "insertDocument :: caused by :: 17280 Btree::insert: key too large to index”。

###  **config DB无法写入**
因config DB无法修改,只可读,导致drop、enablesharding失败:
config server 相关日志:2015-06-11T16:51:19.078+0800 [replmaster] local.oplog.$main Assertion failure isOk() src/mongo/db/storage/extent.h 80
mongos 相关日志: [LockPinger] warning: pinging failed for distributed lock pinger 'xxx:1234/xxx:1235:1433993544:1804289383'. : : caused by :: isOk()
这是同事遇到的问题,不确定是什么操作引起的。重启、configdb做repair均无法解决。
最后通过dump、restore解决:(1)把旧configdb dump出来;(2)restore到新的configure server;(3)mongos采用新的configure server;(4)重启全部mongod。

###  **sort()方法的size限制**
当我对一个没有建索引的字段做find,然后做sort的时候,可能触发sort的size不超过32MB限制,例如:
>db.stotal.find({}).sort({'type':-1})
Error: error: {
        "$err" : "Executor error: Overflow sort stage buffered data usage of 33554493 bytes exceeds internal limit of 33554432 bytes",
        "code" : 17144
}
    
    有两种解决方法:
解决方法一:对需要排序的字段建索引 db.stotal.ensureIndex({'type': -1})
解决方法二:修改默认配置,把sort时可以用的内存设置大点:
>db.adminCommand({setParameter:1, internalQueryExecMaxBlockingSortBytes:335544320})

    这两种解决方法各有利弊:(1)增加了索引会导致数据写入变慢,存储占用变多;(2)不建索引修改默认配置,会导致sort的时候占用更多的内存。

© 著作权归作者所有

Flyer_cao
粉丝 1
博文 39
码字总数 46068
作品 0
深圳
程序员
私信 提问
视觉中国潘凡谈MongoDB应用实践

受访人 潘凡 采访人 黄玲艳 发布于 2011年8月25日 概要 本次采访中,来自视觉中国的技术总监兼架构师潘凡分享了视觉中国网站在技术选型中的一些经验,根据网站业务需求及数据量,最终选择Mon...

zjf_sdnu
2011/10/15
0
0
关于 MongoDB 你需要知道的几件事

Henrique Lobo Weissmann是一位来自于巴西的软件开发者,他是itexto公司的联合创始人,这是一家咨询公司。近日,Henrique在博客上撰文谈到了关于MongoDB的一些内容,其中有些观点值得我们,特...

abing_hu
2013/11/06
8.5K
12
面向 MongoDB 优化 Redhat 和 CentOS

在以前的一篇博客“Tuning Linux for MongoDB”(优化 Linux 下的 MongoDB)中,我罗列了几种方法——如何在 Linux 项目中高效调试 MongoDB 的部署。而这篇博客就是在此基础之上做的扩展。 ...

oschina
2016/12/16
2.7K
2
是时候跟 MongoDB 说再见了

在过去的两到三年的时间内,我一直在一个中等规模的项目中使用 MongoDB。 但因为各种技术上的原因,到了和 MongoDB 说再见的时候了,我的原因有以下几点: MongoDB 当前的内存模型基于内存映...

oschina
2012/05/20
21.5K
59
Henrique Lobo Weissmann: MongoDB 逐渐变得无关紧要

与 TokuMX 和 PostgreSQL 相比较使得 MongoDB 处于劣势。但它仍然是一款很好的产品,而且会继续改进来与这些替代产品竞争,然而目前来看它最多只能排在第三名。2015年MongoDB的改进和发展,我...

shezjl
2015/08/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

在Javascript中Eval函数的使用

【eval()函数】 JavaScript有许多小窍门来使编程更加容易。 其中之一就是eval()函数,这个函数可以把一个字符串当作一个JavaScript表达式一样去执行它。 举个小例子: var the_unevaled_ans...

花漾年华
9分钟前
0
0
[日更-2019.5.22、23] Android 系统的分区和文件系统(二)--Android 文件系统中的文件

声明 Android系统中有很多分区,每个分区内的文件系统一般都不同的,使用ADB进入系统/目录下可发现挂载这很多的目录,不同的目录中可来自不同的分区及文件系统; 那么,就来分下这些目录里面...

小馬佩德罗
13分钟前
0
0
数组操作相关算法

/*数组的相关的算法操作:1、在数组中找最大值/最小值*/class Test11_FindMax{public static void main(String[] args){int[] array = {4,2,6,8,1};//在数组中找最大...

architect刘源源
今天
2
0
okhttp3 以上版本在安卓9.0无法请求数据的解决方案

应用官方的说明:在 Android 6.0 中,我们取消了对 Apache HTTP 客户端的支持。 从 Android 9 开始,默认情况下该内容库已从 bootclasspath 中移除且不可用于应用。且Android P 限制了明文流量...

chenhongjiang
今天
12
0
简单示例:NodeJs连接mysql数据库

开篇引用网上的说法: 简单的说 Node.js 就是运行在服务端的 JavaScript。Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。Node.js是一个事件驱动I/O服务端JavaScript环境,基于...

李朝强
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部