线上遇到的redis和数据库数据未同步问题、redisson内部实现问题

news/2025/11/30 13:27:59/文章来源:https://www.cnblogs.com/grey-wolf/p/19289146

背景

从离职同事手里接了一堆系统,其中一个系统,是对app提供服务的,虽说名义上这是一个系统,实际上,里面包含了五六个微服务,这其中有一个微服务,主要负责用户设备的上报和安全提醒,我们就叫设备管理服务:

1、老手机abc首次登录

如果用户首次用这个app,直接把当前这个设备(abc)设置为已信任设备,后台有个表device,会记录:

用户名 设备id 是否已被用户信任(trust) 安全提醒消息id
张三 abc null

而且由于引入了redis缓存来存储信任设备列表,所以会同步设置缓存(如果没有缓存key存在,就会从数据库中查询并设置缓存):

key:用户名

value类型:redis set

value:[abc]

2、换新手机def后登录

等到用户换新手机(设备id:def)的时候,这次上报上来后,发现222这个设备不在用户已信任设备id的集合中(select 设备id from devie where trust = true),理论上,就需要触发一次安全提醒消息,消息是发给老设备abc.

总的来说,就是,当前这个设备管理服务,会去调用消息微服务,得到消息id后,会在数据库中,加一条记录,如下的第二条(未被用户信任,且发送了安全提醒消息)

用户名 设备id 是否已被用户信任(trust) 安全提醒消息id
张三 abc null
张三 def 1

同时呢,消息服务那边,会记录一条消息:

消息id 用户名 可拉取该消息的设备id列表 消息是否被拉取
1 张三 abc false

3、老手机abc登录

老手机此时如果去登录,就会从消息服务拉取到上述的安全提醒消息,此时消息表的拉取状态变成true:

消息id 用户名 可拉取该消息的设备id列表 消息是否被拉取
1 张三 abc true

并发mq通知设备管理服务。设备管理服务这边,就会修改设备表,将设备def改成:已被用户信任。

用户名 设备id 是否已被用户信任(trust) 安全提醒消息id
张三 abc null
张三 def 1

在这个步骤中,由于新增了用户信任设备,会将缓存过期,过期时间是1秒后。

4、新手机def再次登录

由于此时,def在数据库中,trust标志为true:已被用户信任,所以就不会再触发安全提醒的消息了。

线上问题

然而,接过来也没多久,产品就说线上有问题(好像没接手之前也有吧,忘了),偶尔会存在乱发短信的问题(就是不该发但发了),比如明明是用户自己的设备,但也会提醒你:你的账号在1台或者某几台(这里是占位符,设备型号,如iphone/小米/华为等)上登录,请注意安全,必要时联系客服或报警。

由于这个app和钱相关,而且是大量的钱,所以真就有不少用户去报警了。有的客户可能感觉是程序bug,但和钱相关后,就谨慎了,至少报警留个痕,对吧,免得后续扯皮时吃亏。

这些线上问题出了之后,我当前也是才接了这个系统没多久,出于一些原因和工作优先级的问题,也没花特别多时间来处理这个,产品说这个功能有开关,只能就是把这个安全提醒的功能给关闭了先。

等后续高优先级工作完成后,还是抽空看了下这块。

代码存在的一些问题

其实,大家看上面这个逻辑,是不是也不算复杂,如果纯粹的数据库操作的话,逻辑还是蛮简单;但我接手后,从代码和部分文档,来梳理出上述的逻辑,费了不少事。1个就是,代码里引入了缓存redis,来存储:已被用户信任的设备id集合,key是用户名,value就是redis的set类型,元素类型就是字符串,比如,最开始老手机登录,存的就是abc,等到上面的第三步后(老手机拉取了消息后),缓存里存的就应该是abc和def。

另外呢,引入了mq机制,说实话,mq也就是个用,我看也没有相关的确保消息不丢失的代码,不过这都还好,本次的问题也不是mq的问题。

再一个问题就是,这个设备id号,其实是app来生成的,但它也是可能变化的,同一台手机上,我如果把app完全卸载重装,设备id就会发生变化。其实后来讨论过,为啥不使用手机的硬件相关数据,如:IMEI;app同事好像是说,现在权限管得很严,不让获取IMEI字段,具体我也不清楚。

还有个麻烦的问题,用户app端是可以看到所有这些设备的,就是能查询如下表的记录,而且可以改,可以删:

用户名 设备id 是否已被用户信任(trust) 安全提醒消息id
张三 abc null

可以删,还是他么的物理删除。

当时查问题的时候,一方面代码难看,没啥注释,代码也是面条式代码,再加上数据库里记录被物理删除,真的只能各种翻日志了,日志可能还是横跨好多天的。我这里也给大家放一下这个设备上报接口的逻辑图:

image-20251130123735017

问题处理

思路

首先,这个问题不是必现的,是偶现。我在本地也没复现出来。

所以,我当时的思路是比较暴力的:

1、数据库物理删除改成逻辑删除;

