設計原則與代碼實踐(記一次線上bug)

設計原則與代碼實踐(記一次線上bug)

bug前因後果

前段時間接到一個需求,由於排期很是緊,沒有認真規劃就開幹了。sql

需求描述起來大體是這樣:門店的app首頁會展現6張門店圖片,之前只有A類門店,如今新增了B類門店,也須要首頁圖片。可是不一樣於A類門店不存在狀態,B類門店的首頁圖片須要有 上傳-->審覈\駁回 這樣一個操做流程,分別對應圖片 待審覈、已經過、已駁回 三種狀態。數據庫

以前A類門店是直接將6張圖片的url數據拼接成一個字符串,直接存放門店表中,以下圖:緩存

image-20191212105059917.png

由於B類圖片存在狀態,因此我建立了一個新表,將app_img_url字段拆分若干條記錄,以下圖:併發

image-20191212104540975.png

修改後,對於圖片的增刪改查操做,須要同時操做兩張表,其中有個查詢門店信息的rpc接口,是高頻調用,實現後的調用流程以下:app

image-20191212112104126.png

可是我新增的查詢門店圖片信息實現,沒有加緩存,致使上線後的次日早上,數據訪問量一個小時內激增近千萬。數據庫設計

頭皮發麻。高併發

趕忙加上緩存,緊急部署,然鵝並無訪問量並無所有降下來。又開始排查,發現兩點緣由:性能

  1. 圖片增刪改邏輯複雜,多個地方均有調用,緩存添加的不全;
  2. 有至關一部分門店沒有門店圖片,致使緩存穿透。

冷靜分析一波,忽然感受被本身蠢哭了!以前之因此將門店信息和圖片信息分紅兩個sql查詢,是由於在其餘地方實現了查詢圖片信息的接口,直接複用,當時還沾沾自喜,感受本身遵循了開閉原則。。。僞代碼以下:url

public Class ***ServiceImpl {
    ...
    // 查找門店信息
    Shop shop = getShopInfo();
    // 查找圖片信息  (新增代碼)
    List<Img> imgs = listImgInfo();
    //設置圖片信息  (新增代碼)
    shop.setImgs(imgs);
    ...
}

這樣只是我寫的代碼最少,可是卻不是遵循了開閉原則,而是對原有接口進行了修改,徹底能夠在SQL查詢中關聯兩張表,這樣就不會有上面的問題了。流程以下:spa

image-20191212141224470.png

可是這樣作仍是有一個問題,那就是當查詢量大的時候,left join對性能的損耗仍是很大的。

這樣我就考慮到是否將圖片的url在保存的時候,在兩張表都保存(只針對已經過狀態的圖片,由於查詢只查找已經過狀態的圖片),這樣查詢rpc接口徹底不用作任何修改,和原來的邏輯同樣,只改變的了增刪改的邏輯,而增刪改是低頻操做。

總結

上述bug是多方面緣由致使的:

  1. 排期緊,沒有認真規劃需求的實現邏輯;
  2. 修改對外提供的rpc接口不夠謹慎,直接在原代碼中添加了修改邏輯。
  3. 原來的設計自己存在問題。

緣由1暫且不論,着重討論一下緣由2和緣由3。

修改對外提供的rpc接口不夠謹慎

開閉原則(Open-Closed Principle, OCP):一個軟件實體應當對擴展開放,對修改關閉。即軟件實體應儘可能在不修改原有代碼的狀況下進行擴展。

從開閉原則的定義能夠看出,個人第一次修改,很明顯沒有遵循這一原則,在工做實踐中,大部分狀況下不遵循設計原則並不會形成很大的麻煩,倒黴的是,我趕上了少部分狀況。

因此對於一些普通的業務代碼,遵循設計原則,可能會對代碼後期維護有利,可是收益通常並不明顯。

可是對於一些高頻率高併發的接口,尤爲是其餘系統提供的接口,遵循設計原則就變得尤其重要。由於這些接口通常調用頻率會很高,同時後期擴展變更的概率大。

原來的設計自己存在問題

這一點指的是,原來的數據庫設計就沒有爲後期擴展留出餘量,一個門店對應多個圖片,這種一對多的問題,通常狀況下,應該建立兩張表,兩張表之間經過主鍵id或者編號關聯,這樣若是後期,圖片數量增長了(好比由6張圖片變成100張),圖片類型變化了(好比須要區分圖片的審覈狀態、區分圖片的類型等等)······對於這些變化,兩張表的設計擴展修改起來會方便許多。

相關文章
相關標籤/搜索