一次-事务使用不当引发的生产事故-Redis (一次事务开启几个数据库连接)

教程大全 2025-07-18 11:39:54 浏览

你好,我是悟空。

本文主要内容如下:

一、前言

最近项目的生产环境遇到一个奇怪的问题:

现象:每天早上客服人员在后台创建客服事件时,都会创建失败。当我们重启这个微服务后,后台就可以正常创建了客服事件了。到第二天早上又会创建失败,又得重启这个微服务才行。

初步排查:创建一个客服事件时,会用到 Redis 的递增操作来生成一个唯一的分布式 ID作为事件 id。代码如下所示:

return redisTemplate

而恰巧每天早上这个递增操作都会返回null,进而导致后面的一系列逻辑出错,保存客服事件失败。当重启微服务后,这个递增操作又正常了。

那么排查的方向就是 Redis 的操作为什么会返回 null 了,以及为什么重启就又恢复正常了。

二、排查

根据上面的信息,我们先来看看 Redis 的自增操作在什么情况下会返回 null。

2.1 推测一

根据重启后就恢复正常,我们推测晚上执行了大量的 job,大量 Redis 连接未释放,当早上再来执行 Redis 操作时,执行失败。重启后,连接自动释放了。

但是其他有使用到 Redis 的业务功能又是正常的,所以推测一的方向有问题,排除。

2.2 推测二

可能是 Redis 事务造成的问题。这个推测的依据是根据下面的代码来排查的。

直接看redisTemplate​递增的方法increment,如下所示:

官方注释已经说明什么情况下会返回 null:

事务提供了一种将多个命令打包,然后一次性、有序地执行机制.

Redis

多个命令会被入列到事务队列中,然后按先进先出(FIFO)的顺序执行。

事务在执行过程中不会被中断,当事务队列中的所有命令都被执行完毕之后,事务才会结束。(内容来自 Redis 设计与实现)

继续看代码,发现在操作 Redis 的 ServiceImpl 实现类的上面添加了一个 @Transactional 注解,推测是不是这个注解影响了 Redis 的操作结果。

2.3 验证推测二

如下面的表格所示,第二行中没有添加 Spring 的事务注解@Transactional​时,执行 Redis 的递增命令肯定是正常的,而接下来要验证的是表格中的第一行:加了@Transactional是否对 Redis 的命令有影响。

为了验证上面的推论,我写了一个 Demo 程序。

Controller 类,定义了一个 API,用来模拟前端发起的请求:

Service 实现类,定义了一个方法,用来递增 Redis 中的 count 键,每次递增 1,然后返回命令执行后的结果。而且这个 Service 方法加了@Transactional 注解。

Postman 测试下,发现每发一次请求,count 都会递增 1,并没有返回 null。

然后到 Redis 中查看数据,count 的值也是递增后的值 38,也不是 null。

通过这个实验说明在 @Transactional 注解的方法里面执行 Redis 的操作并不会返回 null,结论我记录到了表格中。

所以说上面的推论不成立(加了 @Transactional 注解并不影响),到这里线索似乎断了。

2.4 推测三

然后跟当时做这块功能的开发人员说明了情况,告诉他可能是 Redis 事务造成的,然后问有没有其他同学在凌晨执行过 Redis 事务相关的 Job。

他说最近有同事加过 Redis 的事务功能,在凌晨执行 Job 的时候用到事务。我将这位同事加的代码简化后如下所示:

下面是针对这段代码的解释,简单来说就是开启事务,将 Redis 命令顺序放到一个队列中,然后最后一起执行,且保证原子性。

setEnableTransactionSupport表示是否开启事务支持,默认不开启。

难道开启了 Redis 事务,还能影响 Spring 事务中的 Redis 操作?

2.5 验证推测三

如下表,序号 3 和 序号 4 的场景都是开启了 Redis 的事务支持,两个场景的区别是是否加了 @Transactional 注解。

为了验证上面的场景,我们来做个实验:

2.5.1 执行 Redis 事务

首先就用 Redis 的 multi 和 exec 命令来设置两个 key 的值。

如下图所示,设置成功了。

2.5.2 @Transactional 中执行 Redis 命令

接下来在标注有 @Transactional 注解的方法中执行 Redis 的递增操作。

多次执行这个命令返回的结果都是 null,这不就正好重现了!

再来看 Redis 中 count 的值,发现每执行一次 API 请求调用,都会递增 1,所以虽然命令返回的是 null,但最后 Redis 中存放的还是递增后的结果。