2、代码简单重构,增加日志,规范变量命名,抽取函数等等;

3、讨论是否直接去掉redis缓存。

第三点,由于涉及架构变动,产品和测试认为动作有点大了,就先只改了前两点。

测试

测试时,没想到测试得同事偶然复现了这个问题,由于这次增加的日志多了不少,几乎就确认了,是缓存和数据库存在不同步的问题。

是这样的,前面背景中说到的,第三步是:老手机去拉取安全提醒的消息,然后此时,消息服务会调用设备管理服务,提示消息被消费了,可以将设备def改成常用了。我们从测试时的日志发现,数据库确实是改了。

用户名 设备id 是否已被用户信任(trust) 安全提醒消息id
张三 abc null
张三 def 1

然后,缓存操作这块呢,代码是这样的:

image-20251130125119402

然后,后续这个用户的新设备def再登录的话,按理来说,就会发现缓存过期了,就会从数据库中加载:

image-20251130125338893

如果真是走了这段数据库加载的逻辑,基本可以说,上面那行日志是一定会打印的,但是,很遗憾啊,没打这行日志,看来,程序是发现从缓存中取到的set不为空。

但是,这怎么会呢,我第三步设置缓存1s后过期;后续def上报时,都过了好几十秒了,不可能缓存还在吧?

那就是这几十秒中间,出了什么事吧。

然后在这几十秒中间的日志里面找,发现:在第三步设置缓存1s过期后,马上几十毫秒内,又有一个该用户的设备上报请求(测试同事手里手机比较多,有4部,这一步我们命名为设备h吧),此时,那个本应该1s后过期的缓存,此时还存在于redis中。

设备h上报时,触发下面的代码:

image-20251130125935097

我们进去看看:

image-20251130130028557

上述这个红框代码,将缓存过期时间又给延长了啊。。。导致本应该1s后过期的缓存(内容:abc),生命周期又变得很长很长,此时,数据库中的已信任设备为:abc、def,而缓存中则是abc,这就产生了不一致。

问题根源

根源就是代码写得有问题。你改了数据库中def为常用设备了,缓存直接删除了不就行了,为啥要1s后过期呢,这不就1s内就是不一致的吗,直接删了也就不存在这个问题了。

尤其是,这个获取缓存的动作,还包含了设置过期时间的功能:

image-20251130130415766

另外,由于这里混用了两个redis框架(spring redisTemplate和redisson),我一开始还怀疑偏了,以为是redisson内部有缓存之类的问题。

所以,对于一般的应用来说,不是严格要求数据库和缓存要一致的那种,一般:先修改数据库,再把缓存key删了,就够用了;对于互联网大厂那种,这种可能还是不够用,就会用什么binlog同步啥的,我就不操心那么多了,大家自己搜索吧。

redisson的问题

判空

在debug的时候,发现redisson原来是这样的。

就比如,我们上述的程序中,获取key:

RSet<String> ret = redissonClient.getSet(keyPrefix.prefix() + key);

其中,Rset是redisson中的类型:

image-20251130131012064

