文章列表

SpringBoot面试杀手锏——自动配置原理

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">引言</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">不论在工作中,亦或是求职面试,Spring Boot已经成为我们必知必会的技能项。除了某些老旧的政府项目或金融项目持有观望态度外,如今的各行各业都在飞速的拥抱这个已经不是很新的Spring启动框架。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">当然,作为Spring Boot的精髓,自动配置原理的工作过程往往只有在“面试”的时候才能用得上,但是如果在工作中你能够深入的理解Spring Boot的自动配置原理,将无往不利。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">Spring Boot的出现,得益于“习惯优于配置”的理念,没有繁琐的配置、难以集成的内容(大多数流行第三方技术都被集成),这是基于Spring 4.x提供的按条件配置Bean的能力。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">Spring Boot的配置文件</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">初识Spring Boot时我们就知道,Spring Boot有一个全局配置文件:<code style="">application.properties</code>或<code style="">application.yml</code>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">我们的各种属性都可以在这个文件中进行配置,最常配置的比如:<code style="">server.port</code>、<code style="">logging.level.*</code> 等等,然而我们实际用到的往往只是很少的一部分,那么这些属性是否有据可依呢?答案当然是肯定的,这些属性都可以在官方文档中查找到:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(178, 174, 197);background: rgb(255, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;color: rgb(102, 102, 102);line-height: 2;">https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#common-application-properties</p> </blockquote> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5230166503428012" src="/upload/84601b2bf4a8bb215482b123d0b62e25.png" data-type="png" data-w="1021" style="border-radius: 0px 0px 5px 5px;display: block;margin: 3px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">(所以,话又说回来,找资料还得是官方文档,百度出来一大堆,还是稍显业余了一些)</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">除了官方文档为我们提供了大量的属性解释,我们也可以使用IDE的相关提示功能,比如IDEA的自动提示,和Eclipse的YEdit插件,都可以很好的对你需要配置的属性进行提示,下图是使用Eclipse的YEdit插件的效果,Eclipse的版本是:STS 4。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.4497354497354497" src="/upload/4b49e8b62f8b62ce203512797eb3b100.png" data-type="png" data-w="378" style="border-radius: 0px 0px 5px 5px;display: block;margin: 3px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">以上,是Spring Boot的配置文件的大致使用方法,其实都是些题外话。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">那么问题来了:<strong>这些配置是如何在Spring Boot项目中生效的呢?</strong> 那么接下来,就需要聚焦本篇博客的主题:自动配置工作原理或者叫实现方式。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">工作原理剖析</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">Spring Boot关于自动配置的源码在<code style="">spring-boot-autoconfigure-x.x.x.x.jar</code>中:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.9358288770053476" src="/upload/536108b88ade3013fb5786908bafa6d9.png" data-type="png" data-w="374" style="border-radius: 0px 0px 5px 5px;display: block;margin: 3px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">当然,自动配置原理的相关描述,官方文档貌似是没有提及。不过我们不难猜出,Spring Boot的启动类上有一个<code style="">@SpringBootApplication</code>注解,这个注解是Spring Boot项目必不可少的注解。那么自动配置原理一定和这个注解有着千丝万缕的联系!</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">@EnableAutoConfiguration</span></h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.22713257965056527" src="/upload/5991f1de0c6aeb6413d60e21eed11c1.png" data-type="png" data-w="973" style="border-radius: 0px 0px 5px 5px;display: block;margin: 3px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><code style="">@SpringBootApplication</code>是一个复合注解或派生注解,在<code style="">@SpringBootApplication</code>中有一个注解<code style="">@EnableAutoConfiguration</code>,翻译成人话就是开启自动配置,其定义如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.30740037950664134" src="/upload/2495dfceb6cc6090d3324f1a3b8f3854.png" data-type="png" data-w="527" style="border-radius: 0px 0px 5px 5px;display: block;margin: 3px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">而这个注解也是一个派生注解,其中的关键功能由@Import提供,其导入的AutoConfigurationImportSelector的<code style="">selectImports()</code>方法通过<code style="">SpringFactoriesLoader.loadFactoryNames()</code>扫描所有具有<strong>META-INF/spring.factories</strong> 的jar包。<code style="">spring-boot-autoconfigure-x.x.x.x.jar</code>里就有一个这样的<code style="">spring.factories</code>文件。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这个<code style="">spring.factories</code>文件也是一组一组的key=value的形式,其中一个key是<code style="">EnableAutoConfiguration</code>类的全类名,而它的value是一个<code style="">xxxxAutoConfiguration</code>的类名的列表,这些类名以逗号分隔,如下图所示:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.2447954055994257" src="/upload/7ecd808980535422b0356178f0bac205.png" data-type="png" data-w="1393" style="border-radius: 0px 0px 5px 5px;display: block;margin: 3px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这个<code style="">@EnableAutoConfiguration</code>注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在<code style="">SpringApplication.run(...)</code>的内部就会执行<code style="">selectImports()</code>方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="color: rgb(0, 0, 0);font-family: PingFangSC-Light;font-size: 15px;letter-spacing: 3px;text-align: left;word-spacing: 1.5px;background-color: rgb(255, 255, 255);">另外关注公众号码猿技术专栏,回复关键词“面试宝典”,送你一份阿里巴巴内部面试宝典!<br></span></p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">自动配置生效</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">每一个XxxxAutoConfiguration自动配置类都是在某些条件之下才会生效的,这些条件的限制在Spring Boot中以注解的形式体现,常见的条件注解有如下几项:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <code style="">@ConditionalOnBean</code>:当容器里有指定的bean的条件下。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <code style="">@ConditionalOnMissingBean</code>:当容器里不存在指定bean的条件下。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <code style="">@ConditionalOnClass</code>:当类路径下有指定类的条件下。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <code style="">@ConditionalOnMissingClass</code>:当类路径下不存在指定类的条件下。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <code style="">@ConditionalOnProperty</code>:指定的属性是否有指定的值,比如 <code style="">@ConditionalOnProperties(prefix=”xxx.xxx”, value=”enable”, matchIfMissing=true)</code>,代表当xxx.xxx为enable时条件的布尔值为true,如果没有设置的情况下也为true。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">以<code style="">ServletWebServerFactoryAutoConfiguration</code>配置类为例,解释一下全局配置文件中的属性如何生效,比如:<code style="">server.port=8081</code>,是如何生效的(当然不配置也会有默认值,这个默认值来自于<code style="">org.apache.catalina.startup.Tomcat</code>)。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.25838926174496646" src="/upload/c17782a5ebeb01bb27998e42ef356906.png" data-type="png" data-w="894" style="border-radius: 0px 0px 5px 5px;display: block;margin: 3px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">在<code style="">ServletWebServerFactoryAutoConfiguration</code>类上,有一个<code style="">@EnableConfigurationProperties</code>注解:开启配置属性,而它后面的参数是一个ServerProperties类,这就是习惯优于配置的最终落地点。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.4312977099236641" src="/upload/58568496b1536c924ae2f8af9e19e12a.png" data-type="png" data-w="786" style="border-radius: 0px 0px 5px 5px;display: block;margin: 3px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">在这个类上,我们看到了一个非常熟悉的注解:<code style="">@ConfigurationProperties</code>,它的作用就是从配置文件中绑定属性到对应的bean上,而<code style="">@EnableConfigurationProperties</code>负责导入这个已经绑定了属性的bean到spring容器中(见上面截图)。那么所有其他的和这个类相关的属性都可以在全局配置文件中定义,也就是说,真正“限制”我们可以在全局配置文件中配置哪些属性的类就是这些XxxxProperties类,它与配置文件中定义的prefix关键字开头的一组属性是唯一对应的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">至此,我们大致可以了解。在全局配置的属性如:<code style="">server.port</code>等,通过<code style="">@ConfigurationProperties</code>注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,然后再通过<code style="">@EnableConfigurationProperties</code>注解导入到Spring容器中。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">而诸多的<code style="">XxxxAutoConfiguration</code>自动配置类,就是Spring容器的JavaConfig形式,作用就是为Spring 容器导入bean,而所有导入的bean所需要的属性都通过<code style="">xxxxProperties</code>的bean来获得。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">可能到目前为止还是有所疑惑,但面试的时候,其实远远不需要回答的这么具体,你只需要这样回答:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(178, 174, 197);background: rgb(255, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;color: rgb(102, 102, 102);line-height: 2;">Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">通过一张图标来理解一下这一繁复的流程:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.2572298325722984" src="/upload/ce72b259a26d3d4e20fbc317c70eab62.png" data-type="png" data-w="1314" style="border-radius: 0px 0px 5px 5px;display: block;margin: 3px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">图片来自于王福强老师的博客:<code style="">https://afoo.me/posts/2015-07-09-how-spring-boot-works.html</code></p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">总结</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">综上是对自动配置原理的讲解。当然,在浏览源码的时候一定要记得不要太过拘泥与代码的实现,而是应该抓住重点脉络。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">一定要记得<code style="">XxxxProperties</code>类的含义是:封装配置文件中相关属性;<code style="">XxxxAutoConfiguration</code>类的含义是:自动配置类,目的是给容器中添加组件。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">而其他的主方法启动,则是为了加载这些五花八门的类。</p> </section>

