关于Redis缓存数据一致性问题

结云坚木 2020-06-07 01:07:30
关于Redis缓存数据一致性的问题,在网上看了一些解决方案,但是有些具体的一些执行细节一直不太明白,比如说先更新缓存再异步更新数据库这个方法,我理解的缓存是基于方法的,也就是说一个查询方法会在redis里面有一条缓存数据(以key-value的方式存储),如果先更新缓存的话,如何在redis中定位到想要更新的数据呢?
打个比方:UserMapper里面有两个查询方法A、B和一个更新方法C,方法A、B的查询结果里面都有id=1的数据,这时候缓存里面就保存的有两个key,value都有id=1的数据,如果执行C方法更新id=1的数据,按照先更新缓存的方案,是如何定位到redis中的两个key呢?如果找到了又是如何做更新的呢?如果在其他mapper中有多表查询同样用到了id=1的数据,又是如何定位到并且修改的呢?求大佬赐教!
...全文
806 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
结云坚木 2020-06-12
  • 打赏
  • 举报
回复
加版本号也得定位到这个方法的key呀,不然所以key的版本号都变了不就是所有缓存都失效了,那就缓存雪崩啦
加油馒头 2020-06-12
  • 打赏
  • 举报
回复
引用 12 楼 结云坚木 的回复:
[quote=引用 11 楼 aw277866304的回复:][quote=引用 10 楼 结云坚木 的回复:] [quote=引用 9 楼 aw277866304的回复:]1、其实你所说的两个id,在redis中应该是两个不同的key,比如aaaId=1,和bbbId=1。至于aaa和bbb代表是什么意思,那就需要根据你的业务去定义了。 2、更新redis的时候,比如更新key=aaaId的值,直接覆盖key=aaaId就行了。就跟map更新是一样一样的。 3、大的方向,redis是内存数据库,独立进程;map是java的数据类型 4、redis支持五种数据类型:string,list,hash(字典),set(集合),zset(有序集合)。java map和redis的hash对应,当然各自包含的方法不同 5、redis可以做主存,也可做缓存。
你的意思就是说保存的时候不能以方法为单位,要以数据库的一条数据为单位是吗?如果有一个查询全部user的方法,数据库里有100条redis中就得缓存100个userId的key吗?那这样的话按其他条件查询是不是又得缓存100个呢?比如deleted=0的有100个。还有就是多表查询要怎么缓存呢?比如说现在有个方法A查询所有的学生信息和username(用的left join 只用到了user表的name字段,返回学生DTO),缓存的结果为100个学生name和username,现在调用新增user的方法B,新增成功了,按道理A方法的缓存也应该删除,因为A方法目的是查询全部的学生信息和username缓存中是100username个现在变成了101username个,那么根据什么定位到这个A方法的key呢?[/quote] 不是的,你想象一下Map能存什么,redis就可以存什么。redis的key只能是普通数据类型,但是值可以是任意对象的json格式字符串。取的时候把值转换为你想要的对象再去处理就行了。[/quote] 所以具体key要怎么存呢?我现在的问题不是说拿不到对象,我是想问如何通过key找到和修改的表相关的所有缓存,因为修改过后的表的缓存都应该删掉,这样才能保证数据一致性嘛,怎么才能找到这样缓存的key并且全部删除呢?[/quote] 有个办法 加版本号呗。 每次读取最新加载最新版本号 旧的版本号定期清理掉
夜风断愁 2020-06-12
  • 打赏
  • 举报
回复
引用 21 楼 结云坚木 的回复:
加版本号也得定位到这个方法的key呀,不然所以key的版本号都变了不就是所有缓存都失效了,那就缓存雪崩啦
这么说吧,如果将redis,定位为数据库的缓存,那通常对于一个表内的数据设计key都是: tableName:pk,这样能确定是一个表的一条数据; 在更新这个表的时候我们必然是可以知道tableName以及pk的.这样就可以直接定位到redis中的对应缓存数据了; 至于你想通过redis里做复杂查询,我认为可以通过多次取redis数据: a join b on a.pk = b.pk 这中情况,你可以mget a:pk b:pk; 至于redis与数据库的数据的幂等性,通常可以通过消息队列的消息消费情况来做异步更新;
结云坚木 2020-06-11
  • 打赏
  • 举报
