NoSQL-Mongodb

NoSQL-Mongodb
是一个基于分布式文件存储的数据库
由C++编写,为Web应用提供可扩展的高性能数据存储解决方案
介于关系数据库和非关系数据库
是关系数据库当中功能最丰富,最像关系数据库的
他支持的数据库结构非常松散,是类似json的bjson格式(二进制)
因此可以存储比较复杂的数据类型
最大的特点是支持的查询语言非常强大,语法类似于面向对象的查询语言
还支持对数据建立索引


国内外nosql网站
新浪微博 redis
google bigtable
amazon simpledb
淘宝 tair
视觉中国 mongodb
优酷 mongodb
飞信空间 handlersocket
豆瓣社区 beansdb


面向集合 collection-orented
    数据被分组存储在数据集中,被称为一个集合
    每个集合有一个唯一标识名,
    可以包含无限数目的文档
    集合概念类似关系型数据库的表
    只是不需要定义任何模式
模式自由schema-free
    集合里面没有行和列的概念,下面两个记录可以存在同一个集合
    {"name":"mongo"}
    {"age":25}
文档型 documents
存储的数据用key-value的集合,key是string,value是任意类型
包括数组和文档,每个文档相当于关系型数据库表中的一条记录


mongodb特性
面向集合存储
模式自由
支持动态查询
支持完全索引,包含内部对象
支持复制和故障恢复
使用高效的二进制数据存储,包括大型对象,如视频
自动处理碎片,以支持云计算层次的扩展
文件存储格式为bson,一种json的扩展


适用场景
持久化缓存层
高效实时性
用于对象及json数据存储
高伸缩性的场景
大尺寸,低价值的数据存储

不使用场景
要求高度事务性的系统
传统的商业只能应用
复杂多表查询



安装
1 下载
在www.mongodb.org找到
wget http://fastdl.mongodb.org/linux/mongodb-linux-i686-1.8.0.tgz

2 解压
tar -zxvf mongodb-linux-i686-2.0.2.tgz

3 源代码安装时,全部cp到/usr/local里面
cd mongodb-linux-i686-2.0.2/
rsync -a bin /usr/local/mongodb/

4 建立一个目录放数据,建立一个文件放log
cd /usr/local/mongodb/bin
mkdir dbdata
touch dblogs

这样mongodb就安装好了


5 启动mongodb 和开机自启动
将启动项目加入rc.local ,服务开机时启动
echo "/usr/local/mongodb/bin/mongod --dbpath=/usr/local/mongodb/data" >> /etc/rc.local

启动mongodb
/usr/local/mongodb/bin/mongod --dbpath=/usr/local/mongodb/data --fork --logpath=/usr/local/mongodb/dblogs
--dbpath执行数据库存放路经,默认是/data/db
--fork以Daemon进程方式运行, 就是&, 表示后台执行
如果指定fork,必须指定logpath,日志文件路径
pstree -p|grep mongod
有一个主进程2596, 还有6个子进程

关闭服务
pkill mongod
或者killall mongod
不能用kill -9 2596, 会造成mongodb再也起不来,
会看到/data下有个lock文件
rm -rf mongod.lock, 删除这个文件才可以重新启动mongodb

启动mongodb参数
--dbpath    数据库目录
--port         端口,默认是27017
--logpath    日志文件目录
--logappend     日志生成方式,追加/覆盖
--keyFile    集群模式的关键标识
--journal    启动日志
--fork        后台Daemon形式运行服务


6 登陆
./mongo
这样就登陆了,默认进入test数据库
exit退出



mongodb体系结构
数据库database, 集合collection, 文档对象document
对应mysql的database, table, row


mongodb默认数据目录/data/
负责存储所有数据文件
数据文件会随着数据量增大而增大
如果有个叫作mydb的数据库
那么构成它的文件会是
mydb.ns, mydb.0, mydb.1等等
而mysql中的myisam则是frm,MYD,MID