带着8个问题5分钟教你学会Arthas诊断工具

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;white-space: normal;font-size: 16px;word-break: break-word;text-align: left;line-height: 1.25;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;background-image: linear-gradient(90deg, rgba(50, 0, 0, 0.04) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(50, 0, 0, 0.04) 3%, rgba(0, 0, 0, 0) 3%);background-size: 20px 20px;background-position: center center;"> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;color: black;border-bottom: 4px solid rgb(64, 184, 250);"><span style="margin-left: 25px;display: flex;color: rgb(64, 184, 250);font-size: 20px;">前言</span><span style="margin-top: -10px;display: flex;width: 200px;height: 10px;border-top-left-radius: 20px;background: rgba(64, 184, 250, 0.5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;"></span></h2> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 14px;word-spacing: 2px;"><code style="margin-right: 2px;margin-left: 2px;padding-right: 2px;padding-left: 2px;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);border-radius: 2px;height: 21px;line-height: 22px;">Arthas</code>&nbsp;是Alibaba开源的Java诊断工具,深受开发者喜爱。</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 14px;word-spacing: 2px;">当你遇到以下类似问题而束手无策时,<code style="margin-right: 2px;margin-left: 2px;padding-right: 2px;padding-left: 2px;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);border-radius: 2px;height: 21px;line-height: 22px;">Arthas</code>可以帮助你解决:</p> <ol data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 533.891px;font-size: 15px;color: rgb(89, 89, 89);"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现! </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 是否有一个全局视角来查看系统的运行状况? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 有什么办法可以监控到JVM的实时运行状态? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 怎么快速定位应用的热点,生成火焰图? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> 怎样直接从JVM内查找某个类的实例? </section></li> </ol> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 14px;word-spacing: 2px;">这 8 个问题,Arthas 官方文档(<code style="margin-right: 2px;margin-left: 2px;padding-right: 2px;padding-left: 2px;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);border-radius: 2px;height: 21px;line-height: 22px;">https://arthas.aliyun.com/doc</code>)中并没有给出答案或标准的解决方案。</p> <p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.9182242990654206" data-s="300,640" src="/upload/c857a27cfd15fb085214dc8ca89a60b9.png" data-type="png" data-w="428" style="box-sizing: border-box !important;"></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <figcaption style="margin-top: 5px;text-align: center;font-size: 13px;"> <span style="margin-right: 5px;margin-bottom: -5px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrdZqwc4PQoHfCQpaVevuymWEsDVlkrib28R8v1T1q9tD1iaibk4oZjJqEfQYXuicj7RZkCVEzrUg6Gueg/640?wx_fmt=png&quot;);display: inline-block;width: 18px;height: 18px;background-size: 18px;background-repeat: no-repeat;background-position: center center;"></span>坑爹啊 </figcaption> </figure> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 14px;word-spacing: 2px;">这不是管杀不管埋吗!!!</p> <p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="1.0723860589812333" data-s="300,640" src="/upload/50815864acc954aa3bd8b2101c639fc7.png" data-type="png" data-w="373" style="box-sizing: border-box !important;"></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <figcaption style="margin-top: 5px;text-align: center;font-size: 13px;"> <span style="margin-right: 5px;margin-bottom: -5px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrdZqwc4PQoHfCQpaVevuymWEsDVlkrib28R8v1T1q9tD1iaibk4oZjJqEfQYXuicj7RZkCVEzrUg6Gueg/640?wx_fmt=png&quot;);display: inline-block;width: 18px;height: 18px;background-size: 18px;background-repeat: no-repeat;background-position: center center;"></span>管杀不管埋 </figcaption> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;color: black;border-bottom: 4px solid rgb(64, 184, 250);"><span style="margin-bottom: -22px;display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrdZqwc4PQoHfCQpaVevuymW46ibEVR2iavELxEdbXYj0ic6QXib6OwPVibnyfF581cfvq0DwDyNOHFNYYg/640?wx_fmt=png&quot;);"></span><span style="margin-left: 25px;display: flex;color: rgb(64, 184, 250);font-size: 20px;">正文</span><span style="margin-top: -10px;display: flex;width: 200px;height: 10px;border-top-left-radius: 20px;background: rgba(64, 184, 250, 0.5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;"></span></h2> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 14px;word-spacing: 2px;"><strong style="color: rgb(53, 148, 247);">「下面是笔者结合多年使用 Arthas 的经验,针对这 8 个问题给出的详细解决方案,如果有疑问欢迎评论区指出。」</strong></p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;color: black;border-bottom: 4px solid rgb(64, 184, 250);"><span style="margin-bottom: -22px;display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrdZqwc4PQoHfCQpaVevuymW46ibEVR2iavELxEdbXYj0ic6QXib6OwPVibnyfF581cfvq0DwDyNOHFNYYg/640?wx_fmt=png&quot;);"></span><span style="margin-left: 25px;display: flex;color: rgb(64, 184, 250);font-size: 20px;">准备</span><span style="margin-top: -10px;display: flex;width: 200px;height: 10px;border-top-left-radius: 20px;background: rgba(64, 184, 250, 0.5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;"></span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;font-weight: bold;font-size: 17px;color: black;text-align: center;"><span style="padding-bottom: 2px;border-bottom: 2px solid rgba(79, 177, 249, 0.65);color: rgb(43, 43, 43);"><span style="margin: auto auto -8px;width: 30px;height: 30px;display: block;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrdZqwc4PQoHfCQpaVevuymWQJpIJPfnra8nIXE6QEuWO34DbdJuoMtEoMfeJLYsByZZC6pnfyOsVQ/640?wx_fmt=png&quot;);background-position: center center;background-size: 30px;opacity: 1;background-repeat: no-repeat;"></span>先给出我的测试代码</span></h3> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding: 0.4em 0.6em;border-radius: 5px;background: rgb(248, 248, 248);box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/et9q9FvjBBodkVQcdIh6uVX88NMCx5mpbrP4pIIVppP6FL3SYQ91XcVOSvlIdia2icRFQvyjTEsYmYEy6ynQRiavakcpxH6DPicV/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 542.812px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;letter-spacing: 0px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">package</span>&nbsp;com.shockang.study;<br><br><span style="color: rgb(198, 120, 221);line-height: 26px;">import</span>&nbsp;com.alibaba.fastjson.JSON;<br><span style="color: rgb(198, 120, 221);line-height: 26px;">import</span>&nbsp;lombok.AccessLevel;<br><span style="color: rgb(198, 120, 221);line-height: 26px;">import</span>&nbsp;lombok.Getter;<br><span style="color: rgb(198, 120, 221);line-height: 26px;">import</span>&nbsp;lombok.Setter;<br><span style="color: rgb(198, 120, 221);line-height: 26px;">import</span>&nbsp;lombok.ToString;<br><span style="color: rgb(198, 120, 221);line-height: 26px;">import</span>&nbsp;lombok.experimental.FieldDefaults;<br><br><span style="color: rgb(198, 120, 221);line-height: 26px;">import</span>&nbsp;java.util.List;<br><span style="color: rgb(198, 120, 221);line-height: 26px;">import</span>&nbsp;java.util.concurrent.TimeUnit;<br><br><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">class</span>&nbsp;<span style="color: rgb(230, 192, 123);line-height: 26px;">ArthasDemo</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">public</span>&nbsp;<span style="color: rgb(198, 120, 221);line-height: 26px;">static</span>&nbsp;<span style="color: rgb(198, 120, 221);line-height: 26px;">void</span>&nbsp;<span style="color: rgb(97, 174, 238);line-height: 26px;">main</span><span style="line-height: 26px;">(String[]&nbsp;args)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;s&nbsp;=&nbsp;<span style="color: rgb(152, 195, 121);line-height: 26px;">"[{\"name\":\"zhangsan\",\"age\":\"10\",\"telephone\":\"123456\",\"interests\":[\"sing\",\"dance\",\"rap\"]},\n"</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(152, 195, 121);line-height: 26px;">"{\"name\":\"lisi\",\"age\":\"20\",\"telephone\":\"123457\",\"interests\":[\"sing\",\"swim\"]},\n"</span>&nbsp;+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(152, 195, 121);line-height: 26px;">"{\"name\":\"wangwu\",\"age\":\"30\",\"telephone\":\"123458\",\"interests\":[\"sing\",\"program\"]}]"</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">//模拟一遍遍的调用方法的过程</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(198, 120, 221);line-height: 26px;">for</span>&nbsp;(;&nbsp;;&nbsp;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(<span style="color: rgb(198, 120, 221);line-height: 26px;">new</span>&nbsp;ArthasDemo().convert(s));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(198, 120, 221);line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TimeUnit.SECONDS.sleep(<span style="color: rgb(209, 154, 102);line-height: 26px;">10</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: rgb(198, 120, 221);line-height: 26px;">catch</span>&nbsp;(InterruptedException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">private</span>&nbsp;List&lt;People&gt;&nbsp;<span style="color: rgb(97, 174, 238);line-height: 26px;">convert</span><span style="line-height: 26px;">(String&nbsp;s)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(198, 120, 221);line-height: 26px;">return</span>&nbsp;JSON.parseArray(s,&nbsp;People<span style="line-height: 26px;">.<span style="color: rgb(198, 120, 221);line-height: 26px;">class</span>)</span>;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(97, 174, 238);line-height: 26px;">@Getter</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(97, 174, 238);line-height: 26px;">@Setter</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(97, 174, 238);line-height: 26px;">@ToString</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(97, 174, 238);line-height: 26px;">@FieldDefaults</span>(level&nbsp;=&nbsp;AccessLevel.PRIVATE)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(198, 120, 221);line-height: 26px;">private</span>&nbsp;<span style="color: rgb(198, 120, 221);line-height: 26px;">static</span>&nbsp;<span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">class</span>&nbsp;<span style="color: rgb(230, 192, 123);line-height: 26px;">People</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;姓名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;name;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;年龄<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;age;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;电话<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;telephone;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;兴趣列表<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&lt;String&gt;&nbsp;interests;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: black;"><span style="height: 16px;line-height: 16px;font-size: 16px;"><span style="margin-right: 6px;margin-bottom: -2px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrdZqwc4PQoHfCQpaVevuymWR8bEXaEQibtiaP5lQoqdIm89hFKiaRSeOzMaYDVYA0iciacaWum2PUvXjdA/640?wx_fmt=png&quot;);display: inline-block;background-size: 100%;background-position: left bottom;background-repeat: no-repeat;width: 16px;height: 15px;line-height: 15px;"></span>以下是控制台正常打印的结果</span></h4> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding: 0.4em 0.6em;border-radius: 5px;background: rgb(248, 248, 248);box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/et9q9FvjBBodkVQcdIh6uVX88NMCx5mpbrP4pIIVppP6FL3SYQ91XcVOSvlIdia2icRFQvyjTEsYmYEy6ynQRiavakcpxH6DPicV/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 542.812px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;font-size: 12px;letter-spacing: 0px;background: rgb(40, 44, 52);border-radius: 5px;">/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/bin/java ...<br>[ArthasDemo.People(name=zhangsan, age=10, telephone=123456, interests=[sing, dance, rap]), ArthasDemo.People(name=lisi, age=20, telephone=123457, interests=[sing, swim]), ArthasDemo.People(name=wangwu, age=30, telephone=123458, interests=[sing, program])]<br>[ArthasDemo.People(name=zhangsan, age=10, telephone=123456, interests=[sing, dance, rap]), ArthasDemo.People(name=lisi, age=20, telephone=123457, interests=[sing, swim]), ArthasDemo.People(name=wangwu, age=30, telephone=123458, interests=[sing, program])]<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;font-weight: bold;font-size: 17px;color: black;text-align: center;"><span style="padding-bottom: 2px;border-bottom: 2px solid rgba(79, 177, 249, 0.65);color: rgb(43, 43, 43);"><span style="margin: auto auto -8px;width: 30px;height: 30px;display: block;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrdZqwc4PQoHfCQpaVevuymWQJpIJPfnra8nIXE6QEuWO34DbdJuoMtEoMfeJLYsByZZC6pnfyOsVQ/640?wx_fmt=png&quot;);background-position: center center;background-size: 30px;opacity: 1;background-repeat: no-repeat;"></span>下载并运行 Arthas</span></h3> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 14px;word-spacing: 2px;">按照下图中的步骤,选择一个 Java 进程进行 attach。</p> <img data-ratio="0.7829059829059829" src="/upload/116b579a61deec0c2def85ad5ea3a2df.png" data-type="png" data-w="585" style="box-sizing: border-box !important;"> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <figcaption style="margin-top: 5px;text-align: center;font-size: 13px;"> <span style="margin-right: 5px;margin-bottom: -5px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrdZqwc4PQoHfCQpaVevuymWEsDVlkrib28R8v1T1q9tD1iaibk4oZjJqEfQYXuicj7RZkCVEzrUg6Gueg/640?wx_fmt=png&quot;);display: inline-block;width: 18px;height: 18px;background-size: 18px;background-repeat: no-repeat;background-position: center center;"></span>下载并运行Arthas </figcaption> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;font-weight: bold;font-size: 17px;color: black;text-align: center;"><span style="padding-bottom: 2px;border-bottom: 2px solid rgba(79, 177, 249, 0.65);color: rgb(43, 43, 43);"><span style="margin: auto auto -8px;width: 30px;height: 30px;display: block;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrdZqwc4PQoHfCQpaVevuymWQJpIJPfnra8nIXE6QEuWO34DbdJuoMtEoMfeJLYsByZZC6pnfyOsVQ/640?wx_fmt=png&quot;);background-position: center center;background-size: 30px;opacity: 1;background-repeat: no-repeat;"></span>访问 WebConsole</span></h3> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 14px;word-spacing: 2px;">attach 成功后可以打开谷歌浏览器输入<code style="margin-right: 2px;margin-left: 2px;padding-right: 2px;padding-left: 2px;font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);border-radius: 2px;height: 21px;line-height: 22px;">http://127.0.0.1:3658/</code>&nbsp;打开 WebConsole</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 14px;word-spacing: 2px;">(吐槽一句 Mac OS 的 Safari 浏览器不支持)</p> <blockquote data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;padding: 10px 10px 10px 20px;box-sizing: inherit;border-width: 1px;border-top-style: solid;border-right-style: solid;border-bottom-style: solid;border-color: rgba(64, 184, 250, 0.4);color: rgb(89, 89, 89);line-height: 1.55em;font-size: 0.9em;overflow: auto;text-size-adjust: 100%;border-radius: 6px;background: rgba(64, 184, 250, 0.1);"> <span style="color: rgba(64, 184, 250, 0.5);font-size: 34px;line-height: 1;font-weight: 700;">❝</span> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;word-spacing: 2px;line-height: 26px;">使用 WebConsole 最方便的是你可以打开多个标签页同时操作</p> <span style="float: right;color: rgba(64, 184, 250, 0.5);">❞</span> </blockquote> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;color: black;border-bottom: 4px solid rgb(64, 184, 250);"><span style="margin-bottom: -22px;display: flex;width: 20px;height: 20px;background-size: 20px 20px;background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/TLH3CicPVibrdZqwc4PQoHfCQpaVevuymW46ibEVR2iavELxEdbXYj0ic6QXib6OwPVibnyfF581cfvq0DwDyNOHFNYYg/640?wx_fmt=png&quot;);"></span><span style="margin-left: 25px;display: flex;color: rgb(64, 184, 250);font-size: 20px;">问题 1:这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?</span><span style="margin-top: -10px;display: flex;width: 200px;height: 10px;border-top-left-radius: 20px;background: rgba(64, 184, 250, 0.5);color: rgb(255, 255, 255);font-size: 16px;letter-spacing: 0.544px;justify-content: flex-end;float: right;"></span></h2> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 14px;word-spacing: 2px;">这个问题我经常在处理各种<strong style="color: rgb(53, 148, 247);">「依赖冲突」</strong>的时候遇到,有一些类的完全名称是一模一样,通过常规的办法无法解决类具体从哪个 jar 包加载。</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 14px;word-spacing: 2px;">别急,看我下面的解决办法。</p> <ol data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 533.891px;font-size: 15px;color: rgb(89, 89, 89);"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;font-size: 14px;"> sc </section></li> </ol> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 14px;word-spacing:

分布式事务 — 可靠消息最终一致性方案

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;line-height: 1.6;letter-spacing: 0.034em;color: rgb(63, 63, 63);font-size: 16px;" data-mpa-powered-by="yiban.io"> <p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.42334739803094235" data-s="300,640" src="/upload/7b060e95e7640789ef4b05062617ba40.png" data-type="png" data-w="1422" style=""></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">大家好,我是悟空,<span style="color: rgb(255, 76, 65);">分布式事务</span>,一直被各大厂面试的时候问到,今天带大家来看下分布式事务其中的一种:可靠消息最终一致性方案。<br></p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;background: rgb(251, 249, 253);color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;padding: 15px 20px;line-height: 27px;border-left-color: rgb(53, 179, 120);"> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">事务想必大家并不陌生,比如经常被人提起的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">ACID</code>,但是为了后续的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">分布式事务</code>的内容,我们先来聊聊 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">ACID</code>,然后再介绍下什么是<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">分布式事务</code>,最后着重讲下基于<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">可靠消息</code>的分布式事务解决方案。</p> </blockquote> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/7VkkuTzAZPp6kYUSKMgqKOzzREjR2yleq4Dqbh3O7LCB50icbN7A5ibM8IQ98BOEtDxib3PHymEmico5biaHKU2XSiag/640?wx_fmt=png&quot;);background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">什么是事务</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">严格意义上的事务应该是具备原子性、一致性、隔离性和持久性,简称 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">ACID</code>。</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">原子性(Atomicity)</code>,可以理解为一个事务内的所有操作要么都执行,要么都不执行。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">一致性(Consistency)</code>,可以理解为数据是满足完整性约束的,也就是不会存在中间状态的数据,比如你钱包有100,我钱包有100,你给我打50块,此时你钱包的钱应该是50,我钱包的钱应该是150,不会存在我钱加了,你钱没扣的中间状态。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">隔离性(Isolation)</code>,指的是多个事务并发执行的时候不会互相干扰,即一个事务内部的数据对于其他事务来说是隔离的。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">持久性(Durability)</code>,指的是一个事务完成了之后数据就被永远保存下来,之后的其他操作或故障都不会对事务的结果产生影响。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">而通俗意义上事务就是为了使得一些更新操作<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">要么都成功,要么都失败</code>。</p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/7VkkuTzAZPp6kYUSKMgqKOzzREjR2yleq4Dqbh3O7LCB50icbN7A5ibM8IQ98BOEtDxib3PHymEmico5biaHKU2XSiag/640?wx_fmt=png&quot;);background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">什么是分布式事务</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">分布式事务</code>顾名思义就是要在分布式系统中实现事务,它其实是由多个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">本地事务</code>组合而成。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">一次大的操作由不同的小操作组成的,这些小的操作分布在不同的服务器上,<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">分布式事务</code>需要保证这些小操作要么全部成功,要么全部失败。从本质上来说,<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">分布式事务</code>就是为了保证<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">不同数据库</code>的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">数据一致性</code>。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">常见的分布式事务的解决方案有以下几种:<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">2PC,3PC,TCC,本地消息表、可靠消息最终一致性、尽最大努力通知</code>等</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">今天我们就着重讲讲<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">可靠消息最终一致性的解决方案</code></p> <h3 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;margin-top: 1.2em;"><span style="background-image: url(&quot;https://mmbiz.qpic.cn/mmbiz_png/7VkkuTzAZPp6kYUSKMgqKOzzREjR2yleq4Dqbh3O7LCB50icbN7A5ibM8IQ98BOEtDxib3PHymEmico5biaHKU2XSiag/640?wx_fmt=png&quot;);background-size: 100% 100%;background-repeat: no-repeat;display: inline-block;width: 16px;height: 15px;line-height: 15px;margin-bottom: -1px;"></span><span style="display: none;"></span><span style="font-size: 17px;display: inline-block;margin-left: 8px;color: rgb(72, 179, 120);">什么是可靠消息最终一致性方案</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">可靠消息最终一致性方案</code>是指当事务发起方执行完成本地事务后发出消息到<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">消息中间件</code>,<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">事务参与方(消息消费者)</code>一定能够接收到消息并处理事务成功,此方案强调的是<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">只要消息发给事务参与方,则最终事务要达到一致</code>。</p> <h4 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 18px;margin-top: 30px;"><span style="display: none;"></span>这个方式存在哪些问题?<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">此方案是通过消息中间件实现的,事务发起方(消息生产方)将消息发给消息中间件,事务参与方从消息中间件接收消息,由于网络通信的不确定性会导致分布式事务问题,如下图:</p> <p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.20179372197309417" data-s="300,640" src="/upload/39950960a8cd96c7898573104e9e3b32.png" data-type="png" data-w="669" style="height: auto !important;"></p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 本地事务与消息的原子性问题 </section></li> </ol> <p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.9557291666666666" data-s="300,640" src="/upload/ca09db0ff68367816bfbe977f48245f0.png" data-type="png" data-w="384" style="height: auto !important;"></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如上图在虚线框内,存在以下几种情况:<br></p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;background: rgb(251, 249, 253);color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;padding: 15px 20px;line-height: 27px;border-left-color: rgb(53, 179, 120);"> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">1)本地事务提交失败,则消息不发送。<br>2)本地事务成功,消息发送失败,本地事务回滚。<br>3)本地消息成功,消息超时,本地事务回滚,消息最终失败。<br>4)本地消息成功,消息超时,本地事务回滚,消息最终成功。<br></p> </blockquote> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">综上所述,存在<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">第四种情况</code>,造成本地事务,与消息参与方的事务不一致。</p> <ol start="2" data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 事务参与方接收消息的可靠性。 <br> </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">消息中间件与<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">事务参与方</code>要确保能够<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">成功消费</code>到消息。</p> <ol start="3" data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 消息重复消费 <br> </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">注意事务参与方的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">接口幂等性</code>问题,消息参与方可能已经成功消费,由于网络问题导致消息中间件认为消息未消费,发起重试之后产生的问题。</p> <h4 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;color: black;font-size: 18px;margin-top: 30px;"><span style="display: none;"></span>解决方案<span style="display: none;"></span></h4> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 本地消息表 <br> </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">本地消息表的关键在于本地有一张存储消息日志的记录表,需要启动一个定时任务去不停地扫描消息日志记录,确保消息能够被发送。具体流程如下图:</p> <p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5960264900662252" data-s="300,640" src="/upload/4256827548b7666bc976f61f4c59d348.png" data-type="png" data-w="755" style="height: auto !important;"></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">上图流程:<br></p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;background: rgb(251, 249, 253);color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;padding: 15px 20px;line-height: 27px;border-left-color: rgb(53, 179, 120);"> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">1)事务发起方本地事务执行成功,在本地消息表中记录消息日志。<br>2)启动定时任务,循环扫描本地消息表。<br>3)定时任务扫描到消息则发送消息到消息中间件。<br>4)消息中间件收到消息,成功返回消息发送成功通知给事务发起方。<br>5)事务发起方收到消息发送成功则删除日志消息。<br>6)事务参与方订阅消息,消费消息。<br>7)事务参与方处理本地事务。<br>8)本地事务处理成功,发送成功ack给消息中间件。<br></p> </blockquote> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">需要注意的点:<br><code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">事务参与方保证接口幂等性</code>。</p> <ol start="2" data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> RocketMq事务消息方案 <br> </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">Apache <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">RocketMQ</code> 4.3之后的版本正式支持<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">事务消息</code>,为分布式事务实现提供了便利性支持。在RocketMQ 4.3后实现了完整的事务消息,实际上其实是<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">对本地消息表的一个封装</code>,将本地消息表移动到了<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">MQ内部</code>,解决 Producer 端的消息发送与本地事务执行的<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">原子性</code>问题。</p> <p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.24826904055390703" data-s="300,640" src="/upload/8a8d8fc7597831f1faab2a65ccb79199.png" data-type="png" data-w="1011" style="height: auto !important;"></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">实现流程:<br></p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;background: rgb(251, 249, 253);color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;padding: 15px 20px;line-height: 27px;border-left-color: rgb(53, 179, 120);"> <p style="line-height: 26px;font-size: 15px;color: rgb(89, 89, 89);">1)事务发起方发送<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">Half</code>事务消息<br>2)RocketMq回复<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">Half</code>发送成功<br>3)事务发起方执行本地事务<br>4)事务发起方执行本地事务成功,发送commit到RocketMq,mq投递消息到事务参与方;事务发起方执行本地事务失败,发送<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">rollback</code>到RocketMq,mq删除消息。<br>5)当RocketMq一定时间内未收到来自事务发起方的确认信息,会对事务发起方进行<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">事务回查</code>。<br>6)事务发起方查询本地事务状态。<br>7)事务发起方根据查询到的事务状态发送<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">commint/rollback</code>到RocketMq。<br>8)当RocketMq发起<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">commit</code>后,收到失败或一定时间未收到成功ack,则会发起重试。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">优点</code>:</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">消息数据独立存储,降低业务系统与消息系统之间的耦合。<br>吞吐量优于本地消息表方案。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">缺点</code>:</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">一次消息发送需要两次网络请求(half消息 + commit/rollback)。<br>需要实现消息回查接口。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">其实每种分布式事务的解决方案都有优劣,我们需要权衡利弊,选择最合适业务场景的一种才是王道!</p> <hr data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;height: 1px;border-width: initial;border-style: none;border-color: initial;text-align: center;background-image: linear-gradient(to right, rgba(93, 186, 133, 0), rgba(93, 186, 133, 0.75), rgba(93, 186, 133, 0));"> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">好了。今天就说到这了,我还会不断分享自己的所学所想,希望我们一起走在成功的道路上!</p> </section> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzkyNjI0MTYwNQ==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/7VkkuTzAZPpHc0kB2AWPsLvdd3oC9u1QZvXmHniaUnicetGssmNiaydZvKibR2zZWgEA1zJzPGQ61doIl2MtBolkHQ/0?wx_fmt=png" data-nickname="狼王编程" data-alias="langwang-yyds" data-signature="狼王专注Java和架构领域,在coding的道路,一边学习一边分享,你可以和我聊篮球,也可以和我交流技术,只要你愿意,我们就有故事!" data-from="0"></mpprofile> <span style="background-color: rgb(253, 245, 13);color: rgb(73, 73, 72);font-weight: bold;letter-spacing: 1.5px;font-size: 15px;word-spacing: 2px;"></span> </section>