接下来我们验证下场景 4,先执行 Redis 事务操作,然后在不添加@Transactional 注解的方法中执行 Redis 递增操作。

用 Postman 调用这个接口后,正常返回自增后的结果,并不是返回 null。说明在非 @Transactional 中执行 Redis 操作并没有受到 Redis 事务的影响。

四个场景的结论如下所示,只有第三个场景下,Redis 的递增操作才会返回 null。

问题原因找到了,说明 RedisTemplete 开启了 Redis 事务支持后,在 @Transactional 中执行的 Redis 命令也会被认为是在 Redis 事务中执行的,要执行的递增命令会被放到队列中,不会立即返回执行后的结果,返回的是一个 null,需要等待事务提交时,队列中的命令才会顺序执行,最后 Redis 数据库的键值才会递增。

三、源码解析

那我们就看下为什么开启了 Redis 事务支持,效果就不一样了。

找到 Redis 执行命令的核心方法, execute 方法。

然后一步一步点进去看,关键代码就是 211 行到 216 行,有一个逻辑判断,当开启了 Redis 事务支持后,就会去绑定一个连接(bindConnection​),否则就去获取新的 Redis 连接(getConnection​)。这里我们是开启了的,所以再到bindConnection方法中查看如何绑定连接的。

接着往下看,关键代码如下所示,当开启了 Redis 事务支持,且添加了 @Transactional 注解时,就会执行 Redis 的 mutil 命令。

关键代码:conn.multi();

Redis Multi 命令用于标记一个事务块的开始,事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行。

真相大白,开启 Redis 事务支持 + @Transactional 注解后,最后其实是标记了一个 Redis 事务块,后续的操作命令是在这个事务块中执行的。

比如下面的的递增命令并不会返回递增后的结果,而是返回 null。

stringRedisTemplate.opsForValue().increment("count", 1);

而我们的生产环境重启服务后,开启的 Redis 事务支持又被重置为默认值了,所以后续的 Redis 递增操作都能正常执行。

四、修复方案

目前想到了两种解决方案:

方案一:每次 Redis 的事务操作完成后,关闭 Redis 事务支持,然后再执行 @Transactional 中的 Redis 命令。(有弊端)

方案二:创建两个 StringRedisTemplate,一个专门用来执行 Redis 事务,一个用来执行普通的 Redis 命令。

4.1 方案一

方案一的写法如下,先开启事务支持,事务执行之后,再关闭事务支持。

但是这种写法有个弊端,如果在执行 Redis 事务期间,在 @Transactional 注解的方法里面执行 Redis 命令,则还是会造成返回结果为 null。

4.2 方案二

弄两个 RedisTemplate Bean,一个是用来执行 Redis 事务的,一个是用来执行普通 Redis 命令的(不支持事务)。不同的地方引入不同的 Bean 就可以了。

先创建一个 RedisConfig 文件,自动装配两个 Bean。一个 Bean 名为stringRedisTemplate​代表不支持事务的,执行命令后立即返回实际的执行结果。另外一个 Bean 名为stringRedisTemplateTransaction,代表开启 Redis 事务支持的。

代码如下所示:

接下来在测试的 Service 类中注入两个不同的 StringRedisTemplate 实例,代码如下所示:

Redis 事务的操作改写成这样,且不需要手动开启 Redis 事务支持了。用到的 StringRedisTemplate 是支持事务的那个实例。

在 Spring 的 @Tranactional 中执行的 Redis 命令如下所示,用到的 StringRedisTemplate 是不支持事务的那个实例。

然后还是按照上面场景 3 的测试步骤,先执行 testRedisMutil 方法,再执行 testTransactionAnnotations 方法。

验证结果:Redis 递增操作正常返回 count 的值,修复完成。

另外关于 Redis 事务使用还有一个坑,就是 Redis 连接未释放,导致获取不到连接了,这是下一个话题了~

参考资料:


仪表礼仪的原则是?注意的要点是?