回复
引用 14 楼 冰思雨的回复:
redis 的使用,一般是有两种观念出现的。 一种是把 redis 作为临时存放数据的工具,所以,里面的数据一般都有一个存放时限,超时会自动删除,并且,这种观念当中,数据库的数据是处于核心地位的,也就是说,数据的更新操作,要优先更新数据库,之后是刷新缓存。 另一种是把 redis 当做内存型数据库来使用,这种观念中,redis 是数据库,他里面存储的东西可不是什么查询结果的缓存,就是活生生的数据。这也就是面试的时候,经常问到的,你了解key-value型数据库吗,巴拉巴拉的内容。 现在造成楼主理解困难的原因,就出现在,楼主想用第二种理念来处理第一种方式存放的数据,从根源上就会有极大的冲突。 换句话说,楼主理解的redis的用法,适用于先更新数据库,再刷新缓存的这种路数,即使将所有缓存都清空,也不会造成数据不一致的问题。 那么,先更新缓存,后更新数据库的这种方案,他适用的场景,要求,key的编码规则,就是数据库里面的pk,至于怎么完成复杂查询,这就要在设计的时候,想想办法了。没有想到,只能说明我们的见识还不够多。
还是你明白我的意思了,我也大概知道你给的解答了,多谢啦!
结云坚木 2020-06-11
  • 打赏
  • 举报
回复
引用 13 楼 aw277866304的回复:
[quote=引用 12 楼 结云坚木 的回复:] [quote=引用 11 楼 aw277866304的回复:][quote=引用 10 楼 结云坚木 的回复:] [quote=引用 9 楼 aw277866304的回复:]1、其实你所说的两个id,在redis中应该是两个不同的key,比如aaaId=1,和bbbId=1。至于aaa和bbb代表是什么意思,那就需要根据你的业务去定义了。 2、更新redis的时候,比如更新key=aaaId的值,直接覆盖key=aaaId就行了。就跟map更新是一样一样的。 3、大的方向,redis是内存数据库,独立进程;map是java的数据类型 4、redis支持五种数据类型:string,list,hash(字典),set(集合),zset(有序集合)。java map和redis的hash对应,当然各自包含的方法不同 5、redis可以做主存,也可做缓存。
你的意思就是说保存的时候不能以方法为单位,要以数据库的一条数据为单位是吗?如果有一个查询全部user的方法,数据库里有100条redis中就得缓存100个userId的key吗?那这样的话按其他条件查询是不是又得缓存100个呢?比如deleted=0的有100个。还有就是多表查询要怎么缓存呢?比如说现在有个方法A查询所有的学生信息和username(用的left join 只用到了user表的name字段,返回学生DTO),缓存的结果为100个学生name和username,现在调用新增user的方法B,新增成功了,按道理A方法的缓存也应该删除,因为A方法目的是查询全部的学生信息和username缓存中是100username个现在变成了101username个,那么根据什么定位到这个A方法的key呢?[/quote] 不是的,你想象一下Map能存什么,redis就可以存什么。redis的key只能是普通数据类型,但是值可以是任意对象的json格式字符串。取的时候把值转换为你想要的对象再去处理就行了。[/quote] 所以具体key要怎么存呢?我现在的问题不是说拿不到对象,我是想问如何通过key找到和修改的表相关的所有缓存,因为修改过后的表的缓存都应该删掉,这样才能保证数据一致性嘛,怎么才能找到这样缓存的key并且全部删除呢?[/quote] 这个看你具体的业务了,比如你有一个用户user表,一个成绩score表,两个都有一个主键id,你存到redis中的key就可以是userId、scoreId。[/quote] 那如果有个成绩表的查询方法只用到了user表的name并没有用到id的话key不就保存不了user表的id了,那这种情况的话更新了user表也要删这个方法的缓存,怎么定位到这个缓存的key呢?
结云坚木 2020-06-11
  • 打赏
  • 举报