./mongo
show dbs
显示有个local(empty)数据库,而没有test
mongodb中的数据库的创建是隐式的
show tables;
show collections;
都可以用来显示当前数据库中多少集合
db 显示当前数据库
当前数据库中的c1集合里面插入json对象
db.c1.insert({name:"user1"});
show dbs
显示了test数据库,不再是空的
show tables;
看到同时创建了c1集合
db.c1.find();
看到c1的数据
{"_id":ObjectId("随机值"),"name":"user1"}
不同的集合有不同的objectid,不会重复

现在在/data/里面就可以看到
test.0, test.1, test.ns
test.0 16M, test.1 32M
如果再增加数据,会有test.2, 是之前的2倍


数据类型
1 null
{"one":null}

2 布尔
{"one":true}

3 32位整数

4 64位整数

5 64位浮点数
{"one":2.02}

6 字符串
{"one":"sfaf"}

7 符号

8 objectid类型
ObjectId(".......")

9 日期
适用时要加new, js方法
{"one":new Date()}

10 正则表达
JS语法来表示
{"one":/ho/i}

11 代码
js代码, 包括mongodb登陆界面里面都是js
{"one":function(){......}}


12 数组
{"one":["a","b",["c","d"]]}

13 内嵌文档
{"one":{"name":"Tom","age":20}}



控制台常用命令
查看当前数据库
db
查看用户列表
db.system.users.find()
查看所有用户
show users
查看所有数据库
show dbs
查看所有集合
show collections
删除当前数据库
db.dropDatabase()
删除集合
db.集合名.drop()
帮助
help
当前数据库支持的方法
db.help()
当前集合支持的方法
db.集合名.help()


保存数据
插入数据
db.users.insert({"_id":1,"name":"mongo"})
db.users.insert({"name":"mongo2"})
db.users.insert({"name":"mongo2","age":30})
查找数据
db.users.find()
db.users.find({name:"user3"})
db.users.findOne()    只查找一条记录
清空集合
db.users.remove()
db.users.remove({name:"user3"})
更新数据
db.user1.update({name:"user3"},{name:"user30"});
db.user1.update({name:"user3"},{$set:{name:"user30"}});
db.user1.update({name:"user3"},{$set:{sex:"nan"}});    增加字段sex


**********************************************************************

mongodb增删改查

1 insert/save
无论插入什么json数据
都会自动插入一个_id:objectId()

mongodb是面向文档存储的数据库

insert 主键相同就错误,不同就插入
save 主键相同就更新,不同就插入

db.users.insert({"_id":1,"name":"mongo",sex:"nan"})
db.users.insert({"name":"mongo2"})
db.users.insert({"name":"mongo2","age":30})
db.users.save({"name":"mongo3"})
db.users.save({"_id":1,"name":"mongo3"})
db.users.insert({"name":"mongo2","age":30, post:"title":"hello","content":"world"})
控制台直接支持js
for(i=1;i<10;i++){
db.users.insert({"name":"mongo"+i})
}

2 remove
db.users.remove(), 全部删除,参数为空json
db.users.remove({name:"user3"})



3 find
支持动态查询
db.users.find()
db.users.find({name:"user3"})
db.users.findOne()    只查找一条记录
_id字段始终会被返回

find的参数2个,前一个是where条件,第二个参数是需要(1)/不需要(0)的字段
//返回除了age以外所有字段
db.user.find({},{age:0});
//返回tags=tennis,并且除了comments的所有列
db.post.find({tags:'tennis'},{comments:0});
//返回userid=16的name字段
db.user.find({userid:16},{name:1});
    {"_id":16, "name":"user16"}
//返回x=john的所有z字段
db.things.find({x:"john"},{z:1});

条件表达式
>, <,  >=, <=, <>
$gt, $lt, $gte, $lte, $ne
db.c1.find({age: $gt:5} ); 年龄大于5的记录

总共多少个json
db.c1.count();
db.c1.find().count();
排序
db.c1.find().sort(age:1); 升序
db.c1.find().sort(age:-1); 降序

取前几个
db.c1.find().limit(3);
取范围,从第六个开始取后面5个
db.c1.find().skip(5),limit(5);

