MongoDB 数据库

一、MongoDB简介

  • 概述
    MongoDB是一个基于分布式文件存储的数据库,由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。

    MongoDB介于关系型数据和非关系型数据库之间,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,类似json格式,因此可以存储比较复杂的数据类型。

    MongoDB最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库表单查询的绝大部分功能,而且还支持对数据建立索引。

  • MySQL
    关系型数据库。 查询语句是使用传统的sql语句,拥有较为成熟的体系,成熟度很高。 关系型数据库遵循ACID规则 开源数据库的份额在不断增加,mysql的份额页在持续增长。 缺点:在海量数据处理的时候效率会显著变慢。

    数据库事务必须具备ACID特性,ACID是Atomic原子性,Consistency一致性,Isolation隔离性,Durability持久性。
    数据的持久存储,尤其是海量数据的持久存储,还是需要一种关系数据库。

  • MongoDB
    非关系型数据库(nosql ),属于文档型数据库。存储方式:虚拟内存+持久化。
    查询语句:是独特的MongoDB的查询方式。
    适合场景:事件的记录,内容管理或者博客平台等等。
    数据处理:数据是存储在硬盘上的,只不过需要经常读取的数据会被加载到内存中,将数据存储在物理内存中,从而达到高速读写。
    成熟度与广泛度:新兴数据库,成熟度较低,Nosql数据库中最为接近关系型数据库,比较完善的DB之一,适用人群不断在增长。
    优势: 快速!在适量级的内存的MongoDB的性能是非常迅速的,它将热数据存储在物理内存中,使得热数据的读写变得十分快, 高扩展, json的存储格式!

    文档的数据库: 即可以存放xml、json、bson类型系那个的数据。这些数据具备自述性(self-describing),呈现分层的树状数据结构。数据结构由键值(key=>value)对组成。

  • 关系型数据库和非关系型数据库的区别
    关系型数据库通过外键关联来建立表与表之间的关系;
    非关系型数据库通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定;

    学生: 张三
    性别: 男
    科目: 语文
    成绩: 80
    
    关系型数据库:
      // 学生表
      create table student(id int primary key, name char(50), sex char(10))
      // 成绩表,stuid存储的是学生表中对应的主键,用于表的关联
      create table score(id int primary key, name char(20),grade int,stuid int, foreign key(stuid) references student(id))
    
    非关系型数据库:
    {
      "name":"张三",
      "sex":"男",
      "score":{
        "name":"语文",
        "grade": 80
      }
    }
    
    

    关系型数据库SQLite、Oracle、mysql
    非关系型数据库 MongoDb、redis

  • MySQL和MongoDB的区别
    数据库: 容器,不管是mysql还是mongodb,一个单一的服务器都可以管理多个数据库;
    集合:是一组mongodb的文件,等价于mysql中的table,集合中文档可以有不同的字段,也可以有不同的数据类型;

    [图片上传失败...(image-52d227-1540208871848)]

二、MongoDB安装和卸载

  • 卸载

    sudo apt-get autoremove mongodb
    sudo apt-get autoclean mongodb
    
    // 清除残留数据
    dpkg -l |grep ^rc|awk '{print $2}' |tr ["\n"] [" "]|sudo xargs dpkg -P   
    
    
  • 安装

    第1步 – 导入公钥**
      Ubuntu软件包管理器apt(高级软件包工具)需要软件分销商的GPG密钥来确保软件包的一致性和真实性。 执行此下面的命令将MongoDB密钥导入到您的服务器:
      sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5
    
    第2步 – 创建源列表文件MongoDB
      检查URL http://repo.mongodb.org/apt/ubuntu/dists/。
      如果您在该网页上看到一个目录“bionic”,则将下述命令中的单词“xenial”替换为“bionic”一词,
    【原因:MongoDB尚未发布Bionic Beaver软件包,但Xenial软件包在Ubuntu 18.04 LTS上运行良好】
      执行以下命令在/etc/apt/sources.list.d/中创建一个MongoDB列表文件:
      echo "deb http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.6 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.6.list
    
    第3步 – 更新存储库
      使用apt命令更新存储库:
      sudo apt-get update
      说明:执行完会提示一些失败,不用在意
    
    第4步 – 安装MongoDB
      执行以下命令来安装MongoDB:
      sudo apt-get install -y mongodb
    
    第5步:启动MongoDB
      执行以下命令启动MongoDB并将其添加为在启动时启动的服务
      sudo systemctl start mongodb
    
        如果执行完这一步终端没有任何输出,则说明是正确的
        如果启动的时候提示:Failed to start mongod.service: Unit mongodb.service not found.
        解决办法如下:
        1创建配置文件:
          cd /etc/systemd/system/
          sudo vi mongodb.service
        2.在里面追加文本:
          [Unit]
          Description=High-performance, schema-free document-oriented database
          After=network.target
          [Service]
          User=mongodb
          ExecStart=/usr/bin/mongod --quiet --config /etc/mongodb.conf
          [Install]
          WantedBy=multi-user.target
        3.退出
          :wq
        4.启动服务
          sudo systemctl start mongodb
          sudo systemctl status mongodb
        5.设置开机自启动
          sudo systemctl enable mongodb
    
    第6步:登录MongoDB
        mongo
    
          如果出现错误全局初始化失败:BadValue无效或无用户区域设置。 请确保LANG和/或LC_ *环境变量设置正确,请尝试命令:
          export LC_ALL=C
    
    