微服务架构和相关的组件

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 14px;padding: 10px;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">本文将介绍微服务架构和相关的组件,介绍他们是什么以及为什么要使用微服务架构和这些组件。本文侧重于简明地表达微服务架构的全局图景,因此不会涉及具体如何使用组件等细节。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">要理解微服务,首先要先理解不是微服务的那些。通常跟微服务相对的是单体应用,即将所有功能都打包成在一个独立单元的应用程序。从单体应用到微服务并不是一蹴而就的,这是一个逐渐演变的过程。本文将以一个网上超市应用为例来说明这一过程。</p> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">最初的需求</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">几年前,小明和小皮一起创业做网上超市。小明负责程序开发,小皮负责其他事宜。当时互联网还不发达,网上超市还是蓝海。只要功能实现了就能随便赚钱。所以他们的需求很简单,只需要一个网站挂在公网,用户能够在这个网站上浏览商品、购买商品;另外还需一个管理后台,可以管理商品、用户、以及订单数据。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们整理一下功能清单:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">网站:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-2"> <ul class="list-paddingleft-2" style="list-style-type: circle;"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="letter-spacing: 0px;">用户注册、登录功能</span> <br> </section></li> <li style="list-style-type: circle;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 商品展示 </section></li> <li style="list-style-type: circle;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 下单 </section></li> </ul> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">管理后台:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-2"> <ul class="list-paddingleft-2" style="list-style-type: circle;"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="letter-spacing: 0px;">用户管理</span> </section></li> <li style="list-style-type: circle;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 商品管理 </section></li> <li style="list-style-type: circle;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 订单管理 </section></li> </ul> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">由于需求简单,小明左手右手一个慢动作,网站就做好了。管理后台出于安全考虑,不和网站做在一起,小明右手左手慢动作重播,管理网站也做好了。总体架构图如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&amp;scene=21&amp;token=899450012&amp;lang=zh_CN#wechat_redirect" textvalue="你已选中了添加链接的内容" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="inset: auto;margin: 10px 0px 0px;"><img data-ratio="0.6446700507614214" src="/upload/28b1411125c841292e9436748ea8e2ab.png" data-type="png" data-w="591" style="display: block;margin: 0px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;"></span></a> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">小明挥一挥手,找了家云服务部署上去,网站就上线了。上线后好评如潮,深受各类肥宅喜爱。小明小皮美滋滋地开始躺着收钱。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">推荐下自己做的 Spring Boot 的实战项目:</p> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">https://github.com/YunaiV/ruoyi-vue-pro</p> </blockquote> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">随着业务发展……</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">好景不长,没过几天,各类网上超市紧跟着拔地而起,对小明小皮造成了强烈的冲击。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在竞争的压力下,小明小皮决定开展一些营销手段:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 开展促销活动。比如元旦全场打折,春节买二送一,情人节狗粮优惠券等等。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 拓展渠道,新增移动端营销。除了网站外,还需要开发移动端APP,微信小程序等。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 精准营销。利用历史数据对用户进行分析,提供个性化服务。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> …… </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这些活动都需要程序开发的支持。小明拉了同学小红加入团队。小红负责数据分析以及移动端相关开发。小明负责促销活动相关功能的开发。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为开发任务比较紧迫,小明小红没有好好规划整个系统的架构,随便拍了拍脑袋,决定把促销管理和数据分析放在管理后台里,微信和移动端APP另外搭建。通宵了几天后,新功能和新应用基本完工。这时架构图如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&amp;mid=2247487551&amp;idx=1&amp;sn=18f64ba49f3f0f9d8be9d1fdef8857d9&amp;chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&amp;scene=21&amp;token=899450012&amp;lang=zh_CN#wechat_redirect" textvalue="你已选中了添加链接的内容" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="inset: auto;margin: 10px 0px 0px;"><img data-ratio="0.8378839590443686" src="/upload/8a19d61b73f96983a6611ab103e77b91.png" data-type="png" data-w="586" style="display: block;margin: 0px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;"></span></a> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这一阶段存在很多不合理的地方:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 网站和移动端应用有很多相同业务逻辑的重复代码。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 数据有时候通过数据库共享,有时候通过接口调用传输。接口调用关系杂乱。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 单个应用为了给其他应用提供接口,渐渐地越改越大,包含了很多本来就不属于它的逻辑。应用边界模糊,功能归属混乱。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 管理后台在一开始的设计中保障级别较低。加入数据分析和促销管理相关功能后出现性能瓶颈,影响了其他应用。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 数据库表结构被多个应用依赖,无法重构和优化。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 所有应用都在一个数据库上操作,数据库出现性能瓶颈。特别是数据分析跑起来的时候,数据库性能急剧下降。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 开发、测试、部署、维护愈发困难。即使只改动一个小功能,也需要整个应用一起发布。有时候发布会不小心带上了一些未经测试的代码,或者修改了一个功能后,另一个意想不到的地方出错了。为了减轻发布可能产生的问题的影响和线上业务停顿的影响,所有应用都要在凌晨三四点执行发布。发布后为了验证应用正常运行,还得盯到第二天白天的用户高峰期…… </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 团队出现推诿扯皮现象。关于一些公用的功能应该建设在哪个应用上的问题常常要争论很久,最后要么干脆各做各的,或者随便放个地方但是都不维护。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">尽管有着诸多问题,但也不能否认这一阶段的成果:快速地根据业务变化建设了系统。不过紧迫且繁重的任务容易使人陷入局部、短浅的思维方式,从而做出妥协式的决策。在这种架构中,每个人都只关注在自己的一亩三分地,缺乏全局的、长远的设计。长此以往,系统建设将会越来越困难,甚至陷入不断推翻、重建的循环。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">推荐下自己做的 Spring Cloud 的实战项目:</p> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">https://github.com/YunaiV/onemall</p> </blockquote> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-ref

