Golang MongoDB

2019-04-03  本文已影响0人  合肥黑

我们选用 MongoDB 作为网站的数据库系统,它是一个开源的 NoSQL 数据库,相比MySQL 那样的关系型数据库,它更为轻巧、灵活,非常适合在数据规模很大、事务性不强的场合下使用。

一、NoSQL

参考
NoSQL 还是 SQL ?这一篇讲清楚
NoSQL经典详解
什么是 NoSQL 呢?为了解释清楚,首先让我们来介绍几个概念。在传统的数据库中,数据库的格式是由表(table)、行(row)、字段(field)组成的。表有固定的结构,规定了每行有哪些字段,在创建时被定义,之后修改很困难。行的格式是相同的,由若干个固定的字段组成。每个表可能有若干个字段作为索引(index),这其中有的是主键(primary key),用于约束表中的数据,还有唯一键(unique key),确保字段中不存放重复数据。表和表之间可能还有相互的约束,称为外键(foreign key)。对数据库的每次查询都要以行为单位,复杂的查询包括嵌套查询、连接查询和交叉表查询。拥有这些功能的数据库被称为关系型数据库,关系型数据库通常使用一种叫做 SQL(Structured Query Language)的查询语言作为接口,因此又称为 SQL 数据库。典型的 SQL 数据库有 MySQL、Oracle、Microsoft SQL Server、PostgreSQL、SQLite,等等。

NoSQL 是 1998 年被提出的,它曾经是一个轻量、开源、不提供SQL功能的关系数据库。但现在 NoSQL 被认为是 Not Only SQL 的简称,主要指非关系型、分布式、不提供 ACID 的数据库系统。正如它的名称所暗示的,NoSQL 设计初衷并不是为了取代 SQL 数据库的,而是作为一个补充,它和 SQL 数据库有着各自不同的适应领域。NoSQL 不像 SQL 数据库一样都有着统一的架构和接口,不同的 NoSQL 数据库系统从里到外可能完全不同。

各个数据之间存在关联是关系型数据库得名的主要原因,为了进行join处理,关系型数据库不得不把数据存储在同一个服务器内,这不利于数据的分散,这也是关系型数据库并不擅长大数据量的写入处理的原因。相反NoSQL数据库原本就不支持Join处理,各个数据都是独立设计的,很容易把数据分散在多个服务器上,故减少了每个服务器上的数据量,即使要处理大量数据的写入,也变得更加容易,数据的读入操作也很容易。例如:谷歌和Facebook每天为他们的用户收集万亿比特的数据。这些数据的存储不需要固定的模式,无需多余的操作就可以横向扩展。

image.png
1.HBase(列存储)

两大用途:

场景:Facebook的消息类应用,包括Messages、Chats、Emails和SMS系统,用的都是HBase;淘宝的WEB版阿里旺旺,后台是HBase;小米的米聊用的也是HBase;移动某省公司的手机详单查询系统。(单次分析,只能scan全表或者一个范围内的)

2.MongoDB
3.RedisRedis
二、知乎 NoSql是一种语言,还是一种概念?

空口说比较无趣,我举一个现实的例子。我曾就职的一个公司有一个系统要是对用户给出推荐的广告。流程很简单,根据cookie查找用户属性,然后有专门的系统给出推荐列表。

我们只讨论这个根据cookie查找用户属性。这个功能用sql可以做吗?当然可以,但是为什么要用sql?

这个业务真正关心的是什么?

所以这个特性我相信业界都是使用key-value数据库,redis放得下就用redis(以前没有集群,单机放不下得想其他办法)。在抛弃掉sql不必要的束缚后,kv数据库可以在同样的软硬件成本下,实现更低的时延和更高的吞吐量。

我的高中数学老师曾经说过一句话,“你一定要掌握一个问题的通解,因为在考试的时候你无法保证可以在有限的时间内找到特解”;而我的大学老师(好像是信号处理)说过另外一句话“如果你没有找到某个问题的特解,那么说明你还不够了解这个问题”。

sql是一个很好的通解,这个框里面你可以放任何东西,而且它的一切都是可以预期的。但是当行业不断发展了之后,大家开始对业务上的一些问题了解越来越深入,而且这些业务的量也大到了你愿意单独为它搭一套系统。此时,不同的nosql作为不同特定问题的特解出现就自然而然了。所以nosql不可能取代sql,但它本身的发展也是不可逆的。另外给一个建议,如果你的系统访问量用一个单机mysql就可以搞定,那么还是继续用mysql吧,别本末倒置。

三、MongoDB简介

参考
知乎 怎样学 MongoDB?
MongoDB 极简实践入门

MongoDB 是一个对象数据库,它没有表、行等概念,也没有固定的模式和结构,所有的数据以文档的形式存储。所谓文档就是一个关联数组式的对象,它的内部由属性组成,一个属性对应的值可能是一个数、字符串、日期、数组,甚至是一个嵌套的文档。下面是一个MongoDB 文档的示例:

