MongoDb基本使用

MongoDB基本使用

Nosql简介

在现代的计算系统上每天网络上都会产生庞大的数据量, 这些数据有很大一部分是由关系数据库管 理系统(RDBMS)来处理。 1970年 E.F.Codd’s提出的关系模型的论文 “A relational model of data for large shared data banks”,这使得数据建模和应用程序编程更加简单。  <br /> 	通过应用实践证明,关系模型是非常适合于客户服务器编程,远远超出预期的利益,今天它是结构 化数据存储在网络和商务应用的主导技术。<br /> NoSQL 是一项全新的数据库革命性运动,早期就有人提出,发展至2009年趋势越发高涨。NoSQL 的拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种 全新的思维的注入。  

什么是是NoSql

NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的 关系型数据库的数据库管理系统的统称。<br /> NoSQL用于超大规模数据的存储。(例如谷歌或Facebook每天为他们的用户收集万亿比特的数 据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。  

为什么使用NoSql

今天我们可以通过第三方平台(如:Google,Facebook等)可以很容易的访问和抓取数据。用户的 个人信息,社交网络,地理位置,用户生成的数据和用户操作日志已经成倍的增加。我们如果要对这些 用户数据进行挖掘,那SQL数据库已经不适合这些应用了, NoSQL 数据库的发展却能很好的处理这些大 的数据。

RDBMS vs NoSQL

RDBMS

  • 高度组织化结构化数据
  • 结构化查询语言(SQL)
  • 数据和关系都存储在单独的表中。
  • 数据操纵语言,数据定义语言
  • 严格的一致性
  • 基础事务

NOSql

  • 代表着不仅仅是SQL
  • 没有声明性查询语言
  • 没有预定义的模式 键 - 值对存储,列存储,文档存储,图形数据库
  • 最终一致性,而非ACID(原子性、一致性、隔离性、持久性)属性
  • 非结构化和不可预知的数据

NoSql优缺点

优点

  • 高可扩展性
  • 分布式计算
  • 低成本
  • 架构的灵活性,半结构化数据
  • 没有复杂的关系

缺点

  • 没有标准化
  • 有限的查询功能(到目前为止)
  • 最终一致是不直观的程序

分布式理论

CAP定理

C(一致性):所有节点上数据时刻保持同步
A(可用性):每个请求都能得到响应,无论成功或失败
P(分区容错):系统应该能持续提供服务,即使系统内部有消息丢失(分区)

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需 求,最多只能同时较好的满足两个。
因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大 类:

  • CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
  • CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。
  • AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

BASE理论

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需 求,最多只能同时较好的满足两个。

BASE是NoSQL数据库对可用性及一致性的弱要求原则:

  • Basically Availble –基本可用
  • Soft-state – 软状态/柔性事务。 Soft state” 可以理解为”无连接”的, 而 “Hard state” 是”面向连接” 的 软状态是指允许系统存在中间状态,并且该中间状态不会影响系统整体可用性。即允许系统在不同 节点间副本同步的时候存在延时。简单来说就是状态可以在一段时间内不同步。
  • Eventual Consistency – 最终一致性, 也是 ACID 的最终目的。

MongoDB 基础

什么是MongoDB

MongoDB 是由C++语言编写的,是一个基于分布式文挡存储的开源数据库系统。

  • 在高负载的情况下,添加更多的节点,可以保证服务器性能。
  • MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。

存储结构

MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。

主要特点

  • 非关系型数据库,基于 Document data model (文档数据模型)
  • MongoDB以 BSON (BinaryJSON) 格式存储数据,类似于 JSON 数据形式
  • 关系型数据库使用 table (tables of rows)形式存储数据,而MongoDB使用 collections (collections of documents)
  • 支持 临时查询( ad hoc queries ): 系统不用提前定义可以接收的查询类型
  • 索引通过 B-tree 数据结构, 3.2版本的WiredTiger 支持 log-structured merge-trees(LSM)
  • 支持索引和次级索引( secondary indexes ): 次级索引是指文档或row有一个 主键( primary key )作为索引,同时允许文档或row内部还拥有一个索引,提升查询的效率,这也是MongoDB比较大的一个特点

基本概念

和传统数据库相比

对比项mongo数据库
tablecollection数据库表/集合
rowdocument数据记录行/文档
columnfield数据字段/域
indexindex索引
table joins表连接,MongoDB不支持
primary keyprimary key主键,MongoDB自动将_id字段设置为主键

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oFGahbfA-1689774476280)(https://cdn.nlark.com/yuque/0/2023/png/26194198/1687786037504-c04ccddf-82dc-447f-ab33-fd80e1838a96.png#averageHue=%23d4d8c5&clientId=u5db58dc5-9299-4&from=paste&height=340&id=ua6d15fe2&originHeight=340&originWidth=1196&originalType=binary&ratio=1&rotation=0&showTitle=false&size=89504&status=done&style=none&taskId=u53132599-5840-4ae3-b927-551137dd160&title=&width=1196)]

数据逻辑层次关系:文档=>集合=>数据库\

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-02CKGvev-1689774476282)(https://cdn.nlark.com/yuque/0/2023/png/26194198/1687786078353-41daa3cb-3411-41d9-952e-ad63b1df3974.png#averageHue=%23eff3f3&clientId=u5db58dc5-9299-4&from=paste&height=382&id=ua8df788a&originHeight=382&originWidth=421&originalType=binary&ratio=1&rotation=0&showTitle=false&size=122140&status=done&style=none&taskId=u8fc361bd-e135-4890-903e-66aca86f2fa&title=&width=421)]

下面我们对里面的每一个概念进行详细解释

数据库

一个mongoDB的实例可以运行多个database,database之间是完全独立的,每个database有自己的权限,每个database存储于磁盘的不同文件。

命名规范

  • 空字符串””是非法的
  • 不允许出现’’,.,$,/,,\0字符
  • 建议名称都是小写
  • 不能超过64个字节

特殊数据库

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。

  • admin:它是root级别的数据库,如果一个用户创建了admin数据库,该用户将自动集成所有数据 库的权限,它可以执行一些服务器级别的命令,如列出所有数据库、关闭服务等。
  • local:该数据库将永远不能被复制,只能在单台服务器本地使用。
  • config:存储分布式部署时shard的配置信息

数据库操作

  • show dbs 查看数据库列表
  • db 显示当前数据库
  • 创建数据库
show dbs;
#创建tmpdb数据库
use tmpdb;
show dbs;

注意:在 MongoDB 中,只有在数据库中插入集合后才会创建! 就是说,创建数据库后要再插入一 个集合,数据库才会真正创建。

  • 删除数据库 可以使用 db.dropDatabase() 删除数据库
show dbs;
use tmpdb;
db;
#删除数据库
db.dropDatabase();
show dbs;

集合

相当于关系数据库的表,不过没有数据结构的定义。它由多个document组成。

命令规范

因为是无结构定义的,所以你可以把任何document存入一个collection里。每个collection用一个 名字标识,需要注意以下几点:

  • 名字不允许是空字符串""
  • 名字不能包含\0字符,因为它表示名字的结束
  • 不能创建以system.开头的

集合操作

创建集合

可以通过 db.createCollection(name,option) 创建集合
参数说明:
name: 要创建的集合名称
options: 可选参数, 指定有关内存大小及索引的选项

# 创建或选择tmpdb数据库
use tmpdb;
# 在db数据库创建一个blog的集合
db.createCollection("blog");

查看集合

show collections;
show tables;

删除集合

MongoDB 中使用 drop() 方法来删除集合 db.collection.drop()

文档

mongoDB的基本单位,相当于关系数据库中的行,它是一组有序的key/value键值对,使用json 格式, 如:{“foo” : 3, “greeting”: “Hello, world!”}。

key的命令规范

key是个UTF-8字符串,以下几点是需要注意的地方:

  • 不能包含\0字符(null字符),它用于标识key的结束 .
  • 字符在 m a n g o d b 中有特殊含义,如 字符在mangodb中有特殊含义,如 字符在mangodb中有特殊含义,如被用于修饰符($inc表示更新修饰符),应该考虑保留,以 免被驱动解析
  • 以_开始的key也应该保留,比如_id是mangodb中的关键字

注意事项

  • 在mangodb中key是不能重复的
  • value 是弱类型,甚至可以嵌入一个document
  • key/value键值对在mangodb中是有序的
  • mangodb是类型和大小写敏感的,如{“foo” : 3}和{“foo” : “3”}是两个不同的document,{“foo” : 3}和{“Foo” : 3}

文档基础使用

插入文档

insert(不推荐)

插入一条或多条数据需要带有允许插入多条的参数,这个方法目前官方已经不推荐了 注意:若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据。

db.blog.insert({
    "title": "MongoDB 教程",
    "description": "MongoDB 是一个 Nosql 数据库",
    "by": "我的博客",
    "url": "http://www.baiyp.ren",
    "tags": [
        "mongodb",
        "database",
        "NoSQL"
   ],
    "likes": 100
});

如果没有添加 _id 参数会自动生成 _id 值的,也可以自定义指定 _id

insertOne(推荐)

官方推荐的写法,向文档中写入一个文档

db.blog.insertOne({
    "title": "MySql 教程",
    "description": "Mysql是一个传统数据库",
    "by": "我的博客",
    "url": "http://www.baiyp.ren",
    "tags": [
        "Mysql",
        "database"
   ],
    "likes": 10000
});
insertMany(推荐)

该语句是进行批量插入的,可以直接进行批量插入

db.blog.insertMany([
{
"title": "MySql 教程1",
"description": "Mysql是一个传统数据库",
"by": "我的博客",
"url": "http://www.baiyp.ren",
"tags": [
"Mysql",
"database"
],
"likes": 10000
},
{
    "title": "MySql 教程2",
    "description": "Mysql是一个传统数据库",
    "by": "我的博客",
    "url": "http://www.baiyp.ren",
    "tags": [
    "Mysql",
    "database"
    ],
    "likes": 10000
}
]);

查询文档

查询所有文档

find 方法用于查询已存在的文档,MongoDB 查询数据的语法格式如下

db.blog.find();

格式化文档

db.blog.find().pretty();

等值查询

我们查询 blog 表中 title=‘MySql 教程2’ 的数据

db.blog.find({
    "title": "MySql 教程2"
}).pretty();

投影

projection 选择可以控制某一列是否显示,语法格式如下 find({},{“title”:1}) 其中如果 title 是 1 则该列显示,否则不显示

// 只显示title列的数据
db.blog.find({"title":"MySql 教程2"},{"title":1}).pretty();
// 只显示title和description列的数据
db.blog.find({"title":"MySql 教程2"},{"title":1,"description":1}).pretty();
// 不显示 title和description列的数据
db.blog.find({"title":"MySql 教程2"},{"title":0,"description":0}).pretty();

更新文档

update() 方法用于更新已存在的文档,更新的时候需要加上关键字 $set

db.blog.find({"_id":"1"});
db.blog.update({"_id":"1"},{$set:{"likes":666}})

save更新

save() 方法通过传入的文档来替换已有文档,_id 主键存在就更新,不存在就插入

db.blog.save({
    "_id": "1",
    "title": "MySql 传统教程教程3",
    "description": "Mysql是一个传统数据库",
    "by": "我的博客",
    "url": "http://www.baiyp.ren",
    "tags": [
        "Mysql",
        "database"
   ],
    "likes": 100000
});

删除文档

条件删除

remove() 方法可以删除文档
db.blog.remove({"_id":"1"})

delete删除文档

官方推荐使用 deleteOne() 和 deleteMany() 方法删除文档

删除单个文档
deleteOne 只会删除符合条件的第一个文档,和 remove({},true) 效果一致
db.blog.deleteOne({});
批量删除文档
deleteMany 可以进行批量删除文档,和 remove({}) 效果一致
db.blog.deleteMany({});

关系表达式

操作格式范例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 }}
大于{key:{$gt:value }}
大于或 等于{key:{$gte:value }}
不等于{key:{$ne:value }}
包含{key:{$in:value }}
不包含{key:{$nin:value }}
判断字段存在{key:{“$exists” :true }}
多条件查询 ( 有时候存在一个字段需要多个条件,比如 pop>=10 and pop<50 这个如何表示呢 ){key:{“ g t e " : 10 , " gte" :10," gte":10,"lte”:100 }}db.zips.find({ "pop": { "$gte": 10, "$lt": 50 } }).pretty();