三、MongoDB之数据库操作

  • 创建数据库

    # mongodb
    use DATABASE_NAME
    注意:如果指定的数据库DATABASE_NAME不存在,则该命令将创建一个新的数据库,否则返回现有的数据库
    
    # mysql中
    创建数据库:create database basename;
    切换数据库:use basename;
    
    

    admin:从权限的角度来说,是root的数据库
    ​ local:本地数据
    ​ config:配置,用于保存MongoDB的配置信息

  • 检查当前选择的数据

    db
    
    

    默认的数据库test

  • 显示数据库列表

    show dbs 
    
    
  • 删除数据库

    // 默认进入数据库是test
    db.dropDatabase()
    
    

    注意:默认删除当前正在工作的数据库

四、MongoDB之集合操作

类似于MySQL中的表。
集合存在于数据库中,集合没有固定的结构,意味着可以对集合插入不同格式和不同类型的数据,但是尽量插入集合的时候保证数据的关联性。

  • 创建集合

    集合名的规范:
      a.不能空字符串
      b.集合名不能含有\0【空字符】,表示集合名的结尾
      c.集合名不能以"system."开头,为系统集合保留的关键字
      d.不能含有保留字符,千万不能含有$
    
    语法:
      // name的类型为String,是要创建的集合的名称
      // options的类型是Document,是一个文档,指定相应的大小和索引,是可选参数
      // 在插入文档时,MongoDB首先检查上限集合capped字段的大小,然后检查max字段
      db.createCollection(name, options)
    
    例如:
      // 没有options选项的集合创建
      db.createCollection("myCollection")
    
      // 有options选项的集合的创建
      db.createCollection("mycol",{capped:true,autoIndexId:true,size:1024,max:10000})
    
    
  • 显示当前数据库中的集合

    show collections
    
    
  • 删除集合

    语法:
      // 如果选定的集合成功删除,drop()方法将返回true,否则返回false
      db.COLLECTION_NAME.drop()
    
    例如:
      db.mycollection.drop()
    
    