当我们在判断rset是否为空时(org.apache.commons.collections4.CollectionUtils#isEmpty):

image-20251130131104455

image-20251130131214316

image-20251130131227115

image-20251130131249229

最终,就会触发一个对redis的SCARD命令,也就是执行scard,获取集合中元素的数量:

image-20251130131404458

contains

我们代码中还包括contains:

image-20251130131542065

这个是触发如下命令:

image-20251130131621937

获取集合中元素

我们还有如下代码:

image-20251130131656175

这个触发的是:

image-20251130131812589

image-20251130131830704

相关抓包截图

image-20251130132140017

image-20251130132214408

image-20251130132231173

总结

引入缓存,是为了解决问题,但是,如果不注意的话,就会发生:缓存数据库不同步问题;

引入redisson,是为了避免频繁操作数据库,但是,不了解内部的话,就会发现,简单的几个方法调用,就触发了一堆的网络调用,这是否真的高性能了呢?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/981862.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年十大知名的媒体邀约品牌企业推荐,比较好的媒体邀约公

在信息爆炸的数字化时代,企业活动的声量传播不再局限于线下场景——一次成功的新品发布、一场高规格的行业峰会,其影响力往往取决于能否通过权威媒体矩阵实现精准触达。然而,市场上媒体邀约服务商鱼龙混杂:有的承诺…

2025苯板造型供应商TOP5权威推荐:甄选定制厂家,助力保

建筑保温与装饰一体化需求的激增,推动苯板造型市场规模年增速突破35%,但行业内28%的投诉集中在造型精度不足、交付延迟、材质与设计不符三大痛点——某商业综合体因苯板浮雕拼接缝隙超2mm返工,某婚礼堂因泡沫雕塑变…

详细介绍:在 SAP 中获取未清采购订单(PO)数量的方式有多种,以下是 6 种常用方法,涵盖标准事务码、报表、数据库表查询及自定义开发等场景

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

nestjs集成grpc服务 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

0,1序列

import torch import torch.nn as nn import random ------------------------- 超参数 ------------------------- N_max = 10 # 最大 n 值(训练时用) input_size = 4 # 字符集大小:0, 1, , hidden…

提升开发效率的关键:Python 在工程应用中的五大实战技巧

在越来越多的工程项目中,Python 已经从辅助脚本工具,变成不少团队的“主力语言”。不仅是数据分析和自动化脚本,在测试开发、运维监控、设备联网、系统接口等场景里,Python 几乎都能找到自己的位置。 项目做得越多…

2025文艺演出资深机构TOP5权威推荐:甄选专业团队助力活

企业年会、品牌发布会、行业峰会等场景中,高质量文艺演出是提升活动质感、强化品牌记忆的核心载体。2024年活动策划市场数据显示,文艺演出服务需求年增速达38%,但超28%的客户投诉集中在节目同质化、现场效果失控、资…

2025年东北玻璃钢雕塑品牌商推荐:十大玻璃钢雕塑制造厂批量

在建筑装饰、商业美陈、文化景观等领域,玻璃钢雕塑凭借轻质高强、造型灵活、耐候性强的优势,成为空间美学与功能需求的重要载体。随着市场对定制化、批量化玻璃钢雕塑需求的激增,如何选择兼具设计实力、生产能力与交…

2025年十大专业的活动策划专业公司推荐,实力强的活动策划公

在企业品牌传播、用户链接的关键场景中,一场优质的活动是企业传递价值、撬动增长的核心载体。面对市场上良莠不齐的服务提供商,如何找到真正专业的活动策划专业公司?以下结合行业属性与服务能力,为你推荐2025年十大…

2025年黑龙江苯板雕刻制造商推荐:苯板雕刻优质供应商和生产

本榜单依托东北地区保温材料及造型定制领域的市场调研与真实客户反馈,深度筛选出五家标杆企业,为建筑装饰、商业美陈、婚庆会展等领域客户选型提供客观依据,助力精准匹配适配的服务伙伴。 TOP1 推荐:哈尔滨万嘉保温…

快懂百科创建代做公司有哪些,推荐一家能做快懂百科的公司

在AI搜索日益普及的当下,快懂百科作为重要的知识展示平台,成为企业建立线上权威形象、提升品牌认知度的关键载体。一份规范、完善的快懂百科词条,不仅能为消费者提供准确的品牌信息,更能凭借平台的公信力增强用户信…

2025年哈尔滨苯板立体雕刻加工厂/制造厂哪家更值得选?

为帮助建筑装饰、婚礼堂布置、影视道具制作等领域的客户精准锁定适配需求的苯板雕刻合作伙伴,避免因工艺粗糙、交付延迟导致项目损失,我们从雕刻精度(毫米级误差控制)、定制化能力(复杂造型还原度)、材料适配性(…

实用指南:实验十三 Z-buffer算法实验

实用指南:实验十三 Z-buffer算法实验2025-11-30 13:11 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !i…

升鲜宝供应链管理系统源代码---仓储式超市门店管理系统设计(一)

1.门店表(mall_shop) CREATE TABLE `mall_shop` (`id` bigint NOT NULL COMMENT 主键id,`shop_name` varchar(255) NOT NULL COMMENT 门店名称/客户名称,`shop_code` varchar(255) DEFAULT NULL COMMENT 门店编码,`sh…

RAG_查询重构与分发 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年全国信誉好的活动策划专业公司排行榜,售后完善、比较

为帮助企业高效锁定适配自身需求的活动策划合作伙伴,避免选型走弯路,我们从创意策划能力(如主题创新、流程设计)、执行落地水准(含设备搭建、现场控场)、全周期服务质量(覆盖前期调研到后期复盘)及真实客户口碑…

2025苯板雕刻加工厂TOP5权威推荐:苯板立体雕刻制造商哪

在建筑节能与装饰美学融合的趋势下,苯板雕刻作为兼具保温功能与艺术价值的关键材料,市场需求持续攀升。2024年数据显示,东北地区苯板雕刻市场规模同比增长32%,但35%的客户投诉集中在雕刻精度不足、交付延期、造型还…

java要记

ArrayList操作自定义对象进行removeAll()时,移除失效原因由于底层最用调用的是Object的equals()方法进行比较的,比较的是地址,两个对象地址当然是不同的了,移除自然会失败。解决方案:重写equals方法。【注意重写e…

关于python更换永久镜像源

在Python中更换永久镜像源主要有两种方式:pip镜像源和conda镜像源。以下是详细的配置方法: 1. pip镜像源配置 方法一:使用命令直接配置(推荐) # 清华镜像源 pip config set global.index-url https://pypi.tuna.t…

【C編程】多個.c文件聯編

W3C C tutorial將一個大型項目多個.c文件稱為模塊化編程。[1] 就是,多個.c文件互相調用。 方法是: 1)被調用方:定義.h和.c成對文件,.c要include .h文件 2) 調用方:include .h文件。 3) 聯合編譯:將兩個.c文件一…