天天看點

Python3+Django配合Mongodb打造高性能高擴充标簽雲存儲方案

作者:劉悅技術分享

書接上回,之前有一篇文章提到了标簽雲系統的建構:Python3.7+jieba(結巴分詞)配合Wordcloud2.js來構造網站标簽雲(關鍵詞集合),但是這篇隻是淺顯的說明了一下如何進行切詞以及前端如何使用wordcloud2.js進行前端展示,本次主要讨論下标簽分詞切出來之後,如何進行存儲。

假設我們目前文章-标簽體系的需求是這樣:

每篇文章都具有唯一的标題、描述以及 URL。

每篇文章都具有一個或多個标簽。

每篇文章都具有作者的名稱,以及喜歡

每篇文章都有使用者的評論,使用者名、消息、日期時間以及評論的喜歡度。

每篇文章都可以有 0 個或多個評論。

那麼如果使用關系型資料庫來設計,比較簡單的設計方案可以是這樣:

Python3+Django配合Mongodb打造高性能高擴充标簽雲存儲方案

可以注意到,标簽和文章的對應關系還是簡單的一對多,如果做成比較靈活的多對多還需要增加一張關系表,這樣就是四張表了。

如果使用nosql比如Mongodb來說,隻需要一張表(聚合)就可以實作:

{  
   _id: POST_ID
   title: TITLE_OF_POST, 
   description: POST_DESCRIPTION,
   by: POST_BY,
   url: URL_OF_POST,
   tags: [TAG1, TAG2, TAG3],
   likes: TOTAL_LIKES, 
   comments: [  
      {
         user:'COMMENT_BY',
         message: TEXT,
         dateCreated: DATE_TIME,
         like: LIKES 
      },
      {
         user:'COMMENT_BY',
         message: TEXT,
         dateCreated: DATE_TIME,
         like: LIKES
      }
   ]
}           

可以看到标簽是由數組實作的,那麼關系型資料庫mysql和非關系型資料庫mongodb在标簽實作中本質上有什麼差別呢?

關系資料庫如mysql中标簽雲的實作是簡單的,标簽和文章分别在不同的表中,通過join可以比較簡單的查詢出标簽的統計資料。 而MongoDB為快速水準擴張以及極高的性能而優化,在MongoDB中沒有join,傾向于使用embedding來代替linking關系。

假設我們的需求又有了變化,普通部落格變身成為具有數百萬篇文章的小說站.每個小說都有許多布爾屬性,大約一萬個可能的屬性,每篇小說都有十幾個章節,假設我希望能夠實時(幾毫秒)請求給出的前n項任何屬性組合的标簽。

你會選擇推薦什麼解決方案?毫無疑問,如果你在尋找極具擴充性的方案,Mongodb無疑更好。

而且從業務角度上來講,無論是通過标簽查文章,還是文章查标簽這樣的需求,都非常靈活,當然了根據文章查标簽一般沒問題,一般都是根據标簽查文章的時候有性能問題,如果是純關系資料庫比如mysql很難解決性能問題,是以要借助 es 索引解決。es 索引的時候可以将 tagid 用逗号分隔,可以很快的根據一個 tagid,或者多個 tagid 查詢到關聯的文章 id,一般文章清單都是分頁的,有這些文章 id 了,再去關系資料庫裡面取文章就行了,但是es又是另外一件事了,回頭我們再讨論。

随後使用Django2.0.4來實作,首先安裝好python的mongodb操作庫pymongo

pip3 install pymongo           

值得一提的是,它會有一個相對應bson子產品 也就是說 PyMongo子產品的實作是基于和它一起的bson子產品的。

bson是一種類json的一種二進制形式的存儲格式,簡稱Binary JSON,它和JSON一樣,支援内嵌的文檔對象和數組對象,但是BSON有JSON沒有的一些資料類型,如Date和BinData類型;BSON有三個特點:輕量性、可周遊性、高效性,但是空間使用率不是很理想。

基于Django插入标簽的視圖:

import pymongo
from bson import json_util as jsonb
mongo_client = pymongo.MongoClient(host='localhost', port=27017)
from django.http import HttpResponse,HttpResponseRedirect,JsonResponse
from django.views import View

class InsertTagsHandler(View):

    def get(self,request):
        db = mongo_client.test12
        table = db.test12
        res = table.find({"title":'123'}).count()
        print(res)

        if res > 0:
            result = '重複資料'
            return HttpResponse(json.dumps({'result':result},ensure_ascii=False))
        else:
            table.insert({'title':'123','desc':['123','123']})
            return HttpResponse(json.dumps({'result':'添加成功'},ensure_ascii=False))           

基于django通過文章查詢标簽

class FindArticleHandler(View):

    def get(self,request):
        db = mongo_client.test12
        table = db.test12


        res = table.find_one({"title":'123'},{"desc":1})

        return HttpResponse(jsonb.dumps(res,ensure_ascii=False))           

基于django分組查詢擷取所有标簽以及标簽出現次數的統計

class TagsStatHandler(View):

    def get(self,request):
        db = mongo_client.test12
        table = db.test12


        pipeline = [{'$unwind':"$tags"},{'$group': {'_id': "$tags", 'count': {'$sum': 1}}},]
        res = table.aggregate(pipeline)

        return HttpResponse(jsonb.dumps(res,ensure_ascii=False))           

基于django通過标簽反查文章

class Tags2ArticleHandler(View):

    def get(self,request):
        db = mongo_client.test12
        table = db.test12


        res = table.find({"tags":{'$in':["123"]}})

        return HttpResponse(jsonb.dumps(res,ensure_ascii=False))           

結語:經此一役,Mongodb的特點躍然紙上:結構靈活,表結構更改相對自由,不用每次alter的時候付出代價,适合業務快速疊代,而且json原生和大多數的語言有天然的契合。還支援數組,嵌套文檔等資料類型。