五、MongoDB之文档操作

  • 文档概念
    文档:相当表中的一条记录【实体】
    是一组键值对,文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型

    注意: 
    a.文档中的键值对是有序的
    b.文档中值除了字符串之外,还可以是其他数据类型【嵌套一个文档】
    c.严格区分大小写和数据类型的,mycol myCol
    d.文档中不能有重复的键
    e.文档中的键基本都是用字符串表示的
    
    
    文档中键的命名:
    a.键不能包含\0
    b.$和.有特殊含义
    c.以下划线开头的键是保留的,尽量不要使用下划线开头
    
    
  • 插入文档

    语法:
      // 在插入的文档中,如果不指定_id参数,那么 MongoDB 会为此文档分配一个唯一的ObjectId
      // _id为集合中的每个文档唯一的12个字节的十六进制数。
      db.COLLECTION_NAME.insert(document)
    
    例如:
      // 插入一个
      db.mycol.insert({id:101, name:'lisi', age:20})
      db.mycol.insert({ 
         item: "canvas", 
         num: 100, 
         tags: ["cotton"], 
         size: { 
              h: 20,
              w: 30, 
          } 
      })
    
      // 插入多个(注意方括号)
      db.mycol.insert( [{id:102, name:'wagnwu', age:18}, {id:103, name:'zhaoliu', age:21}, {id:104, name:'tianqi', age:19}] )
    
      // 查看已插入的文档
      db.mycol.find()
      { "_id" : ObjectId("5b8b59cb5bd1df1fc73dcdc6"), "id" : 101, "name" : "lisi", "age" : 20 }
    
      // 查看已插入的文档
      db.mycol.find().pretty()
      {
      "_id" : ObjectId("5b8b59cb5bd1df1fc73dcdc6"),
      "id" : 101,
      "name" : "lisi",
      "age" : 20
      }
    
    
  • 查询文档

    语法:
      // 基本操作
      db.COLLECTION_NAME.find(document)
       // 以格式化的方式返回查询结果
       db.COLLECTION_NAME.find(document).pretty()
    
    注意: 
      find() 将以非结构化的方式返回查询结果
    
     例如: 
      // 显示所有文档
      db.mycol.find()
    
      // 默认将所有文档显示,为了限制列表,需要显示的字段设置为1,不显示的设置为0
      db.mycol.find( {'name':'liming'}, {'name':1, 'age': 1} )
      db.mycol.find( {'name':'liming'}, {'age':0} )
    
      // 限制字段显示
      db.check.find({},{'_id':1,'title':1})
    
    
  • 查询文档(条件查询)

    - 等于{ <key>:<value> }
      db.mycol.find({'name':'zyz'} ).pretty()
    
    - 小于 { <key>: {$lt:<value>} }
      db.mycol.find( {'age': {$lt:18}} ).pretty()
    
    - 小于等于 { <key>: {$lte:<value>} }
      db.mycol.find( {'age': {$lte:18}} ).pretty()
    
    - 大于 { <key>: {$gt:<value>} }
      db.mycol.find( {'age': {$gt:18}} ).pretty()
    
    - 大于等于 { <key>: {$gte:<value>} }
      db.mycol.find( {'age': {$gte:18}} ).pretty()
    
    - 不等于 { <key>: {$ne:<value>} }
      db.mycol.find( {'age': {$ne:18}} ).pretty()
    
    - 并列关系(and)
      在find()方法中,如果通过使用 ',' 将它们分开传递多个键,则 MongoDB 将其视为AND条件
      db.mycol.find(
        {
          $and: [
            {key1: value1}, {key2: value2}
          ]
        }
      )
    
    - 或者关系(or)
      db.mycol.find(
        {
          $or: [
            {key1: value1}, {key2: value2}
          ]
        }
      )
    
    
  • 更新文档

    update()更新现有文档中的值,语法:
      db.COLLECTION_NAME.update(SELECTION_CRITERIA, UPDATED_DATA)
    例如:
      // update默认只更新一个文档,如果要更新多个文档,则添加参数{multi:true})
      db.check.update( {'title': 'MongoDB Guide'}, {$set: {'title': 'mongo'}} )
      db.check.update( {'title': 'MongoDB Guide'}, {$set: {'title': 'mongo'}, $set: {'say': 'hello'}} )
      db.check.update( {'title': 'MongoDB Guide'}, {$set: {'title': 'mongo'}}, {multi: true} )
    
    save()用传递的文档数据替换现有文档,语法:  
      db.COLLECTION_NAME.save({_id:ObjectId(),NEW_DATA})
    例如:
      db.check.save( {'_id':102, 'title':'hello', 'by':'lalala'} )  
    
    
  • 删除文档

    语法:
      db.COLLECTION_NAME.remove(DELLETION_CRITTERIA)
    
     例如:
       db.check.remove( {'_id':100} )
    
    

