MongoDB篇
--------------------------------------------------------------------------
端口27017,版本3.6.4
mongodb.conf主要配置内容:
dbpath指定数据文件存放路径;logpath指定日志文件存放路径;smallfiles=true指定最小启动;fork=true指定在后台运行;bind_ip设置为了0.0.0.0;port=27017默认端口
--------------------------------------------------------------------------
启动:进入到mongodb跟目录下,执行./bin/mongod -f mongodb.conf
连接:进入到mongodb跟目录下,执行./bin/mongo,连接成功后显示>
退出:退出客户端执行exit命令
关闭:关闭mongodb服务,杀进程ps -ef | grep mongo;kill -9 进程号
--------------------------------------------------------------------------
数据库操作:
查询数据库;show dbs;或者show databases默认有admin、config、local三个数据库
创建数据库:use shop;db.createCollection('goods')必须创建collection,只有use shop不会创建数据库
删除数据库:use shop;db.dropDatabase();
--------------------------------------------------------------------------
集合操作
查询集合: use shop;show collections;先use数据库
创建集合: use shop;db.createCollection('goods');显示创建collection
use shop;db.goods.insert({name:'张三',price:23,sex:'n',address:'北京朝阳'})隐式创建collection
删除集合: use shop;db.goods.drop();
--------------------------------------------------------------------------
数据操作
数据添加:insert
db.goods.insert({name:'电脑',price:5600,goods_num:'00001',click_num:1});------添加单条
db.goods.insert({_id:1,name:'苹果',color:'red',price:12});--------------------添加单条
db.goods.insert([{type:'锅',name:'平底锅',price:300},{_id:2,name:'鱼',num:304}]);---添加多条
注意,如果json中没有_id列,会默认生成一个_id,指定_id的时候不能重复,collection中_id唯一。自动生成的id如:"_id" : ObjectId("5d3efadd7dc39a8207a088c4")
修改数据:update、$set
db.goods.update({_id:1},{name:'苹果2'});执行结果,只有_id、name两列,其余的列都没了。
db.goods.update({_id:1},{$set:{name:'苹果2'}});执行结果,只是修改了name列,其余的不变。正确的
假如有多条数据符合修改条件,只会修改一条。如果要全部修改需要使用 multi:true
db.person.update({name:'张三'},{$set:{address:'北京朝阳2'}},{multi:true});使用multi,必须使用$set
数据删除:remove
db.person.remove({name:'李四'});删除列名为李四的行
db.person.remove({});删除全部数据
查询数据:db.persion.find({"tel":"12342","name":"123"});
--------------------------------------------------------------------------
列的操作
修改列名:update、rename
db.person.update({age:31},{$rename:{address:'address2'}});age为31的列,列名address修改为address2,只会修改一条记录。
db.person.update({name:'张三'},{$rename:{address:'address2'}},{multi:true});name为张三的列,列名address修改为address2,会修改所有满足条件的记录。
删除某列:update、$unset
db.person.update({name:'张三'},{$unset:{age:''}});删除列名为name的值是张三的列,列名和列值都会删除,只会删除一条。
db.person.update({name:'张三'},{$unset:{age:''}},{multi:true});删除列名为name的值是张三的列,列名和列值都会删除,满足条件的都会删除。
列的自增长:
db.person.update({name:'张三'},{$inc:{age:10}});改变1条记录
db.person.update({name:'张三'},{$inc:{age:10}},{multi:ture});改变所有满足条件的记录
没有查询到不更新,直接插入:
db.person.update({_id:4},{name:'李四',class:'三班',score:90},{upsert:true})
显示固定的列:
db.person.find({name:'李四'},{_id:1,name:1});查询结果只显示id和name,1显示,0不显示
--------------------------------------------------------------------------
查询表达式:
db.person.find({_id:{$ne:3}});查询_id不等于3的记录
db.person.find({_id:{$nin:[1,2]}});查询_id不是1、2的记录
db.person.find({like:{$exists:1}});查询记录中包含like列名的记录,1包含,0不包含
db.person.find({name:/李.*/});查询name列以“李“开头的记录
--------------------------------------------------------------------------
分页:db.person.find().skip((page-1)*size).limit(size);查询第page页,每一页size条
--------------------------------------------------------------------------
用户管理:
先以免认证方式启动,为admin库创建用户,再以认证方式启动,以库划分粗粒度权限,不做细的划分
mongodb的用户以数据库为单位,在admin库上建的用户是超级管理员,可管理服务器配置层面的操作,通过超级管理员给其他库设置管理员
创建用户:以下是为admin库创建管理员
use admin;
db.addUser('sa','123456',false);用户名sa ,密码123456,false代表不是只读的。默认是true,默认是只读的。
认证方式启动服务:./bin/mongod -f mongodb.conf --auth
启动客户端连接:./bin/mongo,只是连接上服务器了无法操作,需执行下面的认证
use admin;
db.auth('sa','123456');认证后可以操作所有的库。以超级管理员登录后,为shop库创建用户示例如下:
use shop;
db.addUser('ushop','ushop123',false);用ushop用户登录后只能操作shop库,如下:
./bin/mongo
use shop;
db.auth('ushop','ushop123');
--------------------------------------------------------------------------
修改密码:自己登录后可以修改密码
use shop;
db.auth('ushop','ushop123');
db.changeUserPasswd('ushop','123456');ushop用户的新密码改为了123456。
--------------------------------------------------------------------------
删除用户:admin可删其他库的用户,其他库的用户可删自己
use shop;
db.auth('ushop','ushop123');
db.removeUser('ushop');ushop用户被删除,将不能再登录。
--------------------------------------------------------------------------
导入导出:
bin下的mongoexport、mongoimport(一对针对json或csv);mongodump;mongorestore(一对针对二进制)
./bin/mongoexport -h 192.168.31.151 --port 27017 -d shop -c goods -f type,code,name,onclick -q '{code:/ddys00.*/}' -o goods-shop.json
./bin/mongoimport -d shop2 -c goods2 --type json --file ./goods-shop.json(shop2、goods2如不存在自动创建,不会删除原有数据)
./bin/mongodump -d shop -c goods
./bin/mongorestore -d shop666 --directoryperdb dump/shop2
--------------------------------------------------------------------------
复制集replication:一主一从或一主多从,复制集中所有节点数据一样
mongodb默认选择一个主节点,无需人为指定。复制集中肯定有主节点,假如主节点宕机了,会在从节点中选出一个主节点。主节点rsa:PRIMARY>,从节点rsa:SECONDARY>。主节点能读写,从节点只读。
搭建复制集(主从)简单步骤:
在每一个节点的配置文件mongodb.conf中都指定replSet=rsa,其余配置项一样。依次启动所有节点。登录任一节点,use admin,定义配置变量_id相当于集群id,members,里面是节点id,ip、端口,然后rs.初始化这个变量,集群配置完毕,客户端连接集群:在任一节点执行老命令/usr/local/mongodb/bin/mongo即可
use admin;
var rsconf={
_id:'${rsa}',
members:[
{_id:0,host:'${192.168.7.151}:27017'},
{_id:1,host:'${192.168.7.152}:27018'},
{_id:2,host:'${192.168.7.153}:27019'}
]
};
rs.initiate(rsconf);
虽然搭建完成,但是从机依旧不能读,需要执行:rs.slaveOk();
--------------------------------------------------------------------------
添加节点:主节点下执行语句,rs.add('192.168.31.151:27019');rs.slaveOk();
删除节点:主节点下执行语句,rs.remove('192.168.31.151:27019');主节点不能删除自己
其它命令:
db.isMaster();查看谁是主节点,查看有哪些节点
rs.status();查看谁是主节点,哪些节点不是健康的("stateStr" : "(not reachable/healthy)")
use admin;db.shutdownServer();宕掉某节点
db.help();rs.help();查看有哪些方法可用
--------------------------------------------------------------------------
share分片:
不同数据放在不同节点。除了数据节点还有路由节点mongos(不存数据,路由),中心配置节点configsvr(不存数据,存储哪条数据在哪个片上)。查询数据时先通过mongos,再通过configsvr。
分片的简单步骤:
数据节点跟往常配置一样,configsvr节点设置配置文件的configsvr=true,mongos路由节点设置配置文件的configdb=configsvr节点IP:端口;一次启动数据节点、configsvr节点、mongos节点;登录路由节点客户端/usr/local/mongodb245/bin/mongo --port 30000,增加数据节点sh.addShard('192.168.31.151:27017');sh.addShard('192.168.31.151:27018')。分片搭建完成。
设置分片规则:执行完上面的可以写入数据,但是不会分片,都写入到一个节点中,因为没有设置分片规则。登录路由客户端,执行下面的语句分片:
sh.enableSharding('shop');指定分片的库是shop
sh.shardCollection('shop.goods',{goods_id:1});指定给shop库的goods表分片,根据字段goods_id分片
好,真正的分片完成了。
--------------------------------------------------------------------------
分片的问题:
系统根据片键(上面是goods_id)计算数据放到哪个片上。不是一条记录一条记录的直接写到某个片上,而是多个记录形成一个chunk,优先放到某个片上,当这个片上的chunk比另一个片上的chunk大,该片上的chunk会移动到另一个片上,维持数据平衡。问题:chunk来回移动,增加了服务器的IO,解决方法,手动分片。
相关命令:登录路由客户端,执行sh.status();查看某节点(ip:端口)对应到哪个shard片,shard0000,shard0001,shard0002,shard0003.。。。每个shard上有几个chunk。goods_id多少到多少分布到哪个片上。chunk的默认大小是64M
--------------------------------------------------------------------------
手动分片:
提前分片,在mongodb分片应用搭建好的时候就提前规定好每个片有多少chunk,什么数据分配到哪个chunk里面。一定要提前规划好,后期将不再变动。
简单操作:
mongos> sh.shardCollection('shop.user',{userid:1});与上面一样,知道分片的库、表、字段。
user表中的userid每遇到1k、2k、3k、... ... 40k就写入到所属的块中,如下:
for(var i=1;i<=40;i++){sh.splitAt('shop.user',{userid:i*1000})}分块语句
--------------------------------------------------------------------------
分片与复制集结合使用:将复制集添加到分片中,先分片,每个分片是一个集群,里面数据一致
步骤:先启动几个复制集,Configsvr.conf的配置不变,mongs路由节点配置也不变;登录路由节点,把复制集添加到分片上,如下:
sh.addShard('rs152/192.168.31.152:27017');rs152是复制集的id,192.168.31.152:27017是任意一节点
sh.addShard('rs153/192.168.31.153:27017');
指定分片规则一样,手动分片也一样,如userid达到1000、2000、3000时生成一个chunk
sh.splitAt('shop.user',{userid:1000});
sh.splitAt('shop.user',{userid:2000});
sh.splitAt('shop.user',{userid:3000});
附一个添加数据的例子:
mongos> use shop;
mongos> for(var i=1;i<=4000;i++){db.user.insert({userid:i,otherStr:'anjagslkdadkjfag akjglakjgaj kajgag'})}
注意用复制集的从库登陆客户端查询时也要先执行rs.slaveOk();
--------------------------------------------------------------------------
聚合运算:group、aggregate、mapReduce
group,自己实现逻辑、灵活,不支持分片,举例:根据cat_id分组求分组个数,返回json
> db.goods.group({
key:{cat_id:1},
cond:{},
reduce:function(curr,result){
result.cnt+=1;
},
initial:{cnt:0}
})
key:分组字段,相当于group by;
cond:查询条件,相当于where;
reduce:聚合函数,自己实现逻辑,在分组中做什么
initial:初始化,理解为进入分组先做什么
finalize:统计一组后的回调函数,理解为跳出分组后做什么,在reduce后执行。
aggregate,很多内置方法,如下:
$match(where),$group(group by),$project(select),$sort(order by),$limit(limit),$sum(sum),$count(count)
举例:查询每个栏目下价格大于50元的商品个数、并筛选出“满足条件的商品个数”大于等于3的栏目
[
{$match:{shop_price:{$gt:50}}},
{$group:{_id:'$cat_id',total:{$sum:1}}},
{$match:{total:{$gte:3}}}
]
输出值如:{"result" : [期待的json],"ok" : 1}
mapReduce,支持分布式,工作分两步,如下:
一是映射(即map,将数据按照某一规则映射到一个数组里,比如按type或name映射)
一是规约(即reduce,它接收映射规则和数组,然后计算)
var map=function(){将相同的cat_id和goods_number映射到各自数组
emit(this.cat_id,this.goods_number);
}
var reduce=function(cat_id,numbers){执行求和,生成res表,结果存在res表中
return Array.sum(numbers);//有sum,avg,等
}
db.goods.mapReduce(map,reduce,{out:'res'});//可以写查询条件,如db.goods.mapReduce(map,reduce,{query:{shop_price:{$gt:100}},{out:'res'});
--------------------------------------------------------------------------
java的操作:
引入starter-data-mongodb
application中配置spring.data.mongodb.uri=mongodb://localhost:27017/test(指定库)
可视化工具robo 3t
springboot中使用mongoTemplate,直接注入使用即可
mongoTemplate.findAll(User.class)返回一个list(先db.user.insert{json属性与user类一一对应})
mongoTemplate.insert(user,"user")
mongoTemplate.updateFirst(query,update,"user")
mongoTemplate.remove(query,User.class,"user")
--------------------------------------------------------------------------