SpringBoot+Kafka+ELK 完成海量日志收集

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">整体流程大概如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.31587057010785824" src="/upload/aeb485313acd40997a034053bf459436.png" data-type="png" data-w="1298" style="border-radius: 0px 0px 5px 5px;display: block;margin: 3px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>服务器准备<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">在这先列出各服务器节点,方便同学们在下文中对照节点查看相应内容</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.36363636363636365" src="/upload/cef1e0c12e35d633545e0722f9e52a49.png" data-type="png" data-w="1199" style="border-radius: 0px 0px 5px 5px;display: block;margin: 3px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>SpringBoot项目准备<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">引入log4j2替换SpringBoot默认log,demo项目结构如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.3076923076923077" src="/upload/d3588a5044c320ba45d60d192eb8996a.png" data-type="png" data-w="520" style="border-radius: 0px 0px 5px 5px;display: block;margin: 3px auto;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong>pom</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">dependencies</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">dependency</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">groupId</span>&gt;</span>org.springframework.boot<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">artifactId</span>&gt;</span>spring-boot-starter-web<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">&lt;!--&nbsp;&nbsp;排除spring-boot-starter-logging&nbsp;--&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">exclusions</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">exclusion</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">groupId</span>&gt;</span>org.springframework.boot<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">artifactId</span>&gt;</span>spring-boot-starter-logging<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">exclusion</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">exclusions</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">dependency</span>&gt;</span>&nbsp;<br>&nbsp;<span style="color: #75715e;line-height: 26px;">&lt;!--&nbsp;log4j2&nbsp;--&gt;</span><br>&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">dependency</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">groupId</span>&gt;</span>org.springframework.boot<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">artifactId</span>&gt;</span>spring-boot-starter-log4j2<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">dependency</span>&gt;</span>&nbsp;<br>&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">dependency</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">groupId</span>&gt;</span>com.lmax<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">groupId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">artifactId</span>&gt;</span>disruptor<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">artifactId</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">version</span>&gt;</span>3.3.4<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">version</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">dependency</span>&gt;</span>&nbsp;<br><span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">dependencies</span>&gt;</span>&nbsp;<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong>log4j2.xml</strong></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;">&lt;?xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"?&gt;</span><br><span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">Configuration</span>&nbsp;<span style="line-height: 26px;">status</span>=<span style="color: #a6e22e;line-height: 26px;">"INFO"</span>&nbsp;<span style="line-height: 26px;">schema</span>=<span style="color: #a6e22e;line-height: 26px;">"Log4J-V2.0.xsd"</span>&nbsp;<span style="line-height: 26px;">monitorInterval</span>=<span style="color: #a6e22e;line-height: 26px;">"600"</span>&nbsp;&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">Properties</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">Property</span>&nbsp;<span style="line-height: 26px;">name</span>=<span style="color: #a6e22e;line-height: 26px;">"LOG_HOME"</span>&gt;</span>logs<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">Property</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">property</span>&nbsp;<span style="line-height: 26px;">name</span>=<span style="color: #a6e22e;line-height: 26px;">"FILE_NAME"</span>&gt;</span>collector<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">property</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">property</span>&nbsp;<span style="line-height: 26px;">name</span>=<span style="color: #a6e22e;line-height: 26px;">"patternLayout"</span>&gt;</span>[%d{yyyy-MM-dd'T'HH:mm:ss.SSSZZ}]&nbsp;[%level{length=5}]&nbsp;[%thread-%tid]&nbsp;[%logger]&nbsp;[%X{hostName}]&nbsp;[%X{ip}]&nbsp;[%X{applicationName}]&nbsp;[%F,%L,%C,%M]&nbsp;[%m]&nbsp;##&nbsp;'%ex'%n<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">property</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">Properties</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">Appenders</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">Console</span>&nbsp;<span style="line-height: 26px;">name</span>=<span style="color: #a6e22e;line-height: 26px;">"CONSOLE"</span>&nbsp;<span style="line-height: 26px;">target</span>=<span style="color: #a6e22e;line-height: 26px;">"SYSTEM_OUT"</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">PatternLayout</span>&nbsp;<span style="line-height: 26px;">pattern</span>=<span style="color: #a6e22e;line-height: 26px;">"${patternLayout}"</span>/&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">Console</span>&gt;</span>&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">RollingRandomAccessFile</span>&nbsp;<span style="line-height: 26px;">name</span>=<span style="color: #a6e22e;line-height: 26px;">"appAppender"</span>&nbsp;<span style="line-height: 26px;">fileName</span>=<span style="color: #a6e22e;line-height: 26px;">"${LOG_HOME}/app-${FILE_NAME}.log"</span>&nbsp;<span style="line-height: 26px;">filePattern</span>=<span style="color: #a6e22e;line-height: 26px;">"${LOG_HOME}/app-${FILE_NAME}-%d{yyyy-MM-dd}-%i.log"</span>&nbsp;&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">PatternLayout</span>&nbsp;<span style="line-height: 26px;">pattern</span>=<span style="color: #a6e22e;line-height: 26px;">"${patternLayout}"</span>&nbsp;/&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">Policies</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">TimeBasedTriggeringPolicy</span>&nbsp;<span style="line-height: 26px;">interval</span>=<span style="color: #a6e22e;line-height: 26px;">"1"</span>/&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">SizeBasedTriggeringPolicy</span>&nbsp;<span style="line-height: 26px;">size</span>=<span style="color: #a6e22e;line-height: 26px;">"500MB"</span>/&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">Policies</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">DefaultRolloverStrategy</span>&nbsp;<span style="line-height: 26px;">max</span>=<span style="color: #a6e22e;line-height: 26px;">"20"</span>/&gt;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">RollingRandomAccessFile</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">RollingRandomAccessFile</span>&nbsp;<span style="line-height: 26px;">name</span>=<span style="color: #a6e22e;line-height: 26px;">"errorAppender"</span>&nbsp;<span style="line-height: 26px;">fileName</span>=<span style="color: #a6e22e;line-height: 26px;">"${LOG_HOME}/error-${FILE_NAME}.log"</span>&nbsp;<span style="line-height: 26px;">filePattern</span>=<span style="color: #a6e22e;line-height: 26px;">"${LOG_HOME}/error-${FILE_NAME}-%d{yyyy-MM-dd}-%i.log"</span>&nbsp;&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">PatternLayout</span>&nbsp;<span style="line-height: 26px;">pattern</span>=<span style="color: #a6e22e;line-height: 26px;">"${patternLayout}"</span>&nbsp;/&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">Filters</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">ThresholdFilter</span>&nbsp;<span style="line-height: 26px;">level</span>=<span style="color: #a6e22e;line-height: 26px;">"warn"</span>&nbsp;<span style="line-height: 26px;">onMatch</span>=<span style="color: #a6e22e;line-height: 26px;">"ACCEPT"</span>&nbsp;<span style="line-height: 26px;">onMismatch</span>=<span style="color: #a6e22e;line-height: 26px;">"DENY"</span>/&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">Filters</span>&gt;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">Policies</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">TimeBasedTriggeringPolicy</span>&nbsp;<span style="line-height: 26px;">interval</span>=<span style="color: #a6e22e;line-height: 26px;">"1"</span>/&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">SizeBasedTriggeringPolicy</span>&nbsp;<span style="line-height: 26px;">size</span>=<span style="color: #a6e22e;line-height: 26px;">"500MB"</span>/&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">Policies</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">DefaultRolloverStrategy</span>&nbsp;<span style="line-height: 26px;">max</span>=<span style="color: #a6e22e;line-height: 26px;">"20"</span>/&gt;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">RollingRandomAccessFile</span>&gt;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">Appenders</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">Loggers</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">&lt;!--&nbsp;业务相关&nbsp;异步logger&nbsp;--&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">AsyncLogger</span>&nbsp;<span style="line-height: 26px;">name</span>=<span style="color: #a6e22e;line-height: 26px;">"com.bfxy.*"</span>&nbsp;<span style="line-height: 26px;">level</span>=<span style="color: #a6e22e;line-height: 26px;">"info"</span>&nbsp;<span style="line-height: 26px;">includeLocation</span>=<span style="color: #a6e22e;line-height: 26px;">"true"</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">AppenderRef</span>&nbsp;<span style="line-height: 26px;">ref</span>=<span style="color: #a6e22e;line-height: 26px;">"appAppender"</span>/&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">AsyncLogger</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">AsyncLogger</span>&nbsp;<span style="line-height: 26px;">name</span>=<span style="color: #a6e22e;line-height: 26px;">"com.bfxy.*"</span>&nbsp;<span style="line-height: 26px;">level</span>=<span style="color: #a6e22e;line-height: 26px;">"info"</span>&nbsp;<span style="line-height: 26px;">includeLocation</span>=<span style="color: #a6e22e;line-height: 26px;">"true"</span>&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;<span style="line-height: 26px;">AppenderRef</span>&nbsp;<span style="line-height: 26px;">ref</span>=<span style="color: #a6e22e;line-height: 26px;">"errorAppender"</span>/&gt;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;line-height: 26px;">&lt;/<span style="line-height: 26px;">Asyn