{ "_id" : ObjectId( "4f7fe8432b4a1077a7c551e8" ),
"uid" : 2004,
"username" : "byvoid",
"net9" : { "nickname" : "BYVoid",
"surname" : "Kuo",
"givenname" : "Carbo",
"fullname" : "Carbo Kuo",
"emails" : [ "byvoid@byvoid.com", "byvoid.kcp@gmail.com" ],
"website" : "http://www.byvoid.com",
"address" : "Zijing 2#, Tsinghua University" }
}

上面文档中 uid 是一个整数属性, username 是字符串属性, _id 是文档对象的标识符,格式为特定的 ObjectId 。 net9 是一个嵌套的文档,其内部结构与一般文档无异。从格式来看文档好像 JSON,没错,MongoDB 的数据格式就是 JSON,因此与 JavaScript 的亲和性很强。在 Mongodb 中对数据的操作都是以文档为单位的,当然我们也可以修改文档的部分属性。对于查询操作,我们只需要指定文档的任何一个属性,就可在数据库中将满足条件的所有文档筛选出来。为了加快查询,MongoDB 也对文档实现了索引,这一点和 SQL 数据库一样。

四、MongoDB安装

先是看了一下windows下MongoDB的安装及配置,然后自己下载了安装包,才发现是4.0.9版本的,差别比较大。比较重要一点是:

从 MongoDB 4.0 开始,默认情况下,你可以在安装期间配置和启动 MongoDB 作为服务,并在成功安装后启动 MongoDB 服务。也就是说,MongoDB 4.0 已经不需要像以前版本那样输入一堆命令行来将 MongoDB 配置成 Windows 服务来自动运行了,方便了很多。
Win10 安装配置 MongoDB 4.0 踩坑记

在3中的许多配置(如 设置dbpath、logpath、安装服务等),在4中都可以省去。​​也就是说,在MongoDB4.0.0中,只要安装好了,基本不用配置就可以用了。由于之前不知道这些,而且安装配置的教程都是参照MongoDB3的,所以走了许多弯路。

参考如下,开始安装
Mongodb最新版本安装(4.0以上)
mongoDB的使用学习(一)mongoDB4.0.6的下载安装配置

1.设置service name和配置路径
image.png
2.MongoDB Compass是个可视化工具,如果勾选安装的话是在线下载安装的,据说有的人安装一整晚都没装好就是因为这个。我这里是取消勾选的,因为之后我会装别的工具使用
image.png
3.简单使用

我这里全部默认设置,一路next,然后点完finish,就没了……
呃,只能自己打开cmd,然后在C:\Program Files\MongoDB\Server\4.0\bin路径下(MongoDB配置环境变量,可以任意路径运行mongo命令
)运行mongo命令发现还是安装成功了。在这个路径下打开mongod.cfg,能看到配置,比如data和log的位置。使用浏览器打开http://127.0.0.1:27017/:It looks like you are trying to access MongoDB over HTTP on the native driver port.

使用show dbs看一下:

> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
> show collections
movie
>

show dbs 显示所有数据库的名称和存储情况
use xxx 转到xxx数据库,如果没有该数据库,那就建立该数据库
db.createCollection('author')创建集合;我们试着往我们的数据库里添加一个集合(collection),MongoDB里的集合和SQL里面的表格是类似的
更多命令参考
https://www.mongodb.org.cn/tutorial/
http://www.runoob.com/mongodb/mongodb-tutorial.html

4.可视化工具

参考知乎 MongoDB上有哪些比较好的GUI工具?

如果你是Mongo的企业版用户,不如尝试MongoDB的官方GUI:MongoDB Compass

我最终选了RoboMongo,现在已经改名为robo 3t。以下参考MongoDB可视化工具--Robo 3T 使用教程

image.png
下载后一路next,然后默认连接就能看到数据了。
五、在go中使用
1. Mgo 驱动

地址: https://godoc.org/labix.org/v2/mgohttps://github.com/go-mgo/mgo
地址: https://github.com/globalsign/mgo
文档: https://godoc.org/github.com/globalsign/mgo
说明:上面第一个地址,是 mgo 的原地址,目前作者已经停止维护。第二个地址是基于原作者的社区维护版本,也是作者推荐的方案之一。

2. 官方驱动

地址: https://github.com/mongodb/mongo-go-driver
文档:https://godoc.org/github.com/mongodb/mongo-go-driver/mongo
参考:
MongoDB官方推出的Go驱动库“mongo-go-driver”快速教程

截止到2019.5.24,官方驱动达到2648 star,已经超过mgo驱动了,建议使用官方版本。

3.使用Mgo 驱动实例

参考
Go实战--golang中使用MongoDB(mgo)
三、go语言操作 mongodb mgo --go语言学习笔记

package main

import (
    "fmt"
    "log"

    "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
)

type Person struct {
    Name  string
    Phone string
}