回复
还是你明白我的意思了,我也大概知道你给的解答了,多谢啦!
weixin_48569898 2020-06-11
  • 打赏
  • 举报
回复
好强。。。。。
瑶山 2020-06-11
  • 打赏
  • 举报
回复
相同的key会被覆盖的,不同的id加不同的前缀就好了,比如:redis_user_id = 1,redis_book_id = 1
冰思雨 2020-06-11
  • 打赏
  • 举报
回复
redis 的使用,一般是有两种观念出现的。 一种是把 redis 作为临时存放数据的工具,所以,里面的数据一般都有一个存放时限,超时会自动删除,并且,这种观念当中,数据库的数据是处于核心地位的,也就是说,数据的更新操作,要优先更新数据库,之后是刷新缓存。 另一种是把 redis 当做内存型数据库来使用,这种观念中,redis 是数据库,他里面存储的东西可不是什么查询结果的缓存,就是活生生的数据。这也就是面试的时候,经常问到的,你了解key-value型数据库吗,巴拉巴拉的内容。 现在造成楼主理解困难的原因,就出现在,楼主想用第二种理念来处理第一种方式存放的数据,从根源上就会有极大的冲突。 换句话说,楼主理解的redis的用法,适用于先更新数据库,再刷新缓存的这种路数,即使将所有缓存都清空,也不会造成数据不一致的问题。 那么,先更新缓存,后更新数据库的这种方案,他适用的场景,要求,key的编码规则,就是数据库里面的pk,至于怎么完成复杂查询,这就要在设计的时候,想想办法了。没有想到,只能说明我们的见识还不够多。
CS_草祭先生 2020-06-11
  • 打赏
  • 举报
回复
引用 12 楼 结云坚木 的回复:
[quote=引用 11 楼 aw277866304的回复:][quote=引用 10 楼 结云坚木 的回复:] [quote=引用 9 楼 aw277866304的回复:]1、其实你所说的两个id,在redis中应该是两个不同的key,比如aaaId=1,和bbbId=1。至于aaa和bbb代表是什么意思,那就需要根据你的业务去定义了。 2、更新redis的时候,比如更新key=aaaId的值,直接覆盖key=aaaId就行了。就跟map更新是一样一样的。 3、大的方向,redis是内存数据库,独立进程;map是java的数据类型 4、redis支持五种数据类型:string,list,hash(字典),set(集合),zset(有序集合)。java map和redis的hash对应,当然各自包含的方法不同 5、redis可以做主存,也可做缓存。
你的意思就是说保存的时候不能以方法为单位,要以数据库的一条数据为单位是吗?如果有一个查询全部user的方法,数据库里有100条redis中就得缓存100个userId的key吗?那这样的话按其他条件查询是不是又得缓存100个呢?比如deleted=0的有100个。还有就是多表查询要怎么缓存呢?比如说现在有个方法A查询所有的学生信息和username(用的left join 只用到了user表的name字段,返回学生DTO),缓存的结果为100个学生name和username,现在调用新增user的方法B,新增成功了,按道理A方法的缓存也应该删除,因为A方法目的是查询全部的学生信息和username缓存中是100username个现在变成了101username个,那么根据什么定位到这个A方法的key呢?[/quote] 不是的,你想象一下Map能存什么,redis就可以存什么。redis的key只能是普通数据类型,但是值可以是任意对象的json格式字符串。取的时候把值转换为你想要的对象再去处理就行了。[/quote] 所以具体key要怎么存呢?我现在的问题不是说拿不到对象,我是想问如何通过key找到和修改的表相关的所有缓存,因为修改过后的表的缓存都应该删掉,这样才能保证数据一致性嘛,怎么才能找到这样缓存的key并且全部删除呢?[/quote] 这个看你具体的业务了,比如你有一个用户user表,一个成绩score表,两个都有一个主键id,你存到redis中的key就可以是userId、scoreId。
CS_草祭先生 2020-06-10
  • 打赏
  • 举报
