MongoDB

基础

原理

1. Mongodb 并发控制之乐观锁

Mongodb不善于处理事务,但提供了findAndModify命令。该命令允许对文档进行原子性更新,并在同一次调用中返回:

1
2
3
4
5
db.collection_yown.findAndModify(
{
query:{"name":"yown"},update:{"version":2},new:true or false
}
)

findAndModify是有返回值的,输出中的value字段即返回修改之前的文档,使用 new:true选项返回修改后的文档。

update是更新操作,是没有返回值的。

findAndModify 强调操作的原子性(atomically),比如用来实现自增1的操作或者操作队列。属于 get-and-set 式的操作,一般来讲,findAndModify 比update操作稍慢,因为需要等待数据库的响应。

另外findAndModify ,其中modify可以是update,还可以是remove

2. findAndModify可能引起的原子性问题

update 方法的三个参数是upsert,这个参数是个布尔类型,默认是false。当它为true的时候,update方法会首先查找与第一个参数匹配的记录,在用第二个参数更新之,如果找不到与第一个参数匹配的的记录,就插入一条

当findAndModify()包含upsert:true选项,并且查询字段不是唯一索引时,该方法可能会在某些情况下多次插入文档。

如下:

1
2
3
4
5
6
db.people.findAndModify({
query: { name: "Andy" },
sort: { rating: 1 },
update: { $inc: { score: 1 } },
upsert: true
})

当多个客户端同时发出了这个指令,然后在服务端并行执行,而都没有找到query的匹配,可能同时执行了多个upsert操作。导致数据重复。

如果不使用upsert,就没有这种问题。

3. 使用$isolation来保证隔离性

使用$isolated操作符可以保证单个写入操作修改多个文档的时候不被交错。

$isolated其实是在整个数据库(Mongodb的手册对这点说明不清楚,也可能是在集合层面加独占锁,但是有一点文档中是说明的,不论在哪个层面加独占锁,都会导致真个数据库单线程化)加独占锁(即使是对于WiredTiger存储引擎也是),在这期间不能进行其他任何的读写操作。所以如果$isolated的操作执行的时间过长,会大大的影响系统的并发性能。

1
2
3
4
5
db.foo.update(
{ status : "A" , $isolated : 1 },
{ $inc : { count : 1 } },
{ multi: true }
)
  • $ isolation不保证多个文档操作的原子性。
  • $ isolation保证多个文档操作不会被跟其他操作交错。
  • $ isolation保证此操作在进行到某一个文档的更新的时候,在不提交或者回滚之前,不会被客户端看到。也就是说不会导致这个文档的查询产生脏读。

对比MySQL,你究竟在什么时候更需要MongoDB(转载)

1. 优点:

  • 更高的写负载:MongoDB更关注高的插入速度

  • 不可靠环境保证高可用性:设置副本集(主-从服务器设置)不仅方便而且很快,此外,使用MongoDB还可以快速、安全及自动化的实现节点(或数据中心)故障转移

  • 未来会有一个很大的规模:数据库扩展是非常有挑战性的,当单表格大小达到5-10GB时,MySQL表格性能会毫无疑问的降低。如果你需要分片并且分割你的数据库,MongoDB将很容易实现这一点。容易扩展,可以支持T级别的数据量。

  • 使用基于位置的数据查询:MongoDB支持二维空间索引,因此可以快速及精确的从指定位置获取数据。(唯一索引 复合索引)

  • 非结构化数据的爆发增长:鉴于MongoDB的弱数据结构模式,添加1个新字段不会对旧表格有任何影响,整个过程会非常快速

  • 2000-3000以上读写每秒

  • 数据模型不用确定

  • 大量地理位置查询

2. 不足:

  • 必须避免在要求高事务安全的情景下使用MongoDB:比如一个1000万美元的交易

  • Mongodb在保存数据的时候,不是实时写入到硬盘的,所以有可能出现数据丢失的情况

  • 多表关联仅仅支持Left Outer Join

  • 16MB文档大小限制

  • 不支持中文排序

MongoDB中的一些坑

1. 数据库级锁

建索引导致数据库阻塞

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

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

1
db.posts.ensureIndex({user_id: 1}, {background: 1})

