1. 程式人生 > >實際工作場景中踩過redis的一個坑:不查詢redis,而查詢後端資料庫問題

實際工作場景中踩過redis的一個坑:不查詢redis,而查詢後端資料庫問題

      今天,在工作中遇到專案在查詢碼錶字典轉換時,不走redis而是查詢後臺資料庫問題。做一個簡單記錄,目的是防止以後出現類似問題,同時該碼錶存在快取中採用的資料型別也是值得我學習的。

一、簡單的背景介紹

      該專案以前是一個整體專案,後來因業務需要,把整個專案拆分為多個子工程,這就造成一些類的包路徑改變了(這個是問題的根源),專案拆分後,發現在一些涉及到碼錶轉換的業務,都是沒有走redis而是直接到後端查詢資料庫。

      我就在debug模式跟蹤專案程式碼,原來在查詢redis時報錯(具體是查詢redis是先查詢元資料,在查詢碼值。在查詢元資料時報錯了,原因就是因為專案拆分,一個類的包路徑變了,在反序列化成物件時報錯),而程式碼捕獲了這個異常,在catch塊中執行查詢後端資料庫的邏輯程式碼。這是不走redis發生穿透現象的原因所在。針對這個問題的分析我寫了一個分析報告,報告放在了後面。

     我還發現專案在每次專案啟動時,系統會自動從後臺資料庫讀取碼錶相關資料,並初始化到redis中。具體流程是先刪除redis中的元資料、碼值資料等,再初始化redis中元資料和碼值資料,保證redis中資料和後端資料庫的一致性。

     這種做法是完全沒問題的,專案拆分後重新部署,肯定會重啟專案,那麼redis中資料又是最新的,就不會出現查詢碼錶不走redis傳送穿透的問題。問題就出現在刪除redis中元資料這一步,由於系統採用的刪除方法是先從redis讀取,然後在刪除。但是在讀取中,由於專案拆分一個類的路徑變了,無法反序列化成物件,造成程式碼拋異常,導致後面的刪除redis中元資料和碼錶資料沒有執行。那麼拆分後的專案,在進行碼錶轉換查詢redis,有碰到了獲取元資料失敗的情況(因為路徑還是拆分前的),異常被捕獲,就直接查詢後端資料庫了。

二、解決辦法

       為了避免以後專案變動,造成redis中元資料無法刪除問題,採用了通過key刪除的方式,而不是先從redis讀再刪的方式。

 

                                           =================正文才開始=================

      程式碼不在貼了,因為都是和具體業務有關,而且沒有什麼技術含量,大家都能明白。

    其實我想寫這篇文章的目的是(文章開頭有交代),該專案設計者,把資料庫中字典碼錶存到redis中所採用的資料結構的設計思路不錯,這才是促使我寫這篇文章的原因。

   一、資料庫表結構簡單介紹

     包含主鍵SEQ_NO、字典碼dictCode、源系統編號srcSysCode,目標系統編號destSysCode,源系統字典值srcCodeVal,目標系統字典值destCodeVal,字典編碼等6個關鍵欄位,另外一個特殊情況是目標系統和源系統中碼錶對應關係有一對多和多對一關係(這個很關鍵)。

 

二、redis中儲存結構設計

採用4個set集合集、1個hash、1個元資料的儲存結構

具體如下:

set資料結構

  第一個set集合集,key(實際還有其他字首標識)是資料表中srcSysCode欄位下每個值是一個set集合,value是該欄位對應的                                   所有記錄的SEQ_NO

  第二個set集合集,key(實際還有其他字首標識)是資料表中destSysCode欄位下每個值是一個set集合,value是該欄位對應的                                  所有記錄的SEQ_NO

  第三個set集合集,key(實際還有其他字首標識)是資料表中dictCode欄位下每個值是一個set集合,value是該欄位對應的所有                              記錄的SEQ_NO

  第四個set集合集,key(實際還有其他字首標識)是資料表中srcCodeVal欄位下每個值是一個set集合,value是該欄位對應的                                  所  有記錄的SEQ_NO

hash資料結構

  key是資料庫中SEQ_NO,value是json串,該json包含上述6個欄位及其對應的值

元資料是一個string結構,value是一個json,主要存的是該資料表對應的BO的全限定名,會根據該BO路徑反序列化成物件。

三、從redis查詢目標系統碼值的思路

      首先是獲取redis中元資料,通過json串反序列成對應的BO

      其次是根據條件對4個set求交集,獲取keys

      再次是根據keys,從hash中求對應的value,並裝配到BO中

       最後是從BO中獲取目標系統的destCodeVal值

       

     最後,讓我們思考下,作者為什麼這麼設計redis?我認為關鍵一點就是後端資料庫中存在一對多關係。如果資料庫中的碼錶全部是一對一關係,即源系統A中,字典碼是sex,源系統中字典值是M,在目標系統B中,對應的字典碼sex的值只有一個“男”,可以不用這麼設計redis,直接採用一個hash存該碼錶即可,key就是srcSysCode:destSysCode:dictCode:srcCodeVal這樣一個格式,value就是包含上述6欄位的json就ok,因為這個key不會重複,但是正因為資料庫中存在一對多關係,比如目標系統B中字典sex的值有“男”,還有一個是“1”,那redis設計就必須採用作者這種設計結構了。當然如果有其他好的設計思路,麻煩告知下。

 

 

                                    *****************************問題分析報告****************************

          碼錶轉換時不走redis而直接查詢後端資料庫的問題分析

 

一、問題背景

pcms專案因工作需要拆分成多個子工程,碼錶轉換相關程式碼拆分到pcms-common子工程,其中碼錶轉換時用到的一個類PCodeTransDefPO的包結構的路徑和拆分前的包結構有所改變。

 

二、問題分析

2.1、老碼錶正向轉碼方法具體實現步驟:

第一步先從redis中查詢碼錶

具體步驟是先從redis中讀取正向碼錶元資料,再根據查詢條件,從redis中求set集合交集獲取key,根據key再從 hash結構中獲取destCodeVal的值。

 

第二步如果查詢redis產生異常,再從後端資料庫查詢碼錶

這裡後端資料庫查詢程式碼寫在了catch塊中了,try塊中寫的從redis查詢程式碼

 

2.2、問題原因:

在第一步讀取redis中讀取元資料,並根據元資料反序列成物件時,由於元資料中儲存PCodeTransDefPO的包路徑為拆分前的,所以反序列成物件失敗,則產生了異常,被catch捕獲,所以就查詢了後端資料庫。

 

三、解決辦法

把redis中儲存元資料的json串中itemType的值更改為拆分後的PCodeTransDefPO包路徑即可。

 

四、其他說明

4.1、第一,碼錶中如果某詞彙配置了一對多的情況,該詞彙轉碼不能使用這個方法,否則可能會無法獲取正確的destCodeVal值。一對多情況例子如:某源系統中某一個srcCodeVal對應某目標系統中多個destCodeVal。

4.2、第二,在2.1第二步查詢後端資料庫程式碼寫在catch塊中,這種寫法存在一定機率的風險,如果當第一步中查詢redis產生異常而被捕獲,那麼第二步就不會執行,這樣就會出現redis沒查詢到,也不會查詢後端資料庫,造成碼錶沒有轉換,業務程式碼中會得到目標系統的碼值就是null或者是空串。

在日常開發中,在catch語句塊中建議除了一些異常日誌列印,不要寫其他業務邏輯,除非能保證try語句塊中所有程式碼沒有捕獲異常。