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, 今天关于数据库与缓存联合使用的知识就讲到这里了,谢谢大家。

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