逻辑表达式

操作格式例子
AND{key:value,key:value}db.zips.find({
“state”: “NY”,
“pop”: {
“$gt”: 100000
}
})
ORKaTeX parse error: Expected '}', got 'EOF' at end of input: …ey": {<br /> "lt”: 0
}
}]
db.zips.find({
KaTeX parse error: Expected '}', got 'EOF' at end of input: …p": {<br /> "lt”: 0
}
}]
})

多条件表达式

db.zips.find({
    "$or": [
{
"$and": [
{
"state": "NY"
},
{
    "pop": {
        "$gt": 10,
        "$lte": 50
    }
}
]
},
{
    "$and": [
    {
        "state": {
            "$in": [
            "MD",
            "VA"
            ]
        }
    },
    {
        "pop": {
            "$gt": 10,
            "$lte": 50
        }
    }
    ]
}
]
}).pretty();
 
### 对应sql
select * from zips where (state='NY' and pop>10 and pop <= 50) or (state 
in('MD','VA')  and pop>10 and pop <= 50)

排序

在MongoDB中使用sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而-1是用于降序排列。

语法格式:
db.COLLECTION_NAME.find().sort({KEY1:1,KEY2:-1,....})

分页查询

MongoDB的分页

MongoDB提供了skip()和limit()方法。

  • skip: 跳过指定数量的数据. 可以用来跳过当前页之前的数据,即跳过pageSize*(n-1)。
  • limit: 指定从MongoDB中读取的记录条数,可以当做页面大小pageSize。
