MongoDb基本使用

MongoDB基本使用

Nosql简介

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

什么是是NoSql

NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的 关系型数据库的数据库管理系统的统称。
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 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 表连接,MongoDB不支持
primary key primary 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
}
})
OR KaTeX parse error: Expected '}', got 'EOF' at end of input: …ey": {
"
lt”: 0
}
}]
db.zips.find({
KaTeX parse error: Expected '}', got 'EOF' at end of input: …p": {
"
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(){
    }
}

你可能感兴趣的:(中间件,mongodb,数据库)