Golang MongoDB

我们选用 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(列存储)

两大用途:

  • 特别适用于简单数据写入(如“消息类”应用)和海量、结构简单数据的查询(如“详单类”应用)。特别地,适合稀疏表。(个人觉得存个网页内容是极好极好的)
  • 作为MapReduce的后台数据源,以支撑离线分析型应用。

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

2.MongoDB
  • 是一个介于关系型和非关系型之间的一个产品吧,类SQL语言,支持索引
  • MongoDb在类SQL语句操作方面目前比HBase具备更多一些优势,有二级索引,支持相比于HBase更复杂的集合查找等。
  • BSON的数据结构使得处理文档型数据更为直接。支持复杂的数据结构
  • MongoDb也支持mapreduce,但由于HBase跟Hadoop的结合更为紧密,Mongo在数据分片等mapreduce必须的属性上不如HBase这么直接,需要额外处理。
3.RedisRedis
  • 为内存型KV系统,处理的数据量要小于HBase与MongoDB
  • Redis很适合用来做缓存,但除此之外,它实际上还可以在一些“读写分离”的场景下作为“读库”来用,特别是用来存放Hadoop或Spark的分析结果。
  • Redis的读写性能在100,000ops/s左右,时延一般为10~70微妙左右;而HBase的单机读写性能一般不会超过1,000ops/s,时延则在1~5毫秒之间。
  • Redis的魅力还在于它不像HBase只支持简单的字符串,他还支持集合set,有序集合zset和哈希hash
二、知乎 NoSql是一种语言,还是一种概念?

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

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

  • 1、这个特性永远永远是1对1的查找,不需要任何sum,count等功能;
  • 2、这个特性永远永远是从cookie搜索属性,不需要根据属性来查找,所以where也用不上(更别说join了);
  • 3、数据只有一个更新者,基于hadoop的机器学习程序,所以锁也不是必须的;
  • 4、这个业务甚至不介意脏数据,如果因为同步等原因造成读到的事旧的属性,也不过就是拿昨天的数据给他推广告罢了。

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

  • 时延一定要小,上游对整个广告过程只给了500ms,你还需要去竞价,需要去选择广告,还要预留网络时延。
  • 数据量大,中国的网民数量是比较多的,几亿条记录是下限。
  • 并发要高,每当网民打开一个网页,有几个广告位就会发生几次广告竞价。
  • 最后,很不幸这个业务毛利率很低,要是在软件或者硬件是花费过多有可能亏本的。

所以这个特性我相信业界都是使用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
  • 如果你选择不将 MongoDB 配置为服务,请取消选中 Install MongoD as a Service。

  • 如果你选择将 MongoDB 配置为服务,则可以:

    • 指定以下列用户之一运行服务:
      • 网络服务用户;即 Windows 内置的 Windows 用户帐户
      • 本地或域用户:
      • 对于现有本地用户帐户,Account Domain 指定为 .,并为该用户指定 Account Name 和 Account Password。
      • 对于现有域用户,请为该用户指定 Account Domain,Account Name 和 Account Password。
  • 指定 Service Name。如果你已拥有具有指定名称的服务,则必须选择其他名称。

    • 指定 Data Directory(数据保存目录),对应于 --dbpath。如果该目录不存在,安装程序将创建该目录并为服务用户设置访问权限。
    • 指定 Log Directory(日志保存目录),该目录对应于 --logpath。如果该目录不存在,安装程序将创建该目录并为服务用户设置访问权限。
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)

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

推荐阅读更多精彩内容

  • 云计算背后的秘密:NoSQL诞生的原因和优缺点 我本来一直觉得NoSQL其实很容易理解的,我本身也已经对NoSQL...
    ItStar阅读 2,261评论 0 7
  • 随着大数据时代的到来,越来越多的网站、应用系统需要支撑海量数据存储,高并发请求、高可用、高可扩展性等特性要求,传统...
    caison阅读 32,923评论 4 65
  • 一、MongoDB简介 1.概述 ​ MongoDB是一个基于分布式文件存储的数据库,由C++语言编写。旨在为WE...
    郑元吉阅读 974评论 0 2
  • 概览一下大数据项目中可以使用的数据存储技术,聚焦于Couchbase 和 ElasticSearch,展示如何使用...
    abel_cao阅读 1,739评论 1 29
  • 我早上出去买来回早点他还没起床,过了7点,又去叫他,他在里面说,才几秒又催一次,再催我不上学了。我不再说话,我在厨...
    好运连连高二男阅读 451评论 2 5