// 第一页数据
db.zips.find({},{"_id":1}).skip(0).limit(10);
// 第二页数据
db.zips.find({},{"_id":1}).skip(10).limit(10);
// 第三页页数据
db.zips.find({},{"_id":1}).skip(20).limit(10);

遇到的问题 看起来,分页已经实现了,但是官方文档并不推荐,说会扫描全部文档,然后再返回结果。

正确的分页办法

我们假设基于_id的条件进行查询比较,事实上,这个比较的基准字段可以是任何你想要的有序的 字段,比如时间戳 实现步骤如下 1. 对数据针对于基准字段排序 2. 查找第一页的最后一条数据的基准字段的数据 3. 查找超过基准字段数据然后向前找pagesize条数据

// 第一页数据
db.zips.find({},{_id:1}).sort({"_id":1}).limit(10);
// 第二页数据
db.zips.find({"_id":{$gt:"01020"}},{_id:1}).sort({"_id":1}).limit(10);
// 第三页数据
db.zips.find({"_id":{$gt:"01035"}},{_id:1}).sort({"_id":1}).limit(10);

ObjectId有序性

ObjectId生成规则
比如 “_id” : ObjectId(“5b1886f8965c44c78540a4fc”)
Objectid = 时间戳(4字节) + 机器(3个字节)+ PID(2个字节)+ 计数器(3个字节)
取id的前4个字节。由于id是16进制的string,4个字节就是32位,1个字节是两个字符,4个字节对应 id前8个字符。即 5b1886f8 , 转换成10进制为 1528334072 . 加上1970,就是当前时间。

