mongodb是一个nosql数据库,它有高性能、无模式、文档型的特点。他是nosql数据库中功能最丰富,最像关系数据库的。
这里的安装是以linux为例的,因为实际生产场景大部分都是在linux进行部署。接下来我看介绍一下mongdb的安装步骤;
先进入mongodb的官网MongoDB下载,选择要下载的版本以及系统,需要注意的是,在安装mongodb之前需要先准备好jdk的环境变量,选择好要下载的版本以后,点击copy link,在linux系统中执行以下命令;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZdEJeJ3B-1688777273214)(C:\Users\quyanliang\AppData\Roaming\Typora\typora-user-images\1688776421675.png)]
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-5.0.11.tgz
下载完毕以后,解压该压缩包,接下来需要配置mongodb的环境变量
# 通过vi命令编辑以下文件
vi /etc/profile
# 在该文件中追加以下命令
# 解压完以后mongo的文件夹地址
export MONGODB_HOME=/opt/self/mongodb
export PATH=$PATH:$MONGODB_HOME/bin
# 刷新环境变量
source /etc/profile
配置完环境变量以下,在创建对应的mongodb的数据以及日志存储位置,然后启动即可
# 创建mongodb存储位置
mkdir mongodb-data
# 进入到创建的文件家中,创建数据和日志存储文件夹
mkdir data
mkdir logs
# 启动mongodb,指定数据以及日志存储位置,在解压后的mongodb的bin目录下执行
mongod --dbpath /opt/self/mongodb-data/data --logpath /opt/self/mongodb-home/ogs/mongodb.log --fork
启动完mongodb以后,我们可以通过一下命令,连接mongo
# 如果是在本机测试,可以简化为mongo
mongo --host 127.0.0.1 --port 27017
# 通过以下命令查看当前存在的数据库
show dbs
show databases
mongo在创建完成以后会默认存在admin,config,local这三个数据库,其中admin中存储的是mongodb的用户、角色等信息;config中存储的是分片集群基础信息;local中主要存储的是副本集的元数据。
切换或者创建库
-- 切换指定库,如果该库不存在,则创建该库,在切换到该库
use database;
创建集合
-- 直接创建
db.createCollection(name)
-- 如果collection不存在,则会先创建collection,这是一种隐式创建方式
db.collection.insert({id:1})
-- 查询当前库中存在的集合
show tables
文档的增删改查
-- 查询集合所有
db.collection.find()
-- 查询集合的第一个
db.collection.findOne()
-- 根据条件进行查询,支持传入两个参数
-- 第一个参数是过滤条件
-- 第二个是控制显示的数据列,1代表显示,0-代表不显示,如果不配置,默认显示所有
db.collection.find({id:1},{id:1,name:0})
-- 使用正则表达式,匹配姓名以a开头的数据
db.collection.find({name:/^a/})
-- 插入数据
db.collection.insert({id:1,name:"a"})
-- 批量插入数据
db.collection.insertMany([{id:1,name:"a"},{id:2,name:"b"}])
-- 覆盖更新,将id为一的这条数据更新为只有name=2,即使原数据存在sex=0,执行完以下语句以后,
-- 这条语句也会变成只有name=2
db.correction.update({id:1},{name:2})
-- 选择更新,只更新对应的字段,下面的语句只会吧id=1的数据中,name修改为2,其它不变
-- 但是它只会更新满足条件的第一条数据
db.correction.update({id:1},{$set:{name:2}})
-- 选择更新,将所有id=1的数据中的name变更为2,其它数据不变
db.correction.update({id:1},{$set:{name:2}},{multi:true})
-- 删除数据,删除id为1的数据
db.collection.remove({id:1})
-- 删除全部数据
db.collection.remove({})
统计操作
-- 统计所有数据
db.collection.count()
-- 统计id为1的数据
db.collection.count({id:1})
分页与排序
-- 查询前5条数据
db.collection.find().limit(5)
-- 查询跳过前5条的其它数据
db.collection.find().skip(5)
-- 分页查询,每页显示5条,查询第一页和第二页的数据
db.collection.find().skip(0).limit(5)
db.collection.find().skip(5).limit(5)
-- 排序,1-升序,-1-降序
db.collection.sort({id:1, age:-1})
范围查找
-- $gt-大于,$lt-小于,$gte-大于等于,$lte-小于等于,$ne-不等于
-- 查询id大于10的数据
db.collection.find({id:{$gt:10}})
-- 查询id大于10小于30的数据,需要注意这里必须使用$and
db.collection.find({$and:[{id:{$gt:10}},{id:{$lt:30}}]})
-- 查询id是1和3的数据
db.collection.find({id:{$in:[1,3]}})
索引操作
-- 创建索引,1-升序索引,-1降序索引
db.collection.createIndex({id:1})
-- 查看集合已经创建的索引
db.collection.getIndexs()
-- 移除指定索引
db.collection.dropIndex({id:1})
-- 移除集合的全部索引
db.collection.dropIndexes()
加锁操作
# 加锁
db.collection.fsyncLock()
# 解锁
db.collection.fsyncUnlock()
事务操作(mongodb不推荐使用事务)
# 插入多条数据
db.test_1.insertMany([{ id: 1 }, { id: 2 }]);
# 开启事务
var session = db.getMongo().startSession();
session.startTransaction();
# 获取需要修改的集合
var coll = session.getDatabase('test').getCollection("test_1");
# 进行数据更新
coll.updateOne({id: 1}, {$set: {name: "a"}});
# 获取的文档为id:1,name:a
coll.findOne({id: 1});
# 获取的文档为id:1
db.test_1.findOne({id: 1});
# 事务回滚或者提交
session.abortTransaction();
针对事务有以下几个注意点:
在这里简单介绍一下复制集的搭建,本文是在一台linux搭建三个mongodb节点来进行模拟,真实生产环境一个复制集的不同节点肯定是分布在不同的服务器上的。创建三个目录,分别为db1,db2,db3,在三个目录下添加配置文件:
# 日志文件
systemLog:
# 日志为文件格式
destination: file
# 日志文件的存储路径,不同的节点需要配置不同的路径
path: /data/db1/mongod.log
logAppend: true
storage:
# mongodb数据存储位置,不同的节点需要配置不同的路径
dbPath: /data/db1
net:
bindIp: 0.0.0.0
# 当前节点的端口
port: 28017
replication:
# 复制集名称
replSetName: rs0
processManagement:
fork: true
在启动时需要指定配置文件
mongod -f /data/db1/mongod.conf
连接入作为主节点的mongodb,然后执行以下命令:
# 复制集的初始化
rs.initiate()
# 添加从节点
rs.add("centosvm:28018")
rs.add("centosvm:28019")
#查看当前复制集的状态
rs.status()
通过一下命令进行验证
# 在主节点插入一条数据
db.test.insert({ id:1 })
#在从节点执行一下命令
rs.slaveOk()
db.test.find()
接下来用一个最简单的主从结构介绍复制集,一主三从。如小图所示:
在这个复制集中,数据的同步流程是:当主节点发生写操作时,它的写操作会被记录到oplog中,然后主节点将这个oplog推送给从节点,从节点通过在主节点上打开一个taiable游标读取oplog中的新数据,让后将数据同步到自己节点上,通过这种方式与主节点保持数据一致。
mongdb的复制集两两节点之间每2s发送一次心跳,如果5次心跳未收到,则判断为节点失联。如果失联的节点为主节点,此时就会发起选举,选出新的节点,选举是基于raft一致性算法实现,选举成功的必要条件是超过一半具有投票功能的节点存活。对于上面那个集群,当主节点失联,从节点必须有两个存活才能进行选举(注,复制集最多有50个节点,具有投票权额节点最多有7个)。
在进行选举的时候,能够选取为主节点的从节点必须具有以下的特点:
能够与更多的节点建立链接
oplog读取的位置最新
如果从节点设置了优先级,满足上述要求的优先级最高的节点
针对从节点,还有一些常见选项:
投票权:从节点默认具有投票权,可以关闭,单纯作为复制节点,参数为v;
优先级:优先级越高的节点在选举时同等条件下优先成为主节点,优先级为0的节点无法成为主节点,参数为priority;
隐藏:单纯作为数据备份,无法被应用访问,隐藏节点的优先级必须为0,参数为hidden;
延迟:在复制数据时,复制指定延迟时间之前的数据,与主节点保持时间差,常用来作为数据备份与恢复,参数为slaveDelay,单位为秒。
# 动态修改从节点的信息
conf=rs.conf()
conf.members[2].secondaryDelaySecs = 5
conf.members[2].priority = 0
rs.reconfig(conf)
在复制集中只要节点不是隐藏节点或者仅投票节点,这些节点是都可以向外部提供读取权限的,那么我们在读取的时候,应该从哪个节点进行读取呢?这需要我们根据具体的业务情况进行策略配置,配置参数为readPreference。它有一下几种值可供选择:
primary:只从主节点进行读取,这是mongodb的默认值;
primaryPreferred:优先从主节点读取,如果当前主节点不可用,则选择从节点读取;
secondary:只选择从节点进行读取;
secondaryPreferred:优先选择从节点读取,如果从节点不可用,则从主节点读取;
nearest:选择最近的节点,这里的最近指的是地理位置。
这些策略分别适用的场景为:primary/primaryPreferred:对数据实时性要求较高的业务场景,因为从节点有一个数据复制的过程,会造成数据延迟;secondary/secondartPreferred:适用查询历史数据的业务场景,其中secondary也适用于报表生成这类需要耗费大量资源的业务场景;nearest:适用于地域性较广的应用,进行就近读取,减少传输时间。
readPreferred的配置方式有三种,如下:
# 在配置文件中的mongdb连接串后添加对应参数
mongodb://host1:27107,host2:27107/?replicaSet=rs&readPreference=secondary
# 在java代码中
MongoCollection.withReadPreference(ReadPreference readPref)
#在xshell控制台中
db.collection.find().readPref( "secondary")
上面的这种方式,只能指定一类节点,主从节点。但是在实际生产中可能需要根据硬件配置,业务场景来区分要读取哪些节点。这是我们可以通过给不同的mongodb节点打上不同的tag来区分,方式如下:
# 给mongodb打tag
conf = rs.conf()
conf.members[1].tags = {"type" : "a" }
rs.reconfig(conf)
#应用程序的mongodb配置文件
mongodb://host1:27107,host2:27107/?replicaSet=rs&type=a
上面介绍了我们要从哪个节点读取数据的方式。但是节点上的哪些数据是可以读取的呢?这就需要用到另一个参数readConcern。这个参数的配置值类似于数据库中的隔离级别。它有一下的策略配置,他们的隔离级别由上到下越来越强:
available:读取所有可用的数据,可以直接查询到写入复制集的数据;
local:读取所有可用且属于当前分片的数据,可以直接查询到该分片的数据;
majority:读取在大多数节点上提交的数据,使用该策略可以有效的避免脏读的问题。majority采用mvcc机制来保证数据的读取。通过维护多个快照来链接不同的版本,每个被大多数节点确认过的版本都将是一个快照,快照持续到不再使用的时候才会进行删除。因为该策略是需要写入操作需要大多数节点完成确认,才能够被读取,当本次写入没有被大多数节点确认,主节点就发生回滚,那么本次写入对其它应用是不可见的;
linearizable:可线性化读取文档;
snapshot:读取最近快照中的数据,即每次读取都是从同一个快照中进行读取,那么也就避不会出现脏读,不可重复读以及幻读。下面看一个可重复的例子:
# 开启事务,并且将readConcern设置为快照
var session = db.getMongo().startSession();
session.startTransaction({readConcern: {level: "snapshot"},writeConcern: {w: "majority"}});
var coll = session.getDatabase('test').getCollection("test_1");、
# 读取id为1的数据,获得的文档为id:1
coll.findOne({id: 1});
# 不通过事务对id为1的数据进行更新
db.tx.updateOne({id: 1}, {$set: {name: "b"}});
# 事务外读取id为1的数据,获得的文档为id:1,name:b
db.tx.findOne({id: 1});
# 事务内再次读取id为1的数据,获取的文档为id:1,它与第一次读时用的是同一个快照,所以两次读取是一样的
coll.findOne({id: 1});
session.abortTransaction();