2. 不合理使用嵌入 embed document

  • 解决问题的方法,就是把 embed 关联更改成的普通外键关联,就是类似关系数据库的做法

  • 当关联对象的数据不固定或者经常发生变化时,一定要避免使用 embed 关联,不然会死的很惨。

3. 不合理使用 Array 字段

频繁的增加修改操作导致大量长时间数据库写锁,从而引发 MongoDB 数据库性能急剧下降

解决问题的方法:

  • 我们把Array转移到了内存数据库 redis 中,避免了频繁更改 MongoDB 中的 User, 从而彻底解决问题。

  • 如果不使用 redis,也可以建立一个锁集合,使用外键形式关联。

MongoDb安装

1. 安装

1
2
wget --no-check-certificate http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.0.6.tgz
tar -zxvf mongodb-linux-x86_64-3.0.6.tgz

2. yum安装

增加repo库配置文件

1
/etc/yum.repos.d/mongodb-org-3.2.repo

内容为

1
2
3
4
5
[mongodb-org-3.2]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/7Server/mongodb-org/3.2/x86_64/
gpgcheck=0
enabled=1

安装

1
yum install -y mongodb-org

启动

1
2
3
service mongod restart

mongod -f /etc/mongod.conf #如果没有正常关闭数据库,则用这个命令启动

Cent OS 7上需要把mongoDB添加到systemd,否则会出现下面的错误

1
systemd[1]: Failed to start SYSV: Mongo is a scalable, document-oriented database..
1
2
3
4
5
6
7
8
9
10
11
12
13
# vi /usr/lib/systemd/system/mongod.service
[Unit]
Description=mongodb database

[Service]
User=mongod
Group=mongod
Environment="OPTIONS=--quiet -f /etc/mongod.conf"
ExecStart=/usr/bin/mongod $OPTIONS run
PIDFile=/var/run/mongodb/mongod.pid

[Install]
WantedBy=multi-user.target

建立链接

1
# ln -s /usr/lib/systemd/system/mongod.service /etc/systemd/system/multi-user.target.wants/

重新加载systemctl

1
# systemctl daemon-reload

配置/etc/mongod.conf

两款Mongodb的可视化工具,支持最新的Mongodb 3.2版本。

操作

0. db.help()

查看命令提示

show dbs

显示数据库,需要注意的是show dbs,只会显示有数据的库,没有数据的库,是不会显示的。

1. 根据时间查询

在FIND里面($gte/$lte/$gt/$lt)

1
2
3
4
5
6
7
8
9
10
11
{
"startTime":
{
$gte:"2015/07/12"
}

}

{
"_id" : ObjectId("561b2fb705924b08dc94ff8a")
}

2. 更新列名

在UPDATE里面

1
{$rename : {"groupName" : "group_name"}}

3. 删除列

在UPDATE里面

1
{$unset:{"id":10}}

批量更新内容

1
2
3
4
5
6
db.rank_scores.find({}).forEach(
function(item){
if(item.userName.indexOf("player_")>=0)
db.rank_scores.update({"_id":item._id},{$set:{"userName":"user_"+item.userName.substr(7)}},false,true)
}
)

4. 连接本机

1
./mongo --port 37017

报错:child process failed, exited with error number 100
没有正常关闭mongodb引起的。
先删除/home/cxy/mongodb/interstellar/db/下的mongod.lock
然后以repair的模式启动

1
./mongod --config ../conf/interstellar.conf --repair

然后接着在启动一次

1
./mongod --config ../conf/interstellar.conf

现在就可以查看到mongod的进程存在了,可以正常使用了

5. 如何正常关闭mongodb?

先通过shell连上服务器:

1
2
3
mongo
use admin
db.shutdownServer()

6. 创建用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use admin
db.createUser(
{
user: "root",
pwd: "_gamedo188",
roles: [ "root" ]
}
)

db.createUser(
{
user: "sa",
pwd: "_gamedo188",
roles: [ { role: "__system", db: "admin" } ]
}
)

7. 登录用户

1
db.auth("sa", "_gamedo188")

8. 安全验证启动

1
./mongod --auth --config ../interstellar.conf

9. 查看mongodb最大连接数

1
db.serverStatus().connections

10. 查看一个表wechatkeys的索引

1
db.getCollection('wechatkeys').getIndexes()

11. 新建一个表wechatkeys的索引key,1为升序,2为降序