链式,分页的实现
db.c1.find().sort(age:-1).skip(5),limit(5);
db.c1.find().sort(age:-1).skip(5),limit(5).count();    默认count(0),
db.c1.find().sort(age:-1).skip(5),limit(5).count(0);     忽略前面条件统计
db.c1.find().sort(age:-1).skip(5),limit(5).count(1);    count(1)不忽略前面条件统计


$all 包含
类似$in操作,要求数组里面的value全部被包含在返回记录中
db.c2.insert({name:"user1",post:[1,2,3,4,5]});
其中post是个数组, 求查找包含[1,2,3]的post记录
db.c2.find({post:{$all:[1,2,3]}});
user1记录被查出来

$exists 检查一个字段是否存在
1表示真,0为假, 也可以用true,false
db.c2.find({age: {$exist:1});    没有记录返回
db.c2.find({name: {$exist:1});    返回记录
db.c2.find({post: {$exist:1});    返回记录


$mod 取余
db.c1.find({age:{$mod:[2,0]}});    age/2余0的记录


$in 存在于。。中的记录
db.c2.find({_id:{$in:[1,2,3]}});
user1,user2,user3查找出来

$nin 不存在于。。中的记录
db.c2.find({_id:{$nin:[1,2,3]}});

$or where条件中的or
db.c2.find({$or:[{name:"user1"},{age:30},{name:"user4"}]})

$nor 和or相反,相当于不是a也不是b

$size
查询长度为输入参数的数组记录
db.user.find({post:{$size:5}});

$where
db.user.find({$where:function(){return this.name=="user1" || this.age>20;}})
等同于
db.user.find({function(){return this.name=="user1" || this.age>20;})

$type
根据json类型来检索数据
//返回字符串类型的记录
db.user.find({name:{$type:2}});
//返回int类型记录
db.user.find({age:{$type:16}});
double         1
string         2
object        3
array        4
binary        5
obj id        7
bool        8
date        9
null        10
regex        11
js        13
32bit int        16
timestamp    17
64bit int        18
min key        255
max key    127

正则表达式
//name属性以u开头,4结尾的所有用户
db.user.find({name:/u.*4$/i}).limit(5);
同样效果
db.user.find({name:{$regex:'u.*4$', $options:'i'}}).limit(5);
i表示忽略大小写
db.user.find({name:/user2/i});


distinct
唯一值,相当于mysql中
select distinct name from t1
db.user.distinct("name");  返回所有不重复的name
db.user.distinct("age");
    [1,2,3,4,5,6,7,8,9,10]    返回所有不重复的age



capped collection 固定集合
GridFS 大文件上传下载


$elemMatch
数组属性中元素匹配, 当post属性类型是数组, 可以直接点出其元素
db.user.find({"post.title":"hello"});
相当于
db.user.find({post: {$elemMatch: title:"hello"});
还可以
db.user.find({post: {$elemMatch: title:"hello", title:"world"});

slaveOk
让从服务器有读的权限
rs.slaveOk(); 开启分布式中的slave查询,可以查找slave中的数据


cursor 游标和cursor methods
1 遍历游标
var cur = db.user.find().skip(10).limit(8);
while(cur.hasNext()){
cur.next();
}

cur.forEach(
    function(x){
        print(tojson(x))
    }
);



$query/$orderby/$explain

null查询
db.c1.insert({x:3,y:null});
db.c1.insert({x:4});
查询
db.c1.find({y:ull});
则两个记录都会被查找出来

db.c1.find({y:{$type:10}});
type为10的类型是NULL,这样就只查找到
db.c1.insert({x:3,y:null});

db.c1.find({y:{$exists:false}});
只查找到db.c1.insert({x:4});



$slice
也是针对数组,前多少个元素
前两个
db.c1.find({name:"user1"}, {post: $slice:2});
前三个
db.c1.find({name:"user1"}, {post: $slice:3});
第一个和第二个元素
db.c1.find({name:"user1"}, {post: $slice:[1,2]});





4 update
update(criteria, objNew, upsert, multi)
criteria, 查询条件
objNew, 更新内容
upsert, 设置成1时,如果记录存在,更新,否则新增,默认0
multi, 设置成1时,如果有多个符合条件的记录,全部更新,默认0

db.user1.update({name:"user3"},{name:"user30"});
$set
db.user1.update({name:"user3"},{$set:{name:"user30"}});
db.user1.update({name:"user3"},{$set:{sex:"nan"}});    增加字段sex
db.user1.update({name:"user3"},{sex:"nan"}); 如果这样,就把name:user3变成了sex:nan
如果有多个符合条件的记录,全部更新,如下,否则只更新一条记录,必须和$set配合使用
db.user1.update({name:"user3"},{$set:{name:"user30"}},0,1);

$inc
把某个值增加一个值
{$inc:{field:value}}
要对一个_id=0的user的年龄+1
普通做法:
var u=db.user.findOne({_id:0});
u.age++
使用$inc
db.user.update({_id:0},{$inc:{age:1}});
还可以直接加n
db.user.update({_id:0},{$inc:{age:100}});
如果没有这个字段,则添加这个字段,还可以使用负值


$unset
1为删除
删除某个字段
比如把所有满足条件name=user3的记录的score,age字段删除
db.user1.update({name:"user3"},{$unset:{score:1, age:1}},0,1);


$push,
给数组属性的最后压入一个值,可以重复
db.c1.insert({name:"user1",arr:[1,2,3]});
db.c1.update({name:"user1"},{$push:{arr:4}});
db.c1.find();
{"_id":ObjectId("xxxx"), "arr":[1,2,3,4], "name":"user1"}


$pop
1为真,可以弹出, -1表示第一个元素
把数组属性的最后一个值弹出去
db.c1.update({name:"user1"},{$pop:{arr:1}});
db.c1.find();
{"_id":ObjectId("xxxx"), "arr":[1,2,3], "name":"user1"}


$pushAll
可以压入多个元素,可以重复
db.c1.update({name:"user1"},{$pushAll:{arr:[4,5,6]}});
db.c1.find();
{"_id":ObjectId("xxxx"), "arr":[1,2,3,4,5,6], "name":"user1"}


$addToSet
压入不重复的元素,如果重复就保持不变
db.c1.update({name:"user1"},{$addToSet:{arr:4}});
db.c1.find();
{"_id":ObjectId("xxxx"), "arr":[1,2,3,4,5,6], "name":"user1"}


$each
连续
比如连续压入不重复值
db.c1.update({name:"user1"},{$addToSet:{arr:{$each:[5,6,7,8,9]}}});
db.c1.find();
{"_id":ObjectId("xxxx"), "arr":[1,2,3,4,5,6,7,8,9], "name":"user1"}


$pull
针对数组,删除符合条件的记录
db.c1.update({name:"user1"},{$pull:{arr:4}});
db.c1.find();
{"_id":ObjectId("xxxx"), "arr":[1,2,3,5,6,7,8,9], "name":"user1"}

$pullAll
一次性删除多个元素
db.c1.update({name:"user1"},{$pull:{arr:[7,8]}});
db.c1.find();
{"_id":ObjectId("xxxx"), "arr":[1,2,3,5,6,,9], "name":"user1"}


$rename
重命名字段名
db.c1.update({_id:1},{$rename:{arr:"post"}});

特殊操作符$
代表查询记录中第一个匹配到的记录
t.find()
{"_id":1, "title":"abc", "comments":[{"by":"joe", "votes":3}, {"by":"jane", "votes":7}]}
t.update({'comments.by':'joe'}, {$inc:{'comments.$.votes':1}}, false, true);
t.find()
{"_id":1, "title":"abc", "comments":[{"by":"joe", "votes":4}, {"by":"jane", "votes":7}]}


save
var u=db.user.findOne({_id:0});
u.age++
u.sex="nan"
u.score=20
u.flower="yes"
db.uesr.save(u);
直接更改或者添加属性


drop
db.c1.drop();
db.dropDatabase(); 删除了当前数据库


创建显式的数据库
db.createCollection("c1");
show tables;
    c1


*************************************************************

capped collection 固定集合
是性能出色的有固定大小的集合
以LRU least recently used 最近最少使用的规则和插入顺序进行age-out老化移出处理
自动维护集合众对象的插入顺序,创建时先指定大小
空间用完后,新添加的对象取代集合中最旧的对象,永远保持最新数据

功能特点
可以插入及更新,但更新不能超出collection大小
不允许删除,但可以调用drop删除集合中的所有行
drop后需要显式德重建集合

固定集合插入速度极快
按照插入顺序的查询输出速度极快
能够插入最新数据时,淘汰最早数据
适合存储日志信息
以及缓存一些少量文档

固定集合的创建
db.createCollection("c2": {capped:true,size:10000, max:5})
创建一个固定集合,大小为10000字节,超过后,新数据自动替换最老的数据
还可以加上max:100属性,表示限定的文档(一行记录)个数

查看集合信息
db.c2.status();
命名空间
索引(普通集合有索引,主键索引_id, 固定集合没有索引)
存储大小
capped 1
max 5

普通集合转成固定集合
db.runCommand({convertTocapped:"c1", size:10000, max:3});




GridFS大文件管理
mongodb中存储大二进制文件机制
存储视频,搞清图片等
自动管理磁盘碎片

使用两个集合存储数据
files 包含元数据对象
chunks 包含真正的二进制块
文件和块有个默认前缀,fs
任何默认的gridfs存储将包括命名空间fs.files和fs.chunks


cd /usr/local/mongodb/bin
ls
可以看到有很多命令可以用
./mongofiles list    查看GirdFS文件系统
现在任意制作一个压缩包
tar czf mongosniff.rat.gz mongosniff
上传到GridFS文件系统
./mongofiles put mongosniff.tar.gz

show tables;
可以看到
c1
c2
fs.chunks
fs.files

元数据文件:
db.fs.files.find()
包含文件信息,还有其md5值等
真正的二进制文件是:
db.fs.chunks.find();
查看GridFS下的文件
./mongofiles list
看到有mongosnif.tar.gz
并且大小2.6M
查看linux中该文件大小
du -sh mongosniff.tar.gz
也是2.6M

从GridFS文件系统下载
./mongofiles get mongosniff.tar.gz
删除文件
./mongofiles delelte mongosniff.tar.gz

查看linux文件的md5值
md5sum mongosniff.tar.gz


**********************************************************************

mongodb性能篇
索引管理

索引信息放在system.indexes中
_id字段在创建时候,默认建立了索引,且这个索引不可删除


1 建立索引
在name上建立索引1升序,-1降序
db.c1.ensureIndex({name:1});
在后台执行的话
db.c1.ensureIndex({name:1},{background:true});

2 查看系统中所有索引
db.system.indexes.find();


3 查看某个集中中所有索引
db.c1.getIndexKeys();
看到一个主键索引一个自己添加的索引
[{_id:1},{name:1}]


4 解析语句
db.c1.find().explain();
可以扫描到影响行数,是否用了索引等
当用了索引后,扫描行数很少了


5 唯一索引
指定unique:true
对某个字段加唯一索引
加了唯一索引后,该字段不可重复,否则报错duplicate
db.c1.ensureIndex({age:1},unique:1);

6 删除索引
db.c1,dropIndex({age:1});


*****************************************
性能优化
explain执行计划

db.c1.find().explain();
可以扫描到影响行数,是否用了索引等
当用了索引后,扫描行数很少了


优化器 profile
类似慢查询日志

开启
启动mongodb时候,加上-profile=级别
在客户端用
db.setProfilingLevel(level) 来实时配置
profile信息在system.profile中
通过db.getProfilingLevel()来获取当前的profile级别

级别有3个,0,1,2
0 不开启,默认
1 记录慢命令,默认>100ms
2 记录所有命令

设置时间1秒
db.setProfilingLevel(1, 1000);
还可以在启动时,使用--slowms
./mongodb -profile=1 --slowms=1000



优化方案
1 创建索引
ensureIndex

2 限定返回结果条数
limit

3 查询使用的字段,不是所有字段

4 采用cappedcollection

5 采用profiling


性能监控
Mongosniff
可以从底层监控到底哪些命令发送给mongodb去执行
./mongosniff --source NET lo
它抓的是通讯记录,比如监控lo回环接口
它是实时动态监视的,需要打开另一个客户端进行命令操作
可以将这些数据输出到日志,就可以保留下所有数据库操作的历史记录


./mongostat
会每秒刷新一次
它是实时动态监视的,需要打开另一个客户端进行命令操作
监控的是每次增删改查操作,连接数等等


************************************************************
mongodb管理

1 数据导入导出
导出: mongoexport
导入: mongoimport

./mongoexport -d test -c user --csv -f uid, username,age -o /tmp/user_csv.data
cat /tmp/user_csv.data
uid, username, age
1, "Jerry", 100
2, "Tom", 25
......
--csv 指定要导出为csv格式
-f 要导出哪些列
-d 数据库
-c 集合
具体用mongoexport -help查看

./mongoimport -d test -c user /tmp/user_csv.data

查看user集合
db.user.stats();


2 数据备份恢复
备份, mongodump
恢复, mongorestore

./mongodump -d test
当前目录下会创建一个dump目录存放备份文件,也可以自己指定
 ./mongodump -d test -o /tmp/
还可以备份collection,使用-c
ls /tmp/test/
user.bson    system.indexex.bson    system.profile.bson

./mongorestore -d test /tmp/*
恢复成功
如果需要导入前先删除db,可以用-drop参数
就会先伸出再导入


备份集合
./mongodump -d test -c c1 -o /tmp/
cd /tmp
    test
tree test/ 查看目录树,看到只备份了一个集合
    c1.bson

恢复集合
./mongorstore -d test -c c1 /tmp/*



3 用户安全认证
mongodb默认是不需要用户就可以登录
对所有的库具有root权限
可以在启动服务时指定--auth
./mongod --auth
./mongo 登录客户端


例子
启动服务
./mongod --auth --dbpath=/usr/local/mongodb/data/ --logpath=/usr/local/mongodb/dblogs --fork
首次登陆客户端
./mongo
登陆后默认是超级管理员
可以看所有东西,这样是不安全的
首先要进入admin数据库
use admn
show tables;
db.addUser("root","123");
现在就没有权限show tables了
要退出exit

重新登陆
./mongo localhost:27017/test
这样登陆进去也不行
再试试这样
./mongo localhost:27017/test -u root -p 123
无法登陆
因为root用户只是admin数据库的超级用户,不能进入test数据库
所以先要
./mongo localhost:27017/admin -u root -p 123
show dbs
use admin
db.auth("root","123"); 验证通过
给test数据库添加用户
use test
db.addUser("user1","123");
查看系统用户
show tables
用户都存在system.users
db.system.users.find();
db.auth("user1","123"); 验证通过
然后就可以操作test了
./mongo localhost:27017/test -u user1 -p 123



***************************************************************
mongodb架构篇


mongodb主从集群分为两种
1 master-slave 复制主从
2 replica sets 复制副本集

一,主从复制
mongodb支持在多个机器中通过异步复制达到故障转移和实现冗余
多机器中同一时刻只有一台是用于写操作
正是由于这个情况,为mongodb提供了数据一致性的保障
担当primary角色的机器能把读操作分发给slave

master-slave 复制主从
只需要在一个服务启动时加上-master参数
另一个服务加上-slave与-source参数,就实现同步
mongodb最新版不推荐使用这种方法了


模拟
开两个终端
第一个终端
在/usr/local/mongodb/下创建两个数据目录
mkdir data1 data2
touch dblog1
touch dblog2
./mongod --master  --dbpath=/usr/local/mongodb/data1/ --logpath=/usr/local/mongodb/dblog1 --port 20001 --fork
netstat -tunpl | grep :20001
./mongo --port 20001
show tables;
db.c1.insert({name:"user1"});



第二个终端
./mongod --slave --source 192.168.10.1:20001 --dbpath=/usr/local/mongodb/data2/  --logpath=/usr/local/mongodb/dblog2 --port 20002 --fork
netstat -tunpl | grep :20002
./mongo --port 20002
show tables;
    c1
    system.indexes
db.c1.find();
    {_id:ObjectId(), name:user1}    从服务器上直接可以看到数据
db.c1.insert({name:"user2"})
    not master    从服务器上不可以写数据

备份时候在从服务器上作
./mongodump --port 20002 -d test
在/usr/local/mongodb/bin/dump/test下可以看到数据文件



二,副本集
replica sets 复制副本集
1.6版本开发了replica set
增加了故障自动切换和自动修复成员节点
各个db之间数据完全一致
最显著的区别在于,副本集没有固定的主节点
是整个集群选举出的一个主节点,
当其不工作时,变更其他节点
强烈推荐使用
    活跃
备份    备份    备份

部署replica sets

启动两个实例
1 创建数据文件存储路径
mkdir -p /usr/local/mongodb/data
cd /usr/local/mongodb/data
mkdir -p /data1/
mkdir -p /data2/

2 创建日志文件路径
mkdir -p /usr/local/mongodb/log
touch dblog1
touch dblog2

3 创建主从key文件用于标识集群的私钥的完整路径
如果各个实例的key file内容不一致,程序将不能正常用
mkdir -p /usr/local/mongodb/key
cd /usr/local/mongodb/key
touch key1
touch key2
echo "123456" > key1    导入一串秘钥
echo "123456" > key2
chmod 600 *    两个秘钥权限要600    禁止别人阅读

4 启动2个实例
./mongod --replSet rs1 --keyFile /usr/local/mongodb/key/key1 --fork --port 20001 --dbpath /usr/local/mongodb/data/data1 --logpath=/usr/local/mongodb/log/dblog1.log --logappend

./mongod --replSet rs1 --keyFile /usr/local/mongodb/key/key2 --fork --port 20002 --dbpath /usr/local/mongodb/data/data2 --logpath=/usr/local/mongodb/log/dblog2.log --logappend

--replSet rs1 副本集的名字
这时候这两个进程属于一个小组rs1


5 配置及初始化replica sets
为两个进程配置会议室
一个客户端内
./mongo -port 20001
config_rs1={
    id:"rs1"
    members:[
        {_id:0, host:"localhost:20001", priority:1},
        {_id:1, host:"localhost:20002", priority:2}
    ]
}
初始化会议室
rs.initiate(config_rs1);
看到命令行标识变成
SECONDARY>
在secondary里面首先需要
rs.slaveOk();
否则它连读的权限也没有
show tables
查看状态
rs.status();
可以看到数据同步自localhost:20002
两者可以完全ping通

另一个客户端
./mongo -port 20002
看到命令行标识变成
PRIMARY>

两者已近经过了选举
优先级高的优先变成primary

在primary里面可以进行增删改查
db.c1.insert({name:"user1"});
查看状态
rs.status();


oplog
记录了primary的增删改

故障转移
需要3台以上服务器
一台坏了,从另外两台里面选一台做primary
提高稳定性,负载均衡,扩展性

******************************************************************

mongodb+php 开发实战


php需要用mongodb需要安装mongodb的php扩展
1 下载php mongodb扩展源码
wget http://pecl.php.net/get/mongo-1.2.6.tgz

2 解压缩
tar zxf mongo-1.2.6.tgz

3 利用phpize生成configure
cd mongo-1.2.6
/usr/local/php/bin/phpize

4 编译
./configure -h 查看需什么参数
./configure --with-php-config=/usr/local/php/bin/php-config --enable-mongo

5 安装
make && make install

6 配置扩展
ls /usr/local/php//lib/php/extensions/no-debug-non-zts-20060613/
    mongo.so
vi /usr/local/php/etc/php.ini
extension=mongo.so

7 重启apache
/usr/local/apache2/bin/apachectl restart
 
8 测试首页
cd /usr/local/apache2/htdocs/
vi info.php
<? php
phpinfo();
访问
192.168.10.1/info.php
看到mongo模块
表示php支持mongo


9 设置真个htdocs权限
设置apache用户对apache服务器的进程执行权限
useradd apache
passwd apache
vi /usr/local/apache2/etc/httpd/conf
User apache
Group apache
/usr/local/apache2/bin/apachectl restart
设置权限
cd /usr/local/apache2/
setfacl -m u:apache:rwx -R htdocs/
setfacl -m d:u:apache:rwx -R htdocs/



10 设置samba服务,网络映射到windows的z:\盘
yum -y install samba* --skip-broken
vi /etc/samba/smb.conf
[web]
    path=/usr/local/apache2/htdocs
    broseable=yes
    writable=yes

smbpasswd -a apache
service smb restart
chkconfig smb on
映射到windows的z:\盘


11 开始windows下面的开发
进入z:\
php提供了4类接口
1 针对mongodb连接的操作, Mongo
2 针对mongodb中数据库的操作,MongoDB
3 针对mongodb中的collection的操作,MongoCollection
4 针对查询结果集的操作,MongCursor



12 设置mongodb为用户授权方式启动
cd /usr/local/mongodb/bin
./mongod --auth --dbpath=/usr/local/mongodb/data/ --logpath=/usr/local/mongodb/dblogs --fork
./mongo
use admin
db.addUser("root","123");
exit
./mongo -uroot -p123 localhost:27017/admin
use test
db.addUser("user1","123");
show tables
db.system.users.find();
exit
./mongo -uuser1 -p123 localhost:27017/test
show dbs
db.c1.insert({name:"user1"});
db.c1.insert({name:"user2"});



13 连接mongo服务器
公共连接文件,用户授权方式登录
conn.php
<?php
$conn = new Mongo("mongodb://user1:123@localhost:27017/test");
$db=$conn->test;



14 find.php
<?php
    include "conn.php";
    $c1=$db->c1;
    //php里面无法直接写json这种形式,必须转一下array
    //mongo的bson比如{name:"user1"}相当于php的关联数组array("name"=>"user1")
    //mongo里的一个数组[1,2]相当于php的索引数组array(1,2)
    //查找一个空数组,就是查找所有
    $arr=array();
    //按条件查找
    //$arr=array("name"=>"user1");
    $rst=$c1->find($arr);
    foreach($rst as $val){
        echo $val;
        echo $val['_id'];
        echo $val['name'];
        echo "<a href='user.php?fid={$fid}'>{$val['_id']}</a>"
    }

?>
访问
192.168.100.1/find.php


user.php
<?php
    include "conn.php";
    $c1=$db->c1;    
    //转url传过来的纯字符串成mongoid类型
    $oid=new MongoId($_GET['fid']);
    $arr=array("_id"=>$oid);
    $rst=$c1->find($arr);
    foreach($rst as $val){
        print_r($val);
    }


mongo类型
MongoId
MongoCode
MongoDate
MongoRegex
MongoBinData
MongoInt32
MongoInt64
MongoDBRef
MongoMinKey
MongoMaxKey
MongoTimestamp
......





15 insert.php
<?php
    include "conn.php";
    $c1=$db->c1;
    $arr=array("name"=>"user3","age"=>30, "sex"=>"nv");
    if($c1->insert($arr)){
        echo "<script>location='find.php'</script>";
    }else{
        echo "insert failure";
    }
访问
192.168.100.1/insert.php


16 delete.php
<?php
    include "conn.php";
    $c1=$db->c1;
    $arr=array("name"=>"user2");
    if($c1->remove($arr)){
        echo "<script>location='find.php'</script>";
    }else{
        echo "delete failure";
    }
访问
192.168.100.1/delete.php


17 update.php
<?php
    include "conn.php";
    $c1=$db->c1;
    $sarr=array("name"=>"user1");
    $darr=array("$set"=>array("sex"=>"nv","age"=>300);
    $opts=array("upsert"=>0, "multiple"=>1);
    if($c1->update($sarr, $darr, $opts)){
        echo "<script>location='find.php'</script>";
    }else{
        echo "update failure";
    }
访问
192.168.100.1/update.php


18 关闭连接
<?php
$conn->close();
?>

你可能感兴趣的:(mongodb,NoSQL)