1. 程式人生 > >Node.js(day5)

Node.js(day5)

一、NOSQL

NOSQL是Not Only SQL的簡稱,與關係型資料庫對應,一般稱為非關係型資料庫。關係型資料庫遵循ACID規則,而NOSQL儲存資料時不需要嚴格遵循固定的模式,因此在大資料的今天NOSQL為儲存大資料提供了有效的途徑。

1.關係型資料庫的ACID原則

事務在英文中是transaction,和現實世界中的交易很類似,它有如下四個特性:

  • 1、A (Atomicity) 原子性
    原子性很容易理解,也就是說事務裡的所有操作要麼全部做完,要麼都不做,事務成功的條件是事務裡的所有操作都成功,只要有一個操作失敗,整個事務就失敗,需要回滾。
    比如銀行轉賬,從A賬戶轉100元至B賬戶,分為兩個步驟:1)從A賬戶取100元;2)存入100元至B賬戶。這兩步要麼一起完成,要麼一起不完成,如果只完成第一步,第二步失敗,錢會莫名其妙少了100元。
  • 2、C (Consistency) 一致性
    一致性也比較容易理解,也就是說資料庫要一直處於一致的狀態,事務的執行不會改變資料庫原本的一致性約束。
    例如現有完整性約束a+b=10,如果一個事務改變了a,那麼必須得改變b,使得事務結束後依然滿足a+b=10,否則事務失敗。
  • 3、I (Isolation) 獨立性
    所謂的獨立性是指併發的事務之間不會互相影響,如果一個事務要訪問的資料正在被另外一個事務修改,只要另外一個事務未提交,它所訪問的資料就不受未提交事務的影響。
    比如現在有個交易是從A賬戶轉100元至B賬戶,在這個交易還未完成的情況下,如果此時B查詢自己的賬戶,是看不到新增加的100元的。
  • 4、D (Durability) 永續性
    永續性是指一旦事務提交後,它所做的修改將會永久的儲存在資料庫上,即使出現宕機也不會丟失。

2.NOSQL中的ACP理論