1
db.getCollection('wechatkeys').ensureIndex({key:1})

12. 绑定数据库到指定内网ip,防止外网访问攻击

1
bind_ip=127.0.0.1

13. 返回部分字段

举例:

1
2
3
4
5
6
7
8
// 1. 返回name和age两个字段(默认会返回_id)
db.user.find({}, {"name" : 1, "age" : 1});

// 2. 返回name和age两个(显示排除_id字段)
db.user.find({}, {"name" : 1, "age" : 1, "_id" : 0});

// 3. 不返回password字段
db.user.find({}, {"password" : 0});

14. $exists 当前不能使用索引,但 $ne 可以

1
db.players.count({fb:{$ne:null}})

备份mongo

1. 备份

1
./mongodump -d critz -o /www/backups/mongodb/critz-2016-02-18

mongodump有一个值得一提的选项是–oplog

1
2
3
==mongodump的进行过程中并不会把数据库锁死以保证整个库冻结在一个固定的时间点,这在业务上常常是不允许的。==

所以就有了dump的最终结果中A集合是10点整的状态,而B集合则是10点零1分的状态这种情况。这样的备份即使恢复回去,可以想象得到的结果恐怕意义有限。那么上面这个oplog.bson的意义就在这里体现出来了。如果在dump数据的基础上,再重做一遍oplog中记录的所有操作,这时的数据就可以代表dump结束时那个时间点(point-in-time)的数据库状态.

2. 压缩

1
2
3
cd /www/backups/mongodb
tar zcvf critz-2016-02-18.tar.gz critz-2016-02-18
cp critz-2016-02-18.tar.gz /www/dotado/jetty-8888/webapps/ROOT/

3. 恢复

1
./mongorestore -d critz /www/mongodb/backups/critz-2016-03-30/critz

4. shell备份脚本

1
2
3
4
5
6
7
8
9
#!/bin/sh

# mogodb
mogodbdirname="interstellar-`date +%y-%m-%d`"
mogodbfilename="`date +%y-%m-%d`.tar.gz"
cd /www/mongodb/mongodb-linux-x86_64-3.0.6/bin/
./mongodump -d interstellar -o /www/backups/mongodb/$mogodbdirname
cd /www/backups/mongodb
tar zcvf $mogodbfilename $mogodbdirname

实战

实用技巧

1. sort排序动态字段

1
2
3
var sortOp = {};
sortOp[xxxxxx] = -1;
players.find({}).sort(sortOp).limit(100).exec(cb);

2. MongoDB 实现按列累加统计

1
2
// 统计总共积分是多少
db.actives5.aggregate([{$group:{_id: null,totalScore: { $sum: '$score' }}}])

3. 统计最高最低平均

1
2
3
db.actives5.aggregate([{$group:{_id: null,maxScore: { $max: '$score' },minScore: { $min: '$score' },avgScore: { $avg: '$score' }}}])

db.actives5.aggregate([{$match:{score:{$gte:1500}}},{$group:{_id:null,totalScore: { $sum: '$score' },maxScore: { $max: '$score' },minScore: { $min: '$score' },avgScore: { $avg: '$score' }}}])

4. 导出导入单独表

1
2
mongoexport -d xxxxxx -o actives.json -c actives
mongoimport -d xxxxxx --file actives.json -c actives

5. 建立索引

1
db.players.ensureIndex({fb:1})

6. 模糊查询

1
2
3
const reg = new RegExp(data.search, 'i'); //不区分大小写
options = {content : {$regex : reg}};
db.actives5.find(options)

7. 查看db状态

1
2
3
4
5
# mongostat
insert query update delete getmore command % dirty % used flushes vsize res qr|qw ar|aw netIn netOut conn time
2 332 32 *0 0 49|0 0.8 30.0 0 6.11G 5.23G 0|0 0|0 70.3k 1.62m 324 2018-07-25T08:37:22Z
4 364 40 2 0 48|0 0.8 30.0 0 6.11G 5.23G 0|0 0|0 114k 1.92m 324 2018-07-25T08:37:23Z
6 365 35 4 0 52|0 0.8 30.0 0 6.12G 5.23G 0|0 0|0 79.9k 1.68m 324 2018-07-25T08:37:24Z

8. 查看mongodb的日志

