鍍金池/ 教程/ HTML/ Mongodb 與 Mongoose 的使用
瀏覽器端測試:mocha,chai,phantomjs
搭建 Node.js 開發(fā)環(huán)境
測試用例:mocha,should,istanbul
線上部署:heroku
Mongodb 與 Mongoose 的使用
使用 superagent 與 cheerio 完成簡單爬蟲
js 中的那些最佳實(shí)踐
使用 eventproxy 控制并發(fā)
使用 promise 替代回調(diào)函數(shù)
作用域與閉包:this,var,(function () {})
持續(xù)集成平臺:travis
測試用例:supertest
benchmark 怎么寫
使用 async 控制并發(fā)
學(xué)習(xí)使用外部模塊
一個最簡單的 express 應(yīng)用
正則表達(dá)式
cookie 和 session

Mongodb 與 Mongoose 的使用

目標(biāo)

無明確目標(biāo)

知識點(diǎn)

  1. 了解 mongodb (http://www.mongodb.org/ )
  2. 學(xué)習(xí) mongoose 的使用 (http://mongoosejs.com/ )

課程內(nèi)容

mongodb

mongodb 這個名詞相信大家不會陌生吧。有段時間 nosql 的概念炒得特別火,其中 hbase redis mongodb couchdb 之類的名詞都相繼進(jìn)入了大眾的視野。

hbase 和 redis 和 mongodb 和 couchdb 雖然都屬于 nosql 的大范疇。但它們關(guān)注的領(lǐng)域是不一樣的。hbase 是存海量數(shù)據(jù)的,redis 用來做緩存,而 mongodb 和 couchdb 則試圖取代一些使用 mysql 的場景。

mongodb 的官網(wǎng)是這樣介紹自己的:

MongoDB (from "humongous") is an open-source document database, and the leading NoSQL database. Written in C++

開源、文檔型、nosql。

其中文檔型是個重要的概念需要理解。

在 sql 中,我們的數(shù)據(jù)層級是:數(shù)據(jù)庫(db) -> 表(table) -> 記錄(record)-> 字段;在 mongodb 中,數(shù)據(jù)的層級是:數(shù)據(jù)庫 -> collection -> document -> 字段。這四個概念可以對應(yīng)得上。

文檔型數(shù)據(jù)這個名字中,“文檔”兩個字很容易誤解。其實(shí)這個文檔就是 bson 的意思。bson 是 json 的超集,比如 json 中沒法儲存二進(jìn)制類型,而 bson 拓展了類型,提供了二進(jìn)制支持。mongodb 中存儲的一條條記錄都可以用 bson 來表示。所以你也可以認(rèn)為,mongodb 是個存 bson 數(shù)據(jù)的數(shù)據(jù)庫,或是存哈希數(shù)據(jù)的數(shù)據(jù)庫。

mongodb 相對于它的競爭對手們來說——比如 couchdb,它的一大優(yōu)勢就是盡可能提供與 sql 對應(yīng)的概念。之前說了,sql 中的記錄對應(yīng) mongodb 中的 document,記錄這東西是一維的,而 document 可以嵌套很多層。在某些場景下,比如存儲一個文章的 tags,mongodb 中的字段可以輕松存儲數(shù)組類型,而 sql 中就需要設(shè)計個一對多的表關(guān)系出來。

假設(shè)有一個 blog 應(yīng)用,其中有張 Post 表,表中有用戶發(fā)表的一些博客內(nèi)容(post)。

這些 post 文檔的樣子大概會是這樣:

var post = {
  title: '呵呵的一天',
  author: 'alsotang',
  content: '今天網(wǎng)速很差',
  tags: ['呵呵', '網(wǎng)速', '差'],
};

mongodb 中有個最亮眼的特性,就是 Auto-Sharding,sharding 的意思可以理解成我們 scale sql 時的分表。

在 mongodb 中,表與表之間是沒有聯(lián)系的,不像 sql 中一樣,可以設(shè)定外鍵,可以進(jìn)行表連接。mongodb 中,也無法支持事務(wù)。

所以這樣的表,無債一身輕。可以很輕易地 scale 至多個實(shí)例(假設(shè)實(shí)例都有不同的物理位置)上。在 mongodb 中,實(shí)時的那些查詢,也就只能進(jìn)行條件查詢:某某大于一個值或某某等于一個值。而 sql 中,如果一張表的數(shù)據(jù)存在了多個實(shí)例上的話,當(dāng)與其他表 join 時候,表之間的運(yùn)來運(yùn)去會是個很慢的過程,具體我也不太懂。

反正使用 mongodb 時,一定要思考的兩點(diǎn)就是:表 join 到底要不要,事務(wù)支持到底要不要。

mongodb 中的索引特性跟 sql 中差不多,只是它對于嵌套的數(shù)據(jù)類型也提供了支持。在建立復(fù)合索引時,mongodb 可以指定不同字段的排序,比如兩個字段 is_top(置頂) 和 create_time(創(chuàng)建時間) 要建立復(fù)合索引,我們可以指定 is_top 按正序排,create_time 按逆序排。mysql 說是有計劃支持這個特性,不過目前也沒什么消息。不過這點(diǎn)不重要。

mongodb 中,collection 是 schema-less 的。在 sql 中,我們需要用建表語句來表明數(shù)據(jù)應(yīng)該具有的形式,而 mongodb 中,可以在同一張里存各種各樣不同的形式的數(shù)據(jù)。同一個 collection 中,可以有些 document 具有 100 個字段,而另一些,則只具有 5 個字段。如果你分不清這個特性的使用場景,那么請像使用 sql 一樣的,盡可能保證一個 collection 中數(shù)據(jù)格式是統(tǒng)一的。這個 schema-less 的特性,有個比較典型的場景是用來存儲日志類型的數(shù)據(jù),可以搜搜看這方面的典型場景。

mongodb 和 mysql 要我選的話,無關(guān)緊要的應(yīng)用我會選擇 mongodb,就當(dāng)個簡單的存 json 數(shù)據(jù)的數(shù)據(jù)庫來用;如果是線上應(yīng)用,肯定還是會選擇 mysql。畢竟 sql 比較成熟,而且各種常用場景的最佳實(shí)踐都有先例了。

我所在的阿里巴巴數(shù)據(jù)平臺,有各種各樣的大數(shù)據(jù)系統(tǒng)。有些做離線計算,一算就是幾個小時,算出來的結(jié)果被緩存起來,查詢時候就可以實(shí)時得到結(jié)果,只是數(shù)據(jù)一致性上,不可避免會有 delay;有些做實(shí)時運(yùn)算,可以在 1s 內(nèi)從幾千萬條數(shù)據(jù)中算出一個復(fù)雜條件的結(jié)果。但它們都提供了 sql 的接口,也就是說,無論底層他們是如何讓幾百臺機(jī)器 mapreduce,都讓你可以用已有的 sql 知識進(jìn)行查詢。所以還是選擇 sql 省事啊。

這里還有個很好玩的網(wǎng)站:http://www.mongodb-is-web-scale.com/

順便說說 mongodb 與 redis 的不同。mongodb 是用來存非臨時數(shù)據(jù)的,可以認(rèn)為是存在硬盤上,而 redis 的數(shù)據(jù)可以認(rèn)為都在內(nèi)存中,存儲臨時數(shù)據(jù),丟了也無所謂。對于稍微復(fù)雜的查詢,redis 支持的查詢方式太少太少了,幾乎可以認(rèn)為是 key-value 的。據(jù)說 instagram 的數(shù)據(jù)就全部存在 redis 中,用了好幾個幾十 G 內(nèi)存的 aws ec2 機(jī)器在存。redis 也是支持把數(shù)據(jù)寫入硬盤的,aof 貌似都過時了,好久沒關(guān)注了。

mongodb 與 hbase 的區(qū)別。如果說你已經(jīng)在考慮使用 hbase 了的話,應(yīng)該也不用我介紹它們的區(qū)別了吧..

主題所限,就不展開講了。這之間的選擇和權(quán)衡,說起來真的是個很大的話題。

我對這方面的話題很感興趣,如果要討論這方面話題的話,可以去 https://cnodejs.org/ 發(fā)個帖,詳細(xì)描述一下場景然后 at 我(@alsotang)。

mongodb 的官網(wǎng)中有一些特性介紹:

http://wiki.jikexueyuan.com/project/node-lessons/images/15-1.png" alt="" />

其中標(biāo)有箭頭的是基本概念,圓圈的是進(jìn)階概念,畫叉的不必了解。

安裝 mongodb

課程到這,一直忘記說 mongodb 的安裝了。

ubuntu: http://docs.mongodb.org/manual/tutorial/install-mongodb-on-ubuntu/

mac: $ brew install mongodb

裝好以后,在命令行 $ mongod,然后另外開個 shell 窗口,輸入 $ mongo 就能使用了。

mongoose

mongoose 是個 odm。odm 的概念對應(yīng) sql 中的 orm。也就是 ruby on rails 中的 activerecord 那一層。orm 全稱是 Object-Relational Mapping,對象關(guān)系映射;而 odm 是 Object-Document Mapping,對象文檔映射。

它的作用就是,在程序代碼中,定義一下數(shù)據(jù)庫中的數(shù)據(jù)格式,然后取數(shù)據(jù)時通過它們,可以把數(shù)據(jù)庫中的 document 映射成程序中的一個對象,這個對象有 .save .update 等一系列方法,和 .title .author 等一系列屬性。在調(diào)用這些方法時,odm 會根據(jù)你調(diào)用時所用的條件,自動轉(zhuǎn)換成相應(yīng)的 mongodb shell 語句幫你發(fā)送出去。自然地,在程序中鏈?zhǔn)秸{(diào)用一個個的方法要比手寫數(shù)據(jù)庫操作語句具有更大的靈活性和便利性。

mongoose 的官網(wǎng)給出了類似這樣一個示例,我改造了一下:

// 首先引入 mongoose 這個模塊
var mongoose = require('mongoose');
// 然后連接對應(yīng)的數(shù)據(jù)庫:mongodb://localhost/test
// 其中,前面那個 mongodb 是 protocol scheme 的名稱;localhost 是 mongod 所在的地址;
// 端口號省略則默認(rèn)連接 27017;test 是數(shù)據(jù)庫的名稱
// mongodb 中不需要建立數(shù)據(jù)庫,當(dāng)你需要連接的數(shù)據(jù)庫不存在時,會自動創(chuàng)建一個出來。
// 關(guān)于 mongodb 的安全性,mongodb 我印象中安全機(jī)制很殘廢,用戶名密碼那套都做得不好,更
// 別提細(xì)致的用戶權(quán)限控制了。不過不用擔(dān)心,mongodb 的默認(rèn)配置只接受來自本機(jī)的請求,內(nèi)網(wǎng)都連不上。
// 當(dāng)需要在內(nèi)網(wǎng)中為其他機(jī)器提供 mongodb 服務(wù)時,或許可以去看看 iptables 相關(guān)的東西。
mongoose.connect('mongodb://localhost/test');

// 上面說了,我推薦在同一個 collection 中使用固定的數(shù)據(jù)形式。
// 在這里,我們創(chuàng)建了一個名為 Cat 的 model,它在數(shù)據(jù)庫中的名字根據(jù)傳給 mongoose.model 的第一個參數(shù)決定,mongoose 會將名詞變?yōu)閺?fù)數(shù),在這里,collection 的名字會是 `cats`。
// 這個 model 的定義是,有一個 String 類型的 name,String 數(shù)組類型的 friends,Number 類型的 age。
// mongodb 中大多數(shù)的數(shù)據(jù)類型都可以用 js 的原生類型來表示。至于說 String 的長度是多少,Number 的精度是多少。String 的最大限度是 16MB,Number 的整型是 64-bit,浮點(diǎn)數(shù)的話,js 中 `0.1 + 0.2` 的結(jié)果都是亂來的。。就不指望什么了。。
// 這里可以看到各種示例:http://mongoosejs.com/docs/schematypes.html
var Cat = mongoose.model('Cat', {
  name: String,
  friends: [String],
  age: Number,
});

// new 一個新對象,名叫 kitty
// 接著為 kitty 的屬性們賦值
var kitty = new Cat({ name: 'Zildjian', friends: ['tom', 'jerry']});
kitty.age = 3;

// 調(diào)用 .save 方法后,mongoose 會去你的 mongodb 中的 test 數(shù)據(jù)庫里,存入一條記錄。
kitty.save(function (err) {
  if (err) // ...
  console.log('meow');
});

我們可以驗證一下

$ mongo
MongoDB shell version: 2.6.4
connecting to: test
> show dbs
> use test
> show collections
> db.cats.find()

會發(fā)現(xiàn)里面就有一條記錄了。

設(shè)計個簡單博客程序

如果要寫個博客程序練手。數(shù)據(jù)庫可以這樣設(shè)計

var Post = mongoose.model('Post', {
  title: String,
  content: String,
  author: String,
  create_at: Date,
});

評論就不要自己做了,接入多說:http://duoshuo.com/

編輯器就純文本好了,用 markdown 寫。

用戶系統(tǒng)也不做,硬編碼幾個管理員賬號在配置文件中,然后用 http basic auth:https://github.com/jshttp/basic-auth 來做驗證。

示例程序

Nodeclub 是使用 Node.js 和 MongoDB 開發(fā)的社區(qū)系統(tǒng)

https://github.com/cnodejs/nodeclub

完。