业务背景
消息系统需求实现:
- 需要记录消息本身,以及消息发送对象
- 消息发送后,需要在客户端显示消息的已读、未读的提示。
思路:
- 假如只是单纯消息发送,不管有无看到(类似 UDP连接),则直接创建消息表存消息即可。
- 但需要记录用过户已读、未读消息则需要多建用户消息记录表,同时向n个用户发送m条消息则需要在数据表中记录m*n条数据,发送消息的瞬间将有大量的性能损耗在数据库读写上,此时传统的关系型数据库则会严重影响整个系统的运行。
- 因为消息模块的要存储的数据量太大,普通MySQL读写消耗开销过大映像性能,所以要换能存储海量数据的数据库产品,MongoDB适合存储海量低价值的数据,正好符合消息模块的存储要求。
- 俗话说水滴石穿,考虑到系统的健壮性,用MongoDB存储消息数据,后期用户体量上来了,发送一条公告消息要往数据库里面写入大量的数据,即便MongoDB也支撑不下来瞬时写入百万,此时可以引入消息队列MQ,然后在Java后端系统上面,用异步多线程的方法,向消息队列MQ中发送消息,这样Web系统发布公告消息的时候就不占用数据库正常的CRUD操作。
- 系统消息保存在消息队列中,用它来做削峰填谷,系统消息最终还是要存储在数据库上面。于是我们可以这样设计,在用户登陆系统的时候,用异步线程从消息队列MQ中,接收该用户的系统消息,然后把系统消息存储在数据库中,最后消息队列MQ中的该条消息自动删除。
总结:
使用MongoDB + RabbitMQ 构建消息推送功能
环境搭建
-
使用Docker镜像搭建提高效率
docker入门:【狂神说Java】Docker最新超详细版教程通俗易懂
-
Docker简介:
Docker是一个开放源代码软件项目,让应用程序布署在软件容器下的工作可以自动化进行,借此在 Linux操作系统上,提供一个额外的软件抽象层,以及操作系统层虚拟化的自动管理机制。
-
Docker特性:
传统的软件开发与发布环境复杂,配置繁琐,经常有读者在微信上问:我的代码开发环境可以运行,一 旦部署到服务器上就运行不了了。这个问题很常见,也确实很烦人,但是问题总要解决,开发环境、 测试环境、生产环境,每个环节都有可能出现这样那样的问题,如果能够在各个环境中实现一键部署, 就会方便很多,例如一键安装linux、一键安装mysql、一键安装nginx等,docker彻底解决了这个问题。
-
现场搭建Docker环境:
-
安装
#安装Docker yum install docker -y #启动Docker服务 service docker start
-
因为Docker在线安装镜像是从国外的DockerHub网站下载文件,速度有限,所以得设置加速器,这里我们使用腾讯云加速器:
打开
/etc/docker/daemon.json
文件,然后设置成如下内容:{ "registry-mirrors": ["https://mirror.ccs.tencentyun.com"] }
-
重新启动Docker服务,加速器才能生效
service docker restart
-
-
-
使用Docker搭建MongoDB
-
执行命令,下载最新版本的MongoDB镜像
docker pull mongo
-
创建
/root/mongo/mongod.conf
文件,然后在文件中添加如下内容:mkdir -p /root/mongo vim /root/mongo/mongod.conf
net: port: 27017 bindIp: "0.0.0.0" storage: dbPath: "/data/db" security: authorization: enabled
-
创建容器,为MongoDB分配500M内存
docker run -it -d --name mongo --net=host \ -v /root/mongo:/etc/mongo -m 500m \ -e MONGO_INITDB_ROOT_USERNAME={用户名} \ -e MONGO_INITDB_ROOT_PASSWORD={密码} \ mongo --config /etc/mongo/mongod.conf
-
用Navicat连接MongoDB,检测是否可用。
-
-
使用Docker搭建RabbitMQ
MongoDB 入门:
教程:MongoDB 教程
-
MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
-
概念解析
不管我们学习什么数据库都应该学习其中的基础概念,在mongodb中基本的概念是文档、集合、数据库,下表将帮助您更容易理解Mongo中的一些概念:
SQL术语/概念 MongoDB术语/概念 解释/说明 database database 数据库 table collection 数据库表/集合 row document 数据记录行/文档 column field 数据字段/域 index index 索引 table joins aggregate 表连接 primary key primary key 主键,MongoDB自动将_id字段设置为主键 -
MongoDB的增删改查:
-
增(插入文档):MongoDB 插入文档 | 菜鸟教程 (runoob.com)
以下文档可以存储在 MongoDB 的 runoob 数据库 的 col 集合中:
>db.col.insert({title: 'MongoDB 教程', description: 'MongoDB 是一个 Nosql 数据库', by: '菜鸟教程', url: 'http://www.runoob.com', tags: ['mongodb', 'database', 'NoSQL'], likes: 100 })
以上实例中 col 是我们的集合名,如果该集合不在该数据库中, MongoDB 会自动创建该集合并插入文档。
查看已插入文档:
> db.col.find() { "_id" : ObjectId("56064886ade2f21f36b03134"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 } >
-
删(删除文档)MongoDB 删除文档 | 菜鸟教程 (runoob.com)
db.collection.remove( <query>, { justOne: <boolean>, writeConcern: <document> } )
参数说明:
- query :(可选)删除的文档的条件。
- justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
- writeConcern :(可选)抛出异常的级别
-
改(更新文档)MongoDB 更新文档 | 菜鸟教程 (runoob.com)
我们在集合 col 中插入如下数据:
>db.col.insert({ title: 'MongoDB 教程', description: 'MongoDB 是一个 Nosql 数据库', by: '菜鸟教程', url: 'http://www.runoob.com', tags: ['mongodb', 'database', 'NoSQL'], likes: 100 })
接着我们通过 update() 方法来更新标题(title):
>db.col.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) # 输出信息 > db.col.find().pretty() { "_id" : ObjectId("56064f89ade2f21f36b03136"), "title" : "MongoDB", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 } >
可以看到标题(title)由原来的 “MongoDB 教程” 更新为了 “MongoDB”。
以上语句只会修改第一条发现的文档,如果你要修改多条相同的文档,则需要设置 multi 参数为 true。
>db.col.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}},{multi:true})
-
查(查询文档)MongoDB 查询文档 | 菜鸟教程 (runoob.com)
以下实例我们查询了集合 col 中的数据:
> db.col.find().pretty() { "_id" : ObjectId("56063f17ade2f21f36b03133"), "title" : "MongoDB 教程", "description" : "MongoDB 是一个 Nosql 数据库", "by" : "菜鸟教程", "url" : "http://www.runoob.com", "tags" : [ "mongodb", "database", "NoSQL" ], "likes" : 100 }
除了 find() 方法之外,还有一个 findOne() 方法,它只返回一个文档。
MongoDB 与 RDBMS Where 语句比较
如果你熟悉常规的 SQL 数据,通过下表可以更好的理解 MongoDB 的条件语句查询:
操作 格式 范例 RDBMS中的类似语句 等于 {<key>:<value>
}db.col.find({"by":"菜鸟教程"}).pretty()
where by = '菜鸟教程'
小于 {<key>:{$lt:<value>}}
db.col.find({"likes":{$lt:50}}).pretty()
where likes < 50
小于或等于 {<key>:{$lte:<value>}}
db.col.find({"likes":{$lte:50}}).pretty()
where likes <= 50
大于 {<key>:{$gt:<value>}}
db.col.find({"likes":{$gt:50}}).pretty()
where likes > 50
大于或等于 {<key>:{$gte:<value>}}
db.col.find({"likes":{$gte:50}}).pretty()
where likes >= 50
不等于 {<key>:{$ne:<value>}}
db.col.find({"likes":{$ne:50}}).pretty()
where likes != 50
MongoDB AND 条件
MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,即常规 SQL 的 AND 条件。
语法格式如下:
>db.col.find({key1:value1, key2:value2}).pretty()
-
假设有:商品表goods 和 订单表orders,并具有一对多的关系:
查询所有商品对应的所有订单:
//关联查询 db.goods.aggregate([ { $lookup: { from: "orders", // 要关联查询的集合 localField: "_id", // goods集合中的_id foreignField: "goodsId", // 要查询的集合的 关联id as: "child" } } ])
-