白 潔,武佳麗,余啟旺,謝楚源,魯健華,董元和
(湖北師范大學(xué) 計算機與信息工程學(xué)院,湖北 黃石 435002)
隨著互聯(lián)網(wǎng)的不斷發(fā)展,數(shù)據(jù)庫技術(shù)也為適應(yīng)不同環(huán)境,不斷取得進展。當(dāng)傳統(tǒng)的關(guān)系型數(shù)據(jù)庫難以適應(yīng)動態(tài)網(wǎng)站的快速響應(yīng)速度時,便產(chǎn)生了新型非關(guān)系型數(shù)據(jù)庫,旨在解決大規(guī)模數(shù)據(jù)的多重數(shù)據(jù)種類帶來的諸多性能難題。本文通過對非關(guān)系型數(shù)據(jù)庫的代表MongoDB數(shù)據(jù)庫和SQLsever數(shù)據(jù)庫的性能對比,用實例對MongoDB數(shù)據(jù)庫關(guān)鍵技術(shù)進行分析,用node.js技術(shù)來對MongoDB數(shù)據(jù)庫進行封裝操作,來闡述ModgoDB等非關(guān)系數(shù)據(jù)庫在響應(yīng)式Web應(yīng)用的優(yōu)越性能。
數(shù)據(jù)庫是長期存儲在計算機內(nèi)的有組織的、可共享的數(shù)據(jù)集合,是按照數(shù)據(jù)的結(jié)構(gòu)來組織和管理數(shù)據(jù)的倉庫。內(nèi)存中運行的程序多多少少都會出現(xiàn)數(shù)據(jù)丟失的問題,因此需要將運行的數(shù)據(jù)持久化的保存到硬盤中,以確保數(shù)據(jù)的安全,數(shù)據(jù)庫則成為了數(shù)據(jù)持久化的最佳選擇。
數(shù)據(jù)庫主要分為關(guān)系型數(shù)據(jù)庫(SQL)和非關(guān)系型數(shù)據(jù)庫(NoSQL)兩大類。關(guān)系型數(shù)據(jù)庫主要采用基于行和列的二維表關(guān)系模型來組織和存儲數(shù)據(jù),其代表有MySQL、Microsoft SQL Server等。非關(guān)系型數(shù)據(jù)庫則主要是利用鍵值對方式來組織和存儲[1]數(shù)據(jù),不局限于固定的結(jié)構(gòu),其代表有MongoDB等。兩者比較如下:
1)存儲結(jié)構(gòu):以Microsoft SQL Server和MongoDB數(shù)據(jù)庫[2]為例,前者是從數(shù)據(jù)庫、表、記錄三個層次構(gòu)建數(shù)據(jù)庫,最終以表格形式存儲;后者則是從數(shù)據(jù)庫、集合、文檔對象三個層次進行構(gòu)建,用鍵值對實現(xiàn)存儲。
2)成本:NoSQL容易部署,基本都是開源軟件,不需要像使用Oracle那樣花費大量成本,相比SQL數(shù)據(jù)庫來說成本較低。
3)查詢速度:NoSQL數(shù)據(jù)庫將數(shù)據(jù)存儲在緩存之中,不需要經(jīng)過SQL層解析,而SQL存儲在硬盤當(dāng)中,因此查詢速度遠不及NoSQL數(shù)據(jù)庫。
4)可擴展性:SQL數(shù)據(jù)庫有類似join的多表查詢機制,導(dǎo)致擴展較為困難;而Nosql數(shù)據(jù)庫是基于鍵值對的存儲,數(shù)據(jù)之間耦合度低,獨立性較好,容易擴展。
5)持久性存儲:NoSQL數(shù)據(jù)庫不適合持久存儲,海量數(shù)據(jù)的持久存儲應(yīng)該選擇SQL數(shù)據(jù)庫。
MongoDB是一個基于分布式文件存儲的非關(guān)系型數(shù)據(jù)庫,它是為快速開發(fā)Web應(yīng)用而產(chǎn)生,可以對應(yīng)用進行插入、更新與查詢等實時數(shù)據(jù)處理操作,并具備實時數(shù)據(jù)存儲所需的復(fù)制及高度伸縮性。MongoDB具有面向文檔的、易操作、易部署、易存儲等特點。
MongoDB是非關(guān)系型數(shù)據(jù)庫中最像關(guān)系型數(shù)據(jù)庫的,其中文檔(Document)是 MongoDB 中數(shù)據(jù)的基本單位,類似于關(guān)系數(shù)據(jù)庫中的行數(shù)據(jù),有多個鍵和關(guān)聯(lián)的值有序結(jié)合而組成;集合(Collection)類似于關(guān)系數(shù)據(jù)庫中的表,可將文檔分類放在不同的集合中,結(jié)構(gòu)清晰,以便高效地進行查詢操作;數(shù)據(jù)庫(DataBase)則是由多個集合組成。
MongoDB數(shù)據(jù)庫的層次結(jié)構(gòu)如圖1所示,其基本的層次關(guān)系為:一套數(shù)據(jù)庫管理軟件可以包括多個數(shù)據(jù)庫,而每個數(shù)據(jù)庫下又可以包含多個集合,集合則為該數(shù)據(jù)庫中同一類的數(shù)據(jù),每個集合中又可以包含多個文檔,文檔則是具體一條條的數(shù)據(jù),而每條數(shù)據(jù)的屬性名稱又可以叫做字段。