func main() {
    //连接
    session, err := mgo.Dial("localhost:27017")
    if err != nil {
        panic(err)
    }
    defer session.Close()

    // Optional. Switch the session to a monotonic behavior.
    session.SetMode(mgo.Monotonic, true)

    //通过Database.C()方法切换集合(Collection)
    //func (db Database) C(name string) *Collection
    c := session.DB("test").C("people")

    //插入
    //func (c *Collection) Insert(docs ...interface{}) error
    err = c.Insert(&Person{"superWang", "13478808311"},
        &Person{"David", "15040268074"})
    if err != nil {
        log.Fatal(err)
    }

    result := Person{}
    //查询
    //func (c Collection) Find(query interface{}) Query
    err = c.Find(bson.M{"name": "superWang"}).One(&result)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Name:", result.Name)
    fmt.Println("Phone:", result.Phone)
}
----------------------
Name: superWang
Phone: 13478808311

可以看到上面的Go文件插入了两个数据,在Cmd里可以验证下(关于查询,参考MongoDB系列一(查询).):

> use test
switched to db test
> show collections
people
> db.people.find()
{ "_id" : ObjectId("5ce799b3c6bfff8dd9776a25"), "name" : "superWang", "phone" : "13478808311" }
{ "_id" : ObjectId("5ce799b3c6bfff8dd9776a26"), "name" : "David", "phone" : "15040268074" }
db.people.find({name:'superWang'})
{ "_id" : ObjectId("5ce799b3c6bfff8dd9776a25"), "name" : "superWang", "phone" : "13478808311" }
Robo3T中查看
(1)插入

注意insert插入的Person结构体,自动变成小写属性写入了mongo。再看一个例子:

type User struct {
    Id_       bson.ObjectId `bson:"_id"`
    Name      string        `bson:"name"`
    Age       int           `bson:"age"`
    JoinedAt   time.Time     `bson:"joined_at"`
    Interests []string      `bson:"interests"`
}

通过bson:”name”这种方式(也可以省略bson部分,只写"name")可以定义MongoDB中集合的字段名,如果不定义,mgo自动把struct的字段名首字母小写作为集合的字段名。如果不需要获得id_,Id_可以不定义,在插入的时候会自动生成。

err = c.Insert(&User{
    Id_:       bson.NewObjectId(),
    Name:      "Jimmy Kuu",
    Age:       33,
    JoinedAt:  time.Now(),
    Interests: []string{"Develop", "Movie"},
})

上面可以插入自己生成id的数据,注意如果没有bson:"_id"这个标记,数据里会出现id__id两个属性,因为mongo直接把"Id_"变成小写的"id_"了。这里通过bson.NewObjectId()来创建新的ObjectId,如果创建完需要用到的话,放在一个变量中即可,一般在Web开发中可以作为参数跳转到其他页面。

注:插入也可以使用c.Insert(bson.M{"name":"cuixx"})这种方式。

(2)查询

通过func (c *Collection) Find(query interface{}) *Query来进行查询,返回的Query struct可以有附加各种条件来进行过滤。通过Query.All()可以获得所有结果,通过Query.One()可以获得一个结果,注意如果没有数据或者数量超过一个,One()会报错。条件用bson.M{key: value},注意key必须用MongoDB中的字段名,而不是struct的字段名。

//可以通过id来查询
id := "5204af979955496907000001"
objectId := bson.ObjectIdHex(id)

user := new(User)
c.Find(bson.M{"_id": objectId}).One(&user)

//更简单的方式是直接用FindId()方法:
c.FindId(objectId).One(&user)
(3)更新

c.Update(bson.M{"_id":objectId},bson.M{"$set":bson.M{"name":"cuixu"}}),注意修改单个或多个字段需要通过$set操作符号,否则集合会被替换。

(4)字段增加值
c.Update(bson.M{"_id": objectId,
    bson.M{"$inc": bson.M{
        "age": -1,
    }})
(5)从数组中增加一个元素
c.Update(bson.M{"_id": objectId,
    bson.M{"$push": bson.M{
        "interests": "Golang",
    }})
(6)从数组中删除一个元素
c.Update(bson.M{"_id": objectId,
    bson.M{"$pull": bson.M{
        "interests": "Golang",
    }})
(7)删除
c.Remove(bson.M{"name": "Jimmy Kuu"})
(8)Upsert,UpsertId

如果数据存在就更新,否则就新增一条记录:func (c *Collection) Upsert(selector interface{}, update interface{}) (info *ChangeInfo, err error)

selector := bson.M{"key": "max"}
data := bson.M{"$set": bson.M{"value": 30}}
changeInfo, err := getDB().C("config").Upsert(selector, data)

还有func (c *Collection) UpsertId(id interface{}, update interface{}) (info *ChangeInfo, err error)也是类似的,确定根据ID来查找,比如info, err := collection.Upsert(bson.M{"_id": id}, update)

上一篇下一篇

猜你喜欢

热点阅读