MongoDB实战 : 操作性很强的入门手记
时间:2014-12-04 22:19 来源:linux.it.net.cn 作者:IT
./mongo --port 27017
show dbs ----已有数据库列表
show collections ---- 已有集合列表
show users ----已有用户列表
use dbname ---- 切换数据库,系统会自动延迟创建该数据库
db.account.save({'name':'test','addr':'china'}) -- 创建集合
db.account.find() -- 查看集合数据
db.dropDatabase() -- 删除数据库
数据库组件:mongod、mongos、mongo
数据库工具:
mongodump -h dbhost -d dbname -o dbdirectory -- 数据库备份
mongorestore -h dbhost -d dbname --directoryperdb dbdirectory -- 数据库恢复
./bsondump dump/bbs/account.bson -- 查看bson对象
./mongoexport -d bbs -c account -q {} -f name,addr --csv > account.csv -- 数据导出工具,导出为csv格式
./mongoexport -d bbs -c account -q {} -f name,addr > account.json 导出为json格式
./mongoexport -d bbs -c account -q '{"name":"test2"}' -f name,addr --csv > account.csv 带查询条件导出
./mongoimport -d bbs -c account --type csv --headerline --drop < account.csv -- 数据导入工具,导入csv文件
./mongoimport -d bbs -c account --type json --drop < account.json--导入json文件
===================文档插入=======================
单个文档插入:db.account.insert({"userName" : "bbs10000001", "passwd" : "ddddddd", "acctAttr" : null })
===============文档删除=============
删除文档中所有数据:db.account.remove(),不删除索引
条件删除:db.account.remove({"userName":"bbs1100"})
删除整个集合:db.account.drop()。数据、索引一起删除,性能好
=============文档更新=====================
更新命令:db.account.update({"userName":"bbs10"},{ "_id" : "3e1fd26f4b0f8351760fcc54", "userName" : "bbs10", "passwd" : "fff", "acctAttr" : null })
$set用法,使用修改器进行局部更改:db.account.update({"_id" : "3e1fd26f4b0f8351760fcc54"},{ "$set":{"passwd":"d"}})
去掉一个键:db.account.update({"userName":"bbs10"},{ "$unset":{"passwd":1}})
$inc用法,db.account.update({"userName":"bbs10"},{ "$inc":{"age":30}})
加1:db.account.update({"userName":"bbs10"},{ "$inc":{"age":1}})
减1:db.account.update({"userName":"bbs10"},{ "$inc":{"age":-1}})
$inc的键值必须为数值。
数组修改器$push。db.account.update({"userName":"bbs10"},{$push:{"email":"1@163.com"}})
$addToSet避免重复加入:db.account.update({"userName":"bbs10"}},{$addToSet:{"email":"4@163.com"}})
pop修改器:db.account.update({"userName":"bbs10"},{$pop:{"email":1}}) 从数组尾删除一个元素
从数组头删除一个元素:db.account.update({"userName":"bbs10"},{$pop:{"email":-1}})
指定位置删除删除元素:db.account.update({"userName":"bbs10"},{$pull:{"email":"2@163.com"}})
多文档更新:db.account.update({"userName":"bbs10"},{$set:{"passwd":"a"}},false,true)
看执行结果,有多少文档被更新:db.runCommand({getLastError:1})
执行getLastError时,驱动程序会等待数据库返回结果
=====================查询========基础查询find命令==================
查询集合所有文档:db.account.find()
简单条件查询:db.account.find({"userName":"bbs10"})
多值匹配条件查询,类似"条件1 and 条件2":
db.account.find({"userName":"bbs10","passwd":"ddd"})
指定返回某些键:db.account.find({},{"userName":1,"passwd":1})
db.account.find({},{"passwd":0})
db.account.find({},{"userName":1,"_id":0})
=====================查询========复合条件查询==================
比较操作符lt、lte、gt、gte、ne分别对应<、<=、>、>=、!=
db.account.insert({"userName":"bbs2000000","age":30,"createTime":new Date()})
db.account.find({"createTime":{"$gt":start}})
db.account.find({"age":{"$gt":30,"$lt":40}})
db.account.find({"age":{"$ne":30}})
$in查询:db.account.find({"age":{"$in":[30,32]}})
db.account.find({"age":{"$nin":[30,32]}})
$or 或查询,可多键值或查询:db.account.find({"$or":[{"userName":"bbs2000000"},{"age":32}]})
组合查询db.account.find({"$or":[{"userName":"bbs2000000"},{"age":{"$in":[30,32]}}]})
$not运算符,可运用于任何条件之上,表示取非:db.account.find({"age":{"$not":{"$nin":[30,32]}}})
$mod模运算:db.account.find({"age":{"$mod":[5,0]}})
=====================查询========高级查询规则-null=================
null不仅匹配自身,还匹配不存在db.account.find({"createTime":null})
要结合$exist才能准确查出属性为null的文档:
db.account.find({"createTime":{"$in":[null],"$exists":true}})
====================查询=======正则表达式==================
正则表达式规则遵循javascript正则表达式规则db.account.find({"userName":/bbs200000/i})
带前缀正则表达式查询性能最好:db.account.find({"userName":/^bbs200000/i})
=====================查询========查询数组==================
db.food.insert({"_id":1,"fruit":["apple","banana","peach"]})
db.food.insert({"_id":2,"fruit":["apple","watermelon","orange"]})
db.food.insert({"_id":3,"fruit":["cherry","banana","apple"]})
单元素匹配任何一个就行:db.food.find({"fruit":"apple"})
多元素匹配要用$all,既有apple又有banana的文档:
db.food.find({"fruit":{$all:["apple","banana"]}})
数组下标从0开始,用数组下标指定位置查询:
db.food.find({"fruit.2":"apple"})
$size,查询指定数组长度的数组:db.food.find({"fruit":{"$size":3}})
=====================查询========查询内嵌文档==================
db.account.insert({"userName":{"first":"joe","last":"schmoe"},"age":35})
完全匹配查询:db.account.find({"userName":{"first":"joe","last":"schmoe"}})
改变文档数据模式db.account.update({},{"$set":{"userName.passwd":"ddd"}})
点表示法查询(不受文档数据模式改变影响):db.account.find({"userName.first":"joe","userName.last":"schmoe"})
再次改变文档数据模式,增加数组元素db.account.update({},{"$push":{"comments":{"author":"joe","score":3,"comment":"test"}}})
db.account.update({},{"$push":{"comments":{"author":"tom","score":5,"comment":"test"}}})
查询作者为joe并且得分超过5分的文档:db.account.find({"comments.author":"joe","comments.score":{"$gte":5}})
正确做法,采用$elemMatch对内嵌文档多键匹配:db.account.find({"comments":{"$elemMatch":{"author":"joe","score":{"$gte":5}}}})
=====================查询========where查询==================
db.food.insert({"apple":1,"banana":6,"peach":3})
db.food.insert({"apple":8,"spinach":4,"watermelon":4})
db.food.find({"$where":function(){ for(var current in this){ for(var other in this){ if(current !=other && this[current]==this[other]){ return true; } } } return false; }});
不是逼急了不要用where 查询,能用键/值对尽量用键/值对查询方式
where查询无法使用索引,并且文档要从bson转成javascript对象,查询速度非常慢
=====================查询========游标操作==================
for(i=0;i<100;i++){ db.c.insert({x:i}); }
定义游标,不会立即执行查询:var cursor=db.c.find()
游标迭代器:while(cursor.hasNext()){ obj=cursor.next() }
执行cursor.hasNext()时,查询发往服务器,执行真正查询,shell会获取前100个结果或者4M数据(两者较小者)返回。
限制结果数据:db.c.find().limit(5);
跳过匹配文档:db.c.find().skip(5);
排序:sort用一个对象为参数,键、值对表示,键对应文档键名,值表示排序方向。1-升序,-1 - 降序。
db.c.find().sort({x:-1}) db.c.find().sort({x:1})
多键复合排序:db.account.find().sort({userName:1,age:-1})
=====================查询========游标分页==================
for(i=0;i<100;i++){ db.c.insert({x:i,y:"post"}); }
按条件查询每页20条纪录db.c.find({"y":"post"}).limit(20).sort({"x":1})
db.c.find({"y":"post"}).limit(20).sort({"x":-1})
分页参数就是skip的参数db.c.find({"y":"post"}).limit(20).skip(20).sort({"x":1})
注意,用skip跳过少量文档是可行的,数量太多就会变慢
另一种不用skip进行分页的方法
=====================查询========游标内幕==================
服务器端,游标消耗内存和其它资源,要尽快合理释放
游标遍历完或客户端发消息终止,释放游标
游标在客户端不在作用域,驱动会向服务器发消息销毁游标
超时销毁机制,游标即使是在客户端作用域内,但10分钟不用,也会自动被销毁
若关闭游标超时销毁机制(驱动immortal函数),游标使用完,一定要显式将其关闭,否则其会一直消耗服务器资源。
====================索引创建==================
for(i=0;i<10000;i++){ db.account.insert({userName:"bbs"+i,age:i%60,createTime:new Date()}); }
单键索引:db.account.ensureIndex({"userName":1})
1--表示升序,-1 -- 表示降序
复合索引:db.account.ensureIndex({"userName":1,"age":-1})
db.account.ensureIndex({"userName":1,"age":1,"createTime":1})
只有索引前部的查询会得到优化
索引优化查询的同时,会对增删改带来额外开销
索引创建原则
应用程序会做什么查询,哪些键需要索引
每个键的索引方向是怎样的
如何应对扩展?如何通过不同键的排列使数据尽可能多的保存在内存中,增加命中率
数据量大时为排序创建索引
=====================唯一索引=================
db.account.ensureIndex({"userName":1},{"unique":true})
默认插入数据时不检查数据唯一性
安全插入时才会检查数据唯一性
_id键索引就是唯一索引,并且不能删除
也可创建唯一复合索引,单键可重复,组合后不相同即可
=====================查询分析工具==================
explain可分析查询使用索引的情况,耗时、及扫描文档数的统计
db.account.find({"userName":"bbs22"}).explain()
db.account.find({"age":30}).sort({"userName":1}).explain()
db.account.find({"age":{$gt:20,$lt:30}}).explain()索引前后比较
=====================强制指定索引使用==================
hint可以强制mongodb使用某一个索引
db.account.find({"age":{$gt:20,$lt:30}}).hint({"userName":1,"age":1,"createTime":1}).explain()
通常mongodb会智能选择最优索引使用
=====================聚合统计==================
count
统计集合总数: db.account.count()
条件统计:db.account.count({"age":30})
增加查询条件统计通常用于分页查询,但增加查询条件统计会使统计变慢
distinct
用于找出指定键的不同值
db.runCommand({"distinct":"account","key":"age"})
必须指定集合名,键名
计算distinct后的count总数:
db.runCommand({"distinct":"account","key":"age"}).values.length
db.runCommand({"distinct":"account","key":"age","query":{"age":{"$gt":30}}}).values.length
group
db.account.group({ key:{"age":true}, initial:{number:0}, reduce:function(doc,prev){prev.number+=1 },cond:{"age":{"$gt":30}}})
key关键字标识我们想要汇总的数据(类似于SQL中GROUP BY子句的参数)
initial关键字标识我们记录的关键字的关键值的初始值
reduce关键字定义了一个方法,每次遇一个文档要做的事情
cond关键字定义条件
========================高级指南==================命令的工作原理================
db.account.drop()
db.runCommand({"drop":"account"})
命令响应结果是一个文档,ok键值为true表示执行成功,为false表示执行失败。
errmsg键值表示失败原因。
MongoDB中的命令其实是作为一种特殊类型的查询来执行的。这些查询针对$cmd集合来执行,runCommand仅仅命令文档,执行等价查询。
db.$cmd.findOne({"drop":"c"})
支持的命令浏览
db.listCommands()
访问web console也可获取
版本命令:db.runCommand({"buildInfo":1})
集合统计信息 :db.runCommand({"collStats":"account"})
修复数据库:{"repairDatabase":1} 比较耗时
删除集合:{"drop":collection}
查看对本集合最后一次操作的错误信息:db.runCommand({ "getlasterror" : 1 , "w" : 1 , "wtimeout" : 0})
删除当前数据库:{"dropDatabase":1}
删除索引:{"dropIndexes":collection,"index":name}
检查本服务器是主服务器还是从服务器:{"isMaster":1}
列出服务器上所有数据库(管理专用):{"listDatabases":1}
重命令集合:{"renameCollection":a,"to":b} a、b均必须为完整的命令空间(管理专用)
服务器管理统计信息:{"serverStatus":1}
========================高级指南==================固定集合================
固定集合:事先创建,大小固定
类似环状队列,空间不足队列头文件被替换
不能手工删除文档,只能自动替换
特点;插入性能好,不需额外分配空间,直接插入队列尾.按插入顺利查询速度极快
创建固定大小100k的固定集合:db.createCollection("fixed",{capped:true,size:100000})
插入测试数据:for(i=0;i<10000;i++){ db.fixed.insert({userName:"bbs"+i,age:i%60,createTime:new Date()}); }
创建固定大小100K且文档数最大100的固定集合: db.createCollection("fixed",{capped:true,size:100000,max:100})
容量未满时按文档数限制,容量满时按容量限制
转换语法:db.runCommand({convertToCapped:"account",size:10000})
========================高级指南==================GridFS================
GridFS是MongoDB中存储大二进制文件的机制。
使用GridFS后不需要再使用单独的文件存储架构
可直接利用MongoDB的复制分片机制,使文件存储也具有水平扩展、故障恢复功能
不产生磁盘碎片,因为MongoDB分配数据文件空间时以2GB为一块
GridFS用法:
./mongofiles -d bbs put test.txt
./mongofiles -d bbs get test.txt
./mongofiles -d bbs delete test.txt
./mongofiles -d bbs search test
./mongofiles -d bbs list
GridFS是建立在MongoDB文档规范基础上的轻量级文件存储规范
将大文件分块,每块独立存储
db.fs.chunks.find()
db.fs.files.find()
========================高级指南=================服务端脚本================
db.eval可在MongoDB服务端执行javascript脚本。
db.eval("return 1;")
db.eval("function() {return 1;}")
参数传递:
db.eval("function(u) { print('hello,'+u+'!');}",["user1"])
========================高级指南=================服务端脚本================存储javascript:
system.js保存javascript变量及代码
db.system.js.insert({"_id":"x","value":1})
db.system.js.insert({"_id":"y","value":2})
db.system.js.insert({"_id":"z","value":3})
db.eval中可引用变量:db.eval("function() {return x+y+z;}")
========================高级指南=================服务端脚本================写一个类似log4j的日志函数:
db.system.js.insert({"_id":"log","value": function(msg,level){ var levels=["DEBUG","WARN","ERROR","FATAL"]; level =level?level:0; var now=new Date(); print(now+" "+levels[level]+msg); }})
使用示例:db.eval("x=1;log(' x is '+x);x=2;log(' x is greater than 1',1);")
db.eval("log('refactor log test',2)")
========================高级指南=================服务端脚本================DBRef数据库引用:
DBRef指向一个集合的一个文档引用
类似表关联
{"ref":collection,"$id":id_value,"$db":database}
DBRef不是必须用,通常会引入复杂性让人误解。可有其它方式建立关联。
=======================高级指南=================系统管理高级技巧-系统监控=================
MongoDB持久化配置 :
持久化配置
journal=true
journalCommitInterval=100
正确关闭MongoDB
./mongod --shutdown或./mongod -f ../etc/mongo.conf --shutdown
不能直接kill进程,否则可能造成库文件损坏
=======================高级指南=================系统管理高级技巧-系统监控=================监控serverStatus
db.runCommand({"serverStatus":1})
参数含义简介:
"globalLock" : {
"totalTime" : NumberLong("511789041000"),--自实例启动全局锁创建以来到现在多长时间,单位微秒.
"lockTime" : NumberLong(105416165),--自全局锁创建以来锁定总时间,单位微秒
"currentQueue" : {
"total" : 0,--等待全局锁的队列中操作数目
"readers" : 0,--等待读锁的队列中操作数目
"writers" : 0--等待写锁的队列中操作数目
},
"activeClients" : {
"total" : 0,--连接到server的当前活动client数目
"readers" : 0, --执行读操作的当前活动client数目
"writers" : 0--执行写操作的当前活动client数目
}
},
"mem" : {
"bits" : 64,--64位机器
"resident" : 31,--占用物理内存量
"virtual" : 20478, --占用的虚拟内存量
"supported" : true, --是否支持扩展内存
"mapped" : 10188, --映射到内存的数据文件大小,很接近于你的所有数据库大小。
"mappedWithJournal" : 20376
},
"connections" : {
"current" : 1,--当前活动连接量。连接到server的当前活跃连接数目
"available" : 19999--剩余空闲连接量。剩余的可用连接数目
},
"backgroundFlushing" : {
"flushes" : 8527, --数据库刷新写到磁盘的次数
"total_ms" : 599904,--数据库刷新数据到磁盘花费的微秒数
"average_ms" : 70.35346546264806,--执行单次刷新花费的平均微秒数
"last_ms" : 51,--最后一次执行完成刷新数据到磁盘花费的微秒数
"last_finished" : ISODate("2012-12-26T00:41:32.013Z")-当最后一次刷新数据完成时的时间戳
},
"cursors" : {
"totalOpen" : 0, --server为client保持的游标(cursor)总数
"clientCursors_size" : 0,
"timedOut" : 0--server启动以来游标(cursor)超时的总数
},
"network" : {
"bytesIn" : 1925,--发送到数据库的数据总量(bytes)
"bytesOut" : 4294,--数据库发出的数据总量(bytes)
"numRequests" : 24--发送到数据库的请求量
},
"opcounters" : {
"insert" : 0, --server启动以来总的insert数据量
"query" : 16646, --server启动以来总的query数据量
"update" : 1,--server启动以来总的update数据量
"delete" : 0, --server启动以来总的delete数据量
"getmore" : 0, --server启动以来调用任何游标的getMore总次数
"command" : 22 --server启动以来执行其他命令的总次数
},
"asserts" : {
"regular" : 0, --server启动以来抛出正规断言(assert 类似于异常处理的形式)总数目
"warning" : 0,--server启动以来抛出的告警总数目
"msg" : 0,--消息断言数目。服务器内部定义的良好字符串错误
"user" : 0, --用户断言数目。用户产生的错误,譬如:磁盘空间满;重复键。
"rollovers" : 0--server启动以来,assert counters have rolled over的次数
},
"writeBacksQueued" : false,--是否有从mongos执行的retry操作
"dur" : {
"commits" : 29,--上一间隔journal日志发生commit的次数
"journaledMB" : 0,--上一间隔写到journal日志的数据量(单位:MB)
"writeToDataFilesMB" : 0,--上一间隔journal日志写到数据文件的数据量(单位:MB)
"compression" : 0,
"commitsInWriteLock" : 0, --写锁期间发生commits的次数
"earlyCommits" : 0,--schedule时间前请求commit的次数
"timeMs" : {
"dt" : 3004,
"prepLogBuffer" : 0,--准备写journal日志花费的时间
"writeToJournal" : 0,--写journal日志花费的实际时间
"writeToDataFiles" : 0,--journal日志后写数据文件花费的时间
"remapPrivateView" : 0
}
},
=======================高级指南=================系统管理高级技巧-系统监控=================监控mongostat
mongostat是mongdb自带的状态检测工具,在命令行下使用。它会间隔固定时间获取mongodb的当前运行状态,并输出
insert: 一秒内的插入数
query : 一秒内的查询数
update: 一秒内的更新数
delete: 一秒内的删除数
getmore: 查询时游标(cursor)的getmore操作
command: 一秒内执行的命令数
flushes: 一秒内flush的次数
mapped 所有的被mmap的数据量,单位M
vsize 虚拟内存使用量,单位M
res 物理内存使用量,单位M
faults/s 每秒访问失败数(只有Linux有),数据被交换出物理内存,放到swap。不要超过100,否则就是机器内存太小,造成频繁swap写入。此时要升级内存或者扩展
locked % 被锁的时间百分比,不要太大
idx miss: 索引不命中所占百分比。如果太高的话就要考虑索引是不是少了
qr|qw:客户端查询等待数
ar|aw:客户端文档读写等待数,都为0的话表示mongo毫无压力。高并发时,一般队列值会升高
netIn|netOut:网络带宽压力
conn 当前连接数
time 时间戳
=======================高级指南=================系统管理高级技巧-系统监控=================MongoDB安全
默认没有设置安全认证
增加root用户:
use admin
db.addUser("root","rootabc")
给自定义库增加用户:
use bbs
db.addUser("readwrite","rw")
db.addUser("read","readabc")
增加配置项auth=true,打开库安全认证
db.auth("root","rootabc")
用户存放在数据库的system.users集合中。
use test
db.auth("read","readabc")
=======================高级指南=================系统管理高级技巧-系统监控=================备份恢复
mongodump -h dbhost -d dbname -o dbdirectory -- 数据库备份
mongorestore -h dbhost -d dbname --directoryperdb dbdirectory -- 数据库恢复
可在线实时备份
提取快照方法:db.runCommand({"fsync":1,"lock":1})
fsync强制所有缓冲区写入磁盘
备份完后解锁:db.fsyncUnlock()
缺点:耽误读写操作
=======================高级指南=================系统管理高级技巧-系统监控=================数据库修复
mongod --repair启数据库修复
或db.repairDatabase()
修复原理:将所有文档导出并马上导入,忽略无效文档,然后重建索引
修复会化点时间
=======================高级指南=================复制功能=================
复制优点
数据备份
数据恢复
读写分离
主、从节点建立:
建立启动主节点:./mongod -f ../etc/mongo.conf --master,注意--master表示主节点,可跟--port表明启动端口
建立从节点: mkdir /data/slave,启动从节点: ./mongod --dbpath /data/slave --port 10001 --slave --source localhost:27017,
注意端口必须与主节点不同,--source指明主节点
ps:一个主节点挂载从节点不超过12个
复制管理
打印主从复制集群情况:
主节点上
db.printReplicationInfo()
configured oplog size日志大小
oplog first event time日志什么时候开始记录
oplog last event time日志最后一条记录时间
从节点上
source 主节点地址
syncedTo 最后一次同步时间
日志大小调节
./mongod -f 启动配置文件路径 --oplogSize(log大小调节参数) xxxxx(值)
=======================高级指南================MongoDB数据文件内部结构=================
MongoDB数据文件内部结构
MongoDB在数据存储上按命名空间来划分,一个Collection是一个命名空间,一个索引也是一个命名空间。
同一个命名空间的数据被分成很多个Extent,Extent之间使用双向链表连接。
在每一个Extent中,保存了具体每一行的数据,这些数据也是通过双向链接来连接的。
每一行数据存储空间不仅包括数据占用空间,还可能包含一部分附加空间,这使得在数据Update变大后可以不移动位置。
索引以BTree结构实现
=======================高级指南================副本集数据同步机制=================
红色箭头表示写操作可以写到Primary上,然后异步同步到多个Secondary上。
蓝色箭头表示读操作可以从Primary或Secondary任意一个中读取。
各个Primary与Secondary之间一直保持心跳同步检测,用于判断Replica Sets的状态
=======================高级指南=================副本集=================
概念:
副本集是具有故障恢复功能的主从集群
与主从集群区别:副本集没有固定主节点
本质上是很牛X的具有HA功能的主从集群
副本集创建:
为副本集取名 (jm)
./mongod --dbpath /data/db1 --port 1111 --replSet jm
./mongod --dbpath /data/db1 --port 2222 --replSet jm
./mongod --dbpath /data/db1 --port 3333 --replSet jm
--replSet参数,创建副本集中一个节点
创建启动副本集中第二个节点
副本集初始化,初始化命令只能执行一次
config_jm={"_id":"jm",members:[{_id:0,host:"192.168.154.133:1111"},{_id:1,host:"192.168.154.143:2222"},{_id:2,host:"192.168.154.153:3333"}]}
rs.initiate(config_jm)
查看副本集状态:
rs.status()
db.isMaster()
副本集故障恢复:
副本集任何时间只有一个活跃节点
投票机制决定谁成为活跃节点,优先级最高、数据最新的节点获胜
节点类型:
standard:这种是常规节点,他存储一份完整的数据库副本,参与选举投票,有可能成为活动节点
passive:存储一份完整的数据库副本,参与选举投票,不能成为活动节点
arbiter:仲裁者只参与投票,不接收复制的数据,也不能成为活动节点
ps:注意一点:副本至少3个,因为2个的话有一个挂了另外一个不能投票了! 剩下一个会一直是secondary
=======================高级指南=================分片=================
概念:
分片是指将数据拆分,将其分散到不同机器上的过程
mongodb支持自动分片
何时分片:磁盘不够用了、单个mongd不能满足写数据性能要求、想将大量数据放在内存中提高性能
片键指从集合中挑选一个键,该键值做为数据拆分的依据
创建分片步骤:
选择合适的片键(eg:username)
1 :建立mongodb启动配置mongo.conf文件
logpath=../log/mongodb.log
dbpath=/home/tom/data/dbfp
logappend=false
cpu=false
journal=true
journalCommitInterval=100
rest=true
fork=true
auth=false
2 :启动mongodb
./mongod -f ../etc/mongo1.conf --port 27017
./mongod -f ../etc/mongo2.conf --port 27018
3 : 启动配置服务器
./mongod -f ../etc/mongoconf.conf --port 20000
4 : 启动mongos
./mongos --port 30000 --configdb 配置服务器IP:20000
5 : 配置分片节点
./mongo mongos服务器IP:30000/admin
db.runCommand({addshard:"分片服务器1:27017",allowLocal:true})
db.runCommand({addshard:"分片服务器2:27018",allowLocal:true})
6 : 启动分片
db.runCommand({"enablesharding":"jmtest"})
7 : 在指定列上建立片键
db.runCommand({"shardcollection":"jmtest.account","key":{"userName",1}}) 1表示升序
db.jmtest.getIndexs() 查看索引
8 : 查看各分片状态
登录mongos服务器
./mongo mongos服务器IP:30000/admin
db.printShardingStatus()
以上操作都会保存到配置服务器里面
生产配置:
三个配置服务器
多个mongos服务器
每个片都是副本集
生产环境应建立3个配置服务器
关于分片的其他操作如:删除,请查阅另一篇文章:http://blog.csdn.net/johnstrive/article/details/24690805
=======================高级指南=================分片机制=================
MongoDB的分片是指定一个分片key来进行,数据按范围分成不同的chunk,每个chunk的大小有限制。
有多个分片节点保存这些chunk,每个节点保存一部分的chunk。
每一个分片节点都是一个Replica Sets,这样保证数据的安全性。
当一个chunk超过其限制的最大体积时,会分裂成两个小的chunk。
当chunk在分片节点中分布不均衡时,会引发chunk迁移操作。
客户端访问路由节点mongos来进行数据读写。
config服务器保存了两个映射关系,一个是key值的区间对应哪一个chunk的映射关系,另一个是chunk存在哪一个分片节点的映射关系。
路由节点通过config服务器获取数据信息,通过这些信息,找到真正存放数据的分片节点进行对应操作。
路由节点还会在写操作时判断当前chunk是否超出限定大小。如果超出,就分列成两个chunk。
对于按分片key进行的查询和update操作来说,路由节点会查到具体的chunk然后再进行相关的工作。
对于不按分片key进行的查询和update操作来说,mongos会对所有下属节点发送请求然后再对返回结果进行合并。
(责任编辑:IT)
./mongo --port 27017 show dbs ----已有数据库列表 show collections ---- 已有集合列表 show users ----已有用户列表 use dbname ---- 切换数据库,系统会自动延迟创建该数据库 db.account.save({'name':'test','addr':'china'}) -- 创建集合 db.account.find() -- 查看集合数据 db.dropDatabase() -- 删除数据库 数据库组件:mongod、mongos、mongo 数据库工具: mongodump -h dbhost -d dbname -o dbdirectory -- 数据库备份 mongorestore -h dbhost -d dbname --directoryperdb dbdirectory -- 数据库恢复 ./bsondump dump/bbs/account.bson -- 查看bson对象 ./mongoexport -d bbs -c account -q {} -f name,addr --csv > account.csv -- 数据导出工具,导出为csv格式 ./mongoexport -d bbs -c account -q {} -f name,addr > account.json 导出为json格式 ./mongoexport -d bbs -c account -q '{"name":"test2"}' -f name,addr --csv > account.csv 带查询条件导出 ./mongoimport -d bbs -c account --type csv --headerline --drop < account.csv -- 数据导入工具,导入csv文件 ./mongoimport -d bbs -c account --type json --drop < account.json--导入json文件 ===================文档插入======================= 单个文档插入:db.account.insert({"userName" : "bbs10000001", "passwd" : "ddddddd", "acctAttr" : null }) ===============文档删除============= 删除文档中所有数据:db.account.remove(),不删除索引 条件删除:db.account.remove({"userName":"bbs1100"}) 删除整个集合:db.account.drop()。数据、索引一起删除,性能好 =============文档更新===================== 更新命令:db.account.update({"userName":"bbs10"},{ "_id" : "3e1fd26f4b0f8351760fcc54", "userName" : "bbs10", "passwd" : "fff", "acctAttr" : null }) $set用法,使用修改器进行局部更改:db.account.update({"_id" : "3e1fd26f4b0f8351760fcc54"},{ "$set":{"passwd":"d"}}) 去掉一个键:db.account.update({"userName":"bbs10"},{ "$unset":{"passwd":1}}) $inc用法,db.account.update({"userName":"bbs10"},{ "$inc":{"age":30}}) 加1:db.account.update({"userName":"bbs10"},{ "$inc":{"age":1}}) 减1:db.account.update({"userName":"bbs10"},{ "$inc":{"age":-1}}) $inc的键值必须为数值。 数组修改器$push。db.account.update({"userName":"bbs10"},{$push:{"email":"1@163.com"}}) $addToSet避免重复加入:db.account.update({"userName":"bbs10"}},{$addToSet:{"email":"4@163.com"}}) pop修改器:db.account.update({"userName":"bbs10"},{$pop:{"email":1}}) 从数组尾删除一个元素 从数组头删除一个元素:db.account.update({"userName":"bbs10"},{$pop:{"email":-1}}) 指定位置删除删除元素:db.account.update({"userName":"bbs10"},{$pull:{"email":"2@163.com"}}) 多文档更新:db.account.update({"userName":"bbs10"},{$set:{"passwd":"a"}},false,true) 看执行结果,有多少文档被更新:db.runCommand({getLastError:1}) 执行getLastError时,驱动程序会等待数据库返回结果 =====================查询========基础查询find命令================== 查询集合所有文档:db.account.find() 简单条件查询:db.account.find({"userName":"bbs10"}) 多值匹配条件查询,类似"条件1 and 条件2": db.account.find({"userName":"bbs10","passwd":"ddd"}) 指定返回某些键:db.account.find({},{"userName":1,"passwd":1}) db.account.find({},{"passwd":0}) db.account.find({},{"userName":1,"_id":0}) =====================查询========复合条件查询================== 比较操作符lt、lte、gt、gte、ne分别对应<、<=、>、>=、!= db.account.insert({"userName":"bbs2000000","age":30,"createTime":new Date()}) db.account.find({"createTime":{"$gt":start}}) db.account.find({"age":{"$gt":30,"$lt":40}}) db.account.find({"age":{"$ne":30}}) $in查询:db.account.find({"age":{"$in":[30,32]}}) db.account.find({"age":{"$nin":[30,32]}}) $or 或查询,可多键值或查询:db.account.find({"$or":[{"userName":"bbs2000000"},{"age":32}]}) 组合查询db.account.find({"$or":[{"userName":"bbs2000000"},{"age":{"$in":[30,32]}}]}) $not运算符,可运用于任何条件之上,表示取非:db.account.find({"age":{"$not":{"$nin":[30,32]}}}) $mod模运算:db.account.find({"age":{"$mod":[5,0]}}) =====================查询========高级查询规则-null================= null不仅匹配自身,还匹配不存在db.account.find({"createTime":null}) 要结合$exist才能准确查出属性为null的文档: db.account.find({"createTime":{"$in":[null],"$exists":true}}) ====================查询=======正则表达式================== 正则表达式规则遵循javascript正则表达式规则db.account.find({"userName":/bbs200000/i}) 带前缀正则表达式查询性能最好:db.account.find({"userName":/^bbs200000/i}) =====================查询========查询数组================== db.food.insert({"_id":1,"fruit":["apple","banana","peach"]}) db.food.insert({"_id":2,"fruit":["apple","watermelon","orange"]}) db.food.insert({"_id":3,"fruit":["cherry","banana","apple"]}) 单元素匹配任何一个就行:db.food.find({"fruit":"apple"}) 多元素匹配要用$all,既有apple又有banana的文档: db.food.find({"fruit":{$all:["apple","banana"]}}) 数组下标从0开始,用数组下标指定位置查询: db.food.find({"fruit.2":"apple"}) $size,查询指定数组长度的数组:db.food.find({"fruit":{"$size":3}}) =====================查询========查询内嵌文档================== db.account.insert({"userName":{"first":"joe","last":"schmoe"},"age":35}) 完全匹配查询:db.account.find({"userName":{"first":"joe","last":"schmoe"}}) 改变文档数据模式db.account.update({},{"$set":{"userName.passwd":"ddd"}}) 点表示法查询(不受文档数据模式改变影响):db.account.find({"userName.first":"joe","userName.last":"schmoe"}) 再次改变文档数据模式,增加数组元素db.account.update({},{"$push":{"comments":{"author":"joe","score":3,"comment":"test"}}}) db.account.update({},{"$push":{"comments":{"author":"tom","score":5,"comment":"test"}}}) 查询作者为joe并且得分超过5分的文档:db.account.find({"comments.author":"joe","comments.score":{"$gte":5}}) 正确做法,采用$elemMatch对内嵌文档多键匹配:db.account.find({"comments":{"$elemMatch":{"author":"joe","score":{"$gte":5}}}}) =====================查询========where查询================== db.food.insert({"apple":1,"banana":6,"peach":3}) db.food.insert({"apple":8,"spinach":4,"watermelon":4}) db.food.find({"$where":function(){ for(var current in this){ for(var other in this){ if(current !=other && this[current]==this[other]){ return true; } } } return false; }}); 不是逼急了不要用where 查询,能用键/值对尽量用键/值对查询方式 where查询无法使用索引,并且文档要从bson转成javascript对象,查询速度非常慢 =====================查询========游标操作================== for(i=0;i<100;i++){ db.c.insert({x:i}); } 定义游标,不会立即执行查询:var cursor=db.c.find() 游标迭代器:while(cursor.hasNext()){ obj=cursor.next() } 执行cursor.hasNext()时,查询发往服务器,执行真正查询,shell会获取前100个结果或者4M数据(两者较小者)返回。 限制结果数据:db.c.find().limit(5); 跳过匹配文档:db.c.find().skip(5); 排序:sort用一个对象为参数,键、值对表示,键对应文档键名,值表示排序方向。1-升序,-1 - 降序。 db.c.find().sort({x:-1}) db.c.find().sort({x:1}) 多键复合排序:db.account.find().sort({userName:1,age:-1}) =====================查询========游标分页================== for(i=0;i<100;i++){ db.c.insert({x:i,y:"post"}); } 按条件查询每页20条纪录db.c.find({"y":"post"}).limit(20).sort({"x":1}) db.c.find({"y":"post"}).limit(20).sort({"x":-1}) 分页参数就是skip的参数db.c.find({"y":"post"}).limit(20).skip(20).sort({"x":1}) 注意,用skip跳过少量文档是可行的,数量太多就会变慢 另一种不用skip进行分页的方法 =====================查询========游标内幕================== 服务器端,游标消耗内存和其它资源,要尽快合理释放 游标遍历完或客户端发消息终止,释放游标 游标在客户端不在作用域,驱动会向服务器发消息销毁游标 超时销毁机制,游标即使是在客户端作用域内,但10分钟不用,也会自动被销毁 若关闭游标超时销毁机制(驱动immortal函数),游标使用完,一定要显式将其关闭,否则其会一直消耗服务器资源。 ====================索引创建================== for(i=0;i<10000;i++){ db.account.insert({userName:"bbs"+i,age:i%60,createTime:new Date()}); } 单键索引:db.account.ensureIndex({"userName":1}) 1--表示升序,-1 -- 表示降序 复合索引:db.account.ensureIndex({"userName":1,"age":-1}) db.account.ensureIndex({"userName":1,"age":1,"createTime":1}) 只有索引前部的查询会得到优化 索引优化查询的同时,会对增删改带来额外开销 索引创建原则 应用程序会做什么查询,哪些键需要索引 每个键的索引方向是怎样的 如何应对扩展?如何通过不同键的排列使数据尽可能多的保存在内存中,增加命中率 数据量大时为排序创建索引 =====================唯一索引================= db.account.ensureIndex({"userName":1},{"unique":true}) 默认插入数据时不检查数据唯一性 安全插入时才会检查数据唯一性 _id键索引就是唯一索引,并且不能删除 也可创建唯一复合索引,单键可重复,组合后不相同即可 =====================查询分析工具================== explain可分析查询使用索引的情况,耗时、及扫描文档数的统计 db.account.find({"userName":"bbs22"}).explain() db.account.find({"age":30}).sort({"userName":1}).explain() db.account.find({"age":{$gt:20,$lt:30}}).explain()索引前后比较 =====================强制指定索引使用================== hint可以强制mongodb使用某一个索引 db.account.find({"age":{$gt:20,$lt:30}}).hint({"userName":1,"age":1,"createTime":1}).explain() 通常mongodb会智能选择最优索引使用 =====================聚合统计================== count 统计集合总数: db.account.count() 条件统计:db.account.count({"age":30}) 增加查询条件统计通常用于分页查询,但增加查询条件统计会使统计变慢 distinct 用于找出指定键的不同值 db.runCommand({"distinct":"account","key":"age"}) 必须指定集合名,键名 计算distinct后的count总数: db.runCommand({"distinct":"account","key":"age"}).values.length db.runCommand({"distinct":"account","key":"age","query":{"age":{"$gt":30}}}).values.length group db.account.group({ key:{"age":true}, initial:{number:0}, reduce:function(doc,prev){prev.number+=1 },cond:{"age":{"$gt":30}}}) key关键字标识我们想要汇总的数据(类似于SQL中GROUP BY子句的参数) initial关键字标识我们记录的关键字的关键值的初始值 reduce关键字定义了一个方法,每次遇一个文档要做的事情 cond关键字定义条件 ========================高级指南==================命令的工作原理================ db.account.drop() db.runCommand({"drop":"account"}) 命令响应结果是一个文档,ok键值为true表示执行成功,为false表示执行失败。 errmsg键值表示失败原因。 MongoDB中的命令其实是作为一种特殊类型的查询来执行的。这些查询针对$cmd集合来执行,runCommand仅仅命令文档,执行等价查询。 db.$cmd.findOne({"drop":"c"}) 支持的命令浏览 db.listCommands() 访问web console也可获取 版本命令:db.runCommand({"buildInfo":1}) 集合统计信息 :db.runCommand({"collStats":"account"}) 修复数据库:{"repairDatabase":1} 比较耗时 删除集合:{"drop":collection} 查看对本集合最后一次操作的错误信息:db.runCommand({ "getlasterror" : 1 , "w" : 1 , "wtimeout" : 0}) 删除当前数据库:{"dropDatabase":1} 删除索引:{"dropIndexes":collection,"index":name} 检查本服务器是主服务器还是从服务器:{"isMaster":1} 列出服务器上所有数据库(管理专用):{"listDatabases":1} 重命令集合:{"renameCollection":a,"to":b} a、b均必须为完整的命令空间(管理专用) 服务器管理统计信息:{"serverStatus":1} ========================高级指南==================固定集合================ 固定集合:事先创建,大小固定 类似环状队列,空间不足队列头文件被替换 不能手工删除文档,只能自动替换 特点;插入性能好,不需额外分配空间,直接插入队列尾.按插入顺利查询速度极快 创建固定大小100k的固定集合:db.createCollection("fixed",{capped:true,size:100000}) 插入测试数据:for(i=0;i<10000;i++){ db.fixed.insert({userName:"bbs"+i,age:i%60,createTime:new Date()}); } 创建固定大小100K且文档数最大100的固定集合: db.createCollection("fixed",{capped:true,size:100000,max:100}) 容量未满时按文档数限制,容量满时按容量限制 转换语法:db.runCommand({convertToCapped:"account",size:10000}) ========================高级指南==================GridFS================ GridFS是MongoDB中存储大二进制文件的机制。 使用GridFS后不需要再使用单独的文件存储架构 可直接利用MongoDB的复制分片机制,使文件存储也具有水平扩展、故障恢复功能 不产生磁盘碎片,因为MongoDB分配数据文件空间时以2GB为一块 GridFS用法: ./mongofiles -d bbs put test.txt ./mongofiles -d bbs get test.txt ./mongofiles -d bbs delete test.txt ./mongofiles -d bbs search test ./mongofiles -d bbs list GridFS是建立在MongoDB文档规范基础上的轻量级文件存储规范 将大文件分块,每块独立存储 db.fs.chunks.find() db.fs.files.find() ========================高级指南=================服务端脚本================ db.eval可在MongoDB服务端执行javascript脚本。 db.eval("return 1;") db.eval("function() {return 1;}") 参数传递: db.eval("function(u) { print('hello,'+u+'!');}",["user1"]) ========================高级指南=================服务端脚本================存储javascript: system.js保存javascript变量及代码 db.system.js.insert({"_id":"x","value":1}) db.system.js.insert({"_id":"y","value":2}) db.system.js.insert({"_id":"z","value":3}) db.eval中可引用变量:db.eval("function() {return x+y+z;}") ========================高级指南=================服务端脚本================写一个类似log4j的日志函数: db.system.js.insert({"_id":"log","value": function(msg,level){ var levels=["DEBUG","WARN","ERROR","FATAL"]; level =level?level:0; var now=new Date(); print(now+" "+levels[level]+msg); }}) 使用示例:db.eval("x=1;log(' x is '+x);x=2;log(' x is greater than 1',1);") db.eval("log('refactor log test',2)") ========================高级指南=================服务端脚本================DBRef数据库引用: DBRef指向一个集合的一个文档引用 类似表关联 {"ref":collection,"$id":id_value,"$db":database} DBRef不是必须用,通常会引入复杂性让人误解。可有其它方式建立关联。 =======================高级指南=================系统管理高级技巧-系统监控================= MongoDB持久化配置 : 持久化配置 journal=true journalCommitInterval=100 正确关闭MongoDB ./mongod --shutdown或./mongod -f ../etc/mongo.conf --shutdown 不能直接kill进程,否则可能造成库文件损坏 =======================高级指南=================系统管理高级技巧-系统监控=================监控serverStatus db.runCommand({"serverStatus":1}) 参数含义简介: "globalLock" : { "totalTime" : NumberLong("511789041000"),--自实例启动全局锁创建以来到现在多长时间,单位微秒. "lockTime" : NumberLong(105416165),--自全局锁创建以来锁定总时间,单位微秒 "currentQueue" : { "total" : 0,--等待全局锁的队列中操作数目 "readers" : 0,--等待读锁的队列中操作数目 "writers" : 0--等待写锁的队列中操作数目 }, "activeClients" : { "total" : 0,--连接到server的当前活动client数目 "readers" : 0, --执行读操作的当前活动client数目 "writers" : 0--执行写操作的当前活动client数目 } }, "mem" : { "bits" : 64,--64位机器 "resident" : 31,--占用物理内存量 "virtual" : 20478, --占用的虚拟内存量 "supported" : true, --是否支持扩展内存 "mapped" : 10188, --映射到内存的数据文件大小,很接近于你的所有数据库大小。 "mappedWithJournal" : 20376 }, "connections" : { "current" : 1,--当前活动连接量。连接到server的当前活跃连接数目 "available" : 19999--剩余空闲连接量。剩余的可用连接数目 }, "backgroundFlushing" : { "flushes" : 8527, --数据库刷新写到磁盘的次数 "total_ms" : 599904,--数据库刷新数据到磁盘花费的微秒数 "average_ms" : 70.35346546264806,--执行单次刷新花费的平均微秒数 "last_ms" : 51,--最后一次执行完成刷新数据到磁盘花费的微秒数 "last_finished" : ISODate("2012-12-26T00:41:32.013Z")-当最后一次刷新数据完成时的时间戳 }, "cursors" : { "totalOpen" : 0, --server为client保持的游标(cursor)总数 "clientCursors_size" : 0, "timedOut" : 0--server启动以来游标(cursor)超时的总数 }, "network" : { "bytesIn" : 1925,--发送到数据库的数据总量(bytes) "bytesOut" : 4294,--数据库发出的数据总量(bytes) "numRequests" : 24--发送到数据库的请求量 }, "opcounters" : { "insert" : 0, --server启动以来总的insert数据量 "query" : 16646, --server启动以来总的query数据量 "update" : 1,--server启动以来总的update数据量 "delete" : 0, --server启动以来总的delete数据量 "getmore" : 0, --server启动以来调用任何游标的getMore总次数 "command" : 22 --server启动以来执行其他命令的总次数 }, "asserts" : { "regular" : 0, --server启动以来抛出正规断言(assert 类似于异常处理的形式)总数目 "warning" : 0,--server启动以来抛出的告警总数目 "msg" : 0,--消息断言数目。服务器内部定义的良好字符串错误 "user" : 0, --用户断言数目。用户产生的错误,譬如:磁盘空间满;重复键。 "rollovers" : 0--server启动以来,assert counters have rolled over的次数 }, "writeBacksQueued" : false,--是否有从mongos执行的retry操作 "dur" : { "commits" : 29,--上一间隔journal日志发生commit的次数 "journaledMB" : 0,--上一间隔写到journal日志的数据量(单位:MB) "writeToDataFilesMB" : 0,--上一间隔journal日志写到数据文件的数据量(单位:MB) "compression" : 0, "commitsInWriteLock" : 0, --写锁期间发生commits的次数 "earlyCommits" : 0,--schedule时间前请求commit的次数 "timeMs" : { "dt" : 3004, "prepLogBuffer" : 0,--准备写journal日志花费的时间 "writeToJournal" : 0,--写journal日志花费的实际时间 "writeToDataFiles" : 0,--journal日志后写数据文件花费的时间 "remapPrivateView" : 0 } }, =======================高级指南=================系统管理高级技巧-系统监控=================监控mongostat mongostat是mongdb自带的状态检测工具,在命令行下使用。它会间隔固定时间获取mongodb的当前运行状态,并输出 insert: 一秒内的插入数 query : 一秒内的查询数 update: 一秒内的更新数 delete: 一秒内的删除数 getmore: 查询时游标(cursor)的getmore操作 command: 一秒内执行的命令数 flushes: 一秒内flush的次数 mapped 所有的被mmap的数据量,单位M vsize 虚拟内存使用量,单位M res 物理内存使用量,单位M faults/s 每秒访问失败数(只有Linux有),数据被交换出物理内存,放到swap。不要超过100,否则就是机器内存太小,造成频繁swap写入。此时要升级内存或者扩展 locked % 被锁的时间百分比,不要太大 idx miss: 索引不命中所占百分比。如果太高的话就要考虑索引是不是少了 qr|qw:客户端查询等待数 ar|aw:客户端文档读写等待数,都为0的话表示mongo毫无压力。高并发时,一般队列值会升高 netIn|netOut:网络带宽压力 conn 当前连接数 time 时间戳 =======================高级指南=================系统管理高级技巧-系统监控=================MongoDB安全 默认没有设置安全认证 增加root用户: use admin db.addUser("root","rootabc") 给自定义库增加用户: use bbs db.addUser("readwrite","rw") db.addUser("read","readabc") 增加配置项auth=true,打开库安全认证 db.auth("root","rootabc") 用户存放在数据库的system.users集合中。 use test db.auth("read","readabc") =======================高级指南=================系统管理高级技巧-系统监控=================备份恢复 mongodump -h dbhost -d dbname -o dbdirectory -- 数据库备份 mongorestore -h dbhost -d dbname --directoryperdb dbdirectory -- 数据库恢复 可在线实时备份 提取快照方法:db.runCommand({"fsync":1,"lock":1}) fsync强制所有缓冲区写入磁盘 备份完后解锁:db.fsyncUnlock() 缺点:耽误读写操作 =======================高级指南=================系统管理高级技巧-系统监控=================数据库修复 mongod --repair启数据库修复 或db.repairDatabase() 修复原理:将所有文档导出并马上导入,忽略无效文档,然后重建索引 修复会化点时间 =======================高级指南=================复制功能================= 复制优点 数据备份 数据恢复 读写分离 主、从节点建立: 建立启动主节点:./mongod -f ../etc/mongo.conf --master,注意--master表示主节点,可跟--port表明启动端口 建立从节点: mkdir /data/slave,启动从节点: ./mongod --dbpath /data/slave --port 10001 --slave --source localhost:27017, 注意端口必须与主节点不同,--source指明主节点 ps:一个主节点挂载从节点不超过12个复制管理 打印主从复制集群情况: 主节点上 db.printReplicationInfo() configured oplog size日志大小 oplog first event time日志什么时候开始记录 oplog last event time日志最后一条记录时间 从节点上 source 主节点地址 syncedTo 最后一次同步时间 日志大小调节 ./mongod -f 启动配置文件路径 --oplogSize(log大小调节参数) xxxxx(值)
=======================高级指南================MongoDB数据文件内部结构=================
概念: 副本集是具有故障恢复功能的主从集群 与主从集群区别:副本集没有固定主节点 本质上是很牛X的具有HA功能的主从集群 副本集创建: 为副本集取名 (jm) ./mongod --dbpath /data/db1 --port 1111 --replSet jm ./mongod --dbpath /data/db1 --port 2222 --replSet jm ./mongod --dbpath /data/db1 --port 3333 --replSet jm --replSet参数,创建副本集中一个节点 创建启动副本集中第二个节点 副本集初始化,初始化命令只能执行一次 config_jm={"_id":"jm",members:[{_id:0,host:"192.168.154.133:1111"},{_id:1,host:"192.168.154.143:2222"},{_id:2,host:"192.168.154.153:3333"}]} rs.initiate(config_jm) 查看副本集状态: rs.status() db.isMaster() 副本集故障恢复: 副本集任何时间只有一个活跃节点 投票机制决定谁成为活跃节点,优先级最高、数据最新的节点获胜 节点类型: standard:这种是常规节点,他存储一份完整的数据库副本,参与选举投票,有可能成为活动节点 passive:存储一份完整的数据库副本,参与选举投票,不能成为活动节点 arbiter:仲裁者只参与投票,不接收复制的数据,也不能成为活动节点 ps:注意一点:副本至少3个,因为2个的话有一个挂了另外一个不能投票了! 剩下一个会一直是secondary =======================高级指南=================分片================= 概念: 分片是指将数据拆分,将其分散到不同机器上的过程 mongodb支持自动分片 何时分片:磁盘不够用了、单个mongd不能满足写数据性能要求、想将大量数据放在内存中提高性能 片键指从集合中挑选一个键,该键值做为数据拆分的依据 创建分片步骤: 选择合适的片键(eg:username) 1 :建立mongodb启动配置mongo.conf文件logpath=../log/mongodb.log dbpath=/home/tom/data/dbfp logappend=false cpu=false journal=true journalCommitInterval=100 rest=true fork=true auth=false 2 :启动mongodb ./mongod -f ../etc/mongo1.conf --port 27017 ./mongod -f ../etc/mongo2.conf --port 27018 3 : 启动配置服务器 ./mongod -f ../etc/mongoconf.conf --port 20000 4 : 启动mongos ./mongos --port 30000 --configdb 配置服务器IP:20000 5 : 配置分片节点 ./mongo mongos服务器IP:30000/admin db.runCommand({addshard:"分片服务器1:27017",allowLocal:true}) db.runCommand({addshard:"分片服务器2:27018",allowLocal:true}) 6 : 启动分片 db.runCommand({"enablesharding":"jmtest"}) 7 : 在指定列上建立片键 db.runCommand({"shardcollection":"jmtest.account","key":{"userName",1}}) 1表示升序 db.jmtest.getIndexs() 查看索引 8 : 查看各分片状态登录mongos服务器 ./mongo mongos服务器IP:30000/admin db.printShardingStatus() 以上操作都会保存到配置服务器里面 生产配置: 三个配置服务器 多个mongos服务器 每个片都是副本集 生产环境应建立3个配置服务器 关于分片的其他操作如:删除,请查阅另一篇文章:http://blog.csdn.net/johnstrive/article/details/24690805 =======================高级指南=================分片机制================= MongoDB的分片是指定一个分片key来进行,数据按范围分成不同的chunk,每个chunk的大小有限制。 有多个分片节点保存这些chunk,每个节点保存一部分的chunk。 每一个分片节点都是一个Replica Sets,这样保证数据的安全性。 当一个chunk超过其限制的最大体积时,会分裂成两个小的chunk。 当chunk在分片节点中分布不均衡时,会引发chunk迁移操作。 客户端访问路由节点mongos来进行数据读写。 config服务器保存了两个映射关系,一个是key值的区间对应哪一个chunk的映射关系,另一个是chunk存在哪一个分片节点的映射关系。 路由节点通过config服务器获取数据信息,通过这些信息,找到真正存放数据的分片节点进行对应操作。 路由节点还会在写操作时判断当前chunk是否超出限定大小。如果超出,就分列成两个chunk。 对于按分片key进行的查询和update操作来说,路由节点会查到具体的chunk然后再进行相关的工作。 对于不按分片key进行的查询和update操作来说,mongos会对所有下属节点发送请求然后再对返回结果进行合并。 (责任编辑:IT) |