回复
引用 10 楼 结云坚木 的回复:
[quote=引用 9 楼 aw277866304的回复:]1、其实你所说的两个id,在redis中应该是两个不同的key,比如aaaId=1,和bbbId=1。至于aaa和bbb代表是什么意思,那就需要根据你的业务去定义了。 2、更新redis的时候,比如更新key=aaaId的值,直接覆盖key=aaaId就行了。就跟map更新是一样一样的。 3、大的方向,redis是内存数据库,独立进程;map是java的数据类型 4、redis支持五种数据类型:string,list,hash(字典),set(集合),zset(有序集合)。java map和redis的hash对应,当然各自包含的方法不同 5、redis可以做主存,也可做缓存。
你的意思就是说保存的时候不能以方法为单位,要以数据库的一条数据为单位是吗?如果有一个查询全部user的方法,数据库里有100条redis中就得缓存100个userId的key吗?那这样的话按其他条件查询是不是又得缓存100个呢?比如deleted=0的有100个。还有就是多表查询要怎么缓存呢?比如说现在有个方法A查询所有的学生信息和username(用的left join 只用到了user表的name字段,返回学生DTO),缓存的结果为100个学生name和username,现在调用新增user的方法B,新增成功了,按道理A方法的缓存也应该删除,因为A方法目的是查询全部的学生信息和username缓存中是100username个现在变成了101username个,那么根据什么定位到这个A方法的key呢?[/quote] 不是的,你想象一下Map能存什么,redis就可以存什么。redis的key只能是普通数据类型,但是值可以是任意对象的json格式字符串。取的时候把值转换为你想要的对象再去处理就行了。
结云坚木 2020-06-10
  • 打赏
  • 举报
回复
引用 11 楼 aw277866304的回复:
[quote=引用 10 楼 结云坚木 的回复:] [quote=引用 9 楼 aw277866304的回复:]1、其实你所说的两个id,在redis中应该是两个不同的key,比如aaaId=1,和bbbId=1。至于aaa和bbb代表是什么意思,那就需要根据你的业务去定义了。 2、更新redis的时候,比如更新key=aaaId的值,直接覆盖key=aaaId就行了。就跟map更新是一样一样的。 3、大的方向,redis是内存数据库,独立进程;map是java的数据类型 4、redis支持五种数据类型:string,list,hash(字典),set(集合),zset(有序集合)。java map和redis的hash对应,当然各自包含的方法不同 5、redis可以做主存,也可做缓存。
你的意思就是说保存的时候不能以方法为单位,要以数据库的一条数据为单位是吗?如果有一个查询全部user的方法,数据库里有100条redis中就得缓存100个userId的key吗?那这样的话按其他条件查询是不是又得缓存100个呢?比如deleted=0的有100个。还有就是多表查询要怎么缓存呢?比如说现在有个方法A查询所有的学生信息和username(用的left join 只用到了user表的name字段,返回学生DTO),缓存的结果为100个学生name和username,现在调用新增user的方法B,新增成功了,按道理A方法的缓存也应该删除,因为A方法目的是查询全部的学生信息和username缓存中是100username个现在变成了101username个,那么根据什么定位到这个A方法的key呢?[/quote] 不是的,你想象一下Map能存什么,redis就可以存什么。redis的key只能是普通数据类型,但是值可以是任意对象的json格式字符串。取的时候把值转换为你想要的对象再去处理就行了。[/quote] 所以具体key要怎么存呢?我现在的问题不是说拿不到对象,我是想问如何通过key找到和修改的表相关的所有缓存,因为修改过后的表的缓存都应该删掉,这样才能保证数据一致性嘛,怎么才能找到这样缓存的key并且全部删除呢?
结云坚木 2020-06-09
  • 打赏
  • 举报