MySQL 篇:怎么解决 MySQL 死锁问题的?

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-size: 16px;padding: 10px;font-family: Roboto, Oxygen, Ubuntu, Cantarell, PingFangSC-light, PingFangTC-light, &quot;Open Sans&quot;, &quot;Helvetica Neue&quot;, sans-serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">见字如面,我是码哥</p> <hr data-tool="mdnice编辑器" style="height: 2px;margin-top: 20px;margin-bottom: 20px;border-width: initial;border-style: none;border-color: initial;text-align: center;background-image: linear-gradient(to right, rgba(248, 57, 41, 0), rgb(244, 138, 0), rgba(248, 57, 41, 0));"> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;text-size-adjust: 100%;line-height: 1.75em;border-radius: 5px;box-sizing: inherit;border-width: 1px;border-top-style: solid;border-right-style: solid;border-bottom-style: solid;border-color: rgb(255, 191, 82);background: rgb(255, 248, 230);"> <span style="color: #f48a00;font-size: 32px;line-height: 0.6;margin-left: -15px;">❝</span> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.1em;font-size: 16px;word-spacing: 0.1em;text-align: justify;line-height: 26px;margin-top: -15px;color: rgba(0, 0, 0, 0.85);">咱们使用 MySQL 大概率上都会遇到死锁问题,这实在是个令人非常头痛的问题。本文将会对死锁进行相应介绍,对常见的死锁案例进行相关分析与探讨,以及如何去尽可能避免死锁给出一些建议。</p> </blockquote> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;"><span style="display: none;"></span><span style="font-size: 20px;color: #f48a00;display: inline-block;padding-left: 10px;border-left: 8px solid #ffbf52;">什么是死锁</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">死锁是并发系统中常见的问题,同样也会出现在数据库 MySQL 的并发读写请求场景中。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">当两个及以上的事务,双方都在等待对方释放已经持有的锁或因为加锁顺序不一致造成循环等待锁资源,就会出现“死锁”。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">常见的报错信息为 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">Deadlock found when trying to get lock...</code>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">举例来说 A 事务持有 X1 锁 ,申请 X2 锁,B 事务持有 X2 锁,申请 X1 锁。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">A 和 B 事务持有锁并且申请对方持有的锁进入循环等待,就造成了死锁。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.446875" src="/upload/dc580981da320e46235e41b304353685.png" data-type="png" data-w="640" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">如上图,是右侧的四辆汽车资源请求产生了回路现象,即死循环,导致了死锁。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">从死锁的定义来看,MySQL 出现死锁的几个要素为:</p> <ol data-tool="mdnice编辑器" style="font-size: 15px;margin-top: 8px;margin-bottom: 8px;padding-left: 20px;color: rgb(255, 191, 82);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 两个或者两个以上事务 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 每个事务都已经持有锁并且申请新的锁 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 锁资源同时只能被同一个事务持有或者不兼容 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 事务之间因为持有锁和申请锁导致彼此循环等待 </section></li> </ol> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;"><span style="display: none;"></span><span style="font-size: 20px;color: #f48a00;display: inline-block;padding-left: 10px;border-left: 8px solid #ffbf52;">InnoDB 锁类型</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">为了分析死锁,我们有必要对 InnoDB 的锁类型有一个了解。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.46875" src="/upload/5f135025d77c73b0f9b4e72392d31819.png" data-type="png" data-w="640" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">MySQL InnoDB 引擎实现了标准的<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">行级别锁:共享锁( S lock ) 和排他锁 ( X lock )</code></p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;text-size-adjust: 100%;line-height: 1.75em;border-radius: 5px;box-sizing: inherit;border-width: 1px;border-top-style: solid;border-right-style: solid;border-bottom-style: solid;border-color: rgb(255, 191, 82);background: rgb(255, 248, 230);"> <span style="color: #f48a00;font-size: 32px;line-height: 0.6;margin-left: -15px;">❝</span> <ol style="font-size: 15px;margin-top: 8px;margin-bottom: 8px;padding-left: 20px;color: rgb(255, 191, 82);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 不同事务可以同时对同一行记录加 S 锁。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 如果一个事务对某一行记录加 X 锁,其他事务就不能加 S 锁或者 X 锁,从而导致锁等待。 </section></li> </ol> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">如果事务 T1 持有行 r 的 S 锁,那么另一个事务 T2 请求 r 的锁时,会做如下处理:</p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;text-size-adjust: 100%;line-height: 1.75em;border-radius: 5px;box-sizing: inherit;border-width: 1px;border-top-style: solid;border-right-style: solid;border-bottom-style: solid;border-color: rgb(255, 191, 82);background: rgb(255, 248, 230);"> <span style="color: #f48a00;font-size: 32px;line-height: 0.6;margin-left: -15px;">❝</span> <ol style="font-size: 15px;margin-top: 8px;margin-bottom: 8px;padding-left: 20px;color: rgb(255, 191, 82);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> T2 请求 S 锁立即被允许,结果 T1 T2 都持有 r 行的 S 锁 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> T2 请求 X 锁不能被立即允许 </section></li> </ol> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">如果 T1 持有 r 的 X 锁,那么 T2 请求 r 的 X、S 锁都不能被立即允许,T2 必须等待 T1 释放 X 锁才可以,因为 X 锁与任何的锁都不兼容。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">共享锁和排他锁的兼容性如下所示:<img data-ratio="0.1782608695652174" src="/upload/30a2c55533646fd2576d416107b4ae1a.png" data-type="png" data-w="690" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"></p> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin-top: 15px;"><span style="display: none;"></span><span style="font-size: 18px;color: #f48a00;display: inline-block;padding-left: 10px;border-left: 4px solid #ffe3a3;">间隙锁( gap lock )</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">间隙锁锁住一个间隙以防止插入。假设索引列有 2, 4, 8 三个值,如果对 4 加锁,那么也会同时对(2,4)和(4,8)这两个间隙加锁。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">其他事务无法插入索引值在这两个间隙之间的记录。但是,间隙锁有个例外:</p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;text-size-adjust: 100%;line-height: 1.75em;border-radius: 5px;box-sizing: inherit;border-width: 1px;border-top-style: solid;border-right-style: solid;border-bottom-style: solid;border-color: rgb(255, 191, 82);background: rgb(255, 248, 230);"> <span style="color: #f48a00;font-size: 32px;line-height: 0.6;margin-left: -15px;">❝</span> <ol style="font-size: 15px;margin-top: 8px;margin-bottom: 8px;padding-left: 20px;color: rgb(255, 191, 82);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 如果索引列是唯一索引,那么只会锁住这条记录(只加行锁),而不会锁住间隙。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 对于联合索引且是唯一索引,如果 where 条件只包括联合索引的一部分,那么依然会加间隙锁。 </section></li> </ol> </blockquote> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin-top: 15px;"><span style="display: none;"></span><span style="font-size: 18px;color: #f48a00;display: inline-block;padding-left: 10px;border-left: 4px solid #ffe3a3;">next-key lock</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">next-key lock 实际上就是 行锁+这条记录前面的 gap lock 的组合。假设有索引值 10,11,13 和 20,那么可能的 next-key lock 包括:</p> <h4 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 18px;margin-top: 10px;"><span style="display: none;"></span><span style="font-size: 16px;color: #f48a00;display: inline-block;">(负无穷,10],(10,11],(11,13],(13,20],(20,正无穷)</span><span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">在 RR 隔离级别下,InnoDB 使用 next-key lock 主要是防止<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">幻读</code>问题产生。</p> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin-top: 15px;"><span style="display: none;"></span><span style="font-size: 18px;color: #f48a00;display: inline-block;padding-left: 10px;border-left: 4px solid #ffe3a3;">意向锁( Intention lock )</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">InnoDB 为了支持多粒度的加锁,允许行锁和表锁同时存在。为了支持在不同粒度上的加锁操作,InnoDB 支持了额外的一种锁方式,称之为意向锁( Intention Lock )。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度上进行加锁。意向锁分为两种:</p> <blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;text-size-adjust: 100%;line-height: 1.75em;border-radius: 5px;box-sizing: inherit;border-width: 1px;border-top-style: solid;border-right-style: solid;border-bottom-style: solid;border-color: rgb(255, 191, 82);background: rgb(255, 248, 230);"> <span style="color: #f48a00;font-size: 32px;line-height: 0.6;margin-left: -15px;">❝</span> <ol style="font-size: 15px;margin-top: 8px;margin-bottom: 8px;padding-left: 20px;color: rgb(255, 191, 82);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 意向共享锁( IS ):事务有意向对表中的某些行加共享锁 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 意向排他锁( IX ):事务有意向对表中的某些行加排他锁 </section></li> </ol> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">由于 InnoDB 存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫描以外的任何请求。表级意向锁与行级锁的兼容性如下所示:<img data-ratio="0.2753623188405797" src="/upload/e9fdcf52a2c800aeae801de1640e1dfa.png" data-type="png" data-w="690" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"></p> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin-top: 15px;"><span style="display: none;"></span><span style="font-size: 18px;color: #f48a00;display: inline-block;padding-left: 10px;border-left: 4px solid #ffe3a3;">插入意向锁( Insert Intention lock )</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">插入意向锁是在插入一行记录操作之前设置的一种间隙锁,这个锁释放了一种插入方式的信号,即多个事务在相同的索引间隙插入时如果不是插入间隙中相同的位置就不需要互相等待。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">假设某列有索引值 2,6,只要两个事务插入位置不同(如事务 A 插入 3,事务 B 插入 4),那么就可以同时插入。</p> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin-top: 15px;"><span style="display: none;"></span><span style="font-size: 18px;color: #f48a00;display: inline-block;padding-left: 10px;border-left: 4px solid #ffe3a3;">锁模式兼容矩阵</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">横向是已持有锁,纵向是正在请求的锁:<img data-ratio="0.28695652173913044" src="/upload/3edbbd33829dc2f60324fac61f611718.png" data-type="png" data-w="690" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"></p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;"><span style="display: none;"></span><span style="font-size: 20px;color: #f48a00;display: inline-block;padding-left: 10px;border-left: 8px solid #ffbf52;">阅读死锁日志</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">在进行具体案例分析之前,咱们先了解下如何去读懂死锁日志,尽可能地使用死锁日志里面的信息来帮助我们来解决死锁问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">后面测试用例的数据库场景如下:<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">MySQL 5.7 事务隔离级别为 RR</code></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">表结构和数据如下:<img data-ratio="0.26956521739130435" src="/upload/3445dda96e2ab2ab43e79c58fc51b4e9.png" data-type="png" data-w="690" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">测试用例如下:<img data-ratio="0.4109375" src="/upload/3da07295c8cbb1d8b92c29137578cd02.png" data-type="png" data-w="640" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">通过执行 show engine innodb status 可以查看到最近一次死锁的日志。</p> <h4 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 18px;margin-top: 10px;"><span style="display: none;"></span><span style="font-size: 16px;color: #f48a00;display: inline-block;">日志分析如下:</span><span style="display: none;"></span></h4> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRCWUcW3cJYFo7QgJ3iaRONBl4qGicloW1AYhM3vmUdp8FBBz05ljqwFPqUHaj3JZHT95F4N0I5gOLybJP6wupXibfr/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;">TRANSACTION 2322, ACTIVE 6 sec starting index read<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">事务号为 2322,活跃 6 秒,starting index read 表示事务状态为根据索引读取数据。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">常见的其他状态有:<img data-ratio="0.18985507246376812" src="/upload/2f643edbc3fe98cc4fceb6d7026363be.png" data-type="png" data-w="690" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">mysql tables in use 1</code> 说明当前的事务使用一个表。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">locked 1</code> 表示表上有一个表锁,对于 DML 语句为 LOCK_IX</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)</code></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">LOCK WAIT</code> 表示正在等待锁,<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">2 lock struct(s)</code> 表示 trx-&gt;trx_locks 锁链表的长度为 2,每个链表节点代表该事务持有的一个锁结构,包括表锁,记录锁以及自增锁等。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">本用例中 2locks 表示 IX 锁和 lock_mode X (Next-key lock)</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">1 row lock(s)</code> 表示当前事务持有的行记录锁/ gap 锁的个数。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">MySQL thread id 37, OS thread handle 140445500716800, query id 1234 127.0.0.1 root updating</code></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">MySQL thread id 37</code> 表示执行该事务的线程 ID 为 37 (即 show processlist; 展示的 ID )</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">delete from student where stuno=5</code> 表示事务 1 正在执行的 sql,比较难受的事情是 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">show engine innodb status</code> 是查看不到完整的 sql 的,通常显示当前正在等待锁的 sql。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/7N2JRaWooRCWUcW3cJYFo7QgJ3iaRONBl4qGicloW1AYhM3vmUdp8FBBz05ljqwFPqUHaj3JZHT95F4N0I5gOLybJP6wupXibfr/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #272822;border-radius: 5px;"><br>RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw student trx id 2322 lock_mode X waiting<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">RECORD LOCKS 表示记录锁, 此条内容表示事务 1 正在等待表 student 上的 idx_stuno 的 X 锁,本案例中其实是 Next-Key Lock 。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">事务 2 的 log 和上面分析类似:</p> <ol start="2" data-tool="mdnice编辑器" style="font-size: 15px;margin-top: 8px;margin-bottom: 8px;padding-left: 20px;color: rgb(255, 191, 82);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> HOLDS THE LOCK(S): </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2321 lock_mode X</code></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">显示事务 2 的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">insert into student(stuno,score) values(2,10) 持有了 a=5 的 Lock mode X | LOCK_gap</code>,不过我们从日志里面看不到事务 2 执行的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">delete from student where stuno=5</code>;</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">这点也是造成 DBA 仅仅根据日志难以分析死锁的问题的根本原因。</p> <ol start="2" data-tool="mdnice编辑器" style="font-size: 15px;margin-top: 8px;margin-bottom: 8px;padding-left: 20px;color: rgb(255, 191, 82);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> WAITING FOR THIS LOCK TO BE GRANTED: </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2321 lock_mode X locks gap before rec insert intention waiting</code></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">表示事务 2 的 insert 语句正在等待插入意向锁 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">lock_mode X locks gap before rec insert intention waiting (LOCK_X+LOCK_REC_gap)</code></p> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin-top: 15px;"><span style="display: none;"></span><span style="font-size: 18px;color: #f48a00;display: inline-block;padding-left: 10px;border-left: 4px solid #ffe3a3;">经典案例分析</span><span style="display: none;"></span></h3> <h4 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 18px;margin-top: 10px;"><span style="display: none;"></span><span style="font-size: 16px;color: #f48a00;display: inline-block;">案例一:事务并发 insert 唯一键冲突</span><span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">表结构和数据如下所示:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);"><img data-ratio="0.21594202898550724" src="/upload/68851ce91666b02033a5ed36ce1b3b18.png" data-type="png" data-w="690" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"><img data-ratio="0.7342342342342343" src="/upload/371e6df729463fdea386d18c13bc5616.png" data-type="png" data-w="444" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">测试用例如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.27246376811594203" src="/upload/8f6bc497c0f9c66ff4a7da0c50dcd1e7.png" data-type="png" data-w="690" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">日志分析如下:</p> <ol data-tool="mdnice编辑器" style="font-size: 15px;margin-top: 8px;margin-bottom: 8px;padding-left: 20px;color: rgb(255, 191, 82);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 事务 T2 insert into t7(id,a) values (26,10) 语句 insert 成功,持有 a=10 的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">排他行锁( Xlocks rec but no gap )</code> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 事务 T1 insert into t7(id,a) values (30,10), 因为 T2 的第一条 insert 已经插入 a=10 的记录,事务 T1 insert a=10 则发生唯一键冲突,需要申请对冲突的唯一索引加上 S Next-key Lock( 即 lock mode S waiting ) 这是一个 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">间隙锁</code>会申请锁住(,10],(10,20]之间的 gap 区域。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 事务 T2 insert into t7(id,a) values (40,9)该语句插入的 a=9 的值在事务 T1 申请的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">gap 锁4-10之间</code>, 故需事务 T2 的第二条 insert 语句要等待事务 T1 的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">S-Next-key Lock 锁</code>释放,在日志中显示 lock_mode X locks gap before rec insert intention waiting 。 </section></li> </ol> <h4 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 18px;margin-top: 10px;"><span style="display: none;"></span><span style="font-size: 16px;color: #f48a00;display: inline-block;">案例一:先 update 再 insert 的并发死锁问题</span><span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">表结构如下,无数据:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.24202898550724639" src="/upload/c3fe6a7f19664a860a4a15885c167e20.png" data-type="png" data-w="690" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">测试用例如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5" src="/upload/af6aae427acd20aaaf8fcb4e500b2192.png" data-type="png" data-w="640" style="border-radius: 5px;display: block;margin-right: 10px;margin-bottom: auto;margin-left: 10px;width: 100%;height: 100%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">死锁分析:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">可以看到两个事务 update 不存在的记录,先后获得<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">间隙锁( gap 锁)</code>,gap 锁之间是兼容的所以在 update 环节不会阻塞。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">两者都持有 gap 锁,然后去竞争插入<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">意向锁</code>。当存在其他会话持有 gap 锁的时候,当前会话申请不了插入意向锁,导致死锁。</p> <h3 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 20px;margin-top: 15px;"><span style="display: none;"></span><span style="font-size: 18px;color: #f48a00;display: inline-block;padding-left: 10px;border-left: 4px solid #ffe3a3;">如何尽可能避免死锁</span><span style="display: none;"></span></h3> <ol data-tool="mdnice编辑器" style="font-size: 15px;margin-top: 8px;margin-bottom: 8px;padding-left: 20px;color: rgb(255, 191, 82);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 合理的设计索引,区分度高的列放到组合索引前面,使业务 SQL 尽可能通过索引 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">定位更少的行,减少锁竞争</code>。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 调整业务逻辑 SQL 执行顺序, 避免 update/delete 长时间持有锁的 SQL 在事务前面。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 避免 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">大事务</code>,尽量将大事务拆成多个小事务来处理,小事务发生锁冲突的几率也更小。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 以 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">固定的顺序</code>访问表和行。比如两个更新数据的事务,事务 A 更新数据的顺序为 1,2;事务 B 更新数据的顺序为 2,1。这样更可能会造成死锁。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 在并发比较高的系统中,不要显式加锁,特别是是在事务里显式加锁。如 select … for update 语句,如果是在事务里 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">(运行了 start transaction 或设置了autocommit 等于0)</code>,那么就会锁定所查找到的记录。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 尽量按 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">主键/索引</code>去查找记录,范围查找增加了锁冲突的可能性,也不要利用数据库做一些额外额度计算工作。比如有的程序会用到 “select … where … order by rand();”这样的语句,由于类似这样的语句用不到索引,因此将导致整个表的数据都被锁住。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgba(0, 0, 0, 0.55);"> 优化 SQL 和表设计,减少同时占用太多资源的情况。比如说, <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">减少连接的表</code>,将复杂 SQL <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(244, 138, 0);">分解</code>为多个简单的 SQL。 </section></li> </ol> <hr data-tool="mdnice编辑器" style="height: 2px;margin-top: 20px;margin-bottom: 20px;border-width: initial;border-style: none;border-color: initial;text-align: center;background-image: linear-gradient(to right, rgba(248, 57, 41, 0), rgb(244, 138, 0), rgba(248, 57, 41, 0));"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">好了。今天就说到这了,我还会不断分享自己的所学所想,希望我们一起走在成功的道路上!</p> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzkyNjI0MTYwNQ==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/7VkkuTzAZPpHc0kB2AWPsLvdd3oC9u1QZvXmHniaUnicetGssmNiaydZvKibR2zZWgEA1zJzPGQ61doIl2MtBolkHQ/0?wx_fmt=png" data-nickname="狼王编程" data-alias="langwang-yyds" data-signature="狼王专注Java和架构领域,在coding的道路,一边学习一边分享,你可以和我聊篮球,也可以和我交流技术,只要你愿意,我们就有故事!" data-from="0"></mpprofile> </section> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 5px;margin-bottom: 5px;line-height: 1.75;letter-spacing: 0.1em;word-spacing: 0.1em;text-align: justify;color: rgba(0, 0, 0, 0.85);">欢迎大家留言讨论。</p> </section>

Spring 事务失效的 8 大场景,看看你都遇到过几个?

作者:微信小助手