1
2
3
# tail -n500 -f /var/log/mongodb/mongod.log
2018-07-24T15:19:25.880+0000 I COMMAND [ftdc] serverStatus was very slow: { after basic: 40, after asserts: 150, after connections: 180, after extra_info: 220, after globalLock: 230, after locks: 240, after network: 330, after opcounters: 450, after opcountersRepl: 510, after storageEngine: 570, after tcmalloc: 1900, after wiredTiger: 2260, at end: 2550 }
2018-07-24T15:19:25.901+0000 I COMMAND [conn31541] command admin.$cmd command: isMaster { ismaster: true } keyUpdates:0 writeConflicts:0 numYields:0 reslen:178 locks:{} protocol:op_query 383ms

9. mongodb的快速迁移

1
2
3
4
5
6
7
8
9
mongodb数据库的迁移除了常规的mongodump、mongoexport、mongorestore、mongoimport之外,可以采用直接拷贝数据库文件的方式进行。
拷贝文件需要停止数据库服务,停止后直接拷贝数据库文件目录中的数据即可。
注意事项:
1、.lock文件不要拷贝;
2、diagnostic.data的文件夹不要拷贝,如果拷贝,在新的数据库运行时会出现错误,需要修复,时间有点长。

拷贝完成,用相同的参数和数据库版本启动数据库即可。
这样拷贝数据及完成数据迁移。
采用mongodump需要重建数据库,时间长;如果迁移的机器性能比较差,重建索引有困难。

10. 查询数据导出

  • 新建一个js文件,将查询方法写进去,如playersPage_1_1.js,文件内容如下
1
2
3
4
5
var c = db.playersPage.aggregate([{ $match: {date:{$lt:"2018-09-15"}}},{"$group" : {_id:{lp1:"$lp1"}, count:{$sum:1}}}]);
while(c.hasNext()) {
var document = c.next();
print(document._id.lp1 + "," + document.count);
}
  • 输入命令来执行
1
mongo localhost:27017/ludosocket playersPage_1_1.js > playersPage_1_1.2018-09-17.csv

这样查询的结果就会直接生成在当前文件夹下的playersPage_1_1.2018-09-17.js文件

11. group by count 分组count

如:查询日期小于2018-09-15,根据lp1分组的玩家总数

1
db.playersPage.aggregate([{ $match: {date:{$lt:"2018-09-15"}}},{"$group" : {_id:{lp1:"$lp1"}, count:{$sum:1}}}])

如:查询日期小于2018-09-15,根据lp1和lp2组合分组的玩家总数

1
db.playersPage.aggregate([{ $match: {date:{$lt:"2018-09-15"}}},{"$group" : {_id:{lp1:"$lp1",lp2:"$lp2"}, count:{$sum:1}}}])

数据库高可用和分区解决方案-MongoDB 篇

http://udn.yyuap.com/article-8203.html

MongoDB Sharding 集群配置

参考

  1. Sharding cluster介绍

    这是一种可以水平扩展的模式,在数据量很大时特给力,实际大规模应用一般会采用这种架构去构建monodb系统.

    MongoDB Sharding技术的应用场景:

     A.如果数据集data set大小将要或者已经超过了单个MongoDB实例的容量大小。
     
     B.活动的工作集working set大小将要超过最大物理内存大小
     
     C.单个MongoDB实例无法满足频繁的写操作。
    

    如果以上三种情况没有满足,不需要部署sharding,只会增加复制度,同时在设计数据模型时,也要考虑到以后作分片的情况。

    Sharding只会在数据量比较大的情况下才会发挥作用,默认的chunk大小是64MB,只有在满足特定条件下,balancer进程才会将数据迁移到其他shard上,否则数据会一直存储到单个shard上。

    主要是从几个方面考虑这个问题:

     1、方便扩展,如果需要横向扩展,把shardkey加上,然后加资源即可。
     2、避免不同的程序驱动对rs高可用的不一致情况。
     3、虚拟化后config的资源消耗主要是磁盘io,其他cpu、内存、磁盘容量等都几乎可以忽略不计。
     4、统一资源形式,如果同时既提供rs也提供shard的话,对MongoDB不怎么熟悉的用户其实是很容易混淆。
    

    要构建一个 MongoDB Sharding Cluster,需要三种角色:

     Shard Server: mongod 实例,用于存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组个一个relica set承担,防止主机单点故障
     
     Config Server: mongod 实例,存储了整个 Cluster Metadata,其中包括 chunk 信息。
     
     Route Server: mongos 实例,前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。用于分摊客户端的请求压力。如果使用多个mongos实例,可以使用HAProxy或者LVS等代理来转发客户端请求到后端的mongos,必须要配置成client affinity模式保证来自同一个客户端的请求转发到后端相同的mongos.通常会将mongos实例部署到应用服务器上
    
  2. 实际环境架构

     分别在3台机器运行一个mongod实例(称为mongod shard11,mongod shard12,mongod shard13)组织replica set1,作为cluster的shard1
     分别在3台机器运行一个mongod实例(称为mongod shard21,mongod shard22,mongod shard23)组织replica set2,作为cluster的shard2
     每台机器运行一个mongod实例,作为3个config server
     每台机器运行一个mongos进程,用于客户端连接
    