在電腦科學中, CAP定理(CAP theorem), 又被稱作 布魯爾定理(Brewer's theorem), 它指出對於一個分散式計算系統來說,不可能同時滿足以下三點:

  • 一致性(Consistency) (所有節點在同一時間具有相同的資料)
  • 可用性(Availability) (保證每個請求不管成功或者失敗都有響應)
  • 分隔容忍(Partition tolerance) (系統中任意資訊的丟失或失敗不會影響系統的繼續運作)

CAP理論的核心是:一個分散式系統不可能同時很好的滿足一致性,可用性和分割槽容錯性這三個需求,最多隻能同時較好的滿足兩個。
因此,根據 CAP 原理將 NoSQL 資料庫分成了滿足 CA 原則、滿足 CP 原則和滿足 AP 原則三 大類:

  • CA - 單點叢集,滿足一致性,可用性的系統,通常在可擴充套件性上不太強大。
  • CP - 滿足一致性,分割槽容忍性的系統,通常效能不是特別高。
  • AP - 滿足可用性,分割槽容忍性的系統,通常可能對一致性要求低一些。

二、MongoDB

MongoDB 是由C++語言編寫的,是一個基於分散式檔案儲存的開源資料庫系統。
在高負載的情況下,新增更多的節點,可以保證伺服器效能。
MongoDB 旨在為WEB應用提供可擴充套件的高效能資料儲存解決方案。
MongoDB 將資料儲存為一個文件,資料結構由鍵值(key=>value)對組成。MongoDB 文件類似於 JSON 物件。欄位值可以包含其他文件,陣列及文件陣列。
更多說明請參考:菜鳥教程-MongoDB

1.安裝

  • 下載安裝包
    官網下載安裝包進行安裝:https://www.mongodb.com/
  • 配置環境變數
    安裝成功之後需要配置環境變數:
    比如安裝目錄為:C:\Program Files\MongoDB
    那麼就需要配置環境變數到bin目錄下,即:C:\Program Files\MongoDB\Server\4.0\bin
  • 測試
mongod --version

檢視是否有相關版本資訊。

2.在命令列中簡單使用

  • 啟動資料庫
mongod

注意:資料庫需要儲存資料的位置,預設是安裝目錄所在碟符下的/data/db目錄(如果沒有,需要手動建立)。如果需要修改預設儲存目錄使用mondod --datapath==路徑.

  • 關閉資料庫
    直接關閉命令列視窗或Ctrl + C即可。
  • 連線資料庫
mongo
  • 斷開連線
exit
  • 顯示所有資料庫
show dbs
  • 切換資料庫
use 資料庫名
  • 插入一條資料
db.模型名.insertOne(資料json物件)

這裡的模型名相當於關係型資料庫的表名。

  • 檢視資料
db.模型名.find()

這裡就不再贅述更多命令,我們很少使用命令列來操作資料庫。

3.在Node.js中使用

  • 安裝官方驅動包(不推薦)
    mongodb提供了node.js使用的驅動包,但方法比較原生,且有封裝較完善的第三方包mongoose,所以這裡推薦直接使用第三方包即可。
    可參考官網地址
  • 安裝第三方包mongoose(推薦)
    官網地址
  • 安裝

    npm install --save mongosse
    注意,可能會被牆,如果安裝失敗,可使用cnpm等映象進行安裝。
  • 使用
//載入mongoose模組
var mongoose = require('mongoose');
//連線mongo本地資料庫:test
mongoose.connect('mongodb://localhost/test');

//設計資料庫模組 ==> 相當於設計資料庫表
var Cat = mongoose.model('Cat', { name: String });

//例項化Cat
var kitty = new Cat({ name: 'Zildjian' });

//持久化kitty
kitty.save((error) => {
    if(error){
        console.log(error);
    }else{
        console.log('meow');
    }
});
  • 增刪改查
    更多請參考官方文件:
    https://mongoosejs.com/docs/guide.html
    增:save()
    查:findById() | find() | findOne()
    刪:findByIdAndRemove() | remove() | findOneAndRemove()
    改:findByIdAndUpdate()| update() | findOneAndUpdate()

三、案例:使用MongoDB修改之前的案例

略,即使用mongodb來代替之前的db.json和fs模組。

四、promise物件

1.回撥地獄

回撥函式可以為我們返回非同步操作的資料,但回撥函式還是無法保證非同步函式的執行順序。比如我們有三個檔案:a.txt、b.txt、c.txt。我們使用fs.readFile()按順序來讀取a.txt、b.txt、c.txt。
那我們就需要使用巢狀的寫法才能實現自定義順序非同步操作:

var fs = require('fs');

fs.readFile('./a.txt','utf8',(err,data) => {
    if(err)
        return console.log(err);
    console.log(data);
    fs.readFile('./b.txt','utf8',(err2,data2) => {
        if(err)
            return console.log(err2);
        console.log(data2);
        fs.readFile('./c.txt','utf8',(err3,data3) => {
            if(err)
                return console.log(err3);
            console.log(data3);
        });
    });
});

對於這種層層巢狀的回撥函式,我們一般稱為“回撥地獄”,首先我們發現這種回撥函式不美觀易錯不好維護。所以es6出現了Promise物件,專門用來處理這種回撥地獄問題。

2.Promise物件

Promise 物件用於表示一個非同步操作的最終狀態(完成或失敗),以及其返回的值。
我們可以看一下示意圖:

大致意思就是:

  • Promise物件以一個非同步操作為引數,儲存了其執行狀態。
  • 非同步操作的失敗與否決定了Promise的狀態。
  • 而每一個狀態執行之後的返回值為Promise物件,這樣就實現了順序鏈式執行回撥函式。
    具體看如下例項:
var fs = require('fs');

//將非同步操作封裝為Promise物件
var q1 = new Promise((resoleve,reject) => {
    fs.readFile('./a.txt','utf8',(err,data) => {
        if(err){
            reject(err);
        }else{
            resoleve(data);
        }
    });
});
var q2 = new Promise((resoleve,reject) => {
    fs.readFile('./b.txt','utf8',(err,data) => {
        if(err){
            reject(err);
        }else{
            resoleve(data);
        }
    });
});
var q3 = new Promise((resoleve,reject) => {
    fs.readFile('./c.txt','utf8',(err,data) => {
        if(err){
            reject(err);
        }else{
            resoleve(data);
        }
    });
});

//利用Promise的鏈式特性決定非同步操作的順序:使用then方法執行非同步操作
q1.then((data) => {
    console.log(data);
    return q2;//這句話的意思是返回的物件為q2,那麼q1執行之後就是q2執行
},(err) => {
    console.log(err);
}).then((data) => {
    console.log(data);
    return q3;
},(err) => {
    console.log(err);
}).then((data) => {
    console.log(data);
},(err) => {
    console.log(err);
});
  • then方法有兩個回撥函式引數,當Promise的狀態為resolved是執行第一個回撥函式,狀態為rejected時執行第二個引數。
  • 上面說then()執行非同步操作有點問題,其實應該是new Promise()後就執行了,因為需要先判斷非同步操作狀態。
  • 使用return語句和then()方法就可以鏈式操作回撥函式,還能決定順序。(其實並沒有改變執行順序,但可以控制資料的輸出順序。)

上面Promise建立的方法可以進行封裝讓程式碼更簡潔:

var fs = require('fs');

//將非同步操作封裝為Promise物件
function readFilePromise(filepath){
    return new Promise((resoleve,reject) => {
        fs.readFile(filepath,'utf8',(err,data) => {
            if(err){
                reject(err);
            }else{
                resoleve(data);
            }
        });
    });
}
var q1 = readFilePromise('./a.txt'),
    q2 = readFilePromise('./b.txt'),
    q3 = readFilePromise('./c.txt');

//利用Promise的鏈式特性決定非同步操作的順序:使用then方法"執行"非同步操作
q1.then((data) => {
    console.log(data);
    return q2;//這句話的意思是返回的物件為q2,那麼q1執行之後就是q2執行
},(err) => {
    console.log(err);
}).then((data) => {
    console.log(data);
    return q3;
},(err) => {
    console.log(err);
}).then((data) => {
    console.log(data);
},(err) => {
    console.log(err);
});

其實Jquery中的ajax的相關方法已經實現了Promise的相關方法,這裡應用場景不再舉例,更多可參考:MDN-Promise