管理系統(tǒng)實(shí)戰(zhàn):從環(huán)境搭建到聚合查詢(xún))
簡(jiǎn)介面向MongoDB與Java整合課設(shè)的初學(xué)者這份圖書(shū)管理系統(tǒng)覆蓋從驅(qū)動(dòng)加載、數(shù)據(jù)庫(kù)連接到增刪改查的完整流程業(yè)務(wù)模型簡(jiǎn)潔適合作為課程設(shè)計(jì)或練手項(xiàng)目。壓縮包共38個(gè)文件其中12個(gè)java源文件負(fù)責(zé)業(yè)務(wù)邏輯22個(gè)class為編譯輸出配套Eclipse工程配置與MongoDB驅(qū)動(dòng)jar包整體僅1.3MB便于快速導(dǎo)入IDE閱讀和調(diào)試。目前已有4522人學(xué)習(xí)下載可見(jiàn)其在同類(lèi)課設(shè)中的參考熱度。系統(tǒng)運(yùn)行時(shí)自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)并插入一條初始數(shù)據(jù)無(wú)需手動(dòng)初始化不過(guò)查詢(xún)結(jié)果目前只在控制臺(tái)顯示尚未同步到界面這個(gè)待完善點(diǎn)恰好為讀者提供了二次開(kāi)發(fā)空間。通過(guò)修復(fù)該問(wèn)題可深入理解MongoDB查詢(xún)結(jié)果向UI層回顯的過(guò)程尤其適合需要同時(shí)掌握數(shù)據(jù)庫(kù)操作與界面聯(lián)動(dòng)的學(xué)習(xí)者。 前陣子整理了一份基于 MongoDB 的圖書(shū)管理系統(tǒng)源碼打包成 .rar 發(fā)出去之后陸續(xù)收到不少同學(xué)問(wèn)環(huán)境怎么搭、數(shù)據(jù)庫(kù)怎么連、代碼跑不起來(lái)怎么辦。這份小系統(tǒng)是我平時(shí)給學(xué)生做課程設(shè)計(jì)和畢業(yè)設(shè)計(jì)時(shí)經(jīng)常拿出來(lái)的示范項(xiàng)目壓縮包里有完整的源碼、數(shù)據(jù)庫(kù)腳本和部署說(shuō)明但很多人在 MongoDB 這一層就卡住了。所以干脆把整個(gè)項(xiàng)目的設(shè)計(jì)思路、核心實(shí)現(xiàn)和踩坑記錄整理出來(lái)給那些剛接觸 MongoDB、想用一個(gè)真實(shí)項(xiàng)目練手的人一份能直接照著抄的參考。這個(gè)系統(tǒng)解決的核心問(wèn)題很樸素用文檔型數(shù)據(jù)庫(kù) MongoDB 來(lái)實(shí)現(xiàn)圖書(shū)、讀者、借閱、歸還、統(tǒng)計(jì)這些典型的圖書(shū)管理業(yè)務(wù)讓你跳出傳統(tǒng) MySQL 的建表思維真實(shí)感受 NoSQL 在業(yè)務(wù)里的建模方式和查詢(xún)習(xí)慣。適合正在做課設(shè)、準(zhǔn)備畢設(shè)或者想快速上手 MongoDB 的開(kāi)發(fā)者。哪怕你之前從沒(méi)裝過(guò) MongoDB只要照著下面的步驟走也能把整套系統(tǒng)跑起來(lái)。1. 為什么用 MongoDB 來(lái)做圖書(shū)管理系統(tǒng)1.1 傳統(tǒng)關(guān)系型方案的痛點(diǎn)很多教材里的圖書(shū)管理系統(tǒng)都是基于 MySQL 的book 表、reader 表、borrow 表外鍵關(guān)聯(lián)、多表 JOIN、事務(wù)控制一套下來(lái)確實(shí)很“標(biāo)準(zhǔn)”。但實(shí)際寫(xiě)起來(lái)你會(huì)發(fā)現(xiàn)幾個(gè)讓人頭疼的問(wèn)題圖書(shū)信息字段不好擴(kuò)展比如紙質(zhì)書(shū)有館藏位置和復(fù)本數(shù)電子書(shū)卻需要文件大小和下載鏈接硬塞到同一張表里要么大量字段為空要么就得不停 ALTER TABLE借閱記錄一旦多了連表查詢(xún)的 SQL 寫(xiě)起來(lái)越來(lái)越繞稍微復(fù)雜一點(diǎn)的統(tǒng)計(jì)需求就要嵌套子查詢(xún)。這些痛點(diǎn)本身不是 MySQL 的錯(cuò)而是關(guān)系型模型在“字段多態(tài)、結(jié)構(gòu)多變”的場(chǎng)景下不夠靈活。圖書(shū)管理系統(tǒng)的數(shù)據(jù)雖然不算海量但它天然存在“同類(lèi)型不同結(jié)構(gòu)”的數(shù)據(jù)這恰恰是 MongoDB 這類(lèi)文檔數(shù)據(jù)庫(kù)最擅長(zhǎng)處理的場(chǎng)景。1.2 文檔模型帶來(lái)的靈活性MongoDB 存儲(chǔ)的是 BSON 文檔說(shuō)白了就是長(zhǎng)得像 JSON 的數(shù)據(jù)結(jié)構(gòu)。每本圖書(shū)不需要遵循完全相同的字段 schema你可以讓電子書(shū)有storage和format讓紙質(zhì)書(shū)有l(wèi)ocation和copies它們可以存放在同一個(gè)集合里而不報(bào)錯(cuò)。這對(duì)于圖書(shū)管理來(lái)說(shuō)非常舒服新加一個(gè)“是否可預(yù)約”字段不需要執(zhí)行任何遷移語(yǔ)句直接寫(xiě)入文檔就行。另一個(gè)優(yōu)勢(shì)是查詢(xún)時(shí)的直觀性。在 MongoDB 里查“價(jià)格大于 50 的計(jì)算機(jī)類(lèi)圖書(shū)”寫(xiě)出來(lái)就是db.books.find({ category: 計(jì)算機(jī), price: { $gt: 50 } })完全符合直覺(jué)不需要拼接字符串 SQL也不需要考慮表連接順序。再加上 MongoDB 的聚合管道Aggregation Pipeline做借閱排行榜、分類(lèi)統(tǒng)計(jì)這類(lèi)需求一段聚合就能搞定這在傳統(tǒng) SQL 里要用好幾個(gè) JOIN 加 GROUP BY 才能完成。1.3 這套系統(tǒng)適合什么人參考我個(gè)人的看法是MongoDB 圖書(shū)管理系統(tǒng)最適合三類(lèi)人第一類(lèi)是課程設(shè)計(jì)或畢業(yè)設(shè)計(jì)選了“圖書(shū)管理系統(tǒng)”這個(gè)經(jīng)典題目但又想跟別人做出點(diǎn)差異化不想千篇一律用 SSM MySQL 的同學(xué)第二類(lèi)是已經(jīng)會(huì) MySQL想拓寬技術(shù)棧、了解 NoSQL 建模思維的開(kāi)發(fā)者第三類(lèi)是公司內(nèi)部要做輕量級(jí)原型驗(yàn)證想快速搭一套可演示的圖書(shū)管理 demo 的團(tuán)隊(duì)。這個(gè)項(xiàng)目既能覆蓋 MongoDB 的增刪改查、索引、聚合、關(guān)聯(lián)查詢(xún)又不會(huì)復(fù)雜到讓你失去耐心是一個(gè)恰到好處的練手規(guī)模。2. 系統(tǒng)整體設(shè)計(jì)與數(shù)據(jù)模型2.1 功能模塊的拆解思路圖書(shū)管理系統(tǒng)聽(tīng)起來(lái)簡(jiǎn)單但真正落地還是要把功能邊界理清楚。我在這份項(xiàng)目里把功能分成幾個(gè)核心模塊圖書(shū)管理新增、編輯、刪除、按分類(lèi)和關(guān)鍵詞檢索、讀者管理讀者信息的錄入與維護(hù)、借閱歸還借書(shū)、還書(shū)、逾期判斷和借閱歷史、統(tǒng)計(jì)報(bào)表分類(lèi)圖書(shū)數(shù)量、熱門(mén)圖書(shū)排行、讀者借閱排行。每個(gè)模塊對(duì)應(yīng)一組 API 和一組頁(yè)面操作數(shù)據(jù)層全部落在 MongoDB 的集合上。模塊劃分的關(guān)鍵原則是“每個(gè)集合只承擔(dān)一種核心職責(zé)”。我見(jiàn)過(guò)一些同學(xué)把所有信息都塞進(jìn)一個(gè)集合最后查詢(xún)條件復(fù)雜到自己都看不懂那還不如用回關(guān)系型數(shù)據(jù)庫(kù)。MongoDB 雖然靈活但不代表可以沒(méi)有設(shè)計(jì)合理的集合拆分才是用好它的前提。2.2 核心集合結(jié)構(gòu)與字段設(shè)計(jì)整個(gè)系統(tǒng)的核心數(shù)據(jù)落在三個(gè)集合上books圖書(shū)、readers讀者、loans借閱記錄另外還有一個(gè)categories分類(lèi)集合用來(lái)做樹(shù)形分類(lèi)管理。books集合的一條典型文檔大致長(zhǎng)這樣{ _id: ObjectId(5f7c...), isbn: 9787115428028, title: MongoDB 權(quán)威指南, author: [Kristina Chodorow, Michael Dirolf], category: 數(shù)據(jù)庫(kù), price: 89.0, publisher: 人民郵電出版社, publishYear: 2021, type: paper, location: A-3-12, copies: 5, availableCopies: 3, tags: [MongoDB, NoSQL, 數(shù)據(jù)庫(kù)], createdAt: ISODate(2024-03-15T08:00:00Z) }這里我特意把a(bǔ)uthor設(shè)計(jì)成數(shù)組因?yàn)橐槐緯?shū)可以有多個(gè)作者在文檔模型里直接用數(shù)組存儲(chǔ)比關(guān)系型里建一張作者關(guān)聯(lián)表省事得多。tags數(shù)組則方便后續(xù)做標(biāo)簽檢索。type字段區(qū)分紙質(zhì)書(shū)和電子書(shū)紙質(zhì)書(shū)有l(wèi)ocation館藏位置和copies復(fù)本數(shù)電子書(shū)則可以加fileSize和downloadUrl不同結(jié)構(gòu)的文檔共存于同一個(gè)集合這就是文檔模型的天然優(yōu)勢(shì)。readers集合相對(duì)簡(jiǎn)單主要存儲(chǔ)讀者編號(hào)、姓名、聯(lián)系方式、借閱狀態(tài)loans集合記錄每次借閱行為包括readerId、bookId、borrowDate、dueDate、returnDate和status。這里需要特別說(shuō)明一個(gè)設(shè)計(jì)選擇借閱關(guān)系我用的是傳統(tǒng)的“引用”而不是“嵌入式”。2.3 引用與嵌入的取舍剛開(kāi)始用 MongoDB 的人很容易踩一個(gè)坑覺(jué)得文檔可以嵌套就把借閱記錄直接嵌到讀者文檔里。這在借閱量小的時(shí)候沒(méi)問(wèn)題但一旦某個(gè)讀者借過(guò)幾百本書(shū)讀者文檔會(huì)越來(lái)越大每次更新都得把整個(gè)文檔讀出來(lái)重寫(xiě)性能會(huì)很難看。我的做法是loans里只保存讀者的_id和圖書(shū)的_id需要展示詳情時(shí)再用$lookup做關(guān)聯(lián)這樣既保持了文檔的獨(dú)立性也避免了單文檔無(wú)限膨脹。索引設(shè)計(jì)方面我建了幾個(gè)關(guān)鍵索引isbn建唯一索引保證圖書(shū)編號(hào)不重復(fù)title建普通索引支撐書(shū)名模糊查詢(xún)loans集合里readerId、bookId、status各建一個(gè)索引借閱查詢(xún)和統(tǒng)計(jì)基本都能走索引。這些索引看著不起眼但數(shù)據(jù)量上來(lái)之后查詢(xún)效率差距非常明顯屬于事前收益極高的投入。3. MongoDB 環(huán)境搭建與可視化工具3.1 Windows 下 MongoDB 安裝與啟動(dòng)很多同學(xué)收到 .rar 之后第一關(guān)就卡在 MongoDB 裝不上。這里我強(qiáng)烈推薦用免安裝版zip 包而不是安裝版原因很簡(jiǎn)單安裝版經(jīng)常遇到權(quán)限問(wèn)題、服務(wù)注冊(cè)失敗而且卸載麻煩免安裝版解壓就能用環(huán)境變量配好之后就跟綠色軟件一樣想換版本隨時(shí)換。具體步驟如下從 MongoDB 官網(wǎng)下載 Community Server 的 zip 包選擇對(duì)應(yīng) Windows 版本的壓縮包下載后解壓到比如D:\mongodb。在D:\mongodb下新建兩個(gè)目錄data\db和data\log一個(gè)存數(shù)據(jù)文件一個(gè)存日志文件。啟動(dòng)失敗的案例里八成是忘了建data\db目錄。在D:\mongodb\bin目錄下打開(kāi)命令行Shift 右鍵選擇“在此處打開(kāi) PowerShell 窗口”執(zhí)行.\mongod.exe --dbpath D:\mongodb\data\db看到waiting for connections on port 27017就說(shuō)明啟動(dòng)成功了。想驗(yàn)證的話(huà)另開(kāi)一個(gè)命令行執(zhí)行.\mongo.exe能進(jìn)入 mongo shell 就說(shuō)明一切正常。另外建議把D:\mongodb\bin加到系統(tǒng) PATH 環(huán)境變量里這樣每次啟動(dòng)不用切目錄直接輸mongod --dbpath D:\mongodb\data\db就行。如果不想每次手動(dòng)開(kāi)窗口可以注冊(cè)成 Windows 服務(wù)但那需要管理員權(quán)限新手階段手動(dòng)啟動(dòng)反而看得更清楚。3.2 DBeaver 連接 MongoDB數(shù)據(jù)可視化軟件方面我習(xí)慣用 DBeaver免費(fèi)、跨平臺(tái)、支持幾乎所有主流數(shù)據(jù)庫(kù)。用它連接 MongoDB 的步驟不復(fù)雜但有幾個(gè)細(xì)節(jié)容易踩坑。打開(kāi) DBeaver 后新建連接選擇 MongoDB它會(huì)提示下載驅(qū)動(dòng)點(diǎn)同意即可。連接參數(shù)里主機(jī)填localhost端口填27017數(shù)據(jù)庫(kù)名可以先填admin這個(gè)是 MongoDB 默認(rèn)的管理庫(kù)連接后可以再切換到你實(shí)際的業(yè)務(wù)庫(kù)。認(rèn)證方式如果 MongoDB 沒(méi)開(kāi)啟 auth選 None 就行如果你在 mongod.conf 里開(kāi)啟了安全認(rèn)證就需要填 username 和 password并且要注意認(rèn)證庫(kù)Authentication Database一般填admin而不是業(yè)務(wù)庫(kù)本身。連接成功后左側(cè)導(dǎo)航欄就能看到數(shù)據(jù)庫(kù)列表和集合列表雙擊集合可以瀏覽文檔右鍵可以新建文檔、編輯 JSON寫(xiě)查詢(xún)條件測(cè)試聚合管道也非常方便。對(duì)新手來(lái)說(shuō)用可視化工具先看一遍數(shù)據(jù)結(jié)構(gòu)和查詢(xún)結(jié)果再回頭去理解代碼里的 PyMongo/Mongoose 語(yǔ)句學(xué)習(xí)效率會(huì)高很多。3.3 其他可視化工具怎么選除了 DBeaver市面上還有幾款常見(jiàn)的 MongoDB 客戶(hù)端。MongoDB Compass 是官方出的界面好看內(nèi)置的 Aggregation 管道可視化編輯器對(duì)學(xué)習(xí)聚合操作特別有幫助缺點(diǎn)是啟動(dòng)偏慢、內(nèi)存占用高Studio 3T 功能最強(qiáng)支持 SQL 轉(zhuǎn) MongoDB 查詢(xún)但完整版收費(fèi)Robo 3T現(xiàn)已改名 Robo 3T輕量免費(fèi)適合快速看數(shù)據(jù)。我個(gè)人的建議是日常開(kāi)發(fā)和教學(xué)用 DBeaver 足夠想深入學(xué)習(xí)聚合管道可以用 Compass生產(chǎn)環(huán)境調(diào)試性能問(wèn)題再用 Studio 3T 這類(lèi)專(zhuān)業(yè)工具。工具不用貪多順手最重要。4. 核心功能實(shí)現(xiàn)與實(shí)操過(guò)程4.1 數(shù)據(jù)庫(kù)連接與初始化整套系統(tǒng)我用 Python PyMongo 做的后端演示前端是簡(jiǎn)單的 HTML JavaScript你完全可以用 Node.js、Java Spring 或者 Go 重寫(xiě)同一套邏輯MongoDB 的操作思路是通用的。先看數(shù)據(jù)庫(kù)連接部分from pymongo import MongoClient from pymongo.errors import ConnectionFailure try: client MongoClient(mongodb://localhost:27017/, serverSelectionTimeoutMS3000) db client[library] # 隨便執(zhí)行一個(gè)命令強(qiáng)制檢查連接是否正常 db.command(ping) print(MongoDB 連接成功) except ConnectionFailure: print(MongoDB 連接失敗請(qǐng)檢查服務(wù)是否啟動(dòng))連接串mongodb://localhost:27017/是 MongoDB 默認(rèn)的本地地址27017 是默認(rèn)端口。serverSelectionTimeoutMS3000這個(gè)參數(shù)很有用它把連接超時(shí)設(shè)成 3 秒否則連不上時(shí)默認(rèn)要等 30 秒才報(bào)錯(cuò)排查問(wèn)題時(shí)會(huì)非常痛苦。初始化數(shù)據(jù)模型時(shí)我會(huì)先創(chuàng)建唯一索引再插入一些測(cè)試數(shù)據(jù)。這樣可以保證后面重復(fù)執(zhí)行初始化腳本不會(huì)生成重復(fù)的 isbn 記錄。4.2 圖書(shū)與讀者的增刪改查圖書(shū)的增刪改查是系統(tǒng)的基礎(chǔ)能力。以新增圖書(shū)為例def add_book(book_data): # 判斷 isbn 是否已存在 existing db.books.find_one({isbn: book_data[isbn]}) if existing: raise Exception(該 ISBN 已存在) book_data[createdAt] datetime.now() result db.books.insert_one(book_data) return result.inserted_id這段代碼的核心邏輯是先查重再插入雖然isbn已經(jīng)建了唯一索引但應(yīng)用層做一次友好提示能避免直接拋出底層重復(fù)鍵異常。修改圖書(shū)信息用update_one注意要用條件過(guò)濾鎖定到具體文檔def update_book(book_id, update_data): result db.books.update_one( {_id: ObjectId(book_id)}, {$set: update_data} ) return result.modified_count這里有個(gè)很關(guān)鍵的習(xí)慣字段更新一定要用$set而不是把整個(gè)文檔讀出來(lái)改完再整篇覆蓋。$set只更新指定字段讀改寫(xiě)整篇覆蓋在并發(fā)場(chǎng)景下極容易出現(xiàn)數(shù)據(jù)丟失。刪除圖書(shū)邏輯差不多但要注意如果這本書(shū)還有未歸還的借閱記錄直接刪會(huì)導(dǎo)致 loan 記錄掛空所以刪除前要先檢查loans集合里是否存在status為borrowed的記錄。4.3 借閱與歸還流程的設(shè)計(jì)借閱流程是整個(gè)系統(tǒng)里最值得細(xì)看的業(yè)務(wù)模塊。借書(shū)的操作不是簡(jiǎn)單往loans插一條記錄就完事它涉及兩個(gè)集合的聯(lián)動(dòng)def borrow_book(reader_id, book_id): # 檢查讀者是否存在 reader db.readers.find_one({_id: ObjectId(reader_id)}) if not reader: raise Exception(讀者不存在) # 檢查圖書(shū)是否可借 book db.books.find_one({_id: ObjectId(book_id)}) if not book or book.get(availableCopies, 0) 0: raise Exception(圖書(shū)不存在或已借完) # 創(chuàng)建借閱記錄 loan { readerId: ObjectId(reader_id), bookId: ObjectId(book_id), borrowDate: datetime.now(), dueDate: datetime.now() timedelta(days30), returnDate: None, status: borrowed } db.loans.insert_one(loan) # 扣減可借數(shù)量 db.books.update_one( {_id: ObjectId(book_id), availableCopies: {$gt: 0}}, {$inc: {availableCopies: -1}} )這段代碼里有幾個(gè)設(shè)計(jì)點(diǎn)值得展開(kāi)。dueDate默認(rèn)設(shè)為借書(shū)日期加 30 天這是逾期判斷的基礎(chǔ)用$inc做可借數(shù)量的原子扣減配合過(guò)濾條件里的availableCopies: { $gt: 0 }可以避免兩個(gè)人同時(shí)借最后一本書(shū)時(shí)超借的問(wèn)題。歸還流程是反向操作更新loans記錄狀態(tài)為returned同時(shí)把a(bǔ)vailableCopies加回去并計(jì)算是否逾期逾期可以打上標(biāo)記方便后續(xù)做罰款功能。4.4 統(tǒng)計(jì)報(bào)表與聚合管道圖書(shū)管理系統(tǒng)最出彩的地方在統(tǒng)計(jì)功能。使用 MongoDB 聚合管道一段代碼就能完成分類(lèi)統(tǒng)計(jì)pipeline [ {$group: {_id: $category, total: {$sum: 1}}}, {$sort: {total: -1}} ] result db.books.aggregate(pipeline)如果要做“借閱量最高的前 10 本圖書(shū)”需要把loans和books關(guān)聯(lián)起來(lái)pipeline [ {$match: {status: returned}}, {$group: {_id: $bookId, count: {$sum: 1}}}, {$sort: {count: -1}}, {$limit: 10}, {$lookup: { from: books, localField: _id, foreignField: _id, as: bookInfo }}, {$unwind: $bookInfo}, {$project: {title: $bookInfo.title, count: 1}} ]這個(gè)管道的思路是先過(guò)濾已歸還記錄再按bookId分組統(tǒng)計(jì)排序取前 10最后用$lookup關(guān)聯(lián)圖書(shū)集合拿到書(shū)名。熟悉 SQL 的同學(xué)可以在腦子里把它對(duì)照成WHERE、GROUP BY、ORDER BY、LIMIT和JOIN但 MongoDB 的管道串聯(lián)方式更線(xiàn)性每一步的輸出就是下一步的輸入調(diào)試起來(lái)很直觀。整個(gè)系統(tǒng)實(shí)現(xiàn)下來(lái)我的感受是 MongoDB 特別適合這種“寫(xiě)多讀多、結(jié)構(gòu)有變化、統(tǒng)計(jì)靈活”的管理系統(tǒng)業(yè)務(wù)邏輯不復(fù)雜時(shí)開(kāi)發(fā)效率非常高。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 MongoDB 安裝與啟動(dòng)失敗啟動(dòng)失敗是最常見(jiàn)的問(wèn)題而且很多同學(xué)第一次看到錯(cuò)誤提示就很慌。我把碰到的典型情況整理成了一張速查表現(xiàn)象可能原因解決辦法dbpath (D:\mongodb\data\db) does not exist沒(méi)有創(chuàng)建數(shù)據(jù)目錄執(zhí)行mkdir D:\mongodb\data\db后重新啟動(dòng)bind: address already in use27017 端口被占用先查占用進(jìn)程netstat -ano一閃而過(guò)/立即退出mongod.exe 啟動(dòng)報(bào)錯(cuò)信息被窗口關(guān)閉在命令行前臺(tái)啟動(dòng)不要雙擊 exe無(wú)法識(shí)別mongod命令環(huán)境變量未配置手動(dòng)把D:\mongodb\bin加入 PATH重開(kāi)命令行這里想特別強(qiáng)調(diào)一點(diǎn)不要雙擊mongod.exe來(lái)啟動(dòng)服務(wù)。mongod 是控制臺(tái)程序雙擊啟動(dòng)會(huì)在啟動(dòng)失敗時(shí)瞬間關(guān)閉你什么都看不到。正確的姿勢(shì)永遠(yuǎn)是打開(kāi)命令行在前臺(tái)運(yùn)行這樣才能看到完整的日志輸出。5.2 連接失敗與驅(qū)動(dòng)版本問(wèn)題代碼報(bào)連接失敗的另一個(gè)常見(jiàn)原因是 PyMongo 版本與 MongoDB 版本不兼容。新版本 MongoDB Server 對(duì)過(guò)老的驅(qū)動(dòng)會(huì)拒絕連接報(bào)錯(cuò)信息里通常會(huì)出現(xiàn)ServerSelectionTimeoutError。解決方案很簡(jiǎn)單先看自己的 MongoDB 版本然后升級(jí)驅(qū)動(dòng)。pip install --upgrade pymongo連接字符串拼寫(xiě)錯(cuò)誤也是高頻問(wèn)題比如mongodb://localhost:27017/library里的庫(kù)名寫(xiě)成了別的或者用了錯(cuò)誤的端口。我的排查習(xí)慣是先用 DBeaver 手動(dòng)連一次如果 DBeaver 能連上、代碼連不上那就是代碼的問(wèn)題如果兩邊都連不上基本就是服務(wù)沒(méi)起來(lái)或端口被占用。5.3 可視化工具與認(rèn)證問(wèn)題DBeaver 連接 MongoDB 時(shí)還有一個(gè)很容易踩的坑填寫(xiě)了用戶(hù)名密碼但 MongoDB 其實(shí)沒(méi)開(kāi)認(rèn)證或者認(rèn)證庫(kù)填錯(cuò)了。MongoDB 的認(rèn)證和 MySQL 不太一樣用戶(hù)是綁定特定數(shù)據(jù)庫(kù)的認(rèn)證庫(kù)填錯(cuò)哪怕密碼對(duì)也會(huì)被拒絕。沒(méi)開(kāi)認(rèn)證時(shí)就別填用戶(hù)名密碼開(kāi)了認(rèn)證就確認(rèn)一下創(chuàng)建用戶(hù)時(shí)用的authenticationDatabase是哪個(gè)庫(kù)通常是admin。另外Windows 防火墻偶爾會(huì)攔截 27017 端口的遠(yuǎn)程連接。本地連接一般不受影響但如果你的 MongoDB 裝在服務(wù)器上、客戶(hù)端從另一臺(tái)電腦連接就需要在防火墻里放行 27017 端口否則會(huì)一直卡在連接超時(shí)。5.4 數(shù)據(jù)層面容易忽略的坑除了環(huán)境問(wèn)題系統(tǒng)運(yùn)行起來(lái)之后還有一些數(shù)據(jù)層面的坑。第一個(gè)是字段類(lèi)型不一致比如導(dǎo)入數(shù)據(jù)時(shí)price有的存成了字符串89有的存成了數(shù)字89導(dǎo)致按價(jià)格范圍查詢(xún)時(shí)結(jié)果莫名其妙少了數(shù)據(jù)。遇到這種情況我通常用聚合管道的$convert先清洗一遍數(shù)據(jù)或者干脆在代碼入?yún)㈦A段強(qiáng)制類(lèi)型轉(zhuǎn)換。第二個(gè)是中文亂碼。MongoDB 本身對(duì) UTF-8 支持得很好亂碼基本都是出現(xiàn)在導(dǎo)入導(dǎo)出環(huán)節(jié)比如 CSV 文件是 GBK 編碼導(dǎo)入時(shí)沒(méi)轉(zhuǎn)成 UTF-8。用可視化工具導(dǎo)出數(shù)據(jù)時(shí)注意文件編碼選擇 UTF-8基本就能避免。第三個(gè)是并發(fā)借閱問(wèn)題。前文提到用條件更新加$inc來(lái)原子扣減庫(kù)存這是正確做法。但如果你用的是“先查出來(lái)判斷大于 0再 update 減一”這種兩步操作在高并發(fā)下就會(huì)出現(xiàn)超借。雖然課程設(shè)計(jì)一般不會(huì)有并發(fā)壓力但養(yǎng)成用原子操作的習(xí)慣對(duì)你以后做高并發(fā)項(xiàng)目很有幫助。最后再分享一個(gè)小技巧不管作者本人有多熟悉這套系統(tǒng)交付給別人的時(shí)候一定要把環(huán)境說(shuō)明和啟動(dòng)步驟寫(xiě)清楚。我在 .rar 里加了一份README.md第一頁(yè)就是“從零開(kāi)始部署的完整步驟”包括 MongoDB 免安裝版怎么配置、數(shù)據(jù)目錄怎么創(chuàng)建、初始化腳本怎么執(zhí)行。很多同學(xué)拿到項(xiàng)目跑不起來(lái)不是代碼有問(wèn)題而是漏了某一個(gè)環(huán)境步驟一份清晰的說(shuō)明能幫你省下大量重復(fù)答疑的時(shí)間。如果你準(zhǔn)備在這個(gè)項(xiàng)目基礎(chǔ)上繼續(xù)擴(kuò)展我建議優(yōu)先加這幾個(gè)功能一是 MongoDB 的定期備份與恢復(fù)用mongodump和mongorestore就能實(shí)現(xiàn)二是給圖書(shū)和讀者加上全文搜索MongoDB 的文本索引在這個(gè)場(chǎng)景很合適三是引入 Docker用docker-compose把 MongoDB 和業(yè)務(wù)服務(wù)編排起來(lái)這樣一來(lái)別人部署時(shí)只需要一條命令徹底告別手動(dòng)配置環(huán)境的痛苦。這個(gè)項(xiàng)目我還在持續(xù)完善后續(xù)有新版本也會(huì)把有價(jià)值的改動(dòng)單獨(dú)寫(xiě)出來(lái)分享。本文還有配套的精品資源點(diǎn)擊獲取