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(推薦)
官網地址 安裝
注意,可能會被牆,如果安裝失敗,可使用cnpm等映象進行安裝。npm install --save mongosse
- 使用
//載入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