主机  IP 端口信息
Server1 10.1.1.1 mongod shard11:27017
mongod shard12:27018
mongod config1:20000
mongs1:30000
Server2 10.1.1.2 mongod shard12:27017
mongod shard22:27018
mongod config2:20000
mongs2:30000
Server3 10.1.1.3 mongod shard13:27017
mongod shard23:27018
mongod config3:20000
mongs3:30000
  1. 软件准备

    创建用户

     groupadd -g 20001 mongodb
     useradd -u 20001 -g mongodb mongodb
     passwd mongodb
    

    安装monodb软件

     su – mongodb
     tar zxvf mongodb-linux-x86_64-1.6.2.tar
    

    安装好后,目录结构如下:

     tree mongodb-linux-x86_64-1.6.2
     mongodb-linux-x86_64-1.6.2
     |– GNU-AGPL-3.0
     |– README
     |– THIRD-PARTY-NOTICES
     `– bin
         |– bsondump
         |– mongo
         |– mongod
         |– mongodump
         |– mongoexport
         |– mongofiles
         |– mongoimport
         |– mongorestore
         |– mongos
         |– mongosniff
         `– mongostat
    

    创建数据目录

    根据本例sharding架构图所示,在各台sever上创建shard数据文件目录

     Server1:
     su – monodb
     cd /home/monodb
     mkdir -p data/shard11
     mkdir -p data/shard21
     Server2:
     su – monodb
     cd /home/monodb
     mkdir -p data/shard12
     mkdir -p data/shard22
     Server3:
     su – monodb
     cd /home/monodb
     mkdir -p data/shard13
     mkdir -p data/shard23
    
  2. 配置replica sets

    配置shard1所用到的replica sets:

    Server1:

     cd /home/mongodb/mongodb-linux-x86_64-1.6.2/bin
     ./mongod –shardsvr –replSet shard1 –port 27017 –dbpath /home/mongodb/data/shard11 –oplogSize 100 –logpath /home/mongodb/data/shard11.log –logappend –fork
    

    Server2:

     cd /home/mongodb/mongodb-linux-x86_64-1.6.2/bin
     ./mongod –shardsvr –replSet shard1 –port 27017 –dbpath /home/mongodb/data/shard12 –oplogSize 100 –logpath /home/mongodb/data/shard12.log –logappend –fork
    

    Server3:

     cd /home/mongodb/mongodb-linux-x86_64-1.6.2/bin
     ./mongod –shardsvr –replSet shard1 –port 27017 –dbpath /home/mongodb/data/shard13 –oplogSize 100 –logpath /home/mongodb/data/shard13.log –logappend –fork
    

    初始化replica set

    用mongo连接其中一个mongod,执行:

     config = {_id: ‘shard1′, members: [
                               {_id: 0, host: '10.1.1.1:27017'},
                               {_id: 1, host: '10.1.1.2:27017'},
                               {_id: 2, host: '10.1.1.3:27017'}]
                }
     
     rs.initiate(config);
    

    同样方法,配置shard2用到的replica sets:

    server1:

     cd /home/mongodb/mongodb-linux-x86_64-1.6.2/bin
     ./mongod –shardsvr –replSet shard2 –port 27018 –dbpath /home/mongodb/data/shard21 –oplogSize 100 –logpath /home/mongodb/data/shard21.log –logappend –fork
    

    server2:

     cd /home/mongodb/mongodb-linux-x86_64-1.6.2/bin
     ./mongod –shardsvr –replSet shard2 –port 27018 –dbpath /home/mongodb/data/shard22 –oplogSize 100 –logpath /home/mongodb/data/shard22.log –logappend –fork
    

    server3:

     cd /home/mongodb/mongodb-linux-x86_64-1.6.2/bin
     ./mongod –shardsvr –replSet shard2 –port 27018 –dbpath /home/mongodb/data/shard23 –oplogSize 100 –logpath /home/mongodb/data/shard23.log –logappend –fork
    

    初始化replica set

    用mongo连接其中一个mongod,执行:

     config = {_id: ‘shard2′, members: [
                               {_id: 0, host: '10.1.1.1:27018'},
                               {_id: 1, host: '10.1.1.2:27018'},
                               {_id: 2, host: '10.1.1.3:27018'}]
                }
     
     rs.initiate(config);
    
  3. 配置三台config server

    Server1:

     mkdir -p /home/mongodb/data/config
     ./mongod –configsvr –dbpath /home/mongodb/data/config –port 20000 –logpath /home/mongodb/data/config.log –logappend –fork   #config server也需要dbpath
    

    Server2:

     mkdir -p /home/mongodb/data/config
     ./mongod –configsvr –dbpath /home/mongodb/data/config –port 20000 –logpath /home/mongodb/data/config.log –logappend –fork
    

    Server3:

     mkdir -p /home/mongodb/data/config
     ./mongod –configsvr –dbpath /home/mongodb/data/config –port 20000 –logpath /home/mongodb/data/config.log –logappend –fork
    
  4. 配置mongos实例(query routers)

    在server1,server2,server3上分别执行:

     ./mongos –configdb 10.1.1.1:20000,10.1.1.2:20000,10.1.1.3:20000 –port 30000 –chunkSize 5 –logpath /home/mongodb/data/mongos.log –logappend –fork 
    

    mongs不需要dbpath

  5. Configuring the Shard Cluster

    连接到其中一个mongos进程,并切换到admin数据库做以下配置

    连接到mongs,并切换到admin

     ./mongo 10.1.1.1:30000/admin
     db
     Admin
     
    

    加入shards

    如果shard是单台服务器,用db.runCommand( { addshard : “[:]” } )这样的命令加入,如果shard是replica sets,用replicaSetName/[:port][,serverhostname2[:port],…]这样的格式表示,例如本例执行:

     db.runCommand( { addshard : "shard1/10.1.1.1:27017,10.1.1.2:27017,10.1.1.3:27017″,name:"s1″,maxsize:20480} );
     db.runCommand( { addshard : "shard2/10.1.1.1:27018,10.1.1.2:27018,10.1.1.3:27018″,name:"s2″,maxsize:20480} );
    

    注意:在添加第二个shard时,出现error:test database 已经存在的错误,这里用mongo命令连接到第二个replica set,用db.dropDatabase()命令把test数据库给删除然后就可加入

    可选参数

     Name:用于指定每个shard的名字,不指定的话系统将自动分配
     maxSize:指定各个shard可使用的最大磁盘空间,单位megabytes
    

    Listing shards

     db.runCommand( { listshards : 1 } )
     
    

    如果列出了以上二个你加的shards,表示shards已经配置成功

    激活数据库分片

    命令:

     db.runCommand( { enablesharding : "<dbname>" } );
     
    

    通过执行以上命令,可以让数据库跨shard,如果不执行这步,数据库只会存放在一个shard,一旦激活数据库分片,数据库中不同的collection将被存放在不同的shard上,但一个collection仍旧存放在同一个shard上,要使单个collection也分片,还需单独对collection作些操作

  6. Collecton分片

    要使单个collection也分片存储,需要给collection指定一个分片key,通过以下命令操作:

     db.runCommand( { shardcollection : "<namespace>",key : <shardkeypatternobject> });
    
     注:
      a. 分片的collection系统会自动创建一个索引(也可用户提前创建好)
      b. 分片的collection只能有一个在分片key上的唯一索引,其它唯一索引不被允许
     One note: a sharded collection can have only one unique index, which must exist on the shard key. No other unique indexes can exist on the collection.
    

    分片collection例子

     db.runCommand( { shardcollection : "test.c1″,key : {id: 1} } )
     for (var i = 1; i <= 200003; i++) db.c1.save({id:i,value1:"1234567890″,value2:"1234567890″,value3:"1234567890″,value4:"1234567890″});
     db.c1.stats()
     {
             “sharded” : true,
             “ns” : “test.c1″,
             “count” : 200003,
             “size” : 25600384,
             “avgObjSize” : 128,
             “storageSize” : 44509696,
             “nindexes” : 2,
             “nchunks” : 15,
             “shards” : {
                     “s1″ : {
                             “ns” : “test.c1″,
                             “count” : 141941,
                             “size” : 18168448,
                             “avgObjSize” : 128,
                             “storageSize” : 33327616,
                             “numExtents” : 8,
                             “nindexes” : 2,
                             “lastExtentSize” : 12079360,
                             “paddingFactor” : 1,
                             “flags” : 1,
                             “totalIndexSize” : 11157504,
                             “indexSizes” : {
                                     “_id_” : 5898240,
                                     “id_1″ : 5259264
                             },
                             “ok” : 1
                     },
                     “s2″ : {
                             “ns” : “test.c1″,
                             “count” : 58062,
                             “size” : 7431936,
                             “avgObjSize” : 128,
                             “storageSize” : 11182080,
                             “numExtents” : 6,
                             “nindexes” : 2,
                             “lastExtentSize” : 8388608,
                             “paddingFactor” : 1,
                             “flags” : 1,
                             “totalIndexSize” : 4579328,
                             “indexSizes” : {
                                     “_id_” : 2416640,
                                     “id_1″ : 2162688
                             },
                             “ok” : 1
                     }
             },
             “ok” : 1
     }
    

问题汇总

  1. Mongodb 占用99%CPU并且查询速度很慢原因查找

    因为程序有将近40个线程一直在不停的查询数据库并插入, 而竟然没有在数据库中建索引
    建立索引后CPU使用率瞬间降下来了,并且速度超快,抽查也顺利起来了

分析数据库正在执行的请求

执行 db.currentOp() 命令,能看到数据库当前正在执行的操作

secs_running/microsecs_running: 这个值重点关注,代表请求运行的时间,如果这个值特别大,就得注意了,看看请求是否合理
  1. DeprecationWarning: Mongoose: mpromise (mongoose’s default promise library) is deprecated, plug in your own promise library instead: http://mongoosejs.com/docs/promises.html

建立连接前增加这句即可

1
2
mongoose.Promise = global.Promise; // ADD THIS
mongoose.connect('localhost:27017/mydb');
  1. WiredTiger引起的Cannot allocate memory问题

问题日志:

1
2
3
4
2018-08-28T13:59:02.505+0000 E STORAGE  [conn6943] WiredTiger (12) [1535464742:504284][15309:0x7fc22c103700], file:collection-4--4936006946892585673.wt, WT_CURSOR.insert: memory allocation of 17522688 bytes failed: Cannot allocate memory
2018-08-28T13:59:02.510+0000 I - [conn6943] Invariant failure: ret resulted in status UnknownError: 12: Cannot allocate memory at src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp 1402
2018-08-28T13:59:02.511+0000 E STORAGE [conn6831] WiredTiger (12) [1535464742:511916][15309:0x7fc1f2167700], file:collection-4--4936006946892585673.wt, WT_CURSOR.search: memory allocation of 17522688 bytes failed: Cannot allocate memory
2018-08-28T13:59:02.511+0000 I - [conn6831] Invariant failure: ret resulted in status UnknownError: 12: Cannot allocate memory at src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp 1386

解决办法:

1
2
3
4
5
6
7
配置文件里加入了 cacheSizeGB: 3

storage:
engine: wiredTiger
wiredTiger:
engineConfig:
cacheSizeGB: 3

查看WiredTiger内部缓存到底占用了多少内存的方式是,在mongo shell中之行以下命令

1
db.runCommand( { serverStatus: 1 } ).wiredTiger.cache["bytes currently in the cache"]

如果不想重启mongoDB,可以在线修改,如下

1
db.adminCommand({setParameter: 1, wiredTigerEngineRuntimeConfig: "cache_size=8G"})