六、MongoDB之查询

  • 投影
    查询过程中,只显示指定的字段

    语法:
      db.COLLECTION_NAME.find({},{KEY:1})
    
    例如:
      db.mycol.find( {}, {'title':1, _id:0} )
    
    

    在执行find()方法时,始终都会显示_id字段,如果不想要此字段,则需要将其设置为0

  • 限制筛选记录

    limit()限制MongoDB要返回的记录数,根据指定的参数返回记录数
    语法:
      db.COLLECTION_NAME.find().limit(NUMBER)
    例如: 
      // 在查询文档时仅显示两个文档
      db.mycol.find({},{"title":1,_id:0}).limit(2)
    
    skip() 方法跳过指定数量的数据
    语法:
      // 注意:skip()方法中的默认值为0。
      db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
    例如:
      db.mycol.find({},{"title":1,_id:0}).limit(1).skip(2)
    
    
  • 对查询记录排序

     语法:
        // 使用指定顺序进行排序,1表示升序,-1表示降序
        db.COLLECTION_NAME.find().sort({KEY:1})
      例如:
         db.mycol.find({},{"title":1,_id:0}).sort({"title":-1})
    
    
  • 管道的概念
    MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

    $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档
      db.article.aggregate( [ {$project:{by_user:1, title:1}} ] )
    
    $limit:用来限制MongoDB聚合管道返回的文档数
      db.article.aggregate( [ {$project:{by_user:1, title:1}}, {$limit: 2} ] )  
    
    $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档
      db.article.aggregate( [ {$project:{by_user:1, title:1}}, {$skip: 1} ] )
      db.article.aggregate( [ {$project:{by_user:1, title:1}}, {$limit:2},{$skip: 1} ] ) 
    
    $group:将集合中的文档分组,可用于统计结果
      db.article.aggregate( [ {$group: {_id:'$by_user', num:{$sum:'$likes'}}} ] )
    
    $sort:将输入文档排序后输出
      db.article.aggregate( [ {$group: {_id:'$by_user', num:{$sum:'$likes'}}},{$sort: {'num':-1}} ] )
    
    
  • 分组与聚合函数查询

    aggregate()语法:
      db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
    
    - $sum 从集合中的所有文档中求出定义的值
      // 计算每个作者所写的文章点赞数
      db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])
      // 计算每个作者所写文档数量
      // select by_user, count(*) from article group by by_user
      db.article.aggregate([ { $group: {_id:'$by_user', num:{$sum:1}} } ])
    
    - $avg 计算集合中所有文档的所有给定值的平均值
      db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])
    
    - $max 从集合中的所有文档获取相应值的最大值
      // _id:'$by_user',对应按照by_user分组
      db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])
      // _id对应一个常量,即所有数据的操作
      db.article.aggregate([ { $group:{_id:'max', num_likes:{$max:'$likes'}} } ])
    
    - $min 从集合中的所有文档获取相应值的最小值
      db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])
    
    - 例如:
      db.article.aggregate([{$group:{'_id':'$by_user','num_tutorial':{$sum:1}}}])
    
    

七、MongoDB之关联关系

MongoDB中的关系表示各个文档在逻辑上的相互关联。关系可以通过嵌入式和引用方法建模。 这种关系可以是1:1,1:N,N:1或N:N。

假设有一种情况:要存储用户的地址。一个用户可以拥有多个地址,这就是1:N关系。

// 用户user文档
{
   "_id":10999110,
   "name": "Maxsu",
   "contact": "13800138000",
   "dob": "1992-10-11"
}

// 地址文档
{
   "_id":12200,
   "building": "Hainan Building NO.2100",
   "pincode": 571100,
   "city": "Haikou",
   "province": "Hainan"
}

嵌入式关系建模
在嵌入式方法中,我们将地址(address)文档嵌入到用户(user)文档中

{
   "_id": 21000100,
   "contact": "13800138000",
   "dob": "1991-11-11",
   "name": "Maxsu",
   "address": [
      {
         "building": "Hainan Building NO.2100",
         "pincode": 571100,
         "city": "Haikou",
         "province": "Hainan"
      },
      {
         "building": "Sanya Building NO.2100",
         "pincode": 572200,
         "city": "Sanya",
         "province": "Hainan"
      },
   ]
}

该方法将所有相关数据保存在单个文档中,这使得检索和维护更容易。
可以使用单个查询来在整个文档检索:
 db.users.find( {"name":"Maxsu"},{"address":1, "name":1} )

在上述查询中,db和users分别是数据库和集合。缺点是如果嵌入式文档的大小如果不断增长,可能会影响读/写性能。

建模参考关系
这是设计规范化关系的方法。 
在这种方法中,用户和地址文件将分别维护,但用户文档将包含一个将引用地址文档的id字段的字段。
{
   "_id":ObjectId("52ffc33321332111sdfaf"),
   "contact": "13800138000",
   "dob": "1991-11-11",
   "name": "Maxsu",
   "address_ids": [
      ObjectId("123123"),
      ObjectId("123412")
   ]
}
用户文档包含对应地址的ObjectId的数组字段address_ids。 
使用这些ObjectIds,我们可以从那里查询地址文件并获取地址详细信息。 
使用这种方法,需要两个查询:首先从用户文档获取address_ids字段,然后从地址集中获取这些地址。
var result = db.users.find({"name":"Maxsu"},{"address_ids":1})
var addresses = db.address.find({"_id":{"$in":result["address_ids"]}})

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容