<section class="mp_profile_iframe_wrp" data-mpa-powered-by="yiban.io"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzA5MTU0OTY0Ng==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/zc3KLDBfJlmPt0J5PXYOoiaG8wsQPZrLevbxMZSfgQ0YypNYaicnbS0P9UicluuOySLSP4CjTcRUVHCZzYeXQ9WlA/0?wx_fmt=png" data-nickname="Java派" data-alias="javapai" data-signature="专注Java相关技术栈:Spring全家筒、Docker、k8s、Mysql、集群、微服务、中间件等知识。" data-from="0"></mpprofile> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);"> <h3 data-tool="mdnice编辑器" style="text-align: right;"><em style="color: rgb(136, 136, 136);font-size: 12px;letter-spacing: 0.5px;"></em></h3> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: PingFangSC-Light;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong>用 Spring 的 @Transactional 注解控制事务有哪些不生效的场景?</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">不知道小伙伴们有没有这样的经历,在自己开心的编写业务代码时候,突然某一个方法里的事务好像失效了。然后 debug 跟踪代码时发现,自己第一步的 insert 或者 update 的数据在语句执行完毕后,数据库中并没有立即出现更改或保存完的新数据。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">所以一度怀疑spring 的事务失效了。那么这篇文章就来总结一下,大家给大家造成 “spring事务失效”错觉的 几个常见场景,然后对症下药。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;text-align: left;">Let's GO!!!</span></strong></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">以本人的经历中遇到的问题,大概分有以下几个场景:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 数据库引擎是否支持事务(Mysql 的 MyIsam引擎不支持事务); </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 注解所在的类是否被加载为 Bean(是否被spring 管理); </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 注解所在的方法是否为 public 修饰的; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 是否存在自身调用的问题; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 所用数据源是否加载了事务管理器; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> @Transactional的扩展配置propagation是否正确。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;text-align: left;">下面展开分析每一个场景:</span></strong></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong>数据库引擎不支持事务</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">根据 MySQL 的官方文档:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(178, 174, 197);background: rgb(255, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;color: rgb(102, 102, 102);line-height: 2;">https://dev.mysql.com/doc/refman/5.5/en/storage-engine-setting.html</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">从 MySQL 5.5.5 开始的默认存储引擎是:InnoDB,之前默认的都是:MyISAM,所以这点要值得注意,底层引擎不支持事务再怎么搞都是白搭。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong>没有被 Spring 管理</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">如下面例子所示:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;">//&nbsp;@Service</span><br><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">class</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">OrderServiceImpl</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">implements</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">OrderService</span>&nbsp;</span>{<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">@Transactional</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span>&nbsp;<span style="color: #a6e22e;font-weight: bold;line-height: 26px;">updateOrder</span><span style="line-height: 26px;">(Order&nbsp;order)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">//&nbsp;update&nbsp;order</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">如果此时把 @Service 注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong>方法不是 public 的</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">以下来自 Spring 官方文档:</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(178, 174, 197);background: rgb(255, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;color: rgb(102, 102, 102);line-height: 2;">When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">大概意思就是 @Transactional 只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong>自身调用问题</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">来看两个示例:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;">//示例1</span><br>&nbsp;<br><span style="color: #75715e;line-height: 26px;">@Service</span><br><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">class</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">OrderServiceImpl</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">implements</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">OrderService</span>&nbsp;</span>{<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span>&nbsp;<span style="color: #a6e22e;font-weight: bold;line-height: 26px;">update</span><span style="line-height: 26px;">(Order&nbsp;order)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateOrder(order);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">@Transactional</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span>&nbsp;<span style="color: #a6e22e;font-weight: bold;line-height: 26px;">updateOrder</span><span style="line-height: 26px;">(Order&nbsp;order)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">//&nbsp;update&nbsp;order</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>}<br></code></pre> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;">//示例2</span><br>&nbsp;<br><span style="color: #75715e;line-height: 26px;">@Service</span><br><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">class</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">OrderServiceImpl</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">implements</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">OrderService</span>&nbsp;</span>{<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">@Transactional</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span>&nbsp;<span style="color: #a6e22e;font-weight: bold;line-height: 26px;">update</span><span style="line-height: 26px;">(Order&nbsp;order)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateOrder(order);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">@Transactional</span>(propagation&nbsp;=&nbsp;Propagation.REQUIRES_NEW)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span>&nbsp;<span style="color: #a6e22e;font-weight: bold;line-height: 26px;">updateOrder</span><span style="line-height: 26px;">(Order&nbsp;order)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">//&nbsp;update&nbsp;order</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>}<br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: black;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;word-spacing: 0.1em;">示例1 中,update方法上面没有加 @Transactional 注解,调用有 @Transactional 注解的 updateOrder 方法,updateOrder 方法上的事务管用吗?</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: black;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;word-spacing: 0.1em;">示例2 中,update方法上面没有加 @Transactional 注解,调用有 @Transactional 注解的 updateOrder 方法,updateOrder 方法上的事务管用吗?</p> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这两个例子的答案是:都不管用!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">因为它们发生了自身调用,就调该类自己的方法,而没有经过 Spring 的代理类,默认只有在外部调用事务才会生效,这也是老生常谈的经典问题了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这个的解决方案之一就是在的类中注入自己,用注入的对象再调用另外一个方法,这个不太优雅</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong>数据源没有配置事务管理器</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">如下代码所示,当前数据源若没有配置事务管理器,那也是白搭!</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;">@Bean</span><br><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;PlatformTransactionManager&nbsp;<span style="color: #a6e22e;font-weight: bold;line-height: 26px;">transactionManager</span><span style="line-height: 26px;">(DataSource&nbsp;dataSource)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">return</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">new</span>&nbsp;DataSourceTransactionManager(dataSource);<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong>@Transactional的扩展配置不支持事务</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;">Propagation.NOT_SUPPORTED</code>:表示不以事务运行,当前若存在事务则挂起。这表示不支持以事务的方式运行,所以即使事务生效也是白搭!</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;">@Service</span><br><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">class</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">OrderServiceImpl</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">implements</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">OrderService</span>&nbsp;</span>{<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">@Transactional</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span>&nbsp;<span style="color: #a6e22e;font-weight: bold;line-height: 26px;">update</span><span style="line-height: 26px;">(Order&nbsp;order)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;updateOrder(order);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">@Transactional</span>(propagation&nbsp;=&nbsp;Propagation.NOT_SUPPORTED)<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span>&nbsp;<span style="color: #a6e22e;font-weight: bold;line-height: 26px;">updateOrder</span><span style="line-height: 26px;">(Order&nbsp;order)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">//&nbsp;update&nbsp;order</span><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong>异常被吃了</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这个也是出现比较多的场景:把异常吃了,然后又不抛出来,事务也不会回滚!</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;">@Service</span><br><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">class</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">OrderServiceImpl</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">implements</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">OrderService</span>&nbsp;</span>{<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">@Transactional</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span>&nbsp;<span style="color: #a6e22e;font-weight: bold;line-height: 26px;">updateOrder</span><span style="line-height: 26px;">(Order&nbsp;order)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">//&nbsp;update&nbsp;order</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">catch</span>&nbsp;{<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong>异常类型错误</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">接上面的例子,再抛出一个异常</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;">@Service</span><br><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">class</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">OrderServiceImpl</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">implements</span>&nbsp;<span style="font-weight: bold;color: white;line-height: 26px;">OrderService</span>&nbsp;</span>{<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">@Transactional</span><br>&nbsp;&nbsp;&nbsp;&nbsp;<span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">public</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">void</span>&nbsp;<span style="color: #a6e22e;font-weight: bold;line-height: 26px;">updateOrder</span><span style="line-height: 26px;">(Order&nbsp;order)</span>&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">try</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #75715e;line-height: 26px;">//&nbsp;update&nbsp;order</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">catch</span>&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">throw</span>&nbsp;<span style="color: #f92672;font-weight: bold;line-height: 26px;">new</span>&nbsp;Exception(<span style="color: #a6e22e;line-height: 26px;">"更新错误"</span>);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这样事务也是不生效的,因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下,如:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;">@Transactional(rollbackFor&nbsp;=&nbsp;Exception.class)<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这个配置仅限于 Throwable 异常类及其子类。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;text-align: left;">总结</span></strong></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-top: 10px;margin-bottom: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">本文总结了 8 种事务失效的场景,其实发生最多就是自身调用、异常被吃、异常抛出类型不对这 3 个了,像文章开头说的那样,本文不一定总结得全,只是总结常见的事务失效的场景,如果你还知道其他场景也欢迎留言分享。</p> <blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="72" data-source-title=""></blockquote> </section>

遇到线上问题不要慌,这些命令可以助你迅速定位(建议收藏)

作者:微信小助手

<section style="margin-bottom: 15px;color: rgb(53, 53, 53);font-size: 16px;text-align: left;white-space: normal;word-spacing: 0.8px;letter-spacing: 0.544px;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;background-color: rgb(255, 255, 255);"> <span style="color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;">大家好,我是飘渺。相信各位在日常工作中肯定会遇到应用程序莫名其妙崩溃的情况,比如常见的CPU100%,内存耗尽...当出现这些问题后我们就需要借助Linux命令来帮助我们定位发现问题,本文就给大家罗列一些Java后端线上问题排查的常用命令,建议直接收藏!</span> </section> <section style="margin-bottom: 15px;color: rgb(53, 53, 53);font-size: 16px;text-align: left;white-space: normal;word-spacing: 0.8px;letter-spacing: 0.544px;font-family: -apple-system-font, system-ui, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif;background-color: rgb(255, 255, 255);"> <span style="color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 16px;letter-spacing: 0.8px;text-align: left;word-spacing: 0.8px;"><br></span> </section> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;padding: 5px;font-size: 16px;color: rgb(53, 53, 53);word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">内存瓶颈</span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">free</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">free</code>是查看内存使用情况,包括物理内存、交换内存(swap)和内核缓冲区内存。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">free -h -s 3</code>表示每隔三秒输出一次内存情况,命令如下</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/hNWCQ9bibbzEGR101C4cWRUoaScvHyLpxVHd0tUmqXFDNjw31XRzibicI0jCUibby1WLbRJxCx2S9IqH7or9HElb6ly85qDuxibUu/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">[1014154@cc69dd4c5-4tdb5&nbsp;~]$&nbsp;free<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;total&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;used&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;free&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shared&nbsp;&nbsp;buff/cache&nbsp;&nbsp;&nbsp;available<br>Mem:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;119623656&nbsp;&nbsp;&nbsp;&nbsp;43052220&nbsp;&nbsp;&nbsp;&nbsp;45611364&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4313760&nbsp;&nbsp;&nbsp;&nbsp;30960072&nbsp;&nbsp;&nbsp;&nbsp;70574408<br>Swap:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0<br>[1014154@cc69dd4c5-4tdb5&nbsp;~]$&nbsp;free&nbsp;-h&nbsp;-s&nbsp;3<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;total&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;used&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;free&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shared&nbsp;&nbsp;buff/cache&nbsp;&nbsp;&nbsp;available<br>Mem:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;114G&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;41G&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;43G&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4.1G&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;29G&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;67G<br>Swap:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0B<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;total&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;used&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;free&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shared&nbsp;&nbsp;buff/cache&nbsp;&nbsp;&nbsp;available<br>Mem:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;114G&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;41G&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;43G&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4.1G&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;29G&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;67G<br>Swap:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0B<br></code></pre> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">Mem</code>:是内存的使用情况。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">Swap</code>:是交换空间的使用情况。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">total</code>:系统总的可用物理内存和交换空间大小。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">used</code>:已经被使用的物理内存和交换空间。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">free</code>:还有多少物理内存和交换空间可用使用, <span style="font-weight: 700;color: rgb(248, 57, 41);">是真正尚未被使用的物理内存数量</span>。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">shared</code>:被共享使用的物理内存大小。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">buff/cache</code>:被 buffer(缓冲区) 和 cache(缓存) 使用的物理内存大小。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-

Redis 生产架构选型解决方案

作者:微信小助手

<section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">推荐使用更新的引擎版本以支持更多的特性。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <h2 data-id="heading-1" style="line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;">Redis 6.0新特性说明:</span></h2> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;letter-spacing: 1px;">模块系统新增多个API。</span></p></li> <li><p><span style="font-size: 15px;letter-spacing: 1px;">支持SSL/TLS加密。</span></p></li> <li><p><span style="font-size: 15px;letter-spacing: 1px;">支持新的Redis协议:<span style="font-size: 15px;letter-spacing: 1px;">RESP3。</span></span></p></li> <li><p><span style="">服务端支持多模式的客户端缓存。</span></p></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">支持多线程IO。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">副本中支持无盘复制(diskless replication)。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">Redis-benchmark新增了Redis集群模式。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">支持重写Systemd。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">支持Disque模块。</span> </section></li> </ul> <section style="line-height: 1.75em;"> <br> </section> <h2 data-id="heading-2" style="line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;">Redis 5.0新特性说明</span></h2> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="font-size: 15px;letter-spacing: 1px;">云数据库Redis 5.0版本大幅度优化内核,运行更加稳定,同时新增Stream、账号管理、审计日志等多种特性,满足您更多场景下的使用需求。</span></p></li> <li><p><span style="font-size: 15px;letter-spacing: 1px;">新的数据类型:<span style="font-size: 15px;letter-spacing: 1px;"></span><span style="font-size: 15px;letter-spacing: 1px;">流数据(Stream)。<span style="font-size: 15px;letter-spacing: 1px;">详细说明请参见Redis Streams。</span></span></span></p></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">新增账号管理功能。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">新增日志管理功能,支持审计日志、运行日志和慢日志,您可以通过日志管理查询读写操作、敏感操作(如KEYS、FLUSHALL)和管理类命令的使用记录以及慢日志。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">新增基于快照的缓存分析功能。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">新的定时器(Timers)、集群( Cluster)和字典(Dictionary)模块的API。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">RDB中增加LFU和LRU信息。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">集群管理器从Ruby (redis-trib.rb)移植到了redis-cli中的C语言代码。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">新增有序集合(Sorted Set)命令ZPOPMIN、ZPOPMAX、BZPOPMIN和BZPOPMAX。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">升级Active Defragmentation至v2版本。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">增强HyperLogLog的实现。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">优化内存统计报告。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">为许多有子命令的命令增加了HELP子命令。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">提高了客户端频繁连接和断开连接时的性能表现。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">升级Jemalloc至5.1版本。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">新增命令CLIENT ID和CLIENT UNBLOCK。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">新增了为艺术而生的LOLWUT命令。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">弃用slave术语(需要API向后兼容的情况例外)。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">对网络层进行了多处优化。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">进行了一些Lua相关的改进。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">新增动态HZ(Dynamic HZ)以平衡空闲CPU使用率和响应性。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">对Redis核心代码进行了重构并在许多方面进行了改进。</span> </section></li> </ul> <h1 data-id="heading-3" style="line-height: 1.75em;"><br></h1> <p style="white-space: normal;letter-spacing: 0.544px;background: rgb(255, 255, 255);text-align: center;font-size: 14px;font-family: Calibri, sans-serif;color: rgb(0, 0, 0);line-height: 28px;"><img class="rich_pages" data-ratio="0.9900990099009901" data-s="300,640" src="/upload/aae6600a288d838d113c0d9ec611c195.png" data-type="png" data-w="101" style=""><br></p> <p style="text-align: center;"><span style=""><strong style="color: rgb(4, 181, 107);"><strong style="font-size: 16px;">-&nbsp; &nbsp; &nbsp;</strong><strong style="font-size: 15px;">架构</strong><strong><span style="font-size: 16px;">&nbsp; &nbsp; -</span></strong></strong></span></p> <h1 data-id="heading-3" style="line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;"></span></h1> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">您需要根据业务需求选择:</span> </section> <section style="line-height: 1.75em;"> <br> </section> <ul class="list-paddingleft-2"> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">集群架构可轻松突破Redis自身单线程瓶颈,满足大容量、高性能的业务需求。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">主从架构,提供高性能的缓存服务和数据高可靠。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">读写分离架构提供高可用、高性能、高灵活的读写分离服务,解决热点数据集中及高并发读取的业务需求,最大化地节约用户运维成本。</span> </section></li> </ul> <section style="line-height: 1.75em;"> <br> </section> <h2 data-id="heading-4" style="line-height: 1.75em;"><span style="color: rgb(4, 176, 91);"><strong><span style="font-size: 15px;letter-spacing: 1px;">2.1 主从架构-双副本</span></strong></span><span style="font-size: 15px;letter-spacing: 1px;"></span></h2> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">采用主从(master-replica)模式搭建。主节点提供日常服务访问,备节点提供HA高可用,当主节点发生故障,系统会自动在30秒内切换至备节点,保证业务平稳运行。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <p style="white-space: normal;letter-spacing: 0.544px;background: rgb(255, 255, 255);text-align: center;font-size: 14px;font-family: Calibri, sans-serif;color: rgb(0, 0, 0);line-height: 28px;"><img class="rich_pages" data-ratio="0.9900990099009901" data-s="300,640" src="/upload/aae6600a288d838d113c0d9ec611c195.png" data-type="png" data-w="101" style=""><br></p> <p style="text-align: center;"><span style=""><strong style="color: rgb(4, 181, 107);"><strong style="font-size: 16px;">-&nbsp; &nbsp; &nbsp;</strong><strong style="font-size: 15px;">可靠性</strong><strong><span style="font-size: 16px;">&nbsp; &nbsp; -</span></strong></strong></span></p> <section style="line-height: 1.75em;"> <br> </section> <ul class="list-paddingleft-2"> <li> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">服务可靠采用双机主从(master-replica)架构,主从节点位于不同物理机。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">主节点对外提供访问,用户可通过Redis命令行和通用客户端进行数据的增删改查操作。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">当主节点出现故障,HA系统会自动进行主从切换,保证业务平稳运行。</span> </section></li> <li> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">数据可靠默认开启数据持久化功能,数据全部落盘。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">支持数据备份功能,用户可以针对备份集回滚实例或者克隆实例,有效地解决数据误操作等问题。</span> </section></li> </ul> <section style="line-height: 1.75em;text-align: center;"> <br> </section> <p style="white-space: normal;letter-spacing: 0.544px;background: rgb(255, 255, 255);text-align: center;font-size: 14px;font-family: Calibri, sans-serif;color: rgb(0, 0, 0);line-height: 28px;"><img class="rich_pages" data-ratio="0.9900990099009901" data-s="300,640" src="/upload/aae6600a288d838d113c0d9ec611c195.png" data-type="png" data-w="101" style=""><br></p> <p style="text-align: center;"><span style=""><strong style="color: rgb(4, 181, 107);"><strong style="font-size: 16px;">-&nbsp; &nbsp; &nbsp;</strong><strong style="font-size: 15px;">使用场景</strong><strong><span style="font-size: 16px;">&nbsp; &nbsp; -</span></strong></strong></span></p> <h3 data-id="heading-6" style="line-height: 1.75em;text-align: center;"><span style="font-size: 15px;letter-spacing: 1px;"></span></h3> <section style="line-height: 1.75em;"> <br> </section> <ul class="list-paddingleft-2"> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">Redis作为持久化数据存储使用的业务标准版提供持久化机制及备份恢复机制,极大地保证数据可靠性。</span> </section></li> <li> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">单个Redis性能压力可控的业务由于Redis原生采用单线程机制,性能在10万QPS以下的业务建议使用。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">如果需要更高的性能要求,请选用集群版本。</span> </section></li> <li> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">Redis命令相对简单,排序、计算类命令较少的业务由于Redis的单线程机制,CPU会成为主要瓶颈。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">如排序、计算类较多的业务建议选用集群版配置。</span> </section></li> </ul> <section style="line-height: 1.75em;"> <br> </section> <h2 data-id="heading-7" style="line-height: 1.75em;"><span style="color: rgb(4, 176, 91);"><strong><span style="color: rgb(4, 176, 91);font-size: 15px;letter-spacing: 1px;">2.2 主从架构-单副本</span></strong></span></h2> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">可以在没有数据可靠性要求的纯缓存场景充分发挥性能优势。</span> </section> <h3 data-id="heading-8" style="line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;"><br></span></h3> <h3 data-id="heading-8" style="line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;">使用场景</span></h3> <ul class="list-paddingleft-2"> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">纯缓存类业务场景</span> </section></li> </ul> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">单副本版本只有一个数据库节点,节点出现故障时,系统会重新拉起一个Redis进程(没有数据),当节点故障业务自动切换完成后,应用程序需要将数据重新预热,以免对后端数据库产生访问压力冲击。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">单副本架构不能提供数据可靠性,如果发生节点故障,您需要重新对业务进行预热,因此,在对数据可靠性要求较高的敏感性业务中,建议选用双副本架构。</span> </section> <ul class="list-paddingleft-2"> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">单个Redis性能压力可控</span> </section></li> </ul> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">由于Redis原生采用单线程机制,CPU为单核能力,性能在8万QPS的业务建议使用。如果需要更高的性能要求,请选用集群版配置。</span> </section> <ul class="list-paddingleft-2"> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">Redis命令相对简单,排序、计算类命令较少</span> </section></li> </ul> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">由于Redis的单线程机制,CPU为主要瓶颈。如排序、计算类较多的业务建议选用集群版配置。<br></span> </section> <section style="line-height: 1.75em;"> <br> </section> <h2 data-id="heading-9" style="line-height: 1.75em;"><span style="color: rgb(4, 176, 91);"><strong><span style="color: rgb(4, 176, 91);font-size: 15px;letter-spacing: 1px;">2.3 集群版-双副本</span></strong></span></h2> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">可轻松突破Redis自身单线程瓶颈,满足大容量、高性能的业务需求。双副本集群版实例采用集群架构,每个分片服务器采用主从(master-replica)双副本模式。集群版支持代理和直连两种连接模式,您可以根据本章节的说明,选择适合业务需求的连接模式。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <p style="white-space: normal;letter-spacing: 0.544px;background: rgb(255, 255, 255);text-align: center;font-size: 14px;font-family: Calibri, sans-serif;color: rgb(0, 0, 0);line-height: 28px;"><img class="rich_pages" data-ratio="0.9900990099009901" data-s="300,640" src="/upload/aae6600a288d838d113c0d9ec611c195.png" data-type="png" data-w="101" style=""><br></p> <p style="text-align: center;"><span style=""><strong style="color: rgb(4, 181, 107);"><strong style="font-size: 16px;">-&nbsp; &nbsp; &nbsp;</strong><strong style="font-size: 15px;">代理模式</strong><strong><span style="font-size: 16px;">&nbsp; &nbsp; -</span></strong></strong></span></p> <h3 data-id="heading-10" style="line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;"></span></h3> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">集群架构的本地盘实例默认采用代理(proxy)模式,支持通过一个统一的连接地址(域名)访问Redis集群,客户端的请求通过代理服务器转发到各数据分片,代理服务器、数据分片和配置服务器均不提供单独的连接地址,降低了应用开发难度和代码复杂度。代理模式的服务架构图和组件说明如下。</span> </section> <p><img data-backh="355" data-backw="578" data-ratio="0.6148148148148148" src="/upload/9a0423462ca3e41f2fa7e36eda12ad0a.png" data-type="png" data-w="1080" style="margin: 20px auto 30px;display: block;width: 677px;box-sizing: border-box !important;visibility: visible !important;"></p> <p><img data-backh="143" data-backw="578" data-ratio="0.24814814814814815" src="/upload/66ba9761d0e954ea546e5c531e7416ca.png" data-type="png" data-w="1080" style="margin: 20px auto 30px;border-width: 1px;border-style: solid;border-color: rgb(238, 237, 235);display: block;box-sizing: border-box !important;background-color: rgb(238, 237, 235) !important;background-size: 22px !important;background-position: center center !important;background-repeat: no-repeat !important;height: 169.5px !important;width: 677px !important;"></p> <section style="line-height: 1.75em;"> <br> </section> <h3 data-id="heading-11" style="line-height: 1.75em;"><span style="color: rgb(4, 176, 91);"><strong><span style="color: rgb(4, 176, 91);font-size: 15px;letter-spacing: 1px;">直连模式</span></strong></span></h3> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">因所有请求都要通过代理服务器转发,代理模式在降低业务开发难度的同时也会小幅度影响Redis服务的响应速度。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">如果业务对响应速度的要求非常高,您可以使用直连模式,绕过代理服务器直接连接后端数据分片,从而降低网络开销和服务响应时间。直连模式的服务架构和说明如下。</span> </section> <p><img data-ratio="0.799074074074074" src="/upload/df6c6b80385c8ba8267f47b6c3edce26.png" data-type="png" data-w="1080" style="margin: 20px auto 30px;display: block;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"><span style="">前提条件 使用Jedis、PhpRedis等支持Redis Cluster的客户端。</span></p> <ul class="list-paddingleft-2"> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">使用不支持Redis Cluster的客户端,可能因客户端无法重定向请求到正确的分片而获取不到需要的数据。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">Jedis对于Redis Cluster的支持是基于JedisCluster这个类,详细说明请参见Jedis文档。</span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">您可以在Redis官网的客户端列表里查找更多支持Redis Cluster的客户端。</span> </section></li> </ul> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">使用自定义连接池的示例代码 :</span> </section> <section class="code-snippet__fix code-snippet__js"> <ul class="code-snippet__line-index code-snippet__js"> </ul> <pre class="code-snippet__js" data-lang="java"><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__keyword" style="max-width: 1000%;">import</span> redis.clients.jedis.*;</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__keyword" style="max-width: 1000%;">import</span> java.util.HashSet;</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__keyword" style="max-width: 1000%;">import</span> java.util.Set; </span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__keyword" style="max-width: 1000%;">public</span> <span class="code-snippet__class" style="max-width: 1000%;"><span class="code-snippet__keyword" style="max-width: 1000%;">class</span> <span class="code-snippet__title" style="max-width: 1000%;">main</span> </span>{</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__keyword" style="max-width: 1000%;">private</span> <span class="code-snippet__keyword" style="max-width: 1000%;">static</span> <span class="code-snippet__keyword" style="max-width: 1000%;">final</span> <span class="code-snippet__keyword" style="max-width: 1000%;">int</span> DEFAULT_TIMEOUT = <span class="code-snippet__number" style="max-width: 1000%;">2000</span>; </span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__keyword" style="max-width: 1000%;">private</span> <span class="code-snippet__keyword" style="max-width: 1000%;">static</span> <span class="code-snippet__keyword" style="max-width: 1000%;">final</span> <span class="code-snippet__keyword" style="max-width: 1000%;">int</span> DEFAULT_REDIRECTIONS = <span class="code-snippet__number" style="max-width: 1000%;">5</span>;</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__keyword" style="max-width: 1000%;">private</span> <span class="code-snippet__keyword" style="max-width: 1000%;">static</span> <span class="code-snippet__keyword" style="max-width: 1000%;">final</span> JedisPoolConfig DEFAULT_CONFIG = <span class="code-snippet__keyword" style="max-width: 1000%;">new</span> JedisPoolConfig();</span></code><code style=""><span class="code-snippet_outer"><br></span></code><code style=""><span class="code-snippet_outer"><br></span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"><span class="code-snippet__function" style="max-width: 1000%;"><span class="code-snippet__keyword" style="max-width: 1000%;">public</span> <span class="code-snippet__keyword" style="max-width: 1000%;">static</span> <span class="code-snippet__keyword" style="max-width: 1000%;">void</span> <span class="code-snippet__title" style="max-width: 1000%;">main</span><span class="code-snippet__params" style="max-width: 1000%;">(String args[])</span></span>{</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> JedisPoolConfig config = <span class="code-snippet__keyword" style="max-width: 1000%;">new</span> JedisPoolConfig();</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__comment" style="max-width: 1000%;">// 最大空闲连接数, 根据业务需要设置,不能超过实例规格规定的最大的连接数</span></span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> config.setMaxIdle(<span class="code-snippet__number" style="max-width: 1000%;">200</span>);</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__comment" style="max-width: 1000%;">// 最大连接数, 根据业务需要设置,不能超过实例规格规定的最大的连接数</span></span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> config.setMaxTotal(<span class="code-snippet__number" style="max-width: 1000%;">300</span>);</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> config.setTestOnBorrow(<span class="code-snippet__keyword" style="max-width: 1000%;">false</span>);</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> config.setTestOnReturn(<span class="code-snippet__keyword" style="max-width: 1000%;">false</span>);</span></code><code style=""><span class="code-snippet_outer"><br></span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__comment" style="max-width: 1000%;">// 开通直连访问时申请到的直连地址</span></span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> String host = <span class="code-snippet__string" style="max-width: 1000%;">"r-bp1xxxxxxxxxxxx.redis.rds.aliyuncs.com"</span>; </span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__keyword" style="max-width: 1000%;">int</span> port = <span class="code-snippet__number" style="max-width: 1000%;">6379</span>;</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> <span class="code-snippet__comment" style="max-width: 1000%;">// 实例的密码</span></span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> String password = <span class="code-snippet__string" style="max-width: 1000%;">"xxxxx"</span>;</span></code><code style=""><span class="code-snippet_outer"><br></span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> Set&lt;HostAndPort&gt; jedisClusterNode = <span class="code-snippet__keyword" style="max-width: 1000%;">new</span> HashSet&lt;HostAndPort&gt;();</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> jedisClusterNode.add(<span class="code-snippet__keyword" style="max-width: 1000%;">new</span> HostAndPort(host, port));</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> JedisCluster jc = <span class="code-snippet__keyword" style="max-width: 1000%;">new</span> JedisCluster(jedisClusterNode, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT,</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;"> DEFAULT_REDIRECTIONS,password, <span class="code-snippet__string" style="max-width: 1000%;">"clientName"</span>, config);</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;">}</span></code><code style=""><span class="code-snippet_outer" style="max-width: 1000%;">}</span></code></pre> </section> <p><br></p> <section style="line-height: 1.75em;"> <span style="color: rgb(4, 176, 91);"><strong><span style="color: rgb(4, 176, 91);font-size: 15px;letter-spacing: 1px;">2.4 集群版-单副本</span></strong></span> <br> </section> <p><img data-ratio="0.32314814814814813" src="/upload/e1ff9116bc1a6ce2bf13d5ddb85495e2.png" data-type="png" data-w="1080" style="margin: 20px auto 30px;display: block;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"></p> <section style="line-height: 1.75em;"> <br> </section> <p><img data-ratio="0.25092592592592594" src="/upload/7268a6ff5febbcfa5f1328c01fe0d85c.png" data-type="png" data-w="1080" style="margin: 20px auto 30px;display: block;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"></p> <section style="line-height: 1.75em;"> <br> </section> <h2 data-id="heading-13" style="line-height: 1.75em;"><span style="color: rgb(4, 176, 91);"><strong><span style="color: rgb(4, 176, 91);font-size: 15px;letter-spacing: 1px;">2.5 读写分离版</span></strong></span></h2> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">针对读多写少的业务场景,提供高可用、高性能、灵活的读写分离服务,满足热点数据集中及高并发读取的业务需求,最大化地节约运维成本。读写分离版主要由主备节点、只读节点、Proxy(代理)节点和高可用系统组成。</span> </section> <section style="line-height: 1.75em;"> <br> </section> <p><img data-ratio="0.6953703703703704" src="/upload/724fe79d7b5e7be9fabde8271caabfe9.png" data-type="png" data-w="1080" style="margin: 20px auto 30px;display: block;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"></p> <p><img data-ratio="0.6231481481481481" src="/upload/821ce25de0dd1d3c9f685276ede94a29.png" data-type="png" data-w="1080" style="margin: 20px auto 30px;display: block;box-sizing: border-box !important;width: 677px !important;visibility: visible !important;"></p> <p style="letter-spacing: 0.544px;white-space: normal;background: rgb(255, 255, 255);text-align: center;font-size: 14px;font-family: Calibri, sans-serif;color: rgb(0, 0, 0);line-height: 28px;"><img class="rich_pages" data-ratio="0.9900990099009901" data-s="300,640" src="/upload/aae6600a288d838d113c0d9ec611c195.png" data-type="png" data-w="101" style=""><br></p> <p style="text-align: center;"><span style=""><strong style="color: rgb(4, 181, 107);"><strong style="font-size: 16px;">-&nbsp; &nbsp; &nbsp;</strong><strong style="font-size: 15px;">特点</strong><strong><span style="font-size: 16px;">&nbsp; &nbsp; -</span></strong></strong></span></p> <section style="line-height: 1.75em;"> <br> </section> <h3 data-id="heading-14" style="line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;">特点</span></h3> <ul class="list-paddingleft-2"> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">高可用</span> </section></li> </ul> <ol class="list-paddingleft-2"> <li> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">通过自研的高可用系统自动监控所有数据节点的健康状态,为整个实例的可用性保驾护航。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">主节点不可用时自动选择新的主节点并重新搭建复制拓扑。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">某个只读节点异常时,高可用系统能够自动探知并重新启动新节点完成数据同步,下线异常节点。</span> </section></li> <li> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">Proxy节点实时感知每个只读实例的服务状态。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">在某个只读实例异常期间,Proxy会自动降低该节点的服务权重,发现只读节点连续失败超过一定次数以后,会停止异常节点的服务权利,并具备继续监控后续重新启动节点服务的能力。</span> </section></li> </ol> <section style="line-height: 1.75em;"> <br> </section> <ul class="list-paddingleft-2"> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">高性能</span> </section></li> </ul> <ol class="list-paddingleft-2"> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">读写分离版采取链式复制架构,可以通过扩展只读实例个数使整体实例性能呈线性增长,同时基于源码层面对Redis复制流程的定制优化,可以最大程度地提升线性复制的系统稳定性,充分利用每一个只读节点的物理资源。</span> </section></li> </ol> <section style="line-height: 1.75em;text-align: center;"> <br> </section> <p style="letter-spacing: 0.544px;white-space: normal;background: rgb(255, 255, 255);text-align: center;font-size: 14px;font-family: Calibri, sans-serif;color: rgb(0, 0, 0);line-height: 28px;"><img class="rich_pages" data-ratio="0.9900990099009901" data-s="300,640" src="/upload/aae6600a288d838d113c0d9ec611c195.png" data-type="png" data-w="101" style=""><br></p> <p style="text-align: center;"><span style=""><strong style="color: rgb(4, 181, 107);"><strong style="font-size: 16px;">-&nbsp; &nbsp; &nbsp;</strong><strong style="font-size: 15px;">使用场景</strong><strong><span style="font-size: 16px;">&nbsp; &nbsp; -</span></strong></strong></span></p> <p><br></p> <ul class="list-paddingleft-2"> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">读取请求QPS(Queries Per Second)压力较大</span> </section></li> </ul> <section style="line-height: 1.75em;"> <br> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">标准版Redis无法支撑较大的QPS,如果业务类型是读多写少类型,需要采用多个只读节点的部署方式来突破Redis单线程的性能瓶颈。Redis集群版提供1个、3个、5个只读节点的配置,相比标准版可以将QPS提升近5倍。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">对Redis协议兼容性要求较高的业务 读写分离版完全兼容Redis协议命令,可将自建Redis数据库迁移至读写分离版,同时支持从Redis标准版(双副本)一键平滑升级至读写分离版。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><br></span> </section> <p style="letter-spacing: 0.544px;white-space: normal;background: rgb(255, 255, 255);text-align: center;font-size: 14px;font-family: Calibri, sans-serif;color: rgb(0, 0, 0);line-height: 28px;"><img class="rich_pages" data-ratio="0.9900990099009901" data-s="300,640" src="/upload/aae6600a288d838d113c0d9ec611c195.png" data-type="png" data-w="101" style=""><br></p> <p style="text-align: center;"><span style=""><strong style="color: rgb(4, 181, 107);"><strong style="font-size: 16px;">-&nbsp; &nbsp; &nbsp;</strong><strong style="font-size: 15px;">总结</strong><strong><span style="font-size: 16px;">&nbsp; &nbsp; -</span></strong></strong></span></p> <section style="line-height: 1.75em;"> <br> </section> <ul class="list-paddingleft-2"> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">当一个只读节点发生故障时,请求会转发到其他节点;如果所有只读节点均不可用,请求会全部转发到主节点。只读节点异常可能导致主节点负载提高、响应时间变长,因此在读负载高的业务场景建议使用多个只读节点。</span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"></span> </section> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"></span> </section></li> <li style="font-size: 15px;letter-spacing: 1px;"> <section style="line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">某些场景会触发只读节点的全量同步,例如在主节点触发高可用切换后。全量同步期间只读节点不提供服务并返回-LOADING Redis is loading the dataset in memory\r\n信息。</span> </section></li> </ul>

新技能 MyBatis 千万数据表,快速分页!

作者:微信小助手

<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;line-height: 1.6;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &quot;PingFang SC&quot;, Cambria, Cochin, Georgia, Times, &quot;Times New Roman&quot;, serif;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);"> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzA5MTU0OTY0Ng==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/zc3KLDBfJlmPt0J5PXYOoiaG8wsQPZrLevbxMZSfgQ0YypNYaicnbS0P9UicluuOySLSP4CjTcRUVHCZzYeXQ9WlA/0?wx_fmt=png" data-nickname="Java派" data-alias="javapai" data-signature="专注Java相关技术栈:Spring全家筒、Docker、k8s、Mysql、集群、微服务、中间件等知识。" data-from="0"></mpprofile> <span style="color: rgb(53, 179, 120);font-size: 20px;font-weight: bold;letter-spacing: 0.05em;text-align: left;"></span> </section> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);"><span style="display: none;"></span>基本概念<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。流式查询的好处是能够降低内存使用。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">如果没有流式查询,我们想要从数据库取 1000 万条记录而又没有足够的内存时,就不得不分页查询,而分页查询效率取决于表设计,如果设计的不好,就无法执行高效的分页查询。因此流式查询是一个数据库访问框架必须具备的功能。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">流式查询的过程当中,数据库连接是保持打开状态的,因此要注意的是:执行一个流式查询后,数据库访问框架就不负责关闭数据库连接了,需要应用在取完数据后自己关闭。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);"><span style="display: none;"></span>MyBatis 流式查询接口<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">MyBatis 提供了一个叫<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">org.apache.ibatis.cursor.Cursor</code>的接口类用于流式查询,这个接口继承了 java.io.Closeable 和 java.lang.Iterable 接口,由此可知:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> Cursor 是可关闭的; </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> Cursor 是可遍历的。 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">除此之外,Cursor 还提供了三个方法:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> isOpen():用于在取数据之前判断 Cursor 对象是否是打开状态。只有当打开时 Cursor 才能取数据; </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> isConsumed():用于判断查询结果是否全部取完。 </section></li> <li> <section style="line-height: 26px;color: rgb(1, 1, 1);margin-top: 10px;margin-bottom: 10px;"> getCurrentIndex():返回已经获取了多少条数据 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">因为 Cursor 实现了迭代器接口,因此在实际使用当中,从 Cursor 取数据非常简单:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD2HrFxSZpl6I1pLnfqStCk0IhRdViaSDibt8UetKDt0G8ST5icfpchfxppfAfFUCibePbichnYoL0dSPibjOgKFAQOn7x/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">cursor.forEach(rowObject&nbsp;-&gt;&nbsp;{...});<br></code></pre> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);"><span style="display: none;"></span>但构建 Cursor 的过程不简单<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">我们举个实际例子。下面是一个 Mapper 类:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD2HrFxSZpl6I1pLnfqStCk0IhRdViaSDibt8UetKDt0G8ST5icfpchfxppfAfFUCibePbichnYoL0dSPibjOgKFAQOn7x/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">@Mapper<br>public&nbsp;interface&nbsp;FooMapper&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;@Select(<span style="color: #98c379;line-height: 26px;">"select&nbsp;*&nbsp;from&nbsp;foo&nbsp;limit&nbsp;#{limit}"</span>)<br>&nbsp;&nbsp;&nbsp;&nbsp;Cursor&lt;Foo&gt;&nbsp;scan(@Param(<span style="color: #98c379;line-height: 26px;">"limit"</span>)&nbsp;int&nbsp;<span style="color: #e6c07b;line-height: 26px;">limit</span>);<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">方法 scan() 是一个非常简单的查询。通过指定 Mapper 方法的返回值为 Cursor 类型,MyBatis 就知道这个查询方法一个流式查询。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">然后我们再写一个 SpringMVC Controller 方法来调用 Mapper(无关的代码已经省略):</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD2HrFxSZpl6I1pLnfqStCk0IhRdViaSDibt8UetKDt0G8ST5icfpchfxppfAfFUCibePbichnYoL0dSPibjOgKFAQOn7x/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">@GetMapping(<span style="color: #98c379;line-height: 26px;">"foo/scan/0/{limit}"</span>)<br>public&nbsp;void&nbsp;scanFoo0(@PathVariable(<span style="color: #98c379;line-height: 26px;">"limit"</span>)&nbsp;int&nbsp;<span style="color: #e6c07b;line-height: 26px;">limit</span>)&nbsp;throws&nbsp;Exception&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;(Cursor&lt;Foo&gt;&nbsp;cursor&nbsp;=&nbsp;fooMapper.scan(<span style="color: #e6c07b;line-height: 26px;">limit</span>))&nbsp;{&nbsp;&nbsp;//&nbsp;1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cursor.forEach(foo&nbsp;-&gt;&nbsp;{});&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;2<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">上面的代码中,fooMapper 是 @Autowired 进来的。注释 1 处调用 scan 方法,得到 Cursor 对象并保证它能最后关闭;2 处则是从 cursor 中取数据。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">上面的代码看上去没什么问题,但是执行 scanFoo0() 时会报错:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD2HrFxSZpl6I1pLnfqStCk0IhRdViaSDibt8UetKDt0G8ST5icfpchfxppfAfFUCibePbichnYoL0dSPibjOgKFAQOn7x/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">java.lang.IllegalStateException:&nbsp;A&nbsp;Cursor&nbsp;is&nbsp;already&nbsp;closed.<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这是因为我们前面说了在取数据的过程中需要保持数据库连接,而 Mapper 方法通常在执行完后连接就关闭了,因此 Cusor 也一并关闭了。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">所以,解决这个问题的思路不复杂,保持数据库连接打开即可。我们至少有三种方案可选。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);"><span style="display: none;"></span>方案一:SqlSessionFactory<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">我们可以用 SqlSessionFactory 来手工打开数据库连接,将 Controller 方法修改如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD2HrFxSZpl6I1pLnfqStCk0IhRdViaSDibt8UetKDt0G8ST5icfpchfxppfAfFUCibePbichnYoL0dSPibjOgKFAQOn7x/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">@GetMapping(<span style="color: #98c379;line-height: 26px;">"foo/scan/1/{limit}"</span>)<br>public&nbsp;void&nbsp;scanFoo1(@PathVariable(<span style="color: #98c379;line-height: 26px;">"limit"</span>)&nbsp;int&nbsp;<span style="color: #e6c07b;line-height: 26px;">limit</span>)&nbsp;throws&nbsp;Exception&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SqlSession&nbsp;sqlSession&nbsp;=&nbsp;sqlSessionFactory.openSession();&nbsp;&nbsp;//&nbsp;1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cursor&lt;Foo&gt;&nbsp;cursor&nbsp;=&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sqlSession.getMapper(FooMapper.class).scan(<span style="color: #e6c07b;line-height: 26px;">limit</span>)&nbsp;&nbsp;&nbsp;//&nbsp;2<br>&nbsp;&nbsp;&nbsp;&nbsp;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cursor.forEach(foo&nbsp;-&gt;&nbsp;{&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">上面的代码中,1 处我们开启了一个 SqlSession (实际上也代表了一个数据库连接),并保证它最后能关闭;2 处我们使用 SqlSession 来获得 Mapper 对象。这样才能保证得到的 Cursor 对象是打开状态的。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);"><span style="display: none;"></span>方案二:TransactionTemplate<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">在 Spring 中,我们可以用 TransactionTemplate 来执行一个数据库事务,这个过程中数据库连接同样是打开的。代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD2HrFxSZpl6I1pLnfqStCk0IhRdViaSDibt8UetKDt0G8ST5icfpchfxppfAfFUCibePbichnYoL0dSPibjOgKFAQOn7x/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">@GetMapping(<span style="color: #98c379;line-height: 26px;">"foo/scan/2/{limit}"</span>)<br>public&nbsp;void&nbsp;scanFoo2(@PathVariable(<span style="color: #98c379;line-height: 26px;">"limit"</span>)&nbsp;int&nbsp;<span style="color: #e6c07b;line-height: 26px;">limit</span>)&nbsp;throws&nbsp;Exception&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;TransactionTemplate&nbsp;transactionTemplate&nbsp;=&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new&nbsp;TransactionTemplate(transactionManager);&nbsp;&nbsp;//&nbsp;1<br><br>&nbsp;&nbsp;&nbsp;&nbsp;transactionTemplate.execute(status&nbsp;-&gt;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;(Cursor&lt;Foo&gt;&nbsp;cursor&nbsp;=&nbsp;fooMapper.scan(<span style="color: #e6c07b;line-height: 26px;">limit</span>))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cursor.forEach(foo&nbsp;-&gt;&nbsp;{&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch&nbsp;(IOException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="color: #e6c07b;line-height: 26px;">return</span>&nbsp;null;<br>&nbsp;&nbsp;&nbsp;&nbsp;});<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">上面的代码中,1 处我们创建了一个 TransactionTemplate 对象(此处 transactionManager 是怎么来的不用多解释,本文假设读者对 Spring 数据库事务的使用比较熟悉了),2 处执行数据库事务,而数据库事务的内容则是调用 Mapper 对象的流式查询。注意这里的 Mapper 对象无需通过 SqlSession 创建。</p> <h3 data-tool="mdnice编辑器" style="font-size: 20px;margin-top: 1.2em;margin-bottom: 1em;font-weight: bold;color: rgb(53, 179, 120);"><span style="display: none;"></span>方案三:@Transactional 注解<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">这个本质上和方案二一样,代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&quot;https://mmbiz.qpic.cn/mmbiz_svg/Kwg1Hs1pPD2HrFxSZpl6I1pLnfqStCk0IhRdViaSDibt8UetKDt0G8ST5icfpchfxppfAfFUCibePbichnYoL0dSPibjOgKFAQOn7x/640?wx_fmt=svg&quot;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">@GetMapping(<span style="color: #98c379;line-height: 26px;">"foo/scan/3/{limit}"</span>)<br>@Transactional<br>public&nbsp;void&nbsp;scanFoo3(@PathVariable(<span style="color: #98c379;line-height: 26px;">"limit"</span>)&nbsp;int&nbsp;<span style="color: #e6c07b;line-height: 26px;">limit</span>)&nbsp;throws&nbsp;Exception&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;(Cursor&lt;Foo&gt;&nbsp;cursor&nbsp;=&nbsp;fooMapper.scan(<span style="color: #e6c07b;line-height: 26px;">limit</span>))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cursor.forEach(foo&nbsp;-&gt;&nbsp;{&nbsp;});<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">它仅仅是在原来方法上面加了个<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &quot;Operator Mono&quot;, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 179, 120);">@Transactional</code>注解。这个方案看上去最简洁,但请注意 Spring 框架当中注解使用的坑:只在外部调用时生效。在当前类中调用这个方法,依旧会报错。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">以上是三种实现 MyBatis 流式查询的方法。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin: 10px 5px;border-left-color: rgb(53, 179, 120);border-right: 0px solid rgb(53, 179, 120);color: rgb(97, 97, 97);quotes: none;background: rgb(251, 249, 253);"></blockquote> </section>