ObjectId存在的问题

MongoDB的ObjectId应该是随着时间而增加的,即后插入的id会比之前的大。但考量id的生成规 则,最小时间排序区分是秒,同一秒内的排序无法保证。当然,如果是同一台机器的同一个进程生 成的对象,是有序的。

如果是分布式机器,不同机器时钟同步和偏移的问题。所以,如果你有个字段可以保证是有序的, 那么用这个字段来排序是最好的。 _id 则是最后的备选方案,可以考虑增加 雪花算法ID作为排序ID

统计查询

count

db.zips.find({
    "pop": {
        "$not": {
            "$gte": 10
       }
   }
}).count();

distinct
无条件排重
db.zips.distinct("state");
有条件排重

db.zips.distinct("state", {
	"pop": {
		"$gt": 70000
	}
});

SpringBoot整合Mongo

引入Pom坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

编写配置文件

server:
 port: 8080
spring:
 application:
   name: spring-boot-test
 data:
   mongodb:
     database: test
     host: 192.168.10.30
     port: 27017

定义实体类

Blog类

@Document("blog")
public class Blog {
    @Id
    private String id;
    private String title;
    private String by;
    private String url;
    private List<String> tags;
    private int likes;
    setter getter ....
}

Dao

@Component
public class BlogDao {
    @Autowired
    private MongoTemplate mongoTemplate;
    public void insert(Blog blog) {
        mongoTemplate.insert(blog);
    }
    public Blog findByID(String id) {
        return mongoTemplate.findById(id, Blog.class);
    }
    public void deleteByID(String id) {
        mongoTemplate.remove(Query.query(Criteria.where("_id").is(id)), 
                             Blog.class);
    }
    public List<Blog> find(Blog blog) {
        if (null == blog) {
            return null;
        }
        Criteria criteria = getFilter(blog);
        return mongoTemplate.find(Query.query(criteria), Blog.class);
    }
    public Criteria getFilter(Blog blog) {
        Criteria criteria = new Criteria();
        if (!StringUtils.isEmpty(blog.getTitle())) {
            criteria.andOperator(Criteria.where("title").is(blog.getUrl()));
        }
        if (!StringUtils.isEmpty(blog.getBy())) {
            criteria.andOperator(Criteria.where("by").is(blog.getBy()));
        }
        if (!StringUtils.isEmpty(blog.getLikes())) {
            criteria.andOperator(Criteria.where("likes").is(blog.getLikes()));
        }
        if (null != blog.getTags() && !blog.getTags().isEmpty()) {
            criteria.andOperator(Criteria.where("tags").in(blog.getTags()));
        }
        return criteria;
    }
}

Controller

@RestController
@RequestMapping("/blog")
public class WebController {
    @Resource
    private BlogDao blogDao;
    @RequestMapping("/{id}")
    @ResponseBody
    public String getBlogInfo(@PathVariable("id") String id) {
        Blog blog = blogDao.findByID(id);
        if (null == blog) {
            return "访问的数据不存在";
        }
        return JSON.toJSONString(blog);
    }
    @RequestMapping("/add")
    @ResponseBody
    public String addBlog(@RequestBody Blog blog) {
        blogDao.insert(blog);
        return JSON.toJSONString(blog);
    }
    public void batchAdd(){
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值