仪表礼仪的基本原则:1)男士1.短发,清洁、整齐,不要太新潮;2.精神饱满,面带微笑;3.每天刮胡须,饭后洁牙;4.白色或单色衬衫,领口、袖口无污迹;5.领带紧贴领口,系得美观大方;6.西装平整、清洁;7.西装口袋不放物品;8.西裤平整,有裤线;9.短指甲,保持清洁10.皮鞋光亮,深色袜子11、全身3种颜色以内。 2)女士1. 发型文雅、庄重,梳理整齐,长发要用发夹夹好,不能染鲜艳的颜色;2. 化淡妆,面带微笑;3. 着正规套装,大方、得体;4. 指甲不宜过长,并保持清洁。 涂指甲油时须自然色;5. 裙子长度适宜;6. 肤色丝袜,无破洞;7. 鞋子光亮、清洁;8. 全身3种颜色以内3)养成良好的卫生习惯1. 头发:整洁、无头屑,头发软者可用摩丝定型。 在办公室里,留长发的女士不披头散发;2. 眼睛:清洁、无分泌物,避免眼睛布满血丝;3. 鼻子:别让鼻毛探头探脑,勿当众抠鼻子;4. 嘴巴、牙齿:清洁、无食品残留物;5. 指甲:清洁,定期修剪;6. 男士的胡子:每日一理,刮干净;7. 配件及饰物:检查有否污损或被碰歪了。 仪表礼仪注意要点:(一)应该注重仪表的协调对于年龄来说,不同年龄的人有不同的穿着要求,年轻人应穿着鲜艳、活泼、随意一些,体现出年轻人的朝气和蓬勃向上的青春之美。 而中、老年人的着装则要注意庄重、雅致、整洁,体现出成熟和稳重。 对于不同体型,不同肤色的人,就应考虑到扬长避短,选择合适的服饰。 职业的差异对于仪表的协调也非常重要。 (二)仪表应注意色彩的搭配暖色调(红、橙、黄等)给人以温和,华贵的感觉,冷色调(紫、蓝、绿等)往往使人感到凉爽、恬静、安宁、友好,中和色(白、黑、灰等)给人平和、稳重,可靠的感觉,是最常见的工作服装用色。 在选择服装、饰物的色彩时,应考虑到各种色调的协调与肤色,选定合适的着装、饰物。 (三)仪表应注意场合根据不同的场合来进行着装,喜庆场合,庄重场合及悲伤场合应注意有不同的服装,要遵循不同的规范与风俗。

零基础可以学好Java吗?

第一阶段:企业入门级项目阶段,可掌握Java核心基础、面向对象、JavaSE API、MySQL数据库、JDBS、HTML+CSS、Servlet、JSP、JavaScript等,可以完成常见中小型互联网项目开发,达到初级Java开发工程师能力。 第二阶段企业框架级项目阶段是进阶阶段,增强Java基础、web基础、CSS/JavaScript进阶、Maven项目管理、Spring5、SpringMVC、Mybatis、SMM综合项目、学员项目等,完成本阶段学员可以胜任各行业企业级项目中高级工程师岗位。 第三阶段亿级大并发分布式项目阶段,掌握LINUX、Docker、Vue、SpringBoot、项目整合Vue、分布式项目、Zookeepr、Dubbo、Springcloud、Redis、Elasticsearch、Quartz、RocketMQ、FastDFS、Mycat、CAS、分布式锁、分布式事务、微信开发、学生项目等,学员学完后可以胜任大型、超大型互联网项目开发高级工程师岗位。 初学Java虽然有一定的难度,但Java学习并不是不可逾越,只要你明确方向,找到有效的学习方法,坚持学习,一定能攻克Java难关,成为一名合格的Java开发工程师。 如果你是零基础自学,那么所花费的时间与精力是不可估计的。 如果系统学习5个月的时间可以帮助你快速成长。

求解 青岛李沧炒股开户具体流程

