Room數據庫和Cache緩存聯合使用出現的小問題

前言

在上節我們介紹了Room數據庫的使用,大家感興趣的話,可以參考以下文章:
kotlin版Room數據庫 — 基本使用
那麼在數據庫的使用過程中,我們經常想有一些加快效率的方法。二級緩存的概念便是這麼來的: 先緩存中找數據,緩存中能找到的話就直接使用,緩存中找不到的話就取數據庫中找。大致就是這麼個流程。但是我們在用Room數據庫時,是有結合自己封裝的一個緩存幫助類Cache來使用的,然後在業務流程中出現了取值不正確的問題,下面就來分析下這個小問題吧。

今天涉及到的內容有:

  1. 出現的問題
  2. 節點介紹
  3. 緩存原理介紹
  4. 排查問題
  5. 修正邏輯

一. 出現的問題

今天我在數據操作上大致是這麼個流程:
存數據 ---> 修改數據 ---> 取數據 ---> 其他業務邏輯
問題就出在取數據環節,取的不是我存的數據,然後就導致我後面的其他業務邏輯無法展開。那麼,這裏肯定是存數據修改數據或者取數據的某個環節中出現了問題。下面就來具體看看這幾個節點。

二. 節點介紹

2.1 存數據

下面給出存數據環節的示例代碼:

        var userDaoHelper: UserDaoHelper = UserDaoHelper()

        LogUtil.i("=======存入初始數據========")
        var user: User = User()
        user.name = "張三"
        user.age = 12
        user.sex = "男"
        AppDataBase.instance().userDao().insert(user)

        //遍歷結果
        var list: MutableList<User> = userDaoHelper.getAll()
        list.forEach {
            LogUtil.i("=====遍歷所有數據===name=${it.toString()}")
        }

這裏是使用Room數據庫的insert方法進行存儲的。以上代碼寫了數據存儲和遍歷查詢,代碼很簡單,沒什麼問題,那麼接着讓我們去看下一個節點。

2.2 修改數據

修改數據的示例代碼如下:

        LogUtil.i("=======查詢叫張三的對象========")
        var tempUser:User?=getUserByName("張三")
        if(tempUser==null){
            LogUtil.i("========user=null")
        }else{
            LogUtil.i("========user=${user.toString()}")
        }

        LogUtil.i("=======更新操作========")
        //更新
        tempUser!!.hobby="大神"
        userDaoHelper.update(tempUser!!)
        LogUtil.i("========更新tempUser=${tempUser!!.toString()}")

這裏先是調用getUserByName("張三")獲取存儲的User對象,然後更新完tempUser後通過userDaoHelper.update(tempUser!!)方法存到Room數據中。這裏,我們繼續往下看,後面再細看getUserByName("張三")方法。

2.3 查詢環節

最後一步是查詢已存儲的數據,示例代碼如下:

        LogUtil.i("=======更新後查詢結果========")
        //再取值
        var lastUser:User?=getUserByName("張三")
        if(lastUser==null){
            LogUtil.i("========user=null")
        }else{
            LogUtil.i("========user=${lastUser.toString()}")
        }

        LogUtil.i("=======更新後查數據庫結果========")
        var listp: MutableList<User> = userDaoHelper.getAll()
        listp.forEach {
            if("張三".equals(it.name)){
                LogUtil.i("========user=${it.toString()}")
            }
        }

這個地方用了兩種查詢方式,一種是通過getUserByName("張三")方法查詢出User,另一種是通過Room數據庫相關方法封裝出的方法userDaoHelper.getAll()來獲取數據。
執行完畢後,我們可以來看看log:

=======存入初始數據========
=====遍歷所有數據===name=User(id=1, name=張三, age=12, hobby=null, sex=null)
=======查詢叫張三的對象========
========取數據庫======
========user=User(id=0, name=張三, age=12, hobby=null, sex=男)
=======更新操作========
========更新tempUser=User(id=1, name=張三, age=12, hobby=大神, sex=null)
=======更新後查詢結果========
========取cache======
========user=User(id=1, name=張三, age=12, hobby=null, sex=null)
=======更新後查數據庫結果========
========user=User(id=1, name=張三, age=12, hobby=大神, sex=null)

以上log打印表示的執行邏輯大概是(主要看User對象中的hobby屬性):
存入user時,hobby=null
然後通過getUserByName("張三")取出user時,hobby=null
然後更新user時,hobby=大神
最後getUserByName("張三")查詢出的user中,hobby=null
而數據庫查詢出user時, hobby=大神
這裏可以看出存更新後的user是成功的,因爲數據庫查出來userhobby=大神,那麼問題就出在getUserByName("張三")方法上了。

三. 緩存原理介紹

3.1 getUserByName(name:String) 方法

下面就來看看getUserByName("張三")方法示例代碼:

    private fun getUserByName(name:String): User? {
        var user:User?=null

        var userDaoHelper: UserDaoHelper = UserDaoHelper()
        var obj: Any? = Cache.getInstance().getObject(name, User::class.java)
        if (obj != null) {
            LogUtil.i("========取cache======")
            user= obj as User
        }else{
            //遍歷結果
            var list: MutableList<User> = userDaoHelper.getAll()

            var index:Int=0
            while(true){
                var temp:User=list[index]
                if(name.equals(temp.name)){
                    user=temp
                    LogUtil.i("========取數據庫======")
                    //存儲對象
                    Cache.getInstance().putObject(name, user)
                    break
                }
                index++
            }
        }
        return user
    }

ok,可以看到取數據的邏輯是:先從緩存中取數據,若緩存中沒有,則從數據庫取,並在數據庫查出數據的同時,將數據存到緩存中
接着讓我們來了解下Cache緩存邏輯。

3.2 Cache原理

Cache是先在內存中取數據,存儲利用Lru算法,如果內存中沒有,則在手機硬盤中獲取。而手機硬盤採用的是文件方式存儲,即硬存儲。

四.排查問題

然後再看整個邏輯:
先存數據 ---> 更新Room 數據庫數據 ---> 緩存中找數據,沒有則去Room數據庫查,查到的同時將數據設置到緩存中,若緩存中有則直接返回數據。
於是這裏出現了一個問題:
存時ok,通過緩存查是錯誤數據,但是通過數據庫查卻是正確數據。
那麼問題便出在緩存找數據上了。Cache的強悍存儲之處在於,它最終採用的存儲方式是文件存儲來保持數據的持久性。那麼如果上一次數據存儲到內存之後,若之後進行了其他數據處理,卻沒去更新緩存,則緩存中取到的可能還是上一次的數據,而不是最新數據。

五.修正邏輯

經過以上分析,當我們在使用數據庫時有結合緩存使用的話,我們在操作數據庫的增,刪,改時,記得要去更新緩存,然後在取值時要用以下邏輯

1. 若緩存中有,直接返回
2. 若緩存中無,去查數據庫
3. 若數據庫中有,返回數據的同時,將數據存到緩存中,方便下次使用時取出
4. 若數據庫中無,則直接返回空

ok, 今天關於數據庫與緩存聯合使用的知識就講到這裏了,謝謝大家。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章