Mogodb基础
Memcached redis 都是key value数据库
Mongodb文档数据库,存储的是文档,(bson->josn的二进制化)
特点:内部引擎用js解释器。把文档存储成bson结构,在查询时,转换成js对象。
Mongo和mysql(传统的数据库比):
传统数据库,结构化数据,。定好了表结构后,每一行的内容,必是符合表结构的,就是说--列的个数类型都一样。
Mongo文档数据库,表下的每篇文档都可以有自己的独特的结构(json结构)
思路:如果有电影,影评,影评的回复,回复的打分
在传统的数据库中至少有4张表,关联非常复杂。
在文档数据库中,通过一篇文档即可完成,体现文档数据反凡事化
{
Filem:’天龙八部’
Commemt[
{
Content:’ 王家伟的电影风格’
Reply:[‘ 支持’,’好’]
}
]
}
}
Mongodb的安装:
1. 下载
www.mongodb.org 下载stable版
2. 解压
3. 不用编译。本身就是编译后的二进制
Bsondump:二进制导出
Mongo:客户端(相当于mysql.exe)
Mongod:服务器端(相当于mysqld.exe)
Mongodump整体数据库的导出。(mysqldump)
Mongoexport:导出易识别的json,或csv文档
Mogorestore:数据库整体导入
Mongos:路由器,做集群时(分片时用)
4:启动mongod服务
./bin/mongod -dbpath --logpath --fork(以后台运行) --port 27017
参数解释
--dbpath 数据存储目录
--logpath:日志存储目录
--fork:以后台运行
Df -h
Mkdir -p /home/m17 /home/mlog
./bin/mongod --dbpath /home/m17/ --logpath/home/mlog/m17.log --fork --port 27017
Ps aux|grep mogo
./bin/mongo 直接连
54. Mongodb非常占磁盘空间,你在使用的时候,一般占3,4G的样子、
如果你用虚拟机练习,可能空间不够,导致无法启动。可以用 --smallfiles选项来启动
Mogodb的启动:
sudo./bin/mongod --port=27017 --dbpath=/usr/local/mongodb/data/--logpath=/usr/local/mongodb/log/mongodb.log --fork --smallfiles
没有正常关闭mongodb,会报about to fork child process,waiting until server is ready for connections.
forked process: 12964
ERROR: child process failed, exited witherror number 100
需要删掉data/mongodb.lock
不要用pkill-9 mongodb
要用killallmongod
客户端:./mongo
报错:
error while loading shared libraries:libssl.so.6: cannot open shared object file: No such file or directory
解决:
ln -sf/usr/lib64/libssl.so.10 /usr/lib64/libssl.so.6
ln -sf/usr/lib64/libcrypto.so.10 /usr/lib64/libcrypto.so.6
Show databases;
Use test;
在mongodb中,表不叫table叫collections;
Show collections;
Db.help();查看帮助
Mongodb的库是隐士创建的,你可以use一个不存在的库
然后在改库下创建collection,即可创建库
Show collections;
System.indexes ;索引表
db.createCollectio(‘user’);
db.user.insert({name:’lisi’,age;22});
Db.user.find();
db.user.insert({_id:2,name:'hmm',hobby:['basketball','football']});
Collection允许隐士创建
db.goods.insert({_id:1,name:'nokia'});
Db.goods.drop();删除表
db.dropDatabase(); // 删除数据库
基本的增删改查
db.user.insert({sn:'001',name:'xiaoming'});
db.user.insert({_id:2,sn:'001',name:'xiaoming'});
db.user.insert([{_id:3,name:'lisi',age:23},{sn:3,name:'guo'}]);
db.user.insert({name:{x:'li',m:'shimin',jl:['war','kill']}});
删除:
db.user.remove({_id:3});
删除id为3 的文档
Db.user.remove({gender:’m’,true}) 加上ture就是删除一行。
db.user.remove({});删除所有数据
改 update操作。
db.user.update({name:'zhangsan'},{$set:{name:'aaa'}});
修改时的赋值表达式,
$set 修改莫列的值
$unset 删除莫列。
$rename
$inc
db.user.update({name:'zhangsan'},{$set:{name:'liyulong'},$unset:{gender:1},$inc:{age:10}})
Multi:是指修改多行(即使查询表达式命中多行默认也只改1行,如果想改多行可以选择此选项);
db.user.update({gender:'n'},{$set:{gender:'man'}},{mutil:true});
Upsert
如果有name=’wuyong’,就更新,如果没有,就插入
db.user.update({name:'wuyong'},{$set:{name:'zhangxiaoxiao'}},{upsert:true});
setOnInsert :当upsert为TRUE,并且发生了insert操作时,可以补充的字段。
db.user.update({name:'lllll'},{$set:{name:'maliu'},$setOnInsert:{gender:'mm'}},{upsert:true});
setOnInsert:当upsert为ture,并且发生了insert操作时,可以补充一个字段。
db.student.update({name:'zhongguo'},{$set:{name:'maliu'},$setOnInsert:{age:23,gender:'m'}},{upsert:true});
Db.stu.find({gender:’man’,{name:1,_id:0}});
查出所有gender属性值为man的文档中的name属性
db.student.find({gender:'woman'},{gender:1,name:1});
db.goods.find().count();
$ne 不等于
{fileld:{$ne:value}}
db.goods.find({cat_id:{$ne:3}},{goods_id:1,goods_name:1,_id:0});
$gt:大于300的
db.goods.find({shop_price:{$gt:300}});db.goods.find({shop_price:{$gt:300}},{goods_id:1,goods_name:1,shop_price:1});
$lte 小于等于
db.goods.find({goods_id:{$in:[4,20]}},{goods_id:1,goods_name:1,shop_price:1,_id:0});
db.goods.find({$and:[{shop_price:{$gte:100}},{shop_price:{$lte:500}}]},{goods_name:1});
{cat_id:{$ne:3}}
{cat_id:{$ne:11}}
{$and:[{cat_id:{$ne:3}},{cat_id:{$ne:11}}]}
{$and:[]}
Db.goods.find({$and:[{cat_id:{$ne:3}},{cat_id:{$ne:11}}]}
);
db.goods.find({$and:[{cat_id:{$ne:3}},{cat_id:{$ne:11}}]});
{cat_id:{$nin:[3,10]}}
$nor:既不是,也不是。
Db.stu.find({age:{$exists:1}});
Db.stud.find({age:{$type:2}});//字符串
db.goods.find({cat_id:{$type:1}});//数字
db.goods.find({goods_name:{$all:['Cz702c',’c’]}});
for(var i=0;i<1000;i++){
...db.shops.insert({_id:i+1,title:'helloword',content:'aaa'+i});
... }
游标是什么:
通俗的说
游标不是查询结果,而是查询的返回资源,或者接口,通过这个接口你可以逐条读取。
就像php中fopen打开文件,等到资源一样。
var mycusor=db.shops.find();
Mycusor.hasNext();
var cusor= db.shops.find({_id:{$lte:4}});
printjson(cusor.next());
var mycusor=db.shops.find({_id:{$lte:5}});
> while(mycusor.hasNext()){
... printjson(mycusor.next());
... }
> varmycusor=db.shops.find({_id:{$lte:5}});
>mycusor.forEach(function(obj){printjson(obj)});
cusor.hasNext();判断游标是否已经取到了尽头
Cusor.next();取出游标的下一个单元。
游标在分页的应用。
比如10000行,跳过100页取10行
在mysql中是limit offset,N来实现
在mongo中,用skip(),limit()函数来实现。
跳过900条,每页取10条。
varmycusor=db.shops.find().skip(900).limit(10);
varmycusor=db.shops.find().skip(900).limit(10);
> mycusor.toArray();
varmycusor=db.shops.find().skip(900).limit(10);
> printjson(mycusor.toArray()[4]);
Mogodb的索引
db.user.find({sn:2}).explain();
分析
索引提高了查询速度,降低了写入速度,权衡常用的查询字段,不必再太多的列上建索引。
默认是用btree建立的。版本之后用的hash。
Cusor:”btreeCusor sn 1” 2.4用到的是btree索引
创建索引:
db.user.ensureIndex({sn:1});
查看索引
db.user.getIndexes(); db.user.getIndexes();
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" :"_id_",
"ns" :"shop.user"
},
{
"v" : 1,
"key" : {
"sn" : 1
},
"name" :"sn_1",
"ns" :"shop.user"
}
]
db.user.ensureIndex({name:-1}); // 1 升序 -1.降序
删除索引:
db.user.dropIndex({name:-1});
{ "nIndexesWas" : 3,"ok" : 1 }
删除所有的索引:
db.user.dropIndexes();
{
"nIndexesWas" : 2,
"msg" : "non-_id indexes dropped for collection",
"ok" : 1
}
创建多列索引:
多个列绑定在一起,加索引。
db.user.ensureIndex({sn:1,name:1});
子文档索引:
db.shop.insert({name:'Nokia',spc:[weight:120,area:'taiwan']});
db.shop.find({name:'Nokia'});
查询产地在台湾该如何查:
db.shop.find({area:'taiwan'});是错误的
db.shop.find({spc:{area:'taiwan'}}); 也查不出来
db.shop.find({'spc.area':'taiwan'});
给子文档建立索引。
db.shop.ensureIndex({'spc.area':1});
加唯一索引:
db.tea.ensureIndex({email:1},{unique:true});
删除所有索引:
db.tea.dropIndexes();
创建稀疏索引:
普通索引:当建立普通索引如果没field该字段,也为null建立索引
稀疏索引:如果字段为null,就不建立索引。
普通索引:
db.tea.ensureIndex({emmail:1});
建立稀疏索引:
db.tea.ensureIndex({emmail:1},{sparse:true});
db.tea.find({emmail:null});
如上内容最后一行就没有email列,
如果分别加普通索引,和稀疏索引,
根据{emaill:null}来查询,前者能查到,后者根本看不到。
Hash索引:
给一个值,经过hash一算,落在硬盘的位置,
当查找的时候,直接将这个值hash一下,取得硬盘的位置
。
缺点:
0-100之间,btree 取一个页子,
Hash 是散列值:取一个范围,不太容易。
db.tea.ensureIndex({emmail:'hashed'});
重建索引:
一个表经过很多次修改后,导致表的文件产生空洞,索引文件也是
如此,可以通过索引重建,来提交索引的效率,类似mysql中的
Optimize table。
db.user.reIndex();
Mongodb的管理:
Mysql,是以库,表,主机为管理单位
Mongodb:是以库为单位。
我们在设置用户时,需要先在admin数据库下建立管理员,这个管这个管理员登陆后,相当超级用户。
Use admin;
Db.addUser(‘sa’,’password’,false)//false是否只读。
--auth:
db.addUser(‘sa’,’sa’,false);//2.4以下的版本
db.createUser({user:'sa',pwd:'sa',roles:['dbAdmin','userAdmin']});
db.createUser(
{
user:"sa",
pwd:"sa",
roles:
[
{
role:"userAdminAnyDatabase",
db:"admin"
}
]
}
);
db.createUser(
{
user:"sa",
pwd:"sa",
roles:
[
{
role:"dbOwner",
db:"shop"
}
]
}
);
./bin/mongod --dbpath=/usr/local/mongodb/m17/--logpath=/usr/local/mongodb/mlog/mlog.log --fork --port=27017 --smallfiles--auth
需要认证:
./bin/mongod --dbpath=/usr/local/mongodb/m17--logpath=/usr/local/mongodb/mlog/mog.log --port=27017 --smallfiles --fork;
db.auth('sa','sa');
关闭mogodb:
killallmongod
不要用pkill-9 mongo会产生mongodb.lock导致下次mogodb无法启动。
改密码:
db.changeUserPassword('sa','sa1');
删除用户:
Db.removeUser(‘sa’);
Mogodb的导入和导出:
导出:
二进制导出。
Csv
Json
-d 库
-c 表
-0 导出的文件名
-f 导出那几列。
./bin/mongoexport -d shop -c user -f name -o./test.stu.json
导出csv(便于和传统数据库交换数据):
./bin/mongoexport-d test -c good -f name -o ./test.good1.csv --type=csv;
//./bin/mongoexport -d shop -c user -f name -o ./test.stu.json --csv
//./bin/mongoexport-d shop -c user -f name,id -q '{_id:{$lte:10}}' -o ./liyulong.json --type=csv
导入:
./bin/mongoimport-d shop -c test --type json --file ./ll.stu.json
./bin/mongoimport-d shop -c bird --type csv -f name,id --file ./liyulong.json
./bin/mongoimport -d shop -c bird --type csv-f name,id --headerline --file./liyulong.json
二进制导入导出:(做备份)索引不丢
导出bson结构和索引信息。
./bin/mongodump-d shop -c user
导出bson结构的数据文件,json的索引信息
./bin/mongodump -d shop
./bin/mongorestore-d shop --directoryperdb dump/shop//2.4版本适用
./bin/mongorestore -d shop ./dump/shop/ //2.6版本适用
删除数据库
Useshop
db.dropDatabase();
Replicationset 复制集
:多台服务器维护相同的数据副本
创建目录
Mkdir /home/m17 /home/m18
创建实例
1.
./bin/mongod -dbpath=/home/m17--logpath=/home/mlog/m17.log --fork --smallfiles --port=27017 --replSet=rs2
--replSet rs2 // 进程属于属于哪个复制集
Ps aux|grep mongod
2.初始化
随意找一台连上去。
Useadmin
var rsconf={
_id:'rs2',
members:[
{
_id:0,
host:'10.18.93.124:27017'
},
{
_id:1,
host:'10.18.93.124:27018'
},
{
_id:2,
host:'10.18.93.124:27019'
}
]
}
复制集是rs2复制集,复制集下有三个会员,每个会员说明id和端口。
3.根据配置做初始化
rs.initiate(rsconf);
Rs2:STARTUP2>rs.status();
varrsconf={
_id:'rs2',
members:[
{
_id:0,
host:'10.18.93.120:27017'
},
{
_id:1,
host:'10.18.93.120:27018'
}
]
}
./bin/mongo --port 27018 //登陆secondary
./bin/mongo --port 27019 //登陆secondary
Error:error: { "$err" : "not master and slaveOk=false","code" : 13435 }
rs.slaveOK();
“exception 12520file allocation failure” 错误,在使用MongoDB插入数据时,出现导致数据并不能成功插入数据库。
需要清除冗余文件。
删除
rs.remove('10.18.93.120:27018');
添加
rs.add('10.18.93.120:27018');
rs.help();
Slave 默认即不能读也不能写。
在secondary中 rs.slaveOk();
Use admin;
db.shutdownServer();
自动会在其他的复制集变成primary
#!/bin/bash
IP=192.168.1.202
NA=rs2
If [ $1==’reset’];then
Killall mongo
Rm -rf /home/m*
fi
Mkdir -p /home/m17/home/m18 /home/m19 /home/mlog
/usr/local/mongodb/bin/mongod--dbpath /home/m17 --logpath /home/mlog/m17.log
--fork --port 27017--smallfiles --replSet ${NA}
/usr/local/mongodb/bin/mongod--dbpath /home/m18 --logpath /home/mlog/m18.log
--fork --port 27018--smallfiles --replSet ${NA}
/usr/local/mongodb/bin/mongod--dbpath /home/m19 --logpath /home/mlog/m19.log
--fork --port 27019 --smallfiles --replSet ${NA}
/usr/local/mongodb/bin/mongo<<EOF
Use admin
varrsconf={
_id:'rs2',
members:[
{
_id:0,
host:'${IP}:27017'
},
{
_id:1,
host:'${IP}:27018'
}
]
}
Rs.initiate(rsconf);
EOF
Exit;
Fi
Mongodb 分片
分片要如下要素:
1.要有n(n>2)个mongod服务器节点
2.要有configsvr维护meta信息
3.要设定好数据的分片规则(configsvr才能维护)
4.要启动mongos做路由。
./bin/mongod--dbpath /home/m17 --logpath /home/mlog/m17.log --fork --smallfiles
--port 27017
./bin/mongod--dbpath /home/m18 --logpath /home/mlog/m18.log --fork --smallfiles --port27018
./bin/mongod--dbpath /home/m20 --logpath /home/mlog/m20.log --fork --smallfiles --port27020 --configsvr
./bin/mongos--logpath /home/mlog/m30.log --port 30000 --configdb 10.18.93.120:27020 --fork
./bin/mongo --port 30000
sh.addShard('10.18.93.120:27017');
sh.addShard('10.18.93.120:27018');
Sh.status();
for(var i=0;i<40000;i++){db.goods.insert({goods_id:i,good_price:i*20});}
4.定一个规则怎么分。
sh.enableSharding('shop');
6.添加待分片的库
sh.enableSharding(‘shop’)
7.添加待分片的表
sh.shardCollection(‘dbname’,{field:1})
Field是collection的一个字段,系统会将利用filed的值,来计算
分到哪个片上。这个filed叫片健。
sh.shardCollection('shop.goods',{goods_id:1});
Mongodb不是从单篇文档的级别。绝对平均的散落在各个片shang,
而是N篇文档,形成一个块’chunk‘
优先放在摸个片上,当这片上的chunk比另一个片上的chunk,区别比较大时,以chunk为单位
维护片之间的数据均衡。
为什么插入了10万条数据才2个chunk
Chunk比较大(默认64m)
use config
show tables
db.settings.find();
db.settings.save({_id:'chunksize',value:1}});
在config数据库中,修改chunksize的值,
既然优先往摸个片上插入,当chunk失衡时,在移动chunk,
自然在数据增加时,shard的实例,有chunk来回移动的现象。
这将带来什么问题。
服务器之间的io的增加。
能否定义规则,莫n条数据形成1个块,预告分配M个chunk,M个chunk预告分配在
不同的片上,以后的数据插入各自分配好的chunk,不在来回移动。
手动预分片
Use teacher;
For(vari=0;i<40;i++){
sh.splitAt(‘teacher.user’,{user_id:i*100});
}
预先分40个chunk。
这些界限切好的chunk(虽然chunk是空
的)这些chunk将会均匀的移动到各片上
3.通过mongos添加user数据数据会添加到预先分配好的chunkshang,
Chunk就不会来回的移动。
可以有效的预防服务器之间的io.
Replcation 与shard综合使用
./bin/mongo 10.18.93.120:30000
Rs.addshard(‘rs3/10.18.93.120:27017’)//作为复制集老大出现。
Rs.addshard(‘rs4/10.18.93.121:27017’)
Php连接mongodb。
安装php的扩展。
date -s '2015-4-10 17:01'
clock -w
1 <?php
2 $m=newMongoClient('mongodb://localhost:27017');
3 $db=$m->test;
4 $ta=$db->selectCollection('news');
5 $array=array('title'=>'today issunday','sex'=>'very good');
6 $ta->insert($array)
8 var_dump($ta);
1.php-mongo客户端
2.短网址生成规则
3.
短网址生成规则:短,乱,容量大。
62的6次方
1 <?php
2$str='abcdefghijklmnopzrstuvwxyz1234567890';
3 $str=str_shuffle($str);
4 $array=str_split($str);
5 var_export($array);
<?php
2 class url{
3 protected $sheet=array(
4 0 => 'm', 1 => 'j', 2 => 'u', 3 => '2', 4 => 'f', 5 =>'z', 6 => 'd', 7 => '9', 8 => 'p', 9 => 't', 10 => 's', 11 =>'h', 12 => 'a', 13 => 'x',14 => 'r', 15 => 'v', 16 => '3', 17 => 'g', 18 => '6', 19 =>'n', 20 => '0', 21 => '1', 22 => '8', 23 => '4', 2 4 => 'y', 25 => 'e', 26 =>'i', 27 => 'z', 28 => 'o', 29 => '5', 30 => 'w', 31 => 'k', 32=> 'c', 33 => 'b', 34 => 'l', 35 => '7 '
5
6 );
7
8
9 public function trans($num)
10 {
11 $res='';
12 while($num > 62)
13 {
14 $res=$this->sheet[($num % 62)].$res;
15 $num=floor($num/62);
16
17 }
18
18 if($num>0)
19 {
20 $res=$this->sheet[$num].$res;
21
22 }
23 return $res;
24
25 }
db.cnt.insert({_id:1,sn:0});
db.cnt.findAndModify({query:{_id:1},update:{$inc:{sn:1}}});
<?php
2 $m=newMongoClient('mongodb://localhost:27017');
3 $db=$m->test;
4 $ta=$db->selectCollection('cnt');
5 $tmp=$ta->findAndModify(array('_id'=>1),array('$inc'=>array('sn'=>1)));
6 var_dump($tmp);
7 /*
8 $array=array('title'=>'today issunday','sex'=>'very good');
9 $ta->insert($array);
10
11 var_dump($ta);
12 */
//引进ajax库
http://libs.baidu.com/jquery/1.8.2/jquery.min.js
Mongodb group用法
Db.goods.group
{
key:{cat_id:1},
cond:{},
reduce:function(curr,result){
result.cnt+=1;
},
initial:{cnt:0}
}
{
key:{cat_id:1},
cond:{shop_price:{$gt:20}},
reduce:function(curr,result){
result.cnt+=1;
},
initial:{cnt:0}
}
{
key:{cat_id:1},
cond:{},
reduce:function(curr,result)
{
result.num+=curr.goods_number;
},
initial:{num:0}
}
{
key:{cat_id:1},
cond:{},
reduce:function(curr,result){
if(curr.shop_price>result.max)
{
result.max=curr.shop_price;
}
},
initial:{max:0}
}
查询每个栏目下商品的平均价格:
{
key:{cat_id:1},
cond:{},
reduce:function(curr,result){
result.cnt+1;
Result.sum+=curr.shop_price;
},
initial:{sum:0,cnt:0},
finalize:function(result){
result.avg=result.sum/result.cnt;
}
}
Group不支持分片和集群。
不支持shard cluster
分布式 aggregate() mapReduce()
Aggregate:
Db.collection.aggregate();
[
{$group:{_id:”$cat_id”,total:{$sum:1}}}
]
db.collection.aggregate();
[
{$group:{_id:"$cat_id",total:{$sum:1}}}
]
# 查询有多少条商品
db.goods.aggregate([{$group:{_id:"null",to:{$sum:1}}}]);
{$match:{shop_price:{$gt:10}}},
db.goods.aggregate([{$group:{_id:”$cat_id",total:{$sum:1}}}
]);
#筛选出满足条件的商品个数,大于等于3的栏目
mapreduce:
随着‘大数据’概念而行,
从功能上说,想当于Rdms的group操作。
答:在于分布式,当数据非常大时,数据都不在地球的一端,用group力所不及。
Group既然不支持分布式,单台服务器的运算能力必然是有限的。
Mapreduce支持分布式,支持大量的服务器同时工作了。
如何理解mapreduce的工作过程,
1.map--》映射 把属于同一个组的数据,映射到一个数组上。Cat_id-3[23,2,6,7]
2.Reduce : 把数组数据进行运算。
用 mapreduce 计算每个栏目的库存总量。
Map:函数
用mapreduce 计算每个栏目下的库存总量
Var map=function(){
emit(this.cat_id,this.shop_price);
}
有多少个栏目,就有多少个数组。
varreduce=function(cat_id,all_price){
}
{
Query:{},
Out:’res’
}
var map=function(){
...emit(this.cat_id,this.shop_price);
... }
> varreduce=function(cat_id,all_price){
return Array.sum(all_price);
... }
>db.goods.mapReduce(map,reduce,{query:{},out:'res'})
{
"result" : "res",
"timeMillis" : 38,
"counts" : {
"input" : 11,
"emit" : 11,
"reduce" : 3,
"output" : 4
},
"ok" : 1
}
用mapReduce计算每个栏目的商品的平均价格
Var map=function(){}{
emit(this.cat_id,this.shop_price);
}
Varreduce=function(cat_id,values){
ReturnArray.avg(values);
}
db.goods.mapReduce(map,reduce,{out:’res’});