在北山,李沧区人才交流中心旁边,果园路那里.关于交易费用的问题:一, 深圳:1,印花税 0.1%2,规费 0.%3,券商的手续费,因券商不同而不同,最高0.3%,最低5元二, 上海:1,印花税 0.1%2,规费 0.%3,过户费 每1000股受1元,不够1元收1元3,券商的手续费,因券商不同而不同,最高0.3%,最低5元.简单的说,带着你的身份证、100元钱到券商营业厅的开户柜开户就行了。 开完户后资金怎么入市你自会知道。 以下是详细的说:股市开户(一)开设股票帐户客户欲进入股市必须先开立股票帐户,股票帐户是投资者进入市场的通行证,只有拥有它,才能进场买卖证券。 股票帐户在深圳又叫股东代码卡。 1.开设股票帐户所需要提供的证件。 股票帐户可分为个人帐户与法人帐户两种。 个人开户,个人投资者必须持有本人有效身份证件(一般为本人身份证)。 法人开户所需提供的资料有:有效的法人证明文件(营业执照)及其复印件;法人代表证明书及其本人身份证、法人委托书及受托人身份证。 上海证交所的股票帐户由交易所所属的证券登记公司集中统一管理,具体的开户手续委托有关机构办理。 在深圳证交所,开立股票帐户,除了提供本人身份证外,还需提供指定的银行存折。 深圳的开户工作由深圳证券登记公司统一受理。 自然人或法人可到所选择的证券经营机构所在地的证券登记机构办理开户手续。 2.开设股票帐户所需提供的资料。 个人投资者在开设股票帐户时,应详细提供本人和委托人的详细资料,包括本人和委托人的姓名、性别、身份证号码、家庭地址、职业、联系电话等。 法人投资者应提供法人地址、电话、法定代表人和授权证券交易执行人的姓名、性别、书面授权书、开户银行帐户和帐号、邮编、机构性质等。 3.开设股票帐户的基本条件。 根据国家的有关,下列人员不得办理股票开户:⑴证券主管机关中管理证券事务的有关人员;⑵证券交易所管理人员;⑶证券经营机构中与股票发行或交易有直接关系的人员;⑷与发行者有直接行政隶属或管理关系的机关工作人员;⑸其他与股票发行或交易有关的知情人。 在办理上海证券交易所的股票证券帐户后,需办理指定交易,即可指定该帐户在某一证券商处进行交易。 此种指定交易随时可以办理,也可随时撤销。 深圳证券交易所的股票帐户开设后,只能在指定的证券机构处办理委托买卖。 投资者如需在其他证券经营机构处委托,必须事先办理转托管手续。 随着证券市场的发展,股票帐户的功能已不限于股票已扩大至基金、股权证、无纸化国债等。 (二)开设资金帐户办理了股票帐户后还需办理资金帐户。 目前在上交所系统,资金帐户在证券机构处开立且仅在该机构处有效。 证券经营机构按银行活期存款利率对投资者资金帐户上的存款支付利息。 开立资金帐户所需文件及资料基本与股票帐户相同。 磁卡帐户。 目前股票帐户与资金帐户功能合二为一的磁卡帐户也逐渐普及,以便整个资金帐户联网后,就可集中办理清算工作。 (三)客户填写委托单客户在办妥股票帐户与资金帐户后即可进入市场买卖,客户填写的买卖证券的委托单是客户与证券商之间确定代理关系的文件,具有法律效力。 委托单一般为二联或三联,一联由证券商审核盖章确认后交由客户,一联由证券商据以执行。 买卖成交后,客户凭委托单前往证券商处办理清算与交割。 如果成交结果与委托单内容不符,客户可凭委托单向证券商提出交涉,维护自己的合法权益。 (四)证券商受理委托证券商受理委托包括审查、申报与输入三个基本环节。 目前除这种传统的三个环节方式外,还有两种方式:一是审查、申报、输入三环节一气呵成,客户采用自动委托方式输入电脑,电脑进行审查确认后,直接进入场所内计算机主机;二是证券商接受委托审查后,直接进行电脑输入。 (五)撮合成交现代证券市场的运作是以交易的自动化和股份清算与过户的无纸化为特征,电脑撮合集中交易作业程序是:证券商的买卖申报由终端机输入,每一笔委托由委托序号(即客户委托时的合同序号),买卖区分(输入时分别有0、1表示),证券代码(输入时用指定的4位或6位数字,而回显时用汉字列出证券名称),委托手续,委托限价,有效天数等几项信息组成。 电脑根据输入的信息进行竞价处理(分集合竞价和连续竞价),按“价格优先,时间优先”的原则自动撮合成交。 (六)清算与交割清算是指证券买卖双方在证券交易所进行的证券买卖成交之后,通过证券交易所将证券商之间证券买卖的数量和金额分别予以抵消,计算应收、应付证券和应付股金的差额的一种程序。 目前深市是“集中清算与分散登记”模式,上海股市是“集中清算与集中登记”模式,在此不详细介绍。 交割是指投资者与受托证券商就成交的买卖办理资金与股份清算业务的手续,深沪两地交易均根据集中清算净额交收的原则办理。 (七)过户所谓过户就是办理清算交割后,将原卖出证券的户名变更为买入证券的户名。 对于记名证券来讲,只有办妥过户者是整个交易过程的完成,才表明拥有完整的证券所有权。 目前在两个证券交易所上市的个人股票通常不需要股民亲自去办理过户手续。 A股买卖交易即按上述规程完成。 我个人建议: 在啤酒城后面有一个中信万通,而且在啤酒城西面有一个农业银行,那里面有一个中信万通的开户点,免费开户,但是它的营业厅在南京路,你可以将来转一下。

本文版权声明本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请联系本站客服,一经查实,本站将立刻删除。

发表评论

热门推荐