圖1 MongoDB數(shù)據(jù)庫層次結(jié)構(gòu)
在本設(shè)計中,主要使用Mongoose,它是MongoDB的一個對象模型工具,是基于node-MongoDB-native開發(fā)的MongoDB的node.js驅(qū)動,可以在異步的環(huán)境下執(zhí)行,同時也是針對MongoDB操作的一個對象模型庫[2],封裝了MongoDB對文檔的一些增刪改查等常用方法,讓node.js操作[3]MongoDB數(shù)據(jù)庫變得更加容易。Mongoose提供了幾個新的對象:Schema,Model,Document,其中Schema定義且約束了數(shù)據(jù)庫中的文檔結(jié)構(gòu),Model相當(dāng)于collection集合。
本文實例項目是旅游類的電子導(dǎo)游項目[4],系統(tǒng)中數(shù)據(jù)庫模型包括管理員、新聞、消息、用戶等模型,其系統(tǒng)總體E-R圖如圖2所示。

圖2 系統(tǒng)總體E-R圖
下面主要以用戶登錄功能為例,介紹ModgoDB的基本操作與應(yīng)用過程。其對應(yīng)的數(shù)據(jù)庫設(shè)計的用戶集合如表1以及E-R圖如圖3所示。舉例說明用MongoDB的對象模型Mongoose實現(xiàn)數(shù)據(jù)庫的基本操作。

表1 用戶集合

圖3 用戶E-R圖
1)數(shù)據(jù)庫連接。首先用require語句來加載模塊,創(chuàng)建一個新的數(shù)據(jù)庫,然后用connect語句來對數(shù)據(jù)庫進行連接,語句中的url用來指定服務(wù)器的地址。
const mongoose = require('mongoose')
mongoose.set('useCreateIndex', true)
mongoose.connect('mongodb://localhost:27017/server', {
useNewUrlParser: true,
useUnifiedTopology: true
})
2)創(chuàng)建集合。在數(shù)據(jù)庫連接成功后,則要設(shè)定集合規(guī)則和創(chuàng)建集合,主要分為兩步:一是通過定義schema,來設(shè)定集合規(guī)定,描述該集合中有哪些字段以及字段類型,類似于表結(jié)構(gòu)中行的作用;二是應(yīng)用規(guī)則進行創(chuàng)建,且只有schema中定義的屬性才可被創(chuàng)建。其中主要是用到Mongoose中的schema以及model方法,model函數(shù)的構(gòu)造方法中第一個參數(shù)是集合名稱(首字母要大寫),而第二個參數(shù)就是集合規(guī)則,若短可以直接寫對象在其中。model方法返回當(dāng)前集合的構(gòu)造函數(shù),若想在集合中追加內(nèi)容,就需要先通過該構(gòu)造函數(shù)創(chuàng)建文檔[3]。
創(chuàng)建文檔也分為兩步:一是創(chuàng)建集合實例;二是調(diào)用實例對象下的save方法將數(shù)據(jù)保存到數(shù)據(jù)庫中。另外一種方法則是構(gòu)造create函數(shù)來對文檔進行創(chuàng)建。
const schema = new mongoose.Schema({
username:{ type: String },
password:{
type: String,//select:false密碼默認不能被查詢到
select:false,//對密碼進行散列加密處理
set(val){
return require('bcrypt').hashSync(val, 10)
} },
avatar:{ type: String }
},
{ timestamps: true})
module.exports = mongoose.model('AdminUser',schema)
3)基本操作的實現(xiàn)。同關(guān)系型數(shù)據(jù)庫一樣,MongoDB數(shù)據(jù)庫也可以實現(xiàn)增刪改查的基本操作。本項目通過如圖4所示node.js操作數(shù)據(jù)庫[5],即數(shù)據(jù)庫提供的API去操作數(shù)據(jù)庫,借助Mongoose來對數(shù)據(jù)庫中的model進行調(diào)用,實現(xiàn)數(shù)據(jù)的增刪改查操作。

圖4 node.js操作數(shù)據(jù)庫
下面以數(shù)據(jù)庫的增加信息為例,演示代碼實現(xiàn)數(shù)據(jù)添加的過程,其余集合中的操作與此類似。
一般來說,MongoDB 使用 insert()或 save()方法向集合中插入數(shù)據(jù),它們二者的區(qū)別在于:前者如果插入的數(shù)據(jù)主鍵存在,則會顯示主鍵存在,對當(dāng)前數(shù)據(jù)不進行保存;后者則是主鍵如果存在,就可以對數(shù)據(jù)進行更新,若不存在就插入。插入過程中需要創(chuàng)建一個新的users文件,調(diào)用save函數(shù)來存儲新增用戶的信息。其代碼實現(xiàn)如下:
const User = require('./users');
const user = new User({
username: '李思思',
password: '123',
age: 22,
tel: '13248099856',
sex: '女',
city: '山西'
})
user.save((err)=> {
if(err)throw err;
console.log('用戶插入成功')
})
在本項目的設(shè)計過程中,充分體現(xiàn)了MongoDB數(shù)據(jù)庫低成本,易操作、方便寫入的優(yōu)良性能。同時也暴露出其諸多缺點,一方面,若旅游數(shù)據(jù)量過大,對其查詢速度有明顯的影響;另一方面,在刪除記錄后不主動釋放空間,對空間的占用較大。因此在設(shè)計實踐過程中還需要對數(shù)據(jù)庫進行合理的優(yōu)化設(shè)計。