回复
那如果是修改id=1的数据?是把他的value拿出来修改完再放回去吗?而且如果是一个集合的话是不是就要保存很多个id作为key?再假设修改的时候不是以id做条件怎么办呢?比如where name=xxx。我先把问题说简单点吧,如果是直接删除缓存再操作数据库,假如新增一条user数据,不可能将redis中的所有缓存都删除吧,有些可能只用到了user表的某一个字段,那么是如何定位到这些缓存的key呢?
dkwuxiang 2020-06-09
  • 打赏
  • 举报
回复
key 这样定义 ,应该就有规则了 User:id=1:selectUser User:id=2:selectUser User:id=1:findUser User:id=2:findUser 删除 id=1 user 时,keys User:id=1 的都可以清除了
结云坚木 2020-06-09
  • 打赏
  • 举报
回复
就是因为有高并发的问题,所以肯定有更好的解决方法呀,之前在网上看到一篇说是可以先更新缓存,然后再异步的更新数据库,所以具体的实现细节一头雾水。
结云坚木 2020-06-09
  • 打赏
  • 举报
回复
引用 9 楼 aw277866304的回复:
1、其实你所说的两个id,在redis中应该是两个不同的key,比如aaaId=1,和bbbId=1。至于aaa和bbb代表是什么意思,那就需要根据你的业务去定义了。 2、更新redis的时候,比如更新key=aaaId的值,直接覆盖key=aaaId就行了。就跟map更新是一样一样的。 3、大的方向,redis是内存数据库,独立进程;map是java的数据类型 4、redis支持五种数据类型:string,list,hash(字典),set(集合),zset(有序集合)。java map和redis的hash对应,当然各自包含的方法不同 5、redis可以做主存,也可做缓存。
你的意思就是说保存的时候不能以方法为单位,要以数据库的一条数据为单位是吗?如果有一个查询全部user的方法,数据库里有100条redis中就得缓存100个userId的key吗?那这样的话按其他条件查询是不是又得缓存100个呢?比如deleted=0的有100个。还有就是多表查询要怎么缓存呢?比如说现在有个方法A查询所有的学生信息和username(用的left join 只用到了user表的name字段,返回学生DTO),缓存的结果为100个学生name和username,现在调用新增user的方法B,新增成功了,按道理A方法的缓存也应该删除,因为A方法目的是查询全部的学生信息和username缓存中是100username个现在变成了101username个,那么根据什么定位到这个A方法的key呢?
CS_草祭先生 2020-06-09
  • 打赏
  • 举报
回复
1、其实你所说的两个id,在redis中应该是两个不同的key,比如aaaId=1,和bbbId=1。至于aaa和bbb代表是什么意思,那就需要根据你的业务去定义了。 2、更新redis的时候,比如更新key=aaaId的值,直接覆盖key=aaaId就行了。就跟map更新是一样一样的。 3、大的方向,redis是内存数据库,独立进程;map是java的数据类型 4、redis支持五种数据类型:string,list,hash(字典),set(集合),zset(有序集合)。java map和redis的hash对应,当然各自包含的方法不同 5、redis可以做主存,也可做缓存。
结云坚木 2020-06-09
  • 打赏
  • 举报
回复
数据修改是必须更新,问题是更新完数据之后怎样才能准确的定位到与修改的数据有关的那些缓存呢?是key用什么样的格式存储?还是value用什么样的数据结构?
dkwuxiang 2020-06-09
  • 打赏
  • 举报
回复
缓存本就不适合 不固定 数据,你 要实时同步,数据修改就必须更新, 按你说的 查询 结果 是不适合缓存的,比如:查询条件多,结果任意,缓存就没有意义了,每个缓存并没有很高的命中率。
每天学一. 2020-06-08
  • 打赏
  • 举报
回复
redis数据刷新 常用的就是两种解决方案啊,一个是延时双删一个是使用阿里的canal进行数据同步,无论是先删缓存在更新数据库还是先更新数据库在更新缓存,再高并发下都会存在一定问题....虽然我没有特别明白你上边说的那种场景。
加载更多回复(2)

81,090

社区成员

发帖
与我相关
我的任务
社区描述
Java Web 开发
社区管理员
  • Web 开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