作者:微信小助手
<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;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 0px;"> <h2 data-tool="mdnice编辑器" style="margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="font-size: 20px;color: #ab1942;">前言</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">最近做项目的时候,涉及到一个单点登录,即是项目的登录页面,用的是公司共用的一个登录页面,在该页面统一处理逻辑。最终实现用户只需登录一次,就可以以登录状态访问公司旗下的所有网站。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgb(255, 218, 169);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;font-size: 15px;">单点登录( Single Sign On ,简称 SSO),是目前比较流行的企业业务整合的解决方案之一,用于多个应用系统间,用户只需要登录一次就可以访问所有相互信任的应用系统。</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">其中本文讲的是在登录后如何管理<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">access_token</code>和<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">refresh_token</code>,主要就是封装 axios拦截器,在此记录。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span><span style="font-size: 20px;color: #ab1942;">需求</span></h2> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 前置场景 </section></li> </ul> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.7221095334685599" src="/upload/26d98c23a3f0c426371234999254d245.png" data-type="other" data-w="986" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;"> </figure> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 进入该项目某个页面 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">http://xxxx.project.com/profile</code>需要登录,未登录就跳转至SSO登录平台,此时的登录网址 url为 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">http://xxxxx.com/login?app_id=project_name_id&redirect_url=http://xxxx.project.com/profile</code>,其中 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">app_id</code>是后台那边约定定义好的, <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">redirect_url</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;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">?code=XXXXX</code>,即是 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">http://xxxx.project.com/profile?code=XXXXXX</code>,code的值是使用一次后即无效,且10分钟内过期 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 立马获取这个code值再去请求一个api <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">/access_token/authenticate</code>,携带参数 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">{ verify_code: code }</code>,并且该api已经自带 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">app_id</code>和 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">app_secret</code>两个固定值参数,通过它去请求授权的api,请求成功后得到返回值 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">{ access_token: "xxxxxxx", refresh_token: "xxxxxxxx", expires_in: xxxxxxxx }</code>,存下 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">access_token</code>和 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">refresh_token</code>到cookie中(localStorage也可以),此时用户就算登录成功了。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">access_token</code>为标准JWT格式,是授权令牌,可以理解就是验证用户身份的,是应用在调用api访问和修改用户数据必须传入的参数(放在请求头headers里),2小时后过期。也就是说,做完前三步后,你可以调用需要用户登录才能使用的api;但是假如你什么都不操作,静静过去两个小时后,再去请求这些api,就会报 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">access_token</code>过期,调用失败。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 那么总不能2小时后就让用户退出登录吧,解决方法�
作者:微信小助手
<p style="white-space: normal;margin-bottom: 0px;" data-mpa-powered-by="yiban.io"><span style="font-size: 15px;">Spring Boot 已经成为 Java 届的 No.1 框架,每天都在蹂躏着数百万的程序员们。当服务的压力上升,对 Spring Boot 服务的优化就会被提上议程。</span><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">本文将详细讲解 Spring Boot 服务优化的一般思路,并附上若干篇辅助文章作为开胃菜。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">本文较长,最适合收藏之。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><strong><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;color: rgb(171, 25, 66);">1 有监控才有方向</span></strong></h2> <section style="margin-bottom: 0px;"> <span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">在开始对 Spring Boot 服务进行性能优化之前,我们需要做一些准备,把 Spring Boot 服务的一些数据暴露出来。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">比如,你的服务用到了缓存,就需要把缓存命中率这些数据进行收集;用到了数据库连接池,就需要把连接池的参数给暴露出来。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">我们这里采用的监控工具是 Prometheus,它是一个是时序数据库,能够存储我们的指标。Spring Boot 可以非常方便的接入到 Prometheus 中。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">创建一个 Spring Boot 项目后,首先加入 Maven 依赖。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <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="xml"><code><span class="code-snippet_outer"><span class="code-snippet__tag"><<span class="code-snippet__name">dependency</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"><<span class="code-snippet__name">groupId</span>></span>org.springframework.boot<span class="code-snippet__tag"></<span class="code-snippet__name">groupId</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"><<span class="code-snippet__name">artifactId</span>></span>spring-boot-starter-actuator<span class="code-snippet__tag"></<span class="code-snippet__name">artifactId</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"></<span class="code-snippet__name">dependency</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"><<span class="code-snippet__name">dependency</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"><<span class="code-snippet__name">groupId</span>></span>io.micrometer<span class="code-snippet__tag"></<span class="code-snippet__name">groupId</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"><<span class="code-snippet__name">artifactId</span>></span>micrometer-registry-prometheus<span class="code-snippet__tag"></<span class="code-snippet__name">artifactId</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"></<span class="code-snippet__name">dependency</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"><<span class="code-snippet__name">dependency</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"><<span class="code-snippet__name">groupId</span>></span>io.micrometer<span class="code-snippet__tag"></<span class="code-snippet__name">groupId</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"><<span class="code-snippet__name">artifactId</span>></span>micrometer-core<span class="code-snippet__tag"></<span class="code-snippet__name">artifactId</span>></span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__tag"></<span class="code-snippet__name">dependency</span>></span></span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">然后,我们需要在 application.properties 配置文件中,开放相关的监控接口。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <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="cpp"><code><span class="code-snippet_outer">management.endpoint.metrics.enabled=<span class="code-snippet__literal">true</span></span></code><code><span class="code-snippet_outer">management.endpoints.web.exposure.include=*</span></code><code><span class="code-snippet_outer">management.endpoint.prometheus.enabled=<span class="code-snippet__literal">true</span></span></code><code><span class="code-snippet_outer">management.metrics.<span class="code-snippet__keyword">export</span>.prometheus.enabled=<span class="code-snippet__literal">true</span></span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">启动之后,我们就可以通过访问 http://localhost:8080/actuator/prometheus 来获取监控数据。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p style="white-space: normal;text-align: left;margin-bottom: 0px;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.44061757719714967" data-s="300,640" src="/upload/f0936560b9606452e70a7eea70dcf6fd.png" data-type="png" data-w="842"></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">想要监控业务数据也是比较简单的。你只需要注入一个 MeterRegistry 实例即可。下面是一段示例代码:</span><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <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="kotlin"><code><span class="code-snippet_outer"><span class="code-snippet__meta">@Autowired</span></span></code><code><span class="code-snippet_outer">MeterRegistry registry;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__meta">@GetMapping(<span class="code-snippet__meta-string">"/test"</span>)</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__meta">@ResponseBody</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> String test() {</span></code><code><span class="code-snippet_outer"> registry.counter(<span class="code-snippet__string">"test"</span>, <span class="code-snippet__string">"from"</span>, <span class="code-snippet__string">"127.0.0.1"</span>, <span class="code-snippet__string">"method"</span>, <span class="code-snippet__string">"test"</span>).increment();</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">return</span> <span class="code-snippet__string">"ok"</span>;</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">从监控连接中,我们可以找到刚刚添加的监控信息。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <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="javascript"><code><span class="code-snippet_outer">test_total{<span class="code-snippet__keyword">from</span>=<span class="code-snippet__string">"127.0.0.1"</span>,method=<span class="code-snippet__string">"test"</span>,} <span class="code-snippet__number">5.0</span></span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">这里简单介绍一下流行的 Prometheus 监控体系。Prometheus 使用拉的方式获取监控数据,这个暴露数据的过程可以交给功能更加齐全的 telegraf 组件。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p style="white-space: normal;text-align: left;margin-bottom: 0px;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.47177848775292863" data-s="300,640" src="/upload/a9b0823cecbab287915a90525e6eb838.png" data-type="png" data-w="939"></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">如图,我们通常使用 Grafana 进行监控数据的展示,使用 AlertManager 组件进行提前预警。这一部分的搭建工作不是我们的重点,感兴趣的同学可自行研究。下图便是一张典型的监控图,可以看到 Redis 的缓存命中率等情况。</span><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p style="white-space: normal;text-align: left;margin-bottom: 0px;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.42602230483271375" data-s="300,640" src="/upload/86b81f2d47e81e1df22333d61ed42665.png" data-type="png" data-w="1345"></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></h2> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><strong><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;color: rgb(171, 25, 66);">2 Java 生成火焰图</span></strong><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;color: rgb(171, 25, 66);"></span></h2> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">火焰图是用来分析程序运行瓶颈的工具。在纵向,表示的是调用栈的深度;横向表明的是消耗的时间。所以格子的宽度越大,越说明它可能是一个瓶颈。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">火焰图也可以用来分析 Java 应用。可以从 GitHub 上下载 async-profiler 的压缩包 进行相关操作。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">比如,我们把它解压到 /root 目录。然后以 javaagent 的方式来启动 Java 应用。命令行如下:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <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="nginx"><code><span class="code-snippet_outer"><span class="code-snippet__attribute">java</span> -agentpath:/root/build/libasyncProfiler.so=start,svg,file=profile.svg -jar spring-petclinic-<span class="code-snippet__number">2</span>.<span class="code-snippet__number">3</span>.<span class="code-snippet__number">1</span>.BUILD-SNAPSHOT.jar</span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">运行一段时间后,停止进程,可以看到在当前目录下,生成了 profile.svg 文件,这个文件是可以用浏览器打开的,一层层向下浏览,即可找到需要优化的目标。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">3 Skywalking</span></strong></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">对于一个web服务来说,最缓慢的地方就在于数据库操作。所以,使用本地缓存和分布式缓存优化,能够获得最大的性能提升。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">对于如何定位到复杂分布式环境中的问题,我这里想要分享另外一个工具:Skywalking。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">Skywalking 是使用探针技术(Java Agent)来实现的。通过在 Java 的启动参数中,加入 javaagent 的 Jar 包,即可将性能数据和调用链数据封装、发送到 Skywalking 的服务器。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">下载相应的安装包(如果使用 Elasticsearch 存储,需要下载专用的安装包),配置好存储之后,即可一键启动。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">将 agent 的压缩包,解压到相应的目录。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <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="nginx"><code><span class="code-snippet_outer"><span class="code-snippet__attribute">tar</span> xvf skywalking-agent.tar.gz -C /opt/</span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">在业务启动参数中加入 agent 的包,比如原来的启动命令是:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <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="bash"><code><span class="code-snippet_outer">java -jar /opt/<span class="code-snippet__built_in">test</span>-service/spring-boot-demo.jar --spring.profiles.active=dev</span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">改造后的启动命令是:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <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="nginx"><code><span class="code-snippet_outer"><span class="code-snippet__attribute">java</span> -javaagent:/opt/skywalking-agent/skywalking-agent.jar -Dskywalking.agent.service_name=the-demo-name -jar /opt/test-service/spring-boot-demo.ja --spring.profiles.active=dev</span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">访问一些服务的链接,打开 Skywalking 的 UI,即可看到下图的界面。我们可以从图中找到响应比较慢 QPS 又比较高的的接口,进行专项优化。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p style="white-space: normal;text-align: left;margin-bottom: 0px;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.44945188794153473" data-s="300,640" src="/upload/e8e9e1229ccd957378b6336ad46156d3.png" data-type="png" data-w="1642"></p> <p style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><strong><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;color: rgb(171, 25, 66);">4 优化思路</span></strong></h2> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">对一个普通的 Web 服务来说,我们来看一下,要访问到具体的数据,都要经历哪些主要的环节。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">如下图,在浏览器中输入相应的域名,需要通过 DNS 解析到具体的 IP 地址上。为了保证高可用,我们的服务一般都会部署多份,然后使用 Nginx 做反向代理和负载均衡。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">Nginx 根据资源的特性,会承担一部分动静分离的功能。其中,动态功能部分,会进入我们的 Spring Boot 服务。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p style="white-space: normal;text-align: left;margin-bottom: 0px;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.565947242206235" data-s="300,640" src="/upload/24948ab2fd8499a0e53f3337facef271.png" data-type="png" data-w="834"></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">Spring Boot 默认使用内嵌的 Tomcat 作为 Web 容器,使用典型的 MVC 模式,最终访问到我们的数据。</span><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <h2 data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><strong><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;color: rgb(171, 25, 66);">5 HTTP 优化</span></strong></h2> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">下面我们举例来看一下,哪些动作能够加快网页的获取。为了描述方便,我们仅讨论 HTTP1.1 协议的。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><strong><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">使用 CDN 加速文件获取</span></strong><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">比较大的文件,尽量使用 CDN(Content Delivery Network)分发。甚至是一些常用的前端脚本、样式、图片等,都可以放到 CDN 上。CDN 通常能够加快这些文件的获取,网页加载也更加迅速。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><strong>合理设置 Cache-Control 值</strong></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">浏览器会判断 HTTP 头 Cache-Control 的内容,用来决定是否使用浏览器缓存,这在管理一些静态文件的时候,非常有用。相同作用的头信息还有 Expires。Cache-Control 表示多久之后过期,Expires 则表示什么时候过期。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">这个参数可以在 Nginx 的配置文件中进行设置。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"><br></span></p> <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="nginx"><code><span class="code-snippet_outer"><span class="code-snippet__attribute">location</span> <span class="code-snippet__regexp">~* ^.+\.(ico|gif|jpg|jpeg|png)$</span> { </span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment"># 缓存1年</span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__attribute">add_header</span> Cache-Control: <span class="code-snippet__literal">no</span>-cache, max-age=<span class="code-snippet__number">31536000</span>;</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><strong><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;">减少单页面请求域名的数量</span></strong><span style="font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;text-align: justify;"></span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;margin-bottom: 0px;"><br></p> <p data-tool="mdnice�
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;text-align: left;line-height: 1.6;letter-spacing: 0.034em;color: rgb(63, 63, 63);font-size: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaqYllC4KURKwOxaVBlAgzY3ZU44jwGfeD6KQw330tfCxbBIsdZ9iaeWib7NPjofv28KyD1raofXZ6vg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">1、背景</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">作为在后端圈开车的多年老司机,是不是经常听到过,“mysql 单表最好不要超过 2000w”,“单表超过 2000w 就要考虑数据迁移了”,“你这个表数据都马上要到 2000w 了,难怪查询速度慢”。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这些名言民语就和 “群里只讨论技术,不开车,开车速度不要超过 120 码,否则自动踢群”,只听过,没试过,哈哈。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">下面我们就把车速踩到底,干到 180 码试试…….</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaqYllC4KURKwOxaVBlAgzY3ZU44jwGfeD6KQw330tfCxbBIsdZ9iaeWib7NPjofv28KyD1raofXZ6vg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">2、实验</span></h2> <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> <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("https://mmbiz.qpic.cn/mmbiz_svg/Jiavz9UrH80nwKIusv8JkOCSDcJY6ial8gibhVEHvtO8qgmjFzsiapPMKh3ANETgAc8Qx6o6m8ewbCA4V3KkOekicXz8R6bHZueWZ/640?wx_fmt=svg") 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;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">CREATE</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">TABLE</span> person(<br><span style="color: #f92672;font-weight: bold;line-height: 26px;">id</span> <span style="color: #a6e22e;line-height: 26px;">int</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">NOT</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">NULL</span> AUTO_INCREMENT PRIMARY <span style="color: #f92672;font-weight: bold;line-height: 26px;">KEY</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">comment</span> <span style="color: #a6e22e;line-height: 26px;">'主键'</span>,<br>person_id <span style="color: #a6e22e;line-height: 26px;">tinyint</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">not</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">null</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">comment</span> <span style="color: #a6e22e;line-height: 26px;">'用户id'</span>,<br>person_name <span style="color: #a6e22e;line-height: 26px;">VARCHAR</span>(<span style="line-height: 26px;">200</span>) <span style="color: #f92672;font-weight: bold;line-height: 26px;">comment</span> <span style="color: #a6e22e;line-height: 26px;">'用户名称'</span>,<br>gmt_create datetime <span style="color: #f92672;font-weight: bold;line-height: 26px;">comment</span> <span style="color: #a6e22e;line-height: 26px;">'创建时间'</span>,<br>gmt_modified datetime <span style="color: #f92672;font-weight: bold;line-height: 26px;">comment</span> <span style="color: #a6e22e;line-height: 26px;">'修改时间'</span><br>) <span style="color: #f92672;font-weight: bold;line-height: 26px;">comment</span> <span style="color: #a6e22e;line-height: 26px;">'人员信息表'</span>;<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">插入一条数据。另外,公众 号Java精选,回复java面试,获取面试资料,支持在线刷题。</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("https://mmbiz.qpic.cn/mmbiz_svg/Jiavz9UrH80nwKIusv8JkOCSDcJY6ial8gibhVEHvtO8qgmjFzsiapPMKh3ANETgAc8Qx6o6m8ewbCA4V3KkOekicXz8R6bHZueWZ/640?wx_fmt=svg") 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;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">insert</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">into</span> person <span style="color: #f92672;font-weight: bold;line-height: 26px;">values</span>(<span style="line-height: 26px;">1</span>,<span style="line-height: 26px;">1</span>,<span style="color: #a6e22e;line-height: 26px;">'user_1'</span>, <span style="color: #f92672;font-weight: bold;line-height: 26px;">NOW</span>(), <span style="color: #f92672;font-weight: bold;line-height: 26px;">now</span>())<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">利用 mysql 伪列 rownum 设置伪列起始点为 1</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("https://mmbiz.qpic.cn/mmbiz_svg/Jiavz9UrH80nwKIusv8JkOCSDcJY6ial8gibhVEHvtO8qgmjFzsiapPMKh3ANETgAc8Qx6o6m8ewbCA4V3KkOekicXz8R6bHZueWZ/640?wx_fmt=svg") 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;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">select</span> (@i:=@i+<span style="line-height: 26px;">1</span>) <span style="color: #f92672;font-weight: bold;line-height: 26px;">as</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">rownum</span>, person_name <span style="color: #f92672;font-weight: bold;line-height: 26px;">from</span> person, (<span style="color: #f92672;font-weight: bold;line-height: 26px;">select</span> @i:=<span style="line-height: 26px;">100</span>) <span style="color: #f92672;font-weight: bold;line-height: 26px;">as</span> init;<br><span style="color: #f92672;font-weight: bold;line-height: 26px;">set</span> @i=<span style="line-height: 26px;">1</span>;<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">运行下面的 sql,连续执行 20 次,就是 2 的 20 次方约等于 100w 的数据;执行 23 次就是 2 的 23 次方约等于 800w , 如此下去即可实现千万测试数据的插入,如果不想翻倍翻倍的增加数据,而是想少量,少量的增加,有个技巧,就是在 SQL 的后面增加 where 条件,如 id > 某一个值去控制增加的数据量即可。</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("https://mmbiz.qpic.cn/mmbiz_svg/Jiavz9UrH80nwKIusv8JkOCSDcJY6ial8gibhVEHvtO8qgmjFzsiapPMKh3ANETgAc8Qx6o6m8ewbCA4V3KkOekicXz8R6bHZueWZ/640?wx_fmt=svg") 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;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">insert</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">into</span> person(<span style="color: #f92672;font-weight: bold;line-height: 26px;">id</span>, person_id, person_name, gmt_create, gmt_modified)<br><span style="color: #f92672;font-weight: bold;line-height: 26px;">select</span> @i:=@i+<span style="line-height: 26px;">1</span>,<br><span style="color: #f92672;font-weight: bold;line-height: 26px;">left</span>(<span style="color: #f92672;font-weight: bold;line-height: 26px;">rand</span>()*<span style="line-height: 26px;">10</span>,<span style="line-height: 26px;">10</span>) <span style="color: #f92672;font-weight: bold;line-height: 26px;">as</span> person_id,<br><span style="color: #f92672;font-weight: bold;line-height: 26px;">concat</span>(<span style="color: #a6e22e;line-height: 26px;">'user_'</span>,@i%<span style="line-height: 26px;">2048</span>),<br><span style="color: #f92672;font-weight: bold;line-height: 26px;">date_add</span>(gmt_create,<span style="color: #a6e22e;line-height: 26px;">interval</span> + @i*<span style="color: #f92672;font-weight: bold;line-height: 26px;">cast</span>(<span style="color: #f92672;font-weight: bold;line-height: 26px;">rand</span>()*<span style="line-height: 26px;">100</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">as</span> signed) <span style="color: #f92672;font-weight: bold;line-height: 26px;">SECOND</span>),<br><span style="color: #f92672;font-weight: bold;line-height: 26px;">date_add</span>(<span style="color: #f92672;font-weight: bold;line-height: 26px;">date_add</span>(gmt_modified,<span style="color: #a6e22e;line-height: 26px;">interval</span> +@i*<span style="color: #f92672;font-weight: bold;line-height: 26px;">cast</span>(<span style="color: #f92672;font-weight: bold;line-height: 26px;">rand</span>()*<span style="line-height: 26px;">100</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">as</span> signed) <span style="color: #f92672;font-weight: bold;line-height: 26px;">SECOND</span>), <span style="color: #a6e22e;line-height: 26px;">interval</span> + <span style="color: #f92672;font-weight: bold;line-height: 26px;">cast</span>(<span style="color: #f92672;font-weight: bold;line-height: 26px;">rand</span>()*<span style="line-height: 26px;">1000000</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">as</span> signed) <span style="color: #f92672;font-weight: bold;line-height: 26px;">SECOND</span>)<br><span style="color: #f92672;font-weight: bold;line-height: 26px;">from</span> person;<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">此处需要注意的是,也许你在执行到近 800w 或者 1000w 数据的时候,会报错:The total number of locks exceeds the lock table size,这是由于你的临时表内存设置的不够大,只需要扩大一下设置参数即可。</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("https://mmbiz.qpic.cn/mmbiz_svg/Jiavz9UrH80nwKIusv8JkOCSDcJY6ial8gibhVEHvtO8qgmjFzsiapPMKh3ANETgAc8Qx6o6m8ewbCA4V3KkOekicXz8R6bHZueWZ/640?wx_fmt=svg") 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;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">SET</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">GLOBAL</span> tmp_table_size =<span style="line-height: 26px;">512</span>*<span style="line-height: 26px;">1024</span>*<span style="line-height: 26px;">1024</span>; (512M)<br><span style="color: #f92672;font-weight: bold;line-height: 26px;">SET</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">global</span> innodb_buffer_pool_size= <span style="line-height: 26px;">1</span>*<span style="line-height: 26px;">1024</span>*<span style="line-height: 26px;">1024</span>*<span style="line-height: 26px;">1024</span> (<span style="line-height: 26px;">1</span>G);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">先来看一组测试数据,这组数据是在 mysql8.0 的版本,并且是在我本机上,由于本机还跑着 idea , 浏览器等各种工具,所以并不是机器配置就是用于数据库配置,所以测试数据只限于参考。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="1.0055555555555555" src="/upload/bbd05df38e700685694ccd700089e81b.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;height: auto !important;"><img class="rich_pages wxw-img" data-ratio="0.5920084121976866" src="/upload/7ad8d18a2fa2fc735115ab6afa158283.png" data-type="png" data-w="951" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;height: auto !important;"></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">看到这组数据似乎好像真的和标题对应,当数据达到 2000w 以后,查询时长急剧上升;难道这就是铁律吗?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">那下面我们就来看看这个建议值 2kw 是怎么来的?</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaqYllC4KURKwOxaVBlAgzY3ZU44jwGfeD6KQw330tfCxbBIsdZ9iaeWib7NPjofv28KyD1raofXZ6vg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">3、单表数量限制</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">首先我们先想想数据库单表行数最大多大?</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("https://mmbiz.qpic.cn/mmbiz_svg/Jiavz9UrH80nwKIusv8JkOCSDcJY6ial8gibhVEHvtO8qgmjFzsiapPMKh3ANETgAc8Qx6o6m8ewbCA4V3KkOekicXz8R6bHZueWZ/640?wx_fmt=svg") 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;"><span style="color: #f92672;font-weight: bold;line-height: 26px;">CREATE</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">TABLE</span> person(<br><span style="color: #f92672;font-weight: bold;line-height: 26px;">id</span> <span style="color: #a6e22e;line-height: 26px;">int</span>(<span style="line-height: 26px;">10</span>) <span style="color: #f92672;font-weight: bold;line-height: 26px;">NOT</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">NULL</span> AUTO_INCREMENT PRIMARY <span style="color: #f92672;font-weight: bold;line-height: 26px;">KEY</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">comment</span> <span style="color: #a6e22e;line-height: 26px;">'主键'</span>,<br>person_id <span style="color: #a6e22e;line-height: 26px;">tinyint</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">not</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">null</span> <span style="color: #f92672;font-weight: bold;line-height: 26px;">comment</span> <span style="color: #a6e22e;line-height: 26px;">'用户id'</span>,<br>person_name <span style="color: #a6e22e;line-height: 26px;">VARCHAR</span>(<span style="line-height: 26px;">200</span>) <span style="color: #f92672;font-weight: bold;line-height: 26px;">comment</span> <span style="color: #a6e22e;line-height: 26px;">'用户名称'</span>,<br>gmt_create datetime <span style="color: #f92672;font-weight: bold;line-height: 26px;">comment</span> <span style="color: #a6e22e;line-height: 26px;">'创建时间'</span>,<br>gmt_modified datetime <span style="color: #f92672;font-weight: bold;line-height: 26px;">comment</span> <span style="color: #a6e22e;line-height: 26px;">'修改时间'</span><br>) <span style="color: #f92672;font-weight: bold;line-height: 26px;">comment</span> <span style="color: #a6e22e;line-height: 26px;">'人员信息表'</span>;<br></code></pre> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">看看上面的建表 sql,id 是主键,本身就是唯一的,也就是说主键的大小可以限制表的上限,如果主键声明 int 大小,也就是 32 位,那么支持 2^32-1 ~~21 亿;如果是 bigint,那就是 2^62-1 ?(36893488147419103232),难以想象这个的多大了,一般还没有到这个限制之前,可能数据库已经爆满了!!</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">有人统计过,如果建表的时候,自增字段选择无符号的 bigint , 那么自增长最大值是 18446744073709551615,按照一秒新增一条记录的速度,大约什么时候能用完?</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.47919876733436056" src="/upload/78d944f8ee32200da22c84d968501dd6.png" data-type="png" data-w="649" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;height: auto !important;"> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaqYllC4KURKwOxaVBlAgzY3ZU44jwGfeD6KQw330tfCxbBIsdZ9iaeWib7NPjofv28KyD1raofXZ6vg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">4、表空间</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">下面我们再来看看索引的结构,对了,我们下面讲内容都是基于 Innodb 引擎的,大家都知道 Innodb 的索引内部用的是 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 class="rich_pages wxw-img" data-ratio="0.36614645858343337" src="/upload/46f45b3254c1f1ad5a8bdb313e0e8c55.png" data-type="png" data-w="833" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;height: auto !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">这张表数据,在硬盘上存储也是类似如此的,它实际是放在一个叫 person.ibd (innodb data)的文件中,也叫做表空间;虽然数据表中,他们看起来是一条连着一条,但是实际上在文件中它被分成很多小份的数据页,而且每一份都是 16K。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">大概就像下面这样,当然这只是我们抽象出来的,在表空间中还有段、区、组等很多概念,但是我们需要跳出来看。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.471976401179941" src="/upload/81c7db0281a866cea160ab8c125bbba5.png" data-type="png" data-w="1017" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;height: auto !important;"> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaqYllC4KURKwOxaVBlAgzY3ZU44jwGfeD6KQw330tfCxbBIsdZ9iaeWib7NPjofv28KyD1raofXZ6vg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">5、页的数据结构</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">因为每个页只有 16K 的大小,但是如果数据很多,那一页肯定就放不下这些数据,那数据肯定就会被分到其他的页中,所以为了把这些页关联起来,肯定就会有记录前后页地址,方便找到对应页;同时每页都是唯一的,那就会需要有一个唯一标志来标记页,就是页号;</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">页中会记录数据所以会存在读写操作,读写操作会存在中断或者其他异常导致数据不全等,那就会需要有校验机制,所以里面还有会校验码,而读操作最重要的就是效率问题,如果按照记录一个个进行遍历,那肯定是很费劲的,所以这里面还会为数据生成对应的页目录(Page Directory); 所以实际页的内部结构像是下面这样的。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5765575501583949" src="/upload/798921b1accbb4a925569a8bc646dc9c.png" data-type="png" data-w="947" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;height: auto !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">从图中可以看出,一个 InnoDB 数据页的存储空间大致被划分成了 7 个部分,有的部分占用的字节数是确定的,有的部分占用的字节数是不确定的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在页的 7 个组成部分中,我们自己存储的记录会按照我们指定的行格式存储到 User Records 部分。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">但是在一开始生成页的时候,其实并没有 User Records 这个部分,每当我们插入一条记录,都会从 Free Space 部分,也就是尚未使用的存储空间中申请一个记录大小的空间划分到 User Records 部分,当 Free Space 部分的空间全部被 User Records 部分替代掉之后,也就意味着这个页使用完了,如果还有新的记录插入的话,就需要去申请新的页了。这个过程的图示如下。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.375366568914956" src="/upload/45b5017a592970157c072176d2351456.png" data-type="png" data-w="1023" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;height: auto !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">刚刚上面说到了数据的新增的过程。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">那下面就来说说,数据的查找过程,假如我们需要查找一条记录,我们可以把表空间中的每一页都加载到内存中,然后对记录挨个判断是不是我们想要的,在数据量小的时候,没啥问题,内存也可以撑;但是现实就是这么残酷,不会给你这个局面;为了解决这问题,mysql 中就有了索引的概念;大家都知道索引能够加快数据的查询,那到底是怎么个回事呢?下面我就来看看。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">6、索引的数据结构 在 mysql 中索引的数据结构和刚刚描述的页几乎是一模一样的,而且大小也是 16K, 但是在索引页中记录的是页 (数据页,索引页) 的最小主键 id 和页号,以及在索引页中增加了层级的信息,从 0 开始往上算,所以页与页之间就有了上下层级的概念。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.48518518518518516" src="/upload/4c0b65deb874be43b239274da4e99414.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;height: auto !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">看到这个图之后,是不是有点似曾相似的感觉,是不是像一棵二叉树啊,对,没错!它就是一棵树,只不过我们在这里只是简单画了三个节点,2 层结构的而已,如果数据多了,可能就会扩展到 3 层的树,这个就是我们常说的 B+ 树,最下面那一层的 page level =0, 也就是叶子节点,其余都是非叶子节点。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="1.0617283950617284" src="/upload/d8f231bac8d7ade86da65d5d9cf4c59f.png" data-type="png" data-w="405" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;height: auto !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">看上图中,我们是单拿一个节点来看,首先它是一个非叶子节点(索引页),在它的内容区中有 id 和 页号地址两部分,这个 id 是对应页中记录的最小记录 id 值,页号地址是指向对应页的指针;而数据页与此几乎大同小异,区别在于数据页记录的是真实的行数据而不是页地址,而且 id 的也是顺序的。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaqYllC4KURKwOxaVBlAgzY3ZU44jwGfeD6KQw330tfCxbBIsdZ9iaeWib7NPjofv28KyD1raofXZ6vg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">7、单表建议值</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">下面我们就以 3 层,2 分叉(实际中是 M 分叉)的图例来说明一下查找一个行数据的过程。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">比如说我们需要查找一个 id=6 的行数据,因为在非叶子节点中存放的是页号和该页最小的 id,所以我们从顶层开始对比,首先看页号 10 中的目录,有 [id=1, 页号 = 20],[id=5, 页号 = 30], 说明左侧节点最小 id 为 1,右侧节点最小 id 是 5;6>5, 那按照二分法查找的规则,肯定就往右侧节点继续查找,找到页号 30 的节点后,发现这个节点还有子节点(非叶子节点),那就继续比对,同理,6>5&&6<7, 所以找到了页号 60,找到页号 60 之后,发现此节点为叶子节点(数据节点),于是将此页数据加载至内存进行一一对比,结果找到了 id=6 的数据行。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">从上述的过程中发现,我们为了查找 id=6 的数据,总共查询了三个页,如果三个页都在磁盘中(未提前加载至内存),那么最多需要经历三次的磁盘 IO。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">需要注意的是,图中的页号只是个示例,实际情况下并不是连续的,在磁盘中存储也不一定是顺序的。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4824074074074074" src="/upload/e6f8914982419b9ac2ef5ce23468847c.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;height: auto !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">至此,我们大概已经了解了表的数据是怎么个结构了,也大概知道查询数据是个怎么的过程了,这样我们也就能大概估算这样的结构能存放多少数据了。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">从上面的图解我们知道 B+ 数的叶子节点才是存在数据的,而非叶子节点是用来存放索引数据的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">所以,同样一个 16K 的页,非叶子节点里的每条数据都指向新的页,而新的页有两种可能</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <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-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">假设</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 非叶子节点内指向其他页的数量为 x </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 叶子节点内能容纳的数据行数为 y </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">B+ 数的层数为 z 如下图中所示</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">Total =x^(z-1) *y 也就是说总数会等于 x 的 z-1 次方 与 Y 的乘积。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4888888888888889" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;height: auto !important;" src="/upload/eed210ac3639007d7eeb1d96f507edd1.png"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">X =?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在文章的开头已经介绍了页的结构,索引也也不例外,都会有 File Header (38 byte)、Page Header (56 Byte)、Infimum + Supermum(26 byte)、File Trailer(8byte), 再加上页目录,大概 1k 左右,我们就当做它就是 1K, 那整个页的大小是 16K, 剩下 15k 用于存数据,在索引页中主要记录的是主键与页号,主键我们假设是 Bigint (8 byte), 而页号也是固定的(4Byte), 那么索引页中的一条数据也就是 12byte; 所以 x=15*1024/12≈1280 行。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">Y=?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">叶子节点和非叶子节点的结构是一样的,同理,能放数据的空间也是 15k;但是叶子节点中存放的是真正的行数据,这个影响的因素就会多很多,比如,字段的类型,字段的数量;每行数据占用空间越大,页中所放的行数量就会越少;这边我们暂时按一条行数据 1k 来算,那一页就能存下 15 条,Y≈15。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">算到这边了,是不是心里已经有谱了啊</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">根据上述的公式,Total =x^(z-1) y,已知 x=1280,y=15</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">假设 B+ 树是两层,那就是 Z =2, Total = (1280 ^1 )15 = 19200</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">假设 B+ 树是三层,那就是 Z =3, Total = (1280 ^2) *15 = 24576000 (约 2.45kw)</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">哎呀,妈呀!这不是正好就是文章开头说的最大行数建议值 2000w 嘛!对的,一般 B+ 数的层级最多也就是 3 层,你试想一下,如果是 4 层,除了查询的时候磁盘 IO 次数会增加,而且这个 Total 值会是多少,大概应该是 3 百多亿吧,也不太合理,所以,3 层应该是比较合理的一个值。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">到这里难道就完了?</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">不</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们刚刚在说 Y 的值时候假设的是 1K ,那比如我实际当行的数据占用空间不是 1K , 而是 5K, 那么单个数据页最多只能放下 3 条数据</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">同样,还是按照 Z=3 的值来计算,那 Total = (1280 ^2) *3 = 4915200 (近 500w)</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">所以,在保持相同的层级(相似查询性能)的情况下,在行数据大小不同的情况下,其实这个最大建议值也是不同的,而且影响查询性能的还有很多其他因素,比如,数据库版本,服务器配置,sql 的编写等等,MySQL 为了提高性能,会将表的索引装载到内存中。在 InnoDB buffer size 足够的情况下,其能完成全加载进内存,查询不会有问题。但是,当单表数据库到达某个量级的上限时,导致内存无法存储其索引,使得之后的 SQL 查询会产生磁盘 IO,从而导致性能下降,所以增加硬件配置(比如把内存当磁盘使),可能会带来立竿见影的性能提升哈。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaqYllC4KURKwOxaVBlAgzY3ZU44jwGfeD6KQw330tfCxbBIsdZ9iaeWib7NPjofv28KyD1raofXZ6vg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">8、总结</span></h2> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">Mysql 的表数据是以页的形式存放的,页在磁盘中不一定是连续的。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">页的空间是 16K, 并不是所有的空间都是用来存放数据的,会有一些固定的信息,如,页头,页尾,页码,校验码等等。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在 B+ 树中,叶子节点和非叶子节点的数据结构是一样的,区别在于,叶子节点存放的是实际的行数据,而非叶子节点存放的是主键和页号。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">索引结构不会影响单表最大行数,2kw 也只是推荐值,超过了这个值可能会导致 B + 树层级更高,影响查询性能。</p> </section></li> </ol> </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: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;"> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">简介</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">为什么要持有外部类</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">实例:持有外部类</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">实例:不持有外部类</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">实例:内存泄露</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="font-weight: bold;color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);font-family: STHeitiSC-Light;" data-linktype="2">不会内存泄露的方案</a> </section></li> </ul> <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(248, 57, 41, 0), rgb(14, 136, 235), rgba(248, 57, 41, 0));"> <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);"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" style="color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);" data-linktype="2">简介</a></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「说明」</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">本文介绍 Java 内部类持有外部类导致内存泄露的原因以及其解决方案。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「为什么内部类持有外部类会导致内存泄露?」</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 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: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「解决方案」</strong></p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <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;"> 将非静态内部类改为静态内部类。内部类改为静态的之后,它所引用的对象或属性也必须是静态的,所以静态内部类无法获得外部对象的引用,只能从 JVM 的 Method Area(方法区)获取到static类型的引用。 </section></li> </ol> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 10px;padding-right: 10px;padding-bottom: 10px;line-height: 1.8;border-radius: 0px 0px 10px 10px;color: rgb(14, 136, 235);background: rgb(255, 255, 255);box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.2em;word-spacing: 0.1em;line-height: 26px;font-size: 15px;">基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 视频教程:https://doc.iocoder.cn/video/ </section></li> </ul> </blockquote> <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);"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" style="color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);" data-linktype="2">为什么要持有外部类</a></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">Java 语言中,非静态内部类的主要作用有两个:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <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> </ol> <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("https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG2o1ficQuKkg1qcSEWAUjWpsM9uFQPTG3PyYrFq97PurXU6ZibDIt8536mOwf6W0qpnQc5nl51CvsSicxaciaHz3Dxn/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #569CD6;line-height: 26px;">package</span> org.example.a;<br><br><span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Outer</span></span>{<br> <span style="color: #569CD6;line-height: 26px;">private</span> String outerName = <span style="color: #D69D85;line-height: 26px;">"Tony"</span>;<br><br> <span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Inner</span></span>{<br> <span style="color: #569CD6;line-height: 26px;">private</span> String name;<br><br> <span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span> <span style="line-height: 26px;">Inner</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #569CD6;line-height: 26px;">this</span>.name = outerName;<br> }<br> }<br><br> <span style="line-height: 26px;">Inner <span style="line-height: 26px;">createInner</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #569CD6;line-height: 26px;">return</span> <span style="color: #569CD6;line-height: 26px;">new</span> Inner();<br> }<br>}<br><br><span style="color: #569CD6;line-height: 26px;">public</span> <span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Demo</span> </span>{<br> <span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span> <span style="color: #569CD6;line-height: 26px;">static</span> <span style="color: #569CD6;line-height: 26px;">void</span> <span style="line-height: 26px;">main</span><span style="line-height: 26px;">(String[] args)</span> </span>{<br> Outer.Inner inner = <span style="color: #569CD6;line-height: 26px;">new</span> Outer().createInner();<br> System.out.println(inner);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 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;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG2o1ficQuKkg1qcSEWAUjWpsM9uFQPTG3PyYrFq97PurXU6ZibDIt8536mOwf6W0qpnQc5nl51CvsSicxaciaHz3Dxn/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #569CD6;line-height: 26px;">package</span> org.example.a;<br><br><span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Outer</span></span>{<br> <span style="color: #569CD6;line-height: 26px;">private</span> String outerName = <span style="color: #D69D85;line-height: 26px;">"Tony"</span>;<br><br> <span style="color: #569CD6;line-height: 26px;">static</span> <span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Inner</span></span>{<br> <span style="color: #569CD6;line-height: 26px;">private</span> String name;<br><br> <span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span> <span style="line-height: 26px;">Inner</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #569CD6;line-height: 26px;">this</span>.name = outerName;<br> }<br> }<br><br> <span style="line-height: 26px;">Inner <span style="line-height: 26px;">createInner</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #569CD6;line-height: 26px;">return</span> <span style="color: #569CD6;line-height: 26px;">new</span> Inner();<br> }<br>}<br><br><span style="color: #569CD6;line-height: 26px;">public</span> <span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Demo</span> </span>{<br> <span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span> <span style="color: #569CD6;line-height: 26px;">static</span> <span style="color: #569CD6;line-height: 26px;">void</span> <span style="line-height: 26px;">main</span><span style="line-height: 26px;">(String[] args)</span> </span>{<br> Outer.Inner inner = <span style="color: #569CD6;line-height: 26px;">new</span> Outer().createInner();<br> System.out.println(inner);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 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;"> <a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" linktype="text" imgurl="" imgdata="null" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 20px 41.8542px;right: auto;bottom: auto;"><img class="rich_pages wxw-img" data-ratio="0.5192909280500522" src="/upload/f8d6056ecfb721f42aacac00296919eb.png" data-type="png" data-w="959" style="border-radius: 0px 0px 5px 5px;display: block;margin: 0px;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"></span></a> </figure> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 10px;padding-right: 10px;padding-bottom: 10px;line-height: 1.8;border-radius: 0px 0px 10px 10px;color: rgb(14, 136, 235);background: rgb(255, 255, 255);box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.2em;word-spacing: 0.1em;line-height: 26px;font-size: 15px;">基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 项目地址:https://gitee.com/zhijiantianya/yudao-cloud </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 视频教程:https://doc.iocoder.cn/video/ </section></li> </ul> </blockquote> <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);"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" style="color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);" data-linktype="2">实例:持有外部类</a></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「代码」</strong></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("https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG2o1ficQuKkg1qcSEWAUjWpsM9uFQPTG3PyYrFq97PurXU6ZibDIt8536mOwf6W0qpnQc5nl51CvsSicxaciaHz3Dxn/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #569CD6;line-height: 26px;">package</span> org.example.a;<br><br><span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Outer</span></span>{<br> <span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Inner</span> </span>{<br><br> }<br><br> <span style="line-height: 26px;">Inner <span style="line-height: 26px;">createInner</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #569CD6;line-height: 26px;">return</span> <span style="color: #569CD6;line-height: 26px;">new</span> Inner();<br> }<br>}<br><br><span style="color: #569CD6;line-height: 26px;">public</span> <span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Demo</span> </span>{<br> <span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span> <span style="color: #569CD6;line-height: 26px;">static</span> <span style="color: #569CD6;line-height: 26px;">void</span> <span style="line-height: 26px;">main</span><span style="line-height: 26px;">(String[] args)</span> </span>{<br> Outer.Inner inner = <span style="color: #569CD6;line-height: 26px;">new</span> Outer().createInner();<br> System.out.println(inner);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「断点调试」</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">可以看到:内部类持有外部类的对象的引用,是以“this$0”这个字段来保存的。</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==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" linktype="text" imgurl="" imgdata="null" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 20px 41.8542px;right: auto;bottom: auto;"><img class="rich_pages wxw-img" data-ratio="0.9944674965421854" src="/upload/9eed963fde14c700c44b196e2641af51.png" data-type="png" data-w="723" style="border-radius: 0px 0px 5px 5px;display: block;margin: 0px;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"></span></a> </figure> <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);"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" style="color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);" data-linktype="2">实例:不持有外部类</a></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「代码」</strong></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("https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG2o1ficQuKkg1qcSEWAUjWpsM9uFQPTG3PyYrFq97PurXU6ZibDIt8536mOwf6W0qpnQc5nl51CvsSicxaciaHz3Dxn/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #569CD6;line-height: 26px;">package</span> org.example.a;<br><br><span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Outer</span></span>{<br> <span style="color: #569CD6;line-height: 26px;">static</span> <span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Inner</span> </span>{<br><br> }<br><br> <span style="line-height: 26px;">Inner <span style="line-height: 26px;">createInner</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #569CD6;line-height: 26px;">return</span> <span style="color: #569CD6;line-height: 26px;">new</span> Inner();<br> }<br>}<br><br><span style="color: #569CD6;line-height: 26px;">public</span> <span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Demo</span> </span>{<br> <span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span> <span style="color: #569CD6;line-height: 26px;">static</span> <span style="color: #569CD6;line-height: 26px;">void</span> <span style="line-height: 26px;">main</span><span style="line-height: 26px;">(String[] args)</span> </span>{<br> Outer.Inner inner = <span style="color: #569CD6;line-height: 26px;">new</span> Outer().createInner();<br> System.out.println(inner);<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「断点调试」</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 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;"> <a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" linktype="text" imgurl="" imgdata="null" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 20px 41.8542px;right: auto;bottom: auto;"><img class="rich_pages wxw-img" data-ratio="0.9889807162534435" src="/upload/7dc1204d15c7dc337e10e4021ec716a1.png" data-type="png" data-w="726" style="border-radius: 0px 0px 5px 5px;display: block;margin: 0px;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"></span></a> </figure> <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);"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" style="color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);" data-linktype="2">实例:内存泄露</a></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「简介」</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 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: 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: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「代码」</strong></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("https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG2o1ficQuKkg1qcSEWAUjWpsM9uFQPTG3PyYrFq97PurXU6ZibDIt8536mOwf6W0qpnQc5nl51CvsSicxaciaHz3Dxn/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #569CD6;line-height: 26px;">package</span> org.example.a;<br><br><span style="color: #569CD6;line-height: 26px;">import</span> java.util.ArrayList;<br><span style="color: #569CD6;line-height: 26px;">import</span> java.util.List;<br><br><span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Outer</span></span>{<br> <span style="color: #569CD6;line-height: 26px;">private</span> <span style="color: #569CD6;line-height: 26px;">int</span>[] data;<br><br> <span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span> <span style="line-height: 26px;">Outer</span><span style="line-height: 26px;">(<span style="color: #569CD6;line-height: 26px;">int</span> size)</span> </span>{<br> <span style="color: #569CD6;line-height: 26px;">this</span>.data = <span style="color: #569CD6;line-height: 26px;">new</span> <span style="color: #569CD6;line-height: 26px;">int</span>[size];<br> }<br><br> <span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Innner</span></span>{<br><br> }<br><br> <span style="line-height: 26px;">Innner <span style="line-height: 26px;">createInner</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #569CD6;line-height: 26px;">return</span> <span style="color: #569CD6;line-height: 26px;">new</span> Innner();<br> }<br>}<br><br><span style="color: #569CD6;line-height: 26px;">public</span> <span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Demo</span> </span>{<br> <span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span> <span style="color: #569CD6;line-height: 26px;">static</span> <span style="color: #569CD6;line-height: 26px;">void</span> <span style="line-height: 26px;">main</span><span style="line-height: 26px;">(String[] args)</span> </span>{<br> List<Object> list = <span style="color: #569CD6;line-height: 26px;">new</span> ArrayList<>();<br> <span style="color: #569CD6;line-height: 26px;">int</span> counter = <span style="color: #B8D7A3;line-height: 26px;">0</span>;<br> <span style="color: #569CD6;line-height: 26px;">while</span> (<span style="color: #569CD6;line-height: 26px;">true</span>) {<br> list.add(<span style="color: #569CD6;line-height: 26px;">new</span> Outer(<span style="color: #B8D7A3;line-height: 26px;">100000</span>).createInner());<br> System.out.println(counter++);<br> }<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「测试」</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 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;"> <a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" linktype="text" imgurl="" imgdata="null" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 20px 41.8542px;right: auto;bottom: auto;"><img class="rich_pages wxw-img" data-ratio="0.4744094488188976" src="/upload/8bc9e3ff9cc1ea53930e6aeeef85da74.png" data-type="png" data-w="1016" style="border-radius: 0px 0px 5px 5px;display: block;margin: 0px;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"></span></a> </figure> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 2px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);line-height: 26px;font-size: 14px;word-spacing: 2px;">换了一台 mac 电脑,4000 多就内存溢出了。</p> <p style="text-align: center;margin-bottom: 0em;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.30185185185185187" data-s="300,640" src="/upload/590eb503394c0c7d7f0cf2484aa4e3eb.png" data-type="png" data-w="1080" style=""></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 2px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);display: flex;flex-direction: column;justify-content: center;align-items: center;"></figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><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);"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" style="color: rgb(14, 136, 235);border-bottom: 0px solid rgb(255, 53, 2);" data-linktype="2">不会内存泄露的方案</a></span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「简介」</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">内部类改为静态的之后,它所引用的对象或属性也必须是静态的,所以静态内部类无法获得外部对象的引用,只能从 JVM 的 Method Area(方法区)获取到 static 类型的引用。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「代码」</strong></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("https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG2o1ficQuKkg1qcSEWAUjWpsM9uFQPTG3PyYrFq97PurXU6ZibDIt8536mOwf6W0qpnQc5nl51CvsSicxaciaHz3Dxn/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;"><span style="color: #569CD6;line-height: 26px;">package</span> org.example.a;<br><br><span style="color: #569CD6;line-height: 26px;">import</span> java.util.ArrayList;<br><span style="color: #569CD6;line-height: 26px;">import</span> java.util.List;<br><br><span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Outer</span></span>{<br> <span style="color: #569CD6;line-height: 26px;">private</span> <span style="color: #569CD6;line-height: 26px;">int</span>[] data;<br><br> <span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span> <span style="line-height: 26px;">Outer</span><span style="line-height: 26px;">(<span style="color: #569CD6;line-height: 26px;">int</span> size)</span> </span>{<br> <span style="color: #569CD6;line-height: 26px;">this</span>.data = <span style="color: #569CD6;line-height: 26px;">new</span> <span style="color: #569CD6;line-height: 26px;">int</span>[size];<br> }<br><br> <span style="color: #569CD6;line-height: 26px;">static</span> <span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Inner</span> </span>{<br><br> }<br><br> <span style="line-height: 26px;">Inner <span style="line-height: 26px;">createInner</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #569CD6;line-height: 26px;">return</span> <span style="color: #569CD6;line-height: 26px;">new</span> Inner();<br> }<br>}<br><br><span style="color: #569CD6;line-height: 26px;">public</span> <span style="color: #B8D7A3;line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">class</span> <span style="color: #DCDCDC;line-height: 26px;">Demo</span> </span>{<br> <span style="line-height: 26px;"><span style="color: #569CD6;line-height: 26px;">public</span> <span style="color: #569CD6;line-height: 26px;">static</span> <span style="color: #569CD6;line-height: 26px;">void</span> <span style="line-height: 26px;">main</span><span style="line-height: 26px;">(String[] args)</span> </span>{<br> List<Object> list = <span style="color: #569CD6;line-height: 26px;">new</span> ArrayList<>();<br> <span style="color: #569CD6;line-height: 26px;">int</span> counter = <span style="color: #B8D7A3;line-height: 26px;">0</span>;<br> <span style="color: #569CD6;line-height: 26px;">while</span> (<span style="color: #569CD6;line-height: 26px;">true</span>) {<br> list.add(<span style="color: #569CD6;line-height: 26px;">new</span> Outer(<span style="color: #B8D7A3;line-height: 26px;">100000</span>).createInner());<br> System.out.println(counter++);<br> }<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">「测试」</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 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;"> <a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" textvalue="你已选中了添加链接的内容" linktype="text" imgurl="" imgdata="null" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="top: auto;left: auto;margin: 20px 36.0833px 20px 36.0729px;right: auto;bottom: auto;"><img class="rich_pages wxw-img" data-ratio="0.8440748440748441" src="/upload/9d9eef8b8b72c75ad4e5455c497ed2b9.png" data-type="png" data-w="481" style="border-radius: 0px 0px 5px 5px;display: block;margin: 0px;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"></span></a> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">以上,希望能对大家在使用内部类时会有所帮助。</p> </section>
作者:微信小助手
<section class="channels_iframe_wrp" data-mpa-powered-by="yiban.io"> <mpvideosnap class="js_uneditable custom_select_card channels_iframe videosnap_video_iframe" data-pluginname="videosnap" data-id="export/UzFfAgtgekIEAQAAAAAADYIamL10ngAAAAstQy6ubaLX4KHWvLEZgBPE36JYewMtJuuDzNPgMIu--b1XCMH7p4OcfMqGtUPB" data-url="https://findermp.video.qq.com/251/20304/stodownload?encfilekey=rjD5jyTuFrIpZ2ibE8T7YmwgiahniaXswqzkzEHxDXTYd8JHib8K8XpwlpoianIKWtP7U6baDa91iaZFZtMzB1nT8oNTdytrfDJ3eIHNjyibZR5Ybh0g7M4ibdEK7w&adaptivelytrans=0&bizid=1023&dotrans=0&hy=SH&idx=1&m=&scene=0&token=AxricY7RBHdWBiaWrXBxmo8kxapMxKbHxCo68PDziakOXoCTiaV7wE2hV8kKxKiccoVc5peV3yreycCA" data-headimgurl="http://wx.qlogo.cn/finderhead/Q3auHgzwzM5nv7YHhmhvPsGGX04JCIgibK2x2Ru0TOY9HeZTGSIL1KQ/0" data-username="v2_060000231003b20faec8c5e08a1fc3d5c807ec30b07756771265bc6b6234fb9e05062ae69ab4@finder" data-nickname="儒猿IT" data-desc="你知道你连接的MySQL数据库到底能抗多大并发压力吗?如果MySQL数据库扛不住压力了,应该如何演进?#MySQL #数据库 #高并发 #分库分表 @微信时刻 " data-nonceid="6688676945815912842" data-type="video" data-width="1624" data-height="1080"></mpvideosnap> </section> <p style="margin-bottom: 16px;line-height: 1.6em;text-align: right;"><span style="color: rgb(136, 136, 136);font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 13px;font-weight: 700;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);">文章来源:</span><span style="color: rgb(136, 136, 136);font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 13px;font-weight: 700;letter-spacing: 0.544px;text-align: left;background-color: rgb(255, 255, 255);">https://juejin.cn/post/7103315065352552456</span></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><br></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section mpa-from-tpl="t"> <section style="width:100%;text-align:center;" data-width="100%" mpa-from-tpl="t"> <section style="display:inline-block;width:auto;" mpa-from-tpl="t"> <section style="width: 45px;margin-right: auto;margin-bottom: -12px;margin-left: auto;background-color: #fefefe;transform: translateZ(10px);-webkit-transform: translateZ(10px);-moz-transform: translateZ(10px);-ms-transform: translateZ(10px);-o-transform: translateZ(10px);" mpa-from-tpl="t"> <img class="rich_pages wxw-img" data-ratio="1" src="/upload/2f587a759a9336e658edf1970c0a8632.png" data-w="100" data-width="100%" style="display: block;width: 100%;"> </section> <section style="display:inline-block;width:auto;height:auto;" mpa-from-tpl="t"> <section style="border-width: 1px;border-style: solid;border-color: rgb(102, 102, 102);height: 40px;" mpa-from-tpl="t"> <br> </section> <section style="margin-top:-37px;margin-right:-3px;margin-bottom:-3px;margin-left:3px;" mpa-from-tpl="t"> <section style="display: inline-block;height: 40px;line-height: 40px;padding-right: 15px;padding-left: 15px;border-width: 1px;border-style: solid;border-color: rgb(255, 116, 128);width: 100%;" data-width="100%" mpa-from-tpl="t"> <p mpa-is-content="t">前言</p> </section> </section> </section> </section> </section> </section> </section> <p><br mpa-from-tpl="t"></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">日常开发中,我们经常会遇到<strong>数据库慢查询</strong>。那么导致数据慢查询都有哪些常见的原因?今天就跟大家聊聊导致数据库慢查询的12个常见原因,以及对应的解决方法。</span></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><img class="rich_pages wxw-img" data-ratio="0.465993265993266" src="/upload/8ed4d13b4048cbc8233e6f2e16faacae.jpg" data-type="other" data-w="1485"></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><br></p> <section data-mpa-template="t" mpa-from-tpl="t"> <blockquote style="margin: 5px auto;padding: 0px;max-width: 100%;border-width: 0px;border-style: none;border-color: rgb(68, 153, 231);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 25.6px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;color: rgb(68, 153, 231);" mpa-from-tpl="t"> <section style="margin: 0px auto;padding: 0px;max-width: 100%;border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 0px auto;padding: 0px;max-width: 100%;border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <blockquote style="margin: 5px auto;padding: 0px;max-width: 100%;border-width: 0px;border-style: none;border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 0px auto;padding: 0px;max-width: 100%;text-align: center;border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 0px auto -38px;padding: 0px 15px;max-width: 100%;display: inline-block;background-color: rgb(68, 153, 231);border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <p style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;clear: both;min-height: 1em;color: rgb(68, 153, 231);border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><span style="font-family: Arial, Helvetica, sans-serif;"><strong mpa-from-tpl="t"><span style="border-color: rgb(68, 153, 231);color: rgb(255, 255, 255);font-size: 16px;" mpa-is-content="t">1. SQL没加索引</span></strong></span></p> </section> <section style="margin: -40px 25px 50px;padding: 0px;max-width: 100%;border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 3px 0px 0px;padding: 10px 0px;max-width: 100%;box-sizing: border-box;display: inline-block;width: 495px;color: rgb(68, 153, 231);float: left;border-color: rgb(68, 153, 231);word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 1em 0px 0px;padding: 0px;max-width: 100%;box-sizing: border-box;border-width: 0px;border-style: none;border-color: rgb(68, 153, 231);clear: both;color: rgb(68, 153, 231);word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 0px 0px -3px;padding: 0px;max-width: 100%;float: right;border-color: rgb(68, 153, 231);width: 6px;border-radius: 50%;background-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;height: 6px !important;" mpa-from-tpl="t"> <br> </section> <section style="margin: 0px 0px -2px;padding: 0px;max-width: 100%;text-align: left;border-color: rgb(68, 153, 231);width: 6px;border-radius: 50%;background-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;height: 6px !important;" mpa-from-tpl="t"> <br> </section> <section style="margin: -20px 0px 0px;padding: 0px;max-width: 100%;box-sizing: border-box;text-decoration: inherit;color: rgb(68, 153, 231);border-color: rgb(68, 153, 231);word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;border-top: 1px solid rgb(68, 153, 231);width: 495px;float: left;border-right-color: rgb(68, 153, 231);border-bottom-color: rgb(68, 153, 231);border-left-color: rgb(68, 153, 231);color: rgb(68, 153, 231);word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <br> </section> </section> </section> </section> </section> </section> </blockquote> </section> </section> </blockquote> </section> <p><br mpa-from-tpl="t"></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">很多时候,我们的慢查询,都是因为<strong>没有加索引</strong>。如果没有加索引的话,会导致全表扫描的。因此,应考虑在</span><code><span style="font-size: 16px;">where</span></code><span style="font-size: 16px;">的条件列,<strong>建立索引</strong>,尽量避免全表扫描。</span></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;"><strong>反例:</strong></span></p> <pre> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t"> <pre style="margin:0;padding:0;border-radius:none;background:none;"><code style="border-radius: 4px;font-size: 0.85em;margin: 0px 0.15em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;padding: 5.95px;overflow-x: auto;white-space: nowrap;"><span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">select</span> * <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">from</span> user_info <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">where</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">name</span> =<span style="color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);display: inline;width: 141px;text-decoration: none solid rgb(152, 195, 121);font-weight: 400;font-style: normal;">'捡田螺的小男孩公众号'</span> ;</code></pre> </section><p><br></p></pre> <p style="margin-bottom: 16px;line-height: 1.6em;"><img class="rich_pages wxw-img" data-ratio="0.2215815485996705" src="/upload/dd389c801be83970c40f4cfb107419af.jpg" data-type="other" data-w="1214"></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;"><strong>正例:</strong></span></p> <pre> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t"> <pre style="margin:0;padding:0;border-radius:none;background:none;"><code style="border-radius: 4px;font-size: 0.85em;margin: 0px 0.15em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;padding: 5.95px;overflow-x: auto;white-space: nowrap;">//添加索引<br mpa-from-tpl="t"><span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">alter</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">table</span> user_info <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">add</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">index</span> idx_name (<span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">name</span>);</code></pre> </section><p><br></p></pre> <p style="margin-bottom: 16px;line-height: 1.6em;"><img class="rich_pages wxw-img" data-ratio="0.2019064124783362" src="/upload/57aadcf81e678a6be02a4576ef17ae84.jpg" data-type="other" data-w="1154"></p> <h2 data-id="heading-2" style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;"><br mpa-from-tpl="t"></span></h2> <section data-mpa-template="t" mpa-from-tpl="t"> <blockquote style="margin: 5px auto;padding: 0px;max-width: 100%;border-width: 0px;border-style: none;border-color: rgb(68, 153, 231);font-size: 16px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 25.6px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;color: rgb(68, 153, 231);" mpa-from-tpl="t"> <section style="margin: 0px auto;padding: 0px;max-width: 100%;border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 0px auto;padding: 0px;max-width: 100%;border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <blockquote style="margin: 5px auto;padding: 0px;max-width: 100%;border-width: 0px;border-style: none;border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 0px auto;padding: 0px;max-width: 100%;text-align: center;border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 0px auto -38px;padding: 0px 15px;max-width: 100%;display: inline-block;background-color: rgb(68, 153, 231);border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <p style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;clear: both;min-height: 1em;color: rgb(68, 153, 231);border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;"><span style="font-family: Arial, Helvetica, sans-serif;"><strong mpa-from-tpl="t"><span style="border-color: rgb(68, 153, 231);color: rgb(255, 255, 255);font-size: 16px;" mpa-is-content="t">2. SQL 索引不生效</span></strong></span></p> </section> <section style="margin: -40px 25px 50px;padding: 0px;max-width: 100%;border-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 3px 0px 0px;padding: 10px 0px;max-width: 100%;box-sizing: border-box;display: inline-block;width: 495px;color: rgb(68, 153, 231);float: left;border-color: rgb(68, 153, 231);word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 1em 0px 0px;padding: 0px;max-width: 100%;box-sizing: border-box;border-width: 0px;border-style: none;border-color: rgb(68, 153, 231);clear: both;color: rgb(68, 153, 231);word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 0px 0px -3px;padding: 0px;max-width: 100%;float: right;border-color: rgb(68, 153, 231);width: 6px;border-radius: 50%;background-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;height: 6px !important;" mpa-from-tpl="t"> <br> </section> <section style="margin: 0px 0px -2px;padding: 0px;max-width: 100%;text-align: left;border-color: rgb(68, 153, 231);width: 6px;border-radius: 50%;background-color: rgb(68, 153, 231);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: break-word !important;height: 6px !important;" mpa-from-tpl="t"> <br> </section> <section style="margin: -20px 0px 0px;padding: 0px;max-width: 100%;box-sizing: border-box;text-decoration: inherit;color: rgb(68, 153, 231);border-color: rgb(68, 153, 231);word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <section style="margin: 0px;padding: 0px;max-width: 100%;box-sizing: border-box;border-top: 1px solid rgb(68, 153, 231);width: 495px;float: left;border-right-color: rgb(68, 153, 231);border-bottom-color: rgb(68, 153, 231);border-left-color: rgb(68, 153, 231);color: rgb(68, 153, 231);word-wrap: break-word !important;overflow-wrap: break-word !important;" mpa-from-tpl="t"> <br> </section> </section> </section> </section> </section> </section> </blockquote> </section> </section> </blockquote> </section> <p><br mpa-from-tpl="t"></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">有时候我们明明加了索引了,但是索引却不生效。在哪些场景,索引会不失效呢?主要有以下十大经典场景:</span></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><img class="rich_pages wxw-img" data-ratio="0.7163299663299664" src="/upload/d3ef7f8a19b2ab4e6d2ed1208fef4aa7.jpg" data-type="other" data-w="1188"></p> <h3 data-id="heading-3" style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;"><br mpa-from-tpl="t"></span></h3> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-color="rgb(252, 180, 43)" mpa-from-tpl="t"> <section style="border-width: 0px;border-style: none;border-color: initial;" mpa-from-tpl="t"> <section mpa-from-tpl="t"> <section style="display: inline-block;" mpa-from-tpl="t"> <section style="font-size: 16px;padding-right: 10px;padding-left: 10px;color: rgb(73, 73, 73);" mpa-from-tpl="t"> <p mpa-is-content="t"><span style="font-size: 18px;" mpa-is-content="t">2.1 隐式的类型转换,索引失效</span></p> </section> <section style="background-color: rgb(252, 180, 43);height: 8px;margin-top: -10px;color: rgb(255, 255, 255);" mpa-from-tpl="t"> <br mpa-from-tpl="t"> </section> </section> </section> </section> </section> </section> </section> <p><span style="font-size: 16px;">我们创建一个用户user表</span></p> <pre> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t"> <pre style="margin:0;padding:0;border-radius:none;background:none;"><code style="border-radius: 4px;font-size: 0.85em;margin: 0px 0.15em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;padding: 5.95px;overflow-x: auto;white-space: nowrap;"><span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">CREATE</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">TABLE</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">user</span> (<br mpa-from-tpl="t"> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">id</span> <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">int</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">11</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span> AUTO_INCREMENT,<br mpa-from-tpl="t"> userId <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">varchar</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">32</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span>,<br mpa-from-tpl="t"> age <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">varchar</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">16</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span>,<br mpa-from-tpl="t"> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">name</span> <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">varchar</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">255</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span>,<br mpa-from-tpl="t"> PRIMARY <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">KEY</span> (<span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">id</span>),<br mpa-from-tpl="t"> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">KEY</span> idx_userid (userId) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">USING</span> BTREE<br mpa-from-tpl="t">) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">ENGINE</span>=<span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">InnoDB</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">DEFAULT</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">CHARSET</span>=utf8;</code></pre> </section><p><br></p></pre> <p style="margin-bottom: 16px;line-height: 1.6em;"><code><span style="font-size: 16px;">userId</span></code><span style="font-size: 16px;">字段为<strong>字串类型</strong>,是B+树的普通索引,如果查询条件传了一个<strong>数字</strong>过去,会导致索引失效。如下:</span></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><img class="rich_pages wxw-img" data-ratio="0.20192307692307693" src="/upload/8860fdb40b94404f9f28dd0167f2f061.jpg" data-type="other" data-w="1144"></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">如果给数字加上</span><code><span style="font-size: 16px;">''</span></code><span style="font-size: 16px;">,也就是说,传的是一个字符串呢,<strong>当然是走索引</strong>,如下图:</span></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><img class="rich_pages wxw-img" data-ratio="0.1850877192982456" src="/upload/98dfead3496b0230af80d7fb06ec7339.jpg" data-type="other" data-w="1140"></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section style="padding:1px 5px;font-size:14px;white-space:normal;" mpa-from-tpl="t"> <h2 style="border-left: 5px solid rgb(0, 122, 170);font-weight: bold;line-height: 32px;color: rgb(0, 122, 170);padding-right: 10px;padding-left: 10px;margin: 5px;border-top-color: rgb(0, 122, 170);border-bottom-color: rgb(0, 122, 170);border-right-color: rgb(0, 122, 170);"><p style="border-color: rgb(0, 122, 170);" mpa-is-content="t"><span style="font-size: 15px;">为什么第一条语句<strong>未加单引号就不走索引</strong>了呢?这是因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL会做<strong>隐式的类型转换</strong>,把它们转换为浮点数再做比较。隐式的类型转换,索引会失效。</span></p></h2> </section> </section> <p><br></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-color="rgb(252, 180, 43)" mpa-from-tpl="t"> <section style="border-width: 0px;border-style: none;border-color: initial;" mpa-from-tpl="t"> <section mpa-from-tpl="t"> <section style="display: inline-block;" mpa-from-tpl="t"> <section style="font-size: 16px;padding-right: 10px;padding-left: 10px;color: rgb(73, 73, 73);" mpa-from-tpl="t"> <p mpa-is-content="t"><span style="font-size: 18px;" mpa-is-content="t">2.2 查询条件包含or,可能导致索引失效</span></p> </section> <section style="background-color: rgb(252, 180, 43);height: 8px;margin-top: -10px;color: rgb(255, 255, 255);" mpa-from-tpl="t"> <br mpa-from-tpl="t"> </section> </section> </section> </section> </section> </section> </section> <p><span style="font-size: 16px;">我们还是用这个表结构:</span></p> <pre> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t"> <pre style="margin:0;padding:0;border-radius:none;background:none;"><code style="border-radius: 4px;font-size: 0.85em;margin: 0px 0.15em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;padding: 5.95px;overflow-x: auto;white-space: nowrap;"><span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">CREATE</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">TABLE</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">user</span> (<br mpa-from-tpl="t"> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">id</span> <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">int</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">11</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span> AUTO_INCREMENT,<br mpa-from-tpl="t"> userId <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">varchar</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">32</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span>,<br mpa-from-tpl="t"> age <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">varchar</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">16</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span>,<br mpa-from-tpl="t"> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">name</span> <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">varchar</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">255</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span>,<br mpa-from-tpl="t"> PRIMARY <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">KEY</span> (<span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">id</span>),<br mpa-from-tpl="t"> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">KEY</span> idx_userid (userId) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">USING</span> BTREE<br mpa-from-tpl="t">) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">ENGINE</span>=<span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">InnoDB</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">DEFAULT</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">CHARSET</span>=utf8;</code></pre> </section><p><br></p></pre> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">其中</span><code><span style="font-size: 16px;">userId</span></code><span style="font-size: 16px;">加了索引,但是</span><code><span style="font-size: 16px;">age</span></code><span style="font-size: 16px;">没有加索引的。我们使用了</span><code><span style="font-size: 16px;">or</span></code><span style="font-size: 16px;">,以下SQL是不走索引的,如下:</span></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><img class="rich_pages wxw-img" data-ratio="0.19128113879003558" src="/upload/67869ab8607276dd65e211be343a305e.jpg" data-type="other" data-w="1124"></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">对于</span><code><span style="font-size: 16px;">or</span></code><span style="font-size: 16px;">+没有索引的</span><code><span style="font-size: 16px;">age</span></code><span style="font-size: 16px;">这种情况,假设它走了</span><code><span style="font-size: 16px;">userId</span></code><span style="font-size: 16px;">的索引,但是走到</span><code><span style="font-size: 16px;">age</span></code><span style="font-size: 16px;">查询条件时,它还得全表扫描,也就是需要三步过程:<strong>全表扫描+索引扫描+合并</strong>。</span></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">如果它一开始就走<strong>全表扫描</strong>,直接一遍扫描就完事。Mysql优化器出于效率与成本考虑,遇到</span><code><span style="font-size: 16px;">or</span></code><span style="font-size: 16px;">条件,让索引失效,看起来也合情合理嘛。</span></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;"><strong>注意</strong>:如果</span><code><span style="font-size: 16px;">or</span></code><span style="font-size: 16px;">条件的列都加了索引,<strong>索引可能会走也可能不走</strong>,大家可以自己试一试哈。但是平时大家使用的时候,还是要注意一下这个</span><code><span style="font-size: 16px;">or</span></code><span style="font-size: 16px;">,学会用</span><code><span style="font-size: 16px;">explain</span></code><span style="font-size: 16px;">分析。遇到不走索引的时候,考虑拆开两条SQL。</span></p> <h3 data-id="heading-5" style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;"><br mpa-from-tpl="t"></span></h3> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-color="rgb(252, 180, 43)" mpa-from-tpl="t"> <section style="border-width: 0px;border-style: none;border-color: initial;" mpa-from-tpl="t"> <section mpa-from-tpl="t"> <section style="display: inline-block;" mpa-from-tpl="t"> <section style="font-size: 16px;padding-right: 10px;padding-left: 10px;color: rgb(73, 73, 73);" mpa-from-tpl="t"> <p mpa-is-content="t"><span style="font-size: 18px;" mpa-is-content="t">2.3. like通配符可能导致索引失效。</span></p> </section> <section style="background-color: rgb(252, 180, 43);height: 8px;margin-top: -10px;color: rgb(255, 255, 255);" mpa-from-tpl="t"> <br mpa-from-tpl="t"> </section> </section> </section> </section> </section> </section> </section> <p><span style="font-size: 16px;">并不是用了</span><code><span style="font-size: 16px;">like</span></code><span style="font-size: 16px;">通配符,索引一定会失效,而是like查询是以</span><code><span style="font-size: 16px;">%</span></code><span style="font-size: 16px;">开头,才会导致索引失效。</span><br></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">like查询以</span><code><span style="font-size: 16px;">%</span></code><span style="font-size: 16px;">开头,索引失效</span></p> <pre> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t"> <pre style="margin:0;padding:0;border-radius:none;background:none;"><code style="border-radius: 4px;font-size: 0.85em;margin: 0px 0.15em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;padding: 5.95px;overflow-x: auto;white-space: nowrap;"><span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">explain</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">select</span> * <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">from</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">user</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">where</span> userId <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">like</span> <span style="color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(152, 195, 121);font-weight: 400;font-style: normal;">'%123'</span>;</code></pre> </section><p><br></p></pre> <p style="margin-bottom: 16px;line-height: 1.6em;"><img class="rich_pages wxw-img" data-ratio="0.17310549777117384" src="/upload/65f9e80b52ac0260b8990a1789ec7517.jpg" data-type="other" data-w="1346"></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">把</span><code><span style="font-size: 16px;">%</span></code><span style="font-size: 16px;">放后面,发现索引还是正常走的,如下:</span></p> <pre> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t"> <pre style="margin:0;padding:0;border-radius:none;background:none;"><code style="border-radius: 4px;font-size: 0.85em;margin: 0px 0.15em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;padding: 5.95px;overflow-x: auto;white-space: nowrap;"><span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">explain</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">select</span> * <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">from</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">user</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">where</span> userId <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">like</span> <span style="color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(152, 195, 121);font-weight: 400;font-style: normal;">'123%'</span>;</code></pre> </section><p><br></p></pre> <p style="margin-bottom: 16px;line-height: 1.6em;"><img class="rich_pages wxw-img" data-ratio="0.15526122148638705" src="/upload/569266cdc5f129869010601dba368219.jpg" data-type="other" data-w="1359"></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">既然</span><code><span style="font-size: 16px;">like</span></code><span style="font-size: 16px;">查询以</span><code><span style="font-size: 16px;">%</span></code><span style="font-size: 16px;">开头,会导致索引失效。我们如何优化呢?</span></p> <ul class="list-paddingleft-1"> <li style="font-size: 16px;"> <section style="line-height: 1.6em;margin-bottom: 0px;"> <span style="font-size: 16px;">使用覆盖索引</span> </section></li> <li><p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">把</span><code><span style="font-size: 16px;">%</span></code><span style="font-size: 16px;">放后面</span></p></li> </ul> <h3 data-id="heading-6" style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;"><br mpa-from-tpl="t"></span></h3> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-color="rgb(252, 180, 43)" mpa-from-tpl="t"> <section style="border-width: 0px;border-style: none;border-color: initial;" mpa-from-tpl="t"> <section mpa-from-tpl="t"> <section style="display: inline-block;" mpa-from-tpl="t"> <section style="font-size: 16px;padding-right: 10px;padding-left: 10px;color: rgb(73, 73, 73);" mpa-from-tpl="t"> <p mpa-is-content="t"><span style="font-size: 18px;" mpa-is-content="t">2.4 查询条件不满足联合索引的最左匹配原则</span></p> </section> <section style="background-color: rgb(252, 180, 43);height: 8px;margin-top: -10px;color: rgb(255, 255, 255);" mpa-from-tpl="t"> <br mpa-from-tpl="t"> </section> </section> </section> </section> </section> </section> </section> <p><span style="font-size: 16px;">MySQl建立联合索引时,会遵循最左前缀匹配的原则,即最左优先。如果你建立一个</span><code><span style="font-size: 16px;">(a,b,c)</span></code><span style="font-size: 16px;">的联合索引,相当于建立了</span><code><span style="font-size: 16px;">(a)、(a,b)、(a,b,c)</span></code><span style="font-size: 16px;">三个索引。</span><br></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">假设有以下表结构:</span></p> <pre> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t"> <pre style="margin:0;padding:0;border-radius:none;background:none;"><code style="border-radius: 4px;font-size: 0.85em;margin: 0px 0.15em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;padding: 5.95px;overflow-x: auto;white-space: nowrap;"><span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">CREATE</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">TABLE</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">user</span> (<br mpa-from-tpl="t"> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">id</span> <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">int</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">11</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span> AUTO_INCREMENT,<br mpa-from-tpl="t"> user_id <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">varchar</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">32</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span>,<br mpa-from-tpl="t"> age <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">varchar</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">16</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span>,<br mpa-from-tpl="t"> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">name</span> <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">varchar</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">255</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span>,<br mpa-from-tpl="t"> PRIMARY <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">KEY</span> (<span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">id</span>),<br mpa-from-tpl="t"> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">KEY</span> idx_userid_name (user_id,<span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">name</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">USING</span> BTREE<br mpa-from-tpl="t">) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">ENGINE</span>=<span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">InnoDB</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">DEFAULT</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">CHARSET</span>=utf8;</code></pre> </section><p><br></p></pre> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">有一个联合索引</span><code><span style="font-size: 16px;">idx_userid_name</span></code><span style="font-size: 16px;">,我们执行这个SQL,查询条件是</span><code><span style="font-size: 16px;">name</span></code><span style="font-size: 16px;">,索引是无效:</span></p> <pre> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t"> <pre style="margin:0;padding:0;border-radius:none;background:none;"><code style="border-radius: 4px;font-size: 0.85em;margin: 0px 0.15em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;padding: 5.95px;overflow-x: auto;white-space: nowrap;"><span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">explain</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">select</span> * <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">from</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">user</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">where</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">name</span> =<span style="color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);display: inline;width: 103px;text-decoration: none solid rgb(152, 195, 121);font-weight: 400;font-style: normal;">'捡田螺的小男孩'</span>;</code></pre> </section><p><br></p></pre> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">因为查询条件列</span><code><span style="font-size: 16px;">name</span></code><span style="font-size: 16px;">不是联合索引</span><code><span style="font-size: 16px;">idx_userid_name</span></code><span style="font-size: 16px;">中的第一个列,索引不生效</span></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><img class="rich_pages wxw-img" data-ratio="0.16849529780564262" src="/upload/49da085340c359e20cf417e2fbadac95.jpg" data-type="other" data-w="1276"></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;">在联合索引中,查询条件满足<strong>最左匹配原则</strong>时,索引才正常生效。</span></p> <p style="margin-bottom: 16px;line-height: 1.6em;"><img class="rich_pages wxw-img" data-ratio="0.20480993017843288" src="/upload/1567889ed595a1206be8c96a5d1764c2.jpg" data-type="other" data-w="1289"></p> <h3 data-id="heading-7" style="margin-bottom: 16px;line-height: 1.6em;"><span style="font-size: 16px;"><br mpa-from-tpl="t"></span></h3> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-color="rgb(252, 180, 43)" mpa-from-tpl="t"> <section style="border-width: 0px;border-style: none;border-color: initial;" mpa-from-tpl="t"> <section mpa-from-tpl="t"> <section style="display: inline-block;" mpa-from-tpl="t"> <section style="font-size: 16px;padding-right: 10px;padding-left: 10px;color: rgb(73, 73, 73);" mpa-from-tpl="t"> <p mpa-is-content="t"><span style="font-size: 18px;" mpa-is-content="t">2.5 在索引列上使用mysql的内置函数</span></p> </section> <section style="background-color: rgb(252, 180, 43);height: 8px;margin-top: -10px;color: rgb(255, 255, 255);" mpa-from-tpl="t"> <br mpa-from-tpl="t"> </section> </section> </section> </section> </section> </section> </section> <p><span style="font-size: 16px;">表结构:</span></p> <pre> <section data-mpa-preserve-tpl-color="t" data-mpa-template="t" mpa-preserve="t" mpa-from-tpl="t"> <pre style="margin:0;padding:0;border-radius:none;background:none;"><code style="border-radius: 4px;font-size: 0.85em;margin: 0px 0.15em;background: rgb(40, 44, 52);color: rgb(171, 178, 191);display: block;padding: 5.95px;overflow-x: auto;white-space: nowrap;"><span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">CREATE</span> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">TABLE</span> <span style="color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);display: inline;width: 40px;text-decoration: none solid rgb(152, 195, 121);font-weight: 400;font-style: normal;">`user`</span> (<br mpa-from-tpl="t"> <span style="color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(152, 195, 121);font-weight: 400;font-style: normal;">`id`</span> <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">int</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">11</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span> AUTO_INCREMENT,<br mpa-from-tpl="t"> <span style="color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);display: inline;width: 53px;text-decoration: none solid rgb(152, 195, 121);font-weight: 400;font-style: normal;">`userId`</span> <span style="color: rgb(230, 192, 123);background: rgba(0, 0, 0, 0);display: inline;width: 46px;text-decoration: none solid rgb(230, 192, 123);font-weight: 400;font-style: normal;">varchar</span>(<span style="color: rgb(209, 154, 102);background: rgba(0, 0, 0, 0);display: inline;width: 13px;text-decoration: none solid rgb(209, 154, 102);font-weight: 400;font-style: normal;">32</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span>,<br mpa-from-tpl="t"> <span style="color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);display: inline;width: 79px;text-decoration: none solid rgb(152, 195, 121);font-weight: 400;font-style: normal;">`login_time`</span> datetime <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">NOT</span> <span style="color: rgb(86, 182, 194);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(86, 182, 194);font-weight: 400;font-style: normal;">NULL</span>,<br mpa-from-tpl="t"> PRIMARY <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">KEY</span> (<span style="color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);display: inline;width: 26px;text-decoration: none solid rgb(152, 195, 121);font-weight: 400;font-style: normal;">`id`</span>),<br mpa-from-tpl="t"> <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 20px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">KEY</span> <span style="color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);display: inline;width: 79px;text-decoration: none solid rgb(152, 195, 121);font-weight: 400;font-style: normal;">`idx_userId`</span> (<span style="color: rgb(152, 195, 121);background: rgba(0, 0, 0, 0);display: inline;width: 53px;text-decoration: none solid rgb(152, 195, 121);font-weight: 400;font-style: normal;">`userId`</span>) <span style="color: rgb(198, 120, 221);background: rgba(0, 0, 0, 0);display: inline;width: 33px;text-decoration: none solid rgb(198, 120, 221);font-weight: 400;font-style: normal;">USING</span
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;text-align: left;line-height: 1.6;letter-spacing: 0.034em;color: rgb(63, 63, 63);font-size: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;"> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJEVw5NErvLMr3ehZ3jJ79bEvg6RDJAmtTlF9p0TiaF1XZMcKYyYe3EvNw/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">前言</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">Kafka在美团数据平台承担着统一的数据缓存和分发的角色,随着数据量的增长,集群规模的扩大,Kafka面临的挑战也愈发严峻。本文分享了美团Kafka面临的实际挑战,以及美团针对性的一些优化工作,希望能给从事相关开发工作的同学带来帮助或启发。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJEVw5NErvLMr3ehZ3jJ79bEvg6RDJAmtTlF9p0TiaF1XZMcKYyYe3EvNw/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">一、现状和挑战</span></h2> <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("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJE6ZO3ZNMUrMPULiaIz4ovtmR46WwXgo4z0vWeAKlJtIuDJtpnkw2f1OA/640?wx_fmt=png");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);">1、现状</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;">Kafka是一个开源的流处理平台,我们首先了解一下Kafka在美团数据平台的现状。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.8472222222222222" src="/upload/6051b5bb15a4099ad2e12c054a365d8c.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"><span style="font-size: 11px;">图1-1 Kafka在美团数据平台的现状</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如图1-1所示,蓝色部分描述了Kafka在数据平台定位为流存储层。主要的职责是做数据的缓存和分发,它会将收集到的日志分发到不同的数据系统里,这些日志来源于系统日志、客户端日志以及业务数据库。下游的数据消费系统包括通过ODS入仓提供离线计算使用、直接供实时计算使用、通过DataLink同步到日志中心,以及做OLAP分析使用。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">Kafka在美团的集群规模总体机器数已经超过了15000+台,单集群的最大机器数也已经到了2000+台。在数据规模上,天级消息量已经超过了30+P,天级消息量峰值也达到了4+亿/秒。不过随着集群规模的增大,数据量的增长,Kafka面临的挑战也愈发严峻,下面讲一下具体的挑战都有哪些。</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("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJE6ZO3ZNMUrMPULiaIz4ovtmR46WwXgo4z0vWeAKlJtIuDJtpnkw2f1OA/640?wx_fmt=png");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);">2、挑战</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;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.3814814814814815" src="/upload/3354dcab7cf54cf586192add4562bd65.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"><span style="font-size: 11px;">图1-2 Kafka在美团数据平台面临的挑战</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如图1-2所示,具体的挑战可以概括为两部分:</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">1)慢节点影响读写</strong>,这里慢节点参考了HDFS的一个概念,具体定义指的是读写延迟TP99大于300ms的Broker。造成慢节点的原因有三个:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">集群负载不均衡会导致局部热点,就是整个集群的磁盘空间很充裕或者ioutil很低,但部分磁盘即将写满或者ioutil打满。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">PageCache容量,比如说,80GB的PageCache在170MB/s的写入量下仅能缓存8分钟的数据量。那么如果消费的数据是8分钟前的数据,就有可能触发慢速的磁盘访问。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">Consumer客户端的线程模型缺陷会导致端到端延时指标失真。例如当Consumer消费的多个分区处于同一Broker时,TP90可能小于100ms,但是当多个分区处于不同Broker时,TP90可能会大于1000ms。</p> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">2)大规模集群管理的复杂性</strong>,具体表现有4类问题:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">不同Topic之间会相互影响,个别Topic的流量突增,或者个别消费者的回溯读会影响整体集群的稳定性。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">Kafka原生的Broker粒度指标不够健全,导致问题定位和根因分析困难。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">故障感知不及时,处理成本较高。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">Rack级别的故障会造成部分分区不可用。</p> </section></li> </ul> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJEVw5NErvLMr3ehZ3jJ79bEvg6RDJAmtTlF9p0TiaF1XZMcKYyYe3EvNw/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">二、读写延迟优化</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">接下来我们先介绍一下针对读写延迟问题,美团数据平台做了哪些优化。首先从宏观层面,我们将受影响因素分为应用层和系统层,然后详细介绍应用层和系统层存在的问题,并给出对应的解决方案,包括流水线加速、Fetcher隔离、迁移取消和Cgroup资源隔离等,下面具体介绍各种优化方案的实现。</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("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJE6ZO3ZNMUrMPULiaIz4ovtmR46WwXgo4z0vWeAKlJtIuDJtpnkw2f1OA/640?wx_fmt=png");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);">1、概览</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;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.5120370370370371" src="/upload/6c1a3ff4a1c4e857d1b70e5284d5ffd1.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"><span style="font-size: 11px;">图2-1 Kafka读写延迟优化概览</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">图2-1是针对读写延迟碰到的问题以及对应优化方案的概览图。我们把受影响的因素分为应用层和系统层。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">应用层主要包括3类问题:</strong></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">1)Broker端负载不均衡,例如磁盘使用率不均衡、ioutil不均衡等问题。个别磁盘负载升高影响整个Broker的请求受到影响。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">2)Broker的数据迁移存在效率问题和资源竞争问题。具体来讲,包括以下3个层面:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">迁移只能按批次串行提交,每个批次可能存在少量分区迁移缓慢,无法提交下个批次,导致迁移效率受影响。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">迁移一般在夜间执行,如果迁移拖到了午高峰还未完成,可能会显著影响读写请求。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">迁移请求和实时拉取存在共用Fetcher线程的问题导致分区迁移请求可能会影响实时消费请求。</p> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">Consumer端单线程模型存在缺陷导致运维指标失真,并且单Consumer消费的分区数不受限制,消费能力不足就无法跟上实时最新的数据,当消费的分区数增多时可能会引起回溯读。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">系统层也主要包括3类问题:</strong></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">1)PageCache污染。Kafka利用内核层提供的ZeroCopy技术提升性能,但是内核层无法区分实时读写请求和回溯读请求,导致磁盘读可能污染PageCache,影响实时读写。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">2)HDD在随机读写负载下性能差。HDD对于顺序读写友好,但是面对混合负载场景下的随机读写,性能显著下降。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">3)CPU和内存等系统资源在混部场景下的资源竞争问题。在美团大数据平台,为了提高资源的利用率,IO密集型的服务(比如Kafka)会和CPU密集型的服务(比如实时计算作业)混布,混布存在资源竞争,影响读写延迟。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">以上提到的问题,我们采取了针对性的策略。比如应用层的磁盘均衡、迁移流水线加速、支持迁移取消和Consumer异步化等。系统层的Raid卡加速、Cgroup隔离优化等。此外,针对HDD随机读写性能不足的问题,我们还设计并实现了基于SSD的缓存架构。</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("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJE6ZO3ZNMUrMPULiaIz4ovtmR46WwXgo4z0vWeAKlJtIuDJtpnkw2f1OA/640?wx_fmt=png");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);">2、应用层</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;">1)磁盘均衡</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.8425925925925926" src="/upload/847797e01236abf9eea8cb72ef49330c.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"><span style="font-size: 11px;">图2-2 Kafka应用层磁盘均衡</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">磁盘热点导致两个问题:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">实时读写延迟变高,比如说TP99请求处理时间超过300ms可能会导致实时作业发生消费延迟问题,数据收集拥堵问题等。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">集群整体利用率不足,虽然集群容量非常充裕,但是部分磁盘已经写满,这个时候甚至会导致某些分区停止服务。</p> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">针对这两个问题,我们采用了基于空闲磁盘优先的分区迁移计划,整个计划分为3步,由组件Rebalancer统筹管理:</strong></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">生成迁移计划。Rebalancer通过目标磁盘使用率和当前磁盘使用率(通过Kafka Monitor上报)持续生成具体的分区迁移计划。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">提交迁移计划。Rebalancer向Zookeeper的Reassign节点提交刚才生成的迁移计划,Kafka的Controller收到这个Reassign事件之后会向整个Kafka Broker集群提交Reassign事件。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">检查迁移计划。Kafka Broker负责具体执行数据迁移任务,Rebalancer负责检查任务进展。</p> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如图2-2所示,每块Disk持有3个分区是一个相对均衡的状态,如果部分Disk持有4个分区,比如Broker1-Disk1和Broker4-Disk4;部分Disk持有2个分区,比如Broker2-Disk2,Broker3-Disk3,Reblanacer就会将Broker1-Disk1和Broker4-Disk4上多余的分区分别迁移到Broker2-Disk2和Broker3-Disk3,最终尽可能地保证整体磁盘利用率均衡。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">2)迁移优化</strong></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">虽然基于空闲磁盘优先的分区迁移实现了磁盘均衡,但是迁移本身仍然存在效率问题和资源竞争问题。接下来,我们会详细描述我们采取的针对性策略。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <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);"> 采取Fetcher隔离缓解数据迁移请求和实时读写请求共用Fetcher线程的问题 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">优化一:流水线加速</strong></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.9083333333333333" src="/upload/cce21b41d67189e5aff24d51cfcc5844.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><span style="font-size: 11px;">图2-3 流水线加速</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如图2-3所示,箭头以上原生Kafka版本只支持按批提交,比如说一批提交了四个分区,当TP4这个分区一直卡着无法完成的时候,后续所有分区都无法继续进行。采用流水线加速之后,即使TP4这个分区还没有完成,可以继续提交新的分区。在相同的时间内,原有的方案受阻于TP4没有完成,后续所有分区都没办法完成,在新的方案中,TP4分区已经迁移到TP11分区了。图中虚线代表了一个无序的时间窗口,主要用于控制并发,目的是为了和原有的按组提交的个数保持一致,避免过多的迁移影响读写请求服务。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">优化二:迁移取消</strong></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6453703703703704" src="/upload/3fb554a58e964241b66308276fe682b3.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><span style="font-size: 11px;">图2-4-1 迁移问题</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如图2-4-1所示,箭头左侧描述了因为迁移影响的三种线上类型。第一种是因为迁移会触发最旧读,同步大量的数据,在这个过程中会首先将数据回刷到PageCache上引起PageCache污染,导致某个实时读的分区发生Cache Miss,触发磁盘度进而影响读写请求;第二种是当存在某些异常节点导致迁移Hang住时,部分运维操作无法执行,比如流量上涨触发的Topic自动扩分区。因为在Kafka迁移过程中这类运维操作被禁止执行。第三种和第二种类似,它的主要问题是当目标节点Crash,Topic扩分区也无法完成,用户可能一直忍受读写请求受影响。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.8324074074074074" src="/upload/57c453c43385febaa7e9e3581c5450de.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p style="text-align: center;"><span style="font-family: Helvetica, Arial, sans-serif;font-size: 12px;letter-spacing: 0.544px;text-align: center;caret-color: rgb(34, 34, 34);background-color: rgb(255, 255, 255);color: rgb(0, 0, 0);">图2-4-2 迁移取消</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">优化三:Fetcher隔离</strong></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="1.2435185185185185" src="/upload/3dbf5c7259f0ad78fc0c915da301037.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p style="text-align: center;"><span style="font-family: Helvetica, Arial, sans-serif;font-size: 12px;letter-spacing: 0.544px;text-align: center;caret-color: rgb(34, 34, 34);background-color: rgb(255, 255, 255);color: rgb(0, 0, 0);">图2-5 Fetcher隔离</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如图2-5,绿色代表实时读,红色代表延时读。当某一个Follower的实时读和延时读共享同一个Fetcher时,延时读会影响实时读。因为每一次延时读的数据量是显著大于实时读的,而且延时读容易触发磁盘读,可能数据已经不在PageCache中了,显著地拖慢了Fetcher的拉取效率。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">针对这种问题,我们实施的策略叫Fetcher隔离。也就是说所有ISR的Follower共享Fetcher,所有非ISR的Follower共享Fetcher,这样就能保证所有ISR中的实时读不会被非ISR的回溯读所影响。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">3)Consumer异步化</strong></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.812962962962963" src="/upload/5030ef6658f7de88e47097063b9dba2c.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"><span style="font-size: 11px;">图2-6 Kafka-Broker分阶段延时统计模型</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">在讲述Consumer异步化前,需要解释下图2-6展示的Kafka-Broker分阶段延时统计模型。Kafka-Broker端是一个典型的事件驱动架构,各组件通过队列通信。请求在不同组件流转时,会依次记录时间戳,最终就可以统计出请求在不同阶段的执行耗时。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">具体来说,当一个Kafka的Producer或Consumer请求进入到Kafka-Broker时,Processor组件将请求写入RequestQueue,RequestHandler从RequestQueue拉取请求进行处理,在RequestQueue中的等待时间是RequestQueueTime,RequestHandler具体的执行时间是LocalTime。当RequestHandler执行完毕后会将请求传递给DelayedPurgatory组件中,该组件是一个延时队列。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">当触发某一个延时条件完成了以后会把请求写到ResponseQueue中,在DelayedPurgatory队列持续的时间为RemoteTime,Processor会不断的从ResponseQueue中将数据拉取出来发往客户端,标红的ResponseTime是可能会被客户端影响的,因为如果客户端接收能力不足,那么ResponseTime就会一直持续增加。从Kafka-Broker的视角,每一次请求总的耗时RequestTotalTime,包含了刚才所有流程分阶段计时总和。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><img class="rich_pages wxw-img" data-ratio="1.0324074074074074" src="/upload/fea8e775e4a232ca5cd439d943b2e83b.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"><span style="font-size: 11px;">图2-7 Consumer异步化</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">ResponseTime持续增加的主要问题是因为Kafka原生Consumer基于NIO的单线程模型存在缺陷。如图2-7所示,在Phase1,User首先发起Poll请求,Kafka-Client会同时向Broker1、Broker2和Broker3发送请求,Broker1的数据先就绪时,Kafka Client将数据写入CompleteQueue,并立即返回,而不是继续拉取Broker2和Broker3的数据。后续的Poll请求会直接从CompleteQueue中读取数据,然后直接返回,直到CompleteQueue被清空。在CompleteQueue被清空之前,即使Broker2和Broker3的端的数据已经就绪,也不会得到及时拉取。如图中Phase2,因为单线程模型存在缺陷导致WaitFetch这部分时长变大,导致Kafka-Broker的RespnseTime延时指标不断升高,带来的问题是无法对服务端的处理瓶颈进行精准的监控与细分。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.7175925925925926" src="/upload/42d2ef1854f799452306cbcee56c7f0f.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><span style="font-size: 11px;">图2-8 引入异步拉取线程</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">针对这个问题,我们的改进是引入异步拉取线程。异步拉取线程会及时地拉取就绪的数据,避免服务端延时指标受影响,而且原生Kafka并没有限制同时拉取的分区数,我们在这里做了限速,避免GC和OOM的发生。异步线程在后台持续不断地拉取数据并放到CompleteQueue中。</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("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJE6ZO3ZNMUrMPULiaIz4ovtmR46WwXgo4z0vWeAKlJtIuDJtpnkw2f1OA/640?wx_fmt=png");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);">3、系统层</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;"><strong style="line-height: 1.75em;">1)Raid卡加速</strong></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4638888888888889" src="/upload/28e4dbaf933ecbd9bdaaa1af9e0c77ba.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><span style="font-size: 11px;">图2-9 Raid卡加速</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">HDD存在随机写性能不足的问题,表现为延时升高,吞吐降低。针对这个问题我们引入了Raid卡加速。Raid卡自带缓存,与PageCache类似,在Raid这一层会把数据Merge成更大的Block写入Disk,更加充分利用顺序写HDD的带宽,借助Raid卡保证了随机写性能。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">2)Cgroup隔离优化</strong></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.3490740740740741" src="/upload/f300157759171538fc9b48c4962633c6.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"><span style="font-size: 11px;">图2-10 Cgroup隔离</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">为了提高资源利用率,美团数据平台将IO密集型应用和CPU密集型应用混合部署。IO密集型应用在这里指的就是Kafka,CPU密集型应用在这里指的是Flink和Storm。但是原有的隔离策略存在两个问题:首先是物理核本身会存在资源竞争,在同一个物理核下,共享的L1Cache和L2Cache都存在竞争,当实时平台CPU飙升时会导致Kafka读写延时受到影响;其次,Kafka的HT跨NUMA,增加内存访问耗时,如图2-10所示,跨NUMA节点是通过QPI去做远程访问,而这个远程访问的耗时是40ns。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">针对这两个问题,我们改进了隔离策略,针对物理核的资源竞争,我们新的混布策略保证Kafka独占物理核,也就是说在新的隔离策略中,不存在同一个物理核被Kafka和Flink同时使用;然后是保证Kafka的所有超线程处于同一侧的NUMA,避免Kafka跨NUMA带来的访问延时。通过新的隔离策略,Kafka的读写延时不再受Flink CPU飙升的影响。</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("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJE6ZO3ZNMUrMPULiaIz4ovtmR46WwXgo4z0vWeAKlJtIuDJtpnkw2f1OA/640?wx_fmt=png");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);">4、混合层-SSD新缓存架构</span><span style="display: none;"></span></h3> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3648148148148148" src="/upload/28b0a84b01222cef9403ab003bf3776a.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><span style="font-size: 11px;">图2-11 Page污染引起的性能问题</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">1)背景和挑战</strong></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">Kafka利用操作系统提供的ZeroCopy技术处理数据读取请求,PageCache容量充裕时数据直接从PageCache拷贝到网卡,有效降低了读取延时。但是实际上,PageCache的容量往往是不足的,因为它不会超过一个机器的内存。容量不足时,ZeroCopy就会触发磁盘读,磁盘读不仅显著变慢,还会污染PageCache影响其他读写。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如图2-11中左半部分所示,当一个延迟消费者去拉取数据时,发现PageCache中没有它想要的数据,这个时候就会触发磁盘读。磁盘读后会将数据回写到PageCache,导致PageCache污染,延迟消费者消费延迟变慢的同时也会导致另一个实时消费受影响。因为对于实时消费而言,它一直读的是最新的数据,最新的数据按正常来说时不应该触发磁盘读的。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">2)选型和决策</strong></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">针对这个问题,我们这边在做方案选型时提供了两种方案:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">方案一,读磁盘时不回写PageCache,比如使用DirectIO,不过Java并不支持;</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">方案二,在内存和HDD之间引入中间层,比如SSD。众所周知,SSD和HDD相比具备良好的随机读写能力,非常适合我们的使用场景。针对SSD的方案我们也有两种选型:</p> </section></li> </ul> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.7222222222222222" src="/upload/f53408776181a4de1cb474434da0c69f.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <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;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">方案一:</code>可以基于操作系统的内核实现,这种方案SSD与HDD存储空间按照固定大小分块,并且SSD与HDD建立映射关系,同时会基于数据局部性原理,Cache Miss后数据会按LRU和LFU替换SSD中部分数据,业界典型方案包括OpenCAS和FlashCache。其优势是数据路由对应用层透明,对应用代码改动量小,并且社区活跃可用性好;但是问题在于局部性原理并不满足Kafka的读写特性,而且缓存空间污染问题并未得到根本解决,因为它会根据LRU和LFU去替换SSD中的部分数据。</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;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">方案二:</code>基于Kafka的应用层去实现,具体就是Kafka的数据按照时间维度存储在不同设备上,对于近实时数据直接放在SSD上,针对较为久远的数据直接放在HDD上,然后Leader直接根据Offset从对应设备读取数据。这种方案的优势是它的缓存策略充分考虑了Kafka的读写特性,确保近实时的数据消费请求全部落在SSD上,保证这部分请求处理的低延迟,同时从HDD读取的数据不回刷到SSD防止缓存污染,同时由于每个日志段都有唯一明确的状态,因此每次请求目的明确,不存在因Cache Miss带来的额外性能开销。同时劣势也很明显,需要在Server端代码上进行改进,涉及的开发以及测试的工作量较大。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4703703703703704" src="/upload/a1618260a9c553ef2642b7131a472ec1.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><span style="font-size: 11px;">图2-13 KafkaSSD新缓存架构</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">3)具体实现</strong></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">下面来介绍一下SSD新缓存架构的具体实现。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 首先新的缓存架构会将Log内的多个Segment按时间维度存储在不同的存储设备上。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">如图2-14中的红圈1,新缓存架构数据会有三种典型状态,一种叫Only Cache,指的是数据刚写进SSD,还未同步到HDD上;第2个是Cached,指数据既同步到了HDD也有一部分缓存在SSD上;第三种类型叫WithoutCache,指的是同步到了HDD但是SSD中已经没有缓存了。另外,搜索公众号GitHub猿后台回复“赚钱”,获取一份惊喜礼包。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">然后后台异步线程持续地将SSD数据同步到HDD上。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">随着SSD的持续写入,当存储空间达到阈值后,会按时间顺序删除距当前时间最久的数据,因为SSD的数据空间有限。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">副本可根据可用性要求灵活开启是否写入SSD。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">从HDD读取的数据是不会回刷到SSD上的,防止缓存污染。</p> </section></li> </ul> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="1.2652519893899203" src="/upload/c1eb15d2b301aa41f47b5d5bb0b71eef.png" data-type="png" data-w="754" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><span style="font-size: 11px;">图2-14 SSD新缓存架构细节优化</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">4)细节优化</strong></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">介绍了具体实现之后,再来看一下细节优化。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">首先是关于日志段同步,就是刚才说到的Segment,只同步Inactive的日志段,Inactive指的是现在并没有在写的日志段,低成本解决数据一致性问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">其次是做同步限速优化,在SSD向HDD同步时是需要限速的,同时保护了两种设备,不会影响其他IO请求的处理。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJEVw5NErvLMr3ehZ3jJ79bEvg6RDJAmtTlF9p0TiaF1XZMcKYyYe3EvNw/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">三、大规模集群管理优化</span></h2> <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("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJE6ZO3ZNMUrMPULiaIz4ovtmR46WwXgo4z0vWeAKlJtIuDJtpnkw2f1OA/640?wx_fmt=png");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);">1、隔离策略</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;">美团大数据平台的Kafka服务于多个业务,这些业务的Topic混布在一起的话,很有可能造成不同业务的不同Topic之间相互影响。此外,如果Controller节点同时承担数据读写请求,当负载明显变高时,Controller可能无法及时控制类请求,例如元数据变更请求,最终可能会造成整个集群发生故障。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">针对这些相互影响的问题,我们从业务、角色和优先级三个维度来做隔离优化。</strong></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><img class="rich_pages wxw-img" data-ratio="1.0638888888888889" src="/upload/252134aa79fdf87aa88201203c717321.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"><span style="font-size: 11px;">图3-1 隔离优化</span></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">第一点是业务隔离,如图3-1所示,每一个大的业务会有一个独立的Kafka集群,比如外卖、到店、优选。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">第二点是分角色隔离,这里Kafka的Broker和Controller以及它们依赖的组件Zookeeper是部署在不同机器上的,避免之间相互影响。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">第三点是分优先级,有的业务Topic可用性等级特别高,那么我们就可以给它划分到VIP集群,给它更多的资源冗余去保证其可用性。</p> </section></li> </ul> <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("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJE6ZO3ZNMUrMPULiaIz4ovtmR46WwXgo4z0vWeAKlJtIuDJtpnkw2f1OA/640?wx_fmt=png");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);">2、全链路监控</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;">随着集群规模增长,集群管理碰到了一系列问题,主要包括两方面:</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">1) Broker端延时指标无法及时反应用户问题。</strong></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">随着请求量的增长,Kafka当前提供的Broker端粒度的TP99甚至TP999延时指标都可能无法反应长尾延时。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">Broker端的延时指标不是端到端指标,可能无法反应用户的真实问题。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;"><strong style="line-height: 1.75em;">2)故障感知和处理不及时。</strong></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.8675925925925926" src="/upload/345153ea6c214cda3d66bd2be9c8b467.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><span style="font-size: 11px;">图3-2 全链路监控</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">针对这两个问题,我们采取的策略是全链路监控。全链路监控收集和监控Kafka核心组件的指标和日志。全链路监控架构如图3-2所示。当某一个客户端读写请求变慢时,我们通过全链路监控可以快速定位到具体慢在哪个环节,全链路指标监控如图3-3所示。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.412962962962963" src="/upload/d94aa8cbf14b82554d1db890cc04efcc.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"><span style="font-size: 11px;">图3-3 全链路指标监控</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">图3-4是一个根据全链路指标定位请求瓶颈的示例,可以看出服务端RemoteTime占比最高,这说明耗时主要花费在数据复制。日志和指标的解析服务可以自动实时感知故障和慢节点,大部分的故障(内存、磁盘、Raid卡以及网卡等)和慢节点都已经支持自动化处理,还有一类故障是计划外的故障,比如分区多个副本挂掉导致的不可用,迁移Hang住以及非预期的错误日志等,需要人工介入处理。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.5342592592592592" src="/upload/be92e93fe14d0a2b09f3b6c918d3ab4c.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"><span style="font-size: 11px;">图3-4 全链路监控指标示例</span></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("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJE6ZO3ZNMUrMPULiaIz4ovtmR46WwXgo4z0vWeAKlJtIuDJtpnkw2f1OA/640?wx_fmt=png");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);">3、服务生命周期管理</span><span style="display: none;"></span></h3> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.662962962962963" src="/upload/79b781bcdce1d899887edec6dfa8c3fa.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p style="text-align: center;"><span style="font-family: Helvetica, Arial, sans-serif;letter-spacing: 0.544px;text-align: center;caret-color: rgb(34, 34, 34);background-color: rgb(255, 255, 255);color: rgb(0, 0, 0);font-size: 11px;">图3-5 服务生命周期管理</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">美团线上Kafka的服务器规模在万级别,随着服务规模的增长,我们对服务和机器本身的管理,也在不断迭代。我们的自动化运维系统能够处理大部分的机器故障和服务慢节点,但对于机器和服务本身的管理是割裂的,导致存在两类问题:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">状态语义存在歧义,无法真实反映系统状态,往往需要借助日志和指标去找到真实系统是否健康或者异常。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">状态不全面,异常Case需人工介入处理,误操作风险极大。</p> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">为了解决这两类问题,我们引入了生命周期管理机制,确保能够真实反映系统状态。生命周期管理指的是从服务开始运行到机器报废停止服务的全流程管理,并且做到了服务状态和机器状态联动,无需人工同步变更。而且新的生命周期管理机制的状态变更由特定的自动化运维触发,禁止人工变更。</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("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJE6ZO3ZNMUrMPULiaIz4ovtmR46WwXgo4z0vWeAKlJtIuDJtpnkw2f1OA/640?wx_fmt=png");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);">4、TOR容灾</span><span style="display: none;"></span></h3> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.35462962962962963" src="/upload/42635c5864c7a2f44458353755d9aca0.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><span style="font-size: 11px;">图3-6 TOR容灾挑战</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">我们从工程实现的角度,归纳总结了当前主流图神经网络模型的基本范式,实现一套通用框架,以期涵盖多种GNN模型。以下按照图的类型(同质图、异质图和动态图)分别讨论。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;text-align: center;"><img class="rich_pages wxw-img" data-ratio="0.55" src="/upload/6c41cf6d6315769cb0e442234e362e43.png" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;border-radius: 4px;margin-bottom: 25px;"><span style="font-size: 11px;">图3-7 TOR容灾</span></p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">TOR容灾保证同一个分区的不同副本不在同一个Rack下,如图3-7所示,即使Rack1整个发生故障,也能保证所有分区可用。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/0L82kuLBTiaryxXANVWd283L1zic9hjuJEVw5NErvLMr3ehZ3jJ79bEvg6RDJAmtTlF9p0TiaF1XZMcKYyYe3EvNw/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 50px;margin-top: 1em;margin-bottom: 10px;"><span style="display: none;"></span><span style="display: inline-block;height: 38px;line-height: 42px;color: rgb(72, 179, 120);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">四、未来展望</span></h2> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">过去一段时间,我们围绕降低服务端的读写延迟做了大量的优化,但是在服务高可用方面,依然有一些工作需要完成。未来一段时间,我们会将重心放在提升鲁棒性和通过各种粒度的隔离机制缩小故障域。比如,让客户端主动对一些故障节点进行避让,在服务端通过多队列的方式隔离异常请求,支持服务端热下盘,网络层主动反压与限流等等。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">另外,随着美团实时计算业务整体的发展,实时计算引擎(典型如Flink)和流存储引擎(典型如Kafka)混合部署的模式越来越难以满足业务的需求。因此,我们需要在保持当前成本不变的情况下对Kafka进行独立部署。这就意味着需要用更少的机器(在我们的业务模式下,用原来1/4的机器)来承载不变的业务流量。如何在保障服务稳定的情况下,用更少的机器扛起业务请求,也是我们面临的挑战之一。</p> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 1em;color: rgb(74, 74, 74);line-height: 1.75em;">最后,随着云原生趋势的来临,我们也在探索流存储服务的上云之路。</p> </section>
作者:微信小助手
<section style="max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;margin-bottom: 0px;" data-mpa-powered-by="yiban.io"> <section powered-by="xiumi.us" style="max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;"> <section style="max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;"> <section style="max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;"> <section powered-by="xiumi.us" style="max-width: 100%;overflow-wrap: break-word !important;box-sizing: border-box !important;"> <section> <p style="outline: 0px;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);text-align: right;visibility: visible;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5527397260273973" data-s="300,640" src="/upload/d1ea186b1c394f234f0674c1369bf88c.png" data-type="png" data-w="1460" style="outline: 0px;display: initial;box-sizing: border-box !important;visibility: visible !important;width: 677px !important;"><span style="outline: 0px;font-size: 12px;letter-spacing: 0.2em;word-spacing: 0.1em;color: black;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;text-align: left;visibility: visible;">来源:blog.csdn.net/weixin_44848900/article/details/117701981</span></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;outline: 0px;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;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, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;visibility: visible;"> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;visibility: visible;"> <li style="outline: 0px;visibility: visible;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;visibility: visible;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">前景</span> </section></li> <li style="outline: 0px;visibility: visible;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;visibility: visible;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">1 传统POI的的版本优缺点比较</span> </section></li> <li style="outline: 0px;visibility: visible;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;visibility: visible;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">2 使用方式哪种看情况</span> </section></li> <li style="outline: 0px;visibility: visible;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;visibility: visible;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">3 百万数据导入导出(正菜)</span> </section></li> <li style="outline: 0px;visibility: visible;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;visibility: visible;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">4 总结</span> </section></li> </ul> <hr data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;height: 1px;border-width: initial;border-style: none;border-color: initial;text-align: center;background-image: linear-gradient(to right, rgba(248, 57, 41, 0), rgb(14, 136, 235), rgba(248, 57, 41, 0));visibility: visible;"> <h2 data-tool="mdnice编辑器" style="margin-top: 20px;margin-right: 10px;outline: 0px;font-weight: bold;font-size: 22px;visibility: visible;"><span style="padding-left: 10px;outline: 0px;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;border-left: 5px solid rgb(14, 136, 235);visibility: visible;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"><strong style="outline: 0px;visibility: visible;">前景</strong></span></h2> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;visibility: visible;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">在项目开发中往往需要使用到数据的导入和导出,导入就是从Excel中导入到DB中,而导出就是从DB中查询数据然后使用POI写到Excel上。</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;visibility: visible;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">写本文的背景是因为在工作中遇到了大数据的导入和导出,问题既然来了逃跑不如干掉它!!!</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;visibility: visible;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">只要这一次解决了,后期遇到同样的问题就好解决了。</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;visibility: visible;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">废话不多说,开始撸起来!!!</span></p> <h2 data-tool="mdnice编辑器" style="margin-top: 20px;margin-right: 10px;outline: 0px;font-weight: bold;font-size: 22px;visibility: visible;"><span style="padding-left: 10px;outline: 0px;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;border-left: 5px solid rgb(14, 136, 235);visibility: visible;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"><strong style="outline: 0px;visibility: visible;">1 传统POI的的版本优缺点比较</strong></span></h2> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;visibility: visible;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">其实想到数据的导入导出,理所当然的会想到apache的poi技术,以及Excel的版本问题。</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;visibility: visible;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">既然要做导入导出,那么我们就先来大致看一下传统poi技术的版本以及优缺点对比吧!</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;visibility: visible;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">首先我们知道POI中我们最熟悉的莫过于WorkBook这样一个接口,我们的POI版本也在更新的同时对这个几口的实现类做了更新:</span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;visibility: visible;"> <li style="outline: 0px;visibility: visible;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;visibility: visible;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"><strong style="outline: 0px;color: rgb(14, 136, 235);visibility: visible;">HSSFWorkbook :</strong></span> </section></li> </ul> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;visibility: visible;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">这个实现类是我们早期使用最多的对象,它可以操作Excel2003以前(包含2003)的所有Excel版本。在2003以前Excel的版本后缀还是.xls</span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;visibility: visible;"> <li style="outline: 0px;visibility: visible;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;visibility: visible;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"><strong style="outline: 0px;color: rgb(14, 136, 235);visibility: visible;">XSSFWorkbook :</strong></span> </section></li> </ul> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;visibility: visible;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">这个实现类现在在很多公司都可以发现还在使用,它是操作的Excel2003--Excel2007之间的版本,Excel的扩展名是.xlsx</span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;visibility: visible;"> <li style="outline: 0px;visibility: visible;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;visibility: visible;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"><strong style="outline: 0px;color: rgb(14, 136, 235);visibility: visible;">SXSSFWorkbook :</strong></span> </section></li> </ul> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;visibility: visible;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">这个实现类是POI3.8之后的版本才有的,它可以操作Excel2007以后的所有版本Excel,扩展名是.xlsx</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;visibility: visible;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">大致知道了我们在导入导出操作的时候会用到这样三个实现类以及他们可以操作的Excel版本和后缀之后,我们就要从优缺点分析他们了</span></p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;outline: 0px;font-weight: bold;font-size: 18px;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">HSSFWorkbook</span></h4> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">它是POI版本中最常用的方式,不过:</span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;"> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">它的缺点是 最多只能导出 65535行,也就是导出的数据函数超过这个数据就会报错;</span> </section></li> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">它的优点是 不会报内存溢出。(因为数据量还不到7w所以内存一般都够用,首先你得明确知道这种方式是将数据先读取到内存中,然后再操作)</span> </section></li> </ul> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;outline: 0px;font-weight: bold;font-size: 18px;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">XSSFWorkbook</span></h4> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;"> <li style="outline: 0px;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">优点:这种形式的出现是为了突破</span> <code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;outline: 0px;font-size: 14px;border-radius: 4px;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;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">HSSFWorkbook</span></code> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">的65535行局限,是为了针对Excel2007版本的1048576行,16384列,最多可以导出104w条数据;</span> </section></li> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">缺点:伴随的问题来了,虽然导出数据行数增加了好多倍,但是随之而来的内存溢出问题也成了噩梦。因为你所创建的book,Sheet,row,cell等在写入到Excel之前,都是存放在内存中的(这还没有算Excel的一些样式格式等等),可想而知,内存不溢出就有点不科学了!!!</span> </section></li> </ul> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;outline: 0px;font-weight: bold;font-size: 18px;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">SXSSFWorkbook</span></h4> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">从POI 3.8版本开始,提供了一种基于XSSF的低内存占用的SXSSF方式:</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">优点:</span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;"> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">这种方式不会一般不会出现内存溢出(它使用了硬盘来换取内存空间,</span> </section></li> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">也就是当内存中数据达到一定程度这些数据会被持久化到硬盘中存储起来,而内存中存的都是最新的数据),</span> </section></li> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">并且支持大型Excel文件的创建(存储百万条数据绰绰有余)。</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">缺点:</span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;"> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">既然一部分数据持久化到了硬盘中,且不能被查看和访问那么就会导致,</span> </section></li> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">在同一时间点我们只能访问一定数量的数据,也就是内存中存储的数据;</span> </section></li> <li style="outline: 0px;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;outline: 0px;font-size: 14px;border-radius: 4px;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;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">sheet.clone()</span></code> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">方法将不再支持,还是因为持久化的原因;</span> </section></li> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">不再支持对公式的求值,还是因为持久化的原因,在硬盘中的数据没法读取到内存中进行计算;</span> </section></li> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">在使用模板方式下载数据的时候,不能改动表头,还是因为持久化的问题,写到了硬盘里就不能改变了;</span> </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 20px;margin-right: 10px;outline: 0px;font-weight: bold;font-size: 22px;"><span style="padding-left: 10px;outline: 0px;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;border-left: 5px solid rgb(14, 136, 235);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"><strong style="outline: 0px;">2 使用方式哪种看情况</strong></span></h2> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">经过了解也知道了这三种Workbook的优点和缺点,那么具体使用哪种方式还是需要看情况的:</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">我一般会根据这样几种情况做分析选择:</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">1、当我们经常导入导出的数据不超过7w的情况下,可以使用 </span><code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;outline: 0px;font-size: 14px;border-radius: 4px;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;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">HSSFWorkbook</span></code><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> 或者 </span><code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;outline: 0px;font-size: 14px;border-radius: 4px;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;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">XSSFWorkbook</span></code><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">都行;</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">2、当数据量查过7w并且导出的Excel中不牵扯对Excel的样式,公式,格式等操作的情况下,推荐使用</span><code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;outline: 0px;font-size: 14px;border-radius: 4px;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;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">SXSSFWorkbook</span></code><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">;</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">3、当数据量查过7w,并且我们需要操做Excel中的表头,样式,公式等,这时候我们可以使用 </span><code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;outline: 0px;font-size: 14px;border-radius: 4px;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;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">XSSFWorkbook</span></code><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> 配合进行分批查询,分批写入Excel的方式来做;</span></p> <h2 data-tool="mdnice编辑器" style="margin-top: 20px;margin-right: 10px;outline: 0px;font-weight: bold;font-size: 22px;"><span style="padding-left: 10px;outline: 0px;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;border-left: 5px solid rgb(14, 136, 235);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"><strong style="outline: 0px;">3 百万数据导入导出(正菜)</strong></span></h2> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">铺垫也做了不少,那么现在开始讲讲我在工作中遇到的超百万数据的导入导出解决方案:</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"><strong style="outline: 0px;color: rgb(14, 136, 235);">想要解决问题我们首先要明白自己遇到的问题是什么?</strong></span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">1、 我遇到的数据量超级大,使用传统的POI方式来完成导入导出很明显会内存溢出,并且效率会非常低;</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">2、 数据量大直接使用</span><code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;outline: 0px;font-size: 14px;border-radius: 4px;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;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">select * from tableName</span></code><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">肯定不行,一下子查出来300w条数据肯定会很慢;</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">3、 300w 数据导出到Excel时肯定不能都写在一个Sheet中,这样效率会非常低;估计打开都得几分钟;</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">4、 300w数据导出到Excel中肯定不能一行一行的导出到Excel中。频繁IO操作绝对不行;</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">5、 导入时300万数据存储到DB如果循环一条条插入也肯定不行;</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">6、导入时300w数据如果使用Mybatis的批量插入肯定不行,因为Mybatis的批量插入其实就是SQL的循环;一样很慢。</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"><strong style="outline: 0px;color: rgb(14, 136, 235);">解决思路:</strong></span></p> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;outline: 0px;font-weight: bold;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">针对1 :</span></h5> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">其实问题所在就是内存溢出,我们只要使用对上面介绍的POI方式即可,主要问题就是原生的POI解决起来相当麻烦。</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">经过查阅资料翻看到阿里的一款POI封装工具EasyExcel,上面问题等到解决;</span></p> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;outline: 0px;font-weight: bold;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">针对2:</span></h5> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">不能一次性查询出全部数据,我们可以分批进行查询,只不过时多查询几次的问题,况且市面上分页插件很多。此问题好解决。</span></p> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;outline: 0px;font-weight: bold;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">针对3:</span></h5> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">可以将300w条数据写到不同的Sheet中,每一个Sheet写一百万即可。</span></p> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;outline: 0px;font-weight: bold;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">针对4:</span></h5> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">不能一行一行的写入到Excel上,我们可以将分批查询的数据分批写入到Excel中。</span></p> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;outline: 0px;font-weight: bold;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">针对5:</span></h5> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">导入到DB时我们可以将Excel中读取的数据存储到集合中,到了一定数量,直接批量插入到DB中。</span></p> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;outline: 0px;font-weight: bold;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">针对6:</span></h5> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">不能使用Mybatis的批量插入,我们可以使用JDBC的批量插入,配合事务来完成批量插入到DB。即 Excel读取分批+JDBC分批插入+事务。</span></p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;outline: 0px;font-weight: bold;font-size: 18px;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">3.1 EasyExcel 简介</span></h4> <blockquote data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;padding-top: 0px;padding-right: 10px;padding-bottom: 10px;outline: 0px;border-width: initial;border-style: none;border-color: initial;color: rgb(14, 136, 235);font-size: 0.9em;overflow: auto;line-height: 1.8;border-radius: 0px 0px 10px 10px;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <p style="padding-top: 8px;padding-bottom: 8px;outline: 0px;letter-spacing: 0.2em;word-spacing: 0.1em;line-height: 26px;font-size: 15px;display: inline;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">附上GitHub地址:https://github.com/alibaba/easyexcel</span></p> </blockquote> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">GitHub地址上教程和说明很详细,并且附带有读和写的demo代码,这里对它的介绍我就不再详细说了。</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">至于EasyExcel底层怎么实现的这个还有待研究。</span></p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;outline: 0px;font-weight: bold;font-size: 18px;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">3.2 300w数据导出</span></h4> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">EasyExcel完成300w数据的导出。技术难点已经知道了,接下来就是针对这一难点提供自己的解决思路即可。</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"><strong style="outline: 0px;color: rgb(14, 136, 235);">300w数据的导出解决思路:</strong></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;outline: 0px;"> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">首先在查询数据库层面,需要分批进行查询(我使用的是每次查询20w)</span> </section></li> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">每查询一次结束,就使用EasyExcel工具将这些数据写入一次;</span> </section></li> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">当一个Sheet写满了100w条数据,开始将查询的数据写入到另一个Sheet中;</span> </section></li> <li style="outline: 0px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;"> <section style="margin-top: 5px;margin-bottom: 5px;outline: 0px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">如此循环直到数据全部导出到Excel完毕。</span> </section></li> </ul> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">注意:</span></p> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">1、我们需要计算Sheet个数,以及循环写入次数。特别是最后一个Sheet的写入次数</span></p> <blockquote data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;padding-top: 0px;padding-right: 10px;padding-bottom: 10px;outline: 0px;border-width: initial;border-style: none;border-color: initial;color: rgb(14, 136, 235);font-size: 0.9em;overflow: auto;line-height: 1.8;border-radius: 0px 0px 10px 10px;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <p style="padding-top: 8px;padding-bottom: 8px;outline: 0px;letter-spacing: 0.2em;word-spacing: 0.1em;line-height: 26px;font-size: 15px;display: inline;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">因为你不知道最后一个Sheet选哟写入多少数据,可能是100w,也可能是25w因为我们这里的300w只是模拟数据,有可能导出的数据比300w多也可能少</span></p> </blockquote> <p data-tool="mdnice编辑器" style="margin: 10px;padding-top: 8px;padding-bottom: 8px;outline: 0px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">2、我们需要计算写入次数,因为我们使用的分页查询,所以需要注意写入的次数。</span></p> <blockquote data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;padding-top: 0px;padding-right: 10px;padding-bottom: 10px;outline: 0px;border-width: initial;border-style: none;border-color: initial;color: rgb(14, 136, 235);font-size: 0.9em;overflow: auto;line-height: 1.8;border-radius: 0px 0px 10px 10px;background-image: initial;background-position: initial;background-size: initial;background-repeat: initial;background-attachment: initial;background-origin: initial;background-clip: initial;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <p style="padding-top: 8px;padding-bottom: 8px;outline: 0px;letter-spacing: 0.2em;word-spacing: 0.1em;line-height: 26px;font-size: 15px;display: inline;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">其实查询数据库多少次就是写入多少次</span></p> </blockquote> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;outline: 0px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><code style="padding: 15px 16px 16px;outline: 0px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;">//导出逻辑代码<br style="outline: 0px;">public void dataExport300w(HttpServletResponse response) {<br style="outline: 0px;"> {<br style="outline: 0px;"> OutputStream outputStream = null;<br style="outline: 0px;"> try {<br style="outline: 0px;"> long startTime = System.currentTimeMillis();<br style="outline: 0px;"> System.out.println(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"导出开始时间:"</span> + startTime);<br style="outline: 0px;"><br style="outline: 0px;"> outputStream = response.getOutputStream();<br style="outline: 0px;"> ExcelWriter writer = new ExcelWriter(outputStream, ExcelTypeEnum.XLSX);<br style="outline: 0px;"> String fileName = new String((<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"excel100w"</span>).getBytes(), <span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"UTF-8"</span>);<br style="outline: 0px;"><br style="outline: 0px;"> //title<br style="outline: 0px;"> Table table = new Table(1);<br style="outline: 0px;"> List<List<String>> titles = new ArrayList<List<String>>();<br style="outline: 0px;"> titles.add(Arrays.asList(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"onlineseqid"</span>));<br style="outline: 0px;"> titles.add(Arrays.asList(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"businessid"</span>));<br style="outline: 0px;"> titles.add(Arrays.asList(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"becifno"</span>));<br style="outline: 0px;"> titles.add(Arrays.asList(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"ivisresult"</span>));<br style="outline: 0px;"> titles.add(Arrays.asList(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"createdby"</span>));<br style="outline: 0px;"> titles.add(Arrays.asList(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"createddate"</span>));<br style="outline: 0px;"> titles.add(Arrays.asList(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"updateby"</span>));<br style="outline: 0px;"> titles.add(Arrays.asList(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"updateddate"</span>));<br style="outline: 0px;"> titles.add(Arrays.asList(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"risklevel"</span>));<br style="outline: 0px;"> table.setHead(titles);<br style="outline: 0px;"><br style="outline: 0px;"> //模拟统计查询的数据数量这里模拟100w<br style="outline: 0px;"> int count = 3000001;<br style="outline: 0px;"> //记录总数:实际中需要根据查询条件进行统计即可<br style="outline: 0px;"> Integer totalCount = actResultLogMapper.findActResultLogByCondations(count);<br style="outline: 0px;"> //每一个Sheet存放100w条数据<br style="outline: 0px;"> Integer sheetDataRows = ExcelConstants.PER_SHEET_ROW_COUNT;<br style="outline: 0px;"> //每次写入的数据量20w<br style="outline: 0px;"> Integer writeDataRows = ExcelConstants.PER_WRITE_ROW_COUNT;<br style="outline: 0px;"> //计算需要的Sheet数量<br style="outline: 0px;"> Integer sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);<br style="outline: 0px;"> //计算一般情况下每一个Sheet需要写入的次数(一般情况不包含最后一个sheet,因为最后一个sheet不确定会写入多少条数据)<br style="outline: 0px;"> Integer oneSheetWriteCount = sheetDataRows / writeDataRows;<br style="outline: 0px;"> //计算最后一个sheet需要写入的次数<br style="outline: 0px;"> Integer lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount : (totalCount % sheetDataRows % writeDataRows == 0 ? (totalCount / sheetDataRows / writeDataRows) : (totalCount / sheetDataRows / writeDataRows + 1));<br style="outline: 0px;"><br style="outline: 0px;"> //开始分批查询分次写入<br style="outline: 0px;"> //注意这次的循环就需要进行嵌套循环了,外层循环是Sheet数目,内层循环是写入次数<br style="outline: 0px;"> List<List<String>> dataList = new ArrayList<>();<br style="outline: 0px;"> <span style="outline: 0px;color: rgb(198, 120, 221);line-height: 26px;">for</span> (int i = 0; i < sheetNum; i++) {<br style="outline: 0px;"> //创建Sheet<br style="outline: 0px;"> Sheet sheet = new Sheet(i, 0);<br style="outline: 0px;"> sheet.setSheetName(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"测试Sheet1"</span> + i);<br style="outline: 0px;"> //循环写入次数: j的自增条件是当不是最后一个Sheet的时候写入次数为正常的每个Sheet写入的次数,如果是最后一个就需要使用计算的次数lastSheetWriteCount<br style="outline: 0px;"> <span style="outline: 0px;color: rgb(198, 120, 221);line-height: 26px;">for</span> (int j = 0; j < (i != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount); j++) {<br style="outline: 0px;"> //集合复用,便于GC清理<br style="outline: 0px;"> dataList.clear();<br style="outline: 0px;"> //分页查询一次20w<br style="outline: 0px;"> PageHelper.startPage(j + 1 + oneSheetWriteCount * i, writeDataRows);<br style="outline: 0px;"> List<ActResultLog> reslultList = actResultLogMapper.findByPage100w();<br style="outline: 0px;"> <span style="outline: 0px;color: rgb(198, 120, 221);line-height: 26px;">if</span> (!CollectionUtils.isEmpty(reslultList)) {<br style="outline: 0px;"> reslultList.forEach(item -> {<br style="outline: 0px;"> dataList.add(Arrays.asList(item.getOnlineseqid(), item.getBusinessid(), item.getBecifno(), item.getIvisresult(), item.getCreatedby(), Calendar.getInstance().getTime().toString(), item.getUpdateby(), Calendar.getInstance().getTime().toString(), item.getRisklevel()));<br style="outline: 0px;"> });<br style="outline: 0px;"> }<br style="outline: 0px;"> //写数据<br style="outline: 0px;"> writer.write0(dataList, sheet, table);<br style="outline: 0px;"> }<br style="outline: 0px;"> }<br style="outline: 0px;"><br style="outline: 0px;"> // 下载EXCEL<br style="outline: 0px;"> response.setHeader(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"Content-Disposition"</span>, <span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"attachment;filename="</span> + new String((fileName).getBytes(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"gb2312"</span>), <span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"ISO-8859-1"</span>) + <span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">".xlsx"</span>);<br style="outline: 0px;"> response.setContentType(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"multipart/form-data"</span>);<br style="outline: 0px;"> response.setCharacterEncoding(<span style="outline: 0px;color: rgb(152, 195, 121);line-height: 26px;">"utf-8"</span>);<br style="outline: 0px;"> &nbs
作者:微信小助手
<section style="margin: 24px 8px 16px;"> <span style="color: black;font-size: 24px;font-weight: bold;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0px;text-align: left;">摘要</span> <span style="font-size: 15px;"></span> </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;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 0px;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">开发中无论怎样都会产生网络请求,这样一来自然也就避免不了大量使用<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">then</code>、<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">catch</code>或<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">try catch</code>来捕获错误,而捕获错误的代码量是随着网络请求的增多而增多,那应该如何优雅的系统性捕获某个网络请求中所产生的所有错误呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">首先最常用的两种处理网络请求的形式即<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Promise</code>与<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">async</code>(事实上很多请求库都是基于这两者的封装),使用<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Promise</code>那必然要与<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">then</code>、<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">catch</code>挂钩,也就是说每个请求都对应一个<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Promise</code>实例,然后通过该实例上对应的方法来完成对应的操作,这应该算是比较常用的一种形式了</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">但如果涉及嵌套请求,那可能还要不断的增加<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">then</code>、<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">catch</code>来完成需求,好了,现在可以使用看起来真的像同步编程的<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">async</code>来着手优化了,即<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">await promise</code>,那这种情况下就根本不需要手动<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">then</code>了,但如果<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">await promise</code>抛出了错误呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">那恐怕不得不让<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">try catch</code>来帮忙了,而如果也是嵌套请求,那与<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Promise</code>写法类似的问题又来了,有多少次请求难道我就要多少次<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">try catch</code>吗?那这样看来的话,<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Promise</code>与<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">async</code>在面对这种屎山请求的时候确实有点心有余而力不足了</p> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 24px;"><span style="display: none;"></span>前言</h1> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">之所以写作本篇文章是因为在优化数据库操作时,发现要不停<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">try catch</code>,且操作数据库的代码越多,则<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">try catch</code>就越多,于是突发奇想,能不能封装一个工具类来实现智能化捕获错误呢?在这种思维的推动下,我觉得这个工具类不仅仅是以一种创意的形式出现,更多的是实用性!(先不考虑这个创意能否实现)</p> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 24px;"><span style="display: none;"></span>一个令人头疼的需求</h1> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">家在吉林的小明想去海南看望他的老奶奶,但小明觉得旅途如此之长,不如先去山东学习学习马保国老师的“<strong>接化发</strong>”,然后再去云南拍一个“<strong>我是云南的 云南怒江的...</strong> ”的视频发一下朋友圈,最后再去海南看望老奶奶</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">请你运用所学知识帮帮小明,查询<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">吉林--山东--云南--海南</code>的车票还有吗?</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如果有的话,老奶奶希望小明不要在车票上花费太多的钱,所以当小明出发时,需要告诉老奶奶 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">本次所有车票的开销是多少</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;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">可能会换个路线</code>去找老奶奶 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">注意,当确定<strong>吉林-山东</strong>的车票未售空时才去查询<strong>山东-云南</strong>的车票是否已售空,并以此类推;因为这样的话,小明可以知道是哪个地方的车票没有了,并及时换乘</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然<strong>吉林--山东--云南--海南</strong>的车票可以一次性查询完毕,但为了体现嵌套请求的复杂度,我们此处不讨论并发请求的情况,关于并发,你可以使用<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">Promise.all</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 class="rich_pages wxw-img" data-ratio="1.043010752688172" src="/upload/12d1d057a7df29a063bb95f8d38ff8f.png" data-type="other" data-w="651" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">先来细化题目,可以看到路线依次为:<strong>吉林-山东</strong>、<strong>山东-云南</strong>、<strong>云南-海南</strong>,也就分别对应三个请求,且这三个请求又是嵌套发出的。而每次发出的请求,最终都会有两种情况:请求成功/失败,<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">请求成功则代表本轮次车票未售空</code>,<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">请求失败则代表本轮次车票已售空</code></p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgb(255, 218, 169);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;font-size: 15px;">之所以请求失败对应车票已售空,是为了模拟请求失败的情况,而不是通过返回一个标识来代表本轮次车票是否已售空</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这个令人头疼的需求,我建议你再认真读一遍</p> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 24px;"><span style="display: none;"></span>准备工作</h1> <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;">以下是用于查询车票的接口,我们称之为<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(233, 105, 0);background: rgb(248, 248, 248);">请求函数</code></p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgb(255, 218, 169);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"> <p style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;font-size: 15px;">在下文中所指的<strong>请求函数</strong>就是requestJS、requestSY、requestYH</p> </blockquote> <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("https://mmbiz.qpic.cn/mmbiz_svg/UDa9R1yl9UYt8NibAs3FjDCCZth7xtrCnkZDUlNnhOT0NAIM0xOSYMjWUrbrwMeXU6SH1JIpf3OLU7Q7DzPhibVtleB4ico6EIa/640?wx_fmt=svg&random=0.7522547544935645") 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;"><span style="color: #5c6370;font-style: italic;line-height: 26px;">// 标识每次请求的成功与否(吉林-山东、山东-云南、云南-海南)</span><br><span style="color: #c678dd;line-height: 26px;">const</span> interface = [<span style="color: #56b6c2;line-height: 26px;">true</span>, <span style="color: #56b6c2;line-height: 26px;">true</span>, <span style="color: #56b6c2;line-height: 26px;">true</span>]<br><br><span style="color: #5c6370;font-style: italic;line-height: 26px;">// 查询 吉林-山东 的车票是否已售空的接口</span><br><span style="color: #c678dd;line-height: 26px;">const</span> requestJS = <span style="line-height: 26px;"><span style="line-height: 26px;">()</span> =></span> <span style="color: #c678dd;line-height: 26px;">new</span> <span style="color: #e6c07b;line-height: 26px;">Promise</span>(<span style="line-height: 26px;">(<span style="line-height: 26px;">res, rej</span>) =></span> {<br> setTimeout(<span style="line-height: 26px;"><span style="line-height: 26px;">()</span> =></span> {<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 请求成功(resolve)则代表车票未售空</span><br> <span style="color: #c678dd;line-height: 26px;">if</span> (interface[<span style="color: #d19a66;line-height: 26px;">0</span>]) <span style="color: #c678dd;line-height: 26px;">return</span> res({ <span style="color: #d19a66;line-height: 26px;">ticket</span>: <span style="color: #56b6c2;line-height: 26px;">true</span>, <span style="color: #d19a66;line-height: 26px;">price</span>: <span style="color: #d19a66;line-height: 26px;">530</span>, <span style="color: #d19a66;line-height: 26px;">destination</span>: <span style="color: #98c379;line-height: 26px;">'吉林-山东'</span> })<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 请求成功(rejected)则代表车票已售空</span><br> rej({ <span style="color: #d19a66;line-height: 26px;">ticket</span>: <span style="color: #56b6c2;line-height: 26px;">false</span>, <span style="color: #d19a66;line-height: 26px;">destination</span>: <span style="color: #98c379;line-height: 26px;">'吉林-山东'</span> })<br> }, <span style="color: #d19a66;line-height: 26px;">1000</span>)<br>})<br><span style="color: #5c6370;font-style: italic;line-height: 26px;">// 查询 山东-云南 的车票是否已售空的接口</span><br><span style="color: #c678dd;line-height: 26px;">const</span> requestSY = <span style="line-height: 26px;"><span style="line-height: 26px;">()</span> =></span> <span style="color: #c678dd;line-height: 26px;">new</span> <span style="color: #e6c07b;line-height: 26px;">Promise</span>(<span style="line-height: 26px;">(<span style="line-height: 26px;">res, rej</span>) =></span> {<br> setTimeout(<span style="line-height: 26px;"><span style="line-height: 26px;">()</span> =></span> {<br> <span style="color: #c678dd;line-height: 26px;">if</span> (interface[<span style="color: #d19a66;line-height: 26px;">1</span>]) <span style="color: #c678dd;line-height: 26px;">return</span> res({ <span style="color: #d19a66;line-height: 26px;">ticket</span>: <span style="color: #56b6c2;line-height: 26px;">true</span>, <span style="color: #d19a66;line-height: 26px;">price</span>: <span style="color: #d19a66;line-height: 26px;">820</span>, <span style="color: #d19a66;line-height: 26px;">destination</span>: <span style="color: #98c379;line-height: 26px;">'山东-云南'</span> })<br> rej({ <span style="color: #d19a66;line-height: 26px;">ticket</span>: <span style="color: #56b6c2;line-height: 26px;">false</span>, <span style="color: #d19a66;line-height: 26px;">destination</span>: <span style="color: #98c379;line-height: 26px;">'山东-云南'</span> })<br> }, <span style="color: #d19a66;line-height: 26px;">1500</span>)<br>})<br><span style="color: #5c6370;font-style: italic;line-height: 26px;">// 查询 云南-海南 的车票是否已售空的接口</span><br><span style="color: #c678dd;line-height: 26px;">const</span> requestYH = <span style="line-height: 26px;"><span style="line-height: 26px;">()</span> =></span> <span style="color: #c678dd;line-height: 26px;">new</span> <span style="color: #e6c07b;line-height: 26px;">Promise</span>(<span style="line-height: 26px;">(<span style="line-height: 26px;">res, rej</span>) =></span> {<br> setTimeout(<span style="line-height: 26px;"><s
作者:微信小助手
<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: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-bottom: 16px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;font-size: 15px;white-space: normal;text-size-adjust: auto;line-height: 1.75em;">写此文章的目的是为了记录一下在工作中解决的 XSS漏洞 问题。 XSS漏洞是生产上比较常见的问题。虽然是比较常见并且是基本的安全问题,但是我们没有做🙄️ ,也怪我没有安全意识。于是终于有一天被制裁了。所以这次就补上了,记录一下。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin: 10px auto;height: 40px;background-color: rgb(251, 251, 251);border-bottom: 1px solid rgb(246, 246, 246);overflow: hidden;"><span style="display: none;"></span><span style="margin-left: -10px;display: inline-block;width: auto;height: 40px;background-color: rgb(33, 33, 34);border-bottom-right-radius: 100px;color: rgb(255, 255, 255);padding-right: 30px;padding-left: 30px;line-height: 40px;font-size: 16px;">看看问题</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-bottom: 16px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;font-size: 15px;white-space: normal;text-size-adjust: auto;line-height: 1.75em;">XSS 漏洞到底是什么,说实话我讲不太清楚。但是可以通过遇到的现象了解一下。在前端Form表单的输入框中,用户没有正常输入,而是输入了一段代码:<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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"></input><img src=1 onerror=alert1></code> 这个正常保存没有问题。问题出在了列表查询的时候,上面的代码就生效了,由于图片的地址乱写的,所以这个alert就起作用了来看图。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.1819672131147541" src="/upload/3f1bf0fdababa09fbaa401c56ab5bd42.png" data-type="png" data-w="610" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-bottom: 16px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;font-size: 15px;white-space: normal;text-size-adjust: auto;line-height: 1.75em;">那根据这个原理,实际上如果没有做任何的限制,有心人就可以为所欲为了。可以在里面嵌入一些关键代码,把你的信息拿走。确实是个很严重的问题。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin: 10px auto;height: 40px;background-color: rgb(251, 251, 251);border-bottom: 1px solid rgb(246, 246, 246);overflow: hidden;"><span style="display: none;"></span><span style="margin-left: -10px;display: inline-block;width: auto;height: 40px;background-color: rgb(33, 33, 34);border-bottom-right-radius: 100px;color: rgb(255, 255, 255);padding-right: 30px;padding-left: 30px;line-height: 40px;font-size: 16px;">解决思路</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-bottom: 16px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;font-size: 15px;white-space: normal;text-size-adjust: auto;line-height: 1.75em;">既然是因为输入框中输入了不该输入的东西,那自然就萌生一些想法:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;"> <strong style="color: black;">校验输入内容</strong>,不允许用户输入特殊字符,特殊标签 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;"> <strong style="color: black;">允许用户输入</strong>,但是保存的时候将特殊的字符直接替换为空串 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;"> <strong style="color: black;">允许用户输入</strong>,将特殊字符转译保存。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-bottom: 16px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;font-size: 15px;white-space: normal;text-size-adjust: auto;line-height: 1.75em;">第一种方法,特殊字符过滤。既然要过滤特殊字符,那就得自己把所有的特殊字符列出来进行匹配,比较麻烦,而且要定义好什么才是特殊字符?况且用户本身不知道什么是特殊字符。突如其来的报错,会让用户有点摸不着头脑,不是很友好。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-bottom: 16px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;font-size: 15px;white-space: normal;text-size-adjust: auto;line-height: 1.75em;">第二种方法,特殊字符替换为空串。未免有点太暴力。万一真的需要输入一点特殊的字符,保存完查出来发现少了好多东西,人家以为我们的BUG呢。也不是很好的办法。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-bottom: 16px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;font-size: 15px;white-space: normal;text-size-adjust: auto;line-height: 1.75em;">第三种办法,特殊字符转译。这个办法不但用户数据不丢失,而且浏览器也不会执行代码。比较符合预期。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-bottom: 16px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;font-size: 15px;white-space: normal;text-size-adjust: auto;line-height: 1.75em;">那办法确定了,怎么做呢?前端来做还是后端来做?想了想还是要后端来做。毕竟使用切面或者Filter可以一劳永逸。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin: 10px auto;height: 40px;background-color: rgb(251, 251, 251);border-bottom: 1px solid rgb(246, 246, 246);overflow: hidden;"><span style="display: none;"></span><span style="margin-left: -10px;display: inline-block;width: auto;height: 40px;background-color: rgb(33, 33, 34);border-bottom-right-radius: 100px;color: rgb(255, 255, 255);padding-right: 30px;padding-left: 30px;line-height: 40px;font-size: 16px;">心路历程</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-bottom: 16px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;font-size: 15px;white-space: normal;text-size-adjust: auto;line-height: 1.75em;">经过抄袭,我发现了一些问题,也渐渐的有了一些理解。下面再说几句废话:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-bottom: 16px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;font-size: 15px;white-space: normal;text-size-adjust: auto;line-height: 1.75em;">查到的预防XSS攻击的,大多数的流程是:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;"> 拦截请求 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;"> 重新包装请求 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;"> 重写 <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: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">HttpServletRequest</code>中的获取参数的方法 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;"> 将获得的参数进行XSS处理 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;"> 拦截器放行 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin-bottom: 16px;font-family: "Helvet
作者:微信小助手
<section data-role="outer" label="edit by 135editor" data-mpa-powered-by="yiban.io"> <p style="text-align:justify;margin: 20px 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">曾经有一家巨头公司和我们公司进行战略合作,经过双方的不懈努力及精诚合作,双方公司决定共同举办一场秒杀活动,我们公司提供优质商品和强有力的吸引价格以及使用场景,对方公司提供巨大的用户流量,再加上我们公司自己的用户流量,粗略估算下来有5000万的用户流量。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">其实,当时我们的架构是完全支撑不了千万级流量的瞬时冲击的,但是双方老板已经达成协议就要快速干起来,而且给了一个基本无法完成的时间期限。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">由于时间紧急,我们公司技术部召开了紧急会议,最终得出结论就是在原有架构基础上增加秒杀的相关接口,增加两个H5页面作为前端秒杀活动的承接页面,然后等待洪水般流量的到来。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">当秒杀活动真正开始时,流量远超过我们的估算,很快就卡住不动了,后台服务器内存、CPU、数据库负载等全满负荷了。期间,正常下单的用户也不能正常访问公司App以及下单。不难猜出,这个结果老板肯定是不满意的,要求必须解决,不能终止本次活动。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: #C00000;"><strong><span style="font-size: 15px;letter-spacing: 1px;font-family: Helvetica, Arial, sans-serif;">最后的解决办法是:使用金钱来砸——立刻增加120多台云服务器来承载当时的秒杀活动。</span></strong></span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">那次活动对于技术部门来讲无疑是一个痛苦的经历,也是一个“不光彩”的经历。于是,后面增加了针对秒杀架构的设计。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><br></p> <section data-id="98856" data-tools="135编辑器"> <section style="margin: 10px auto;text-align: center;"> <section style="display: flex;flex-direction: column;justify-content: center;align-items: center;" hm_fix="225:661"> <section style="color:#c00000;font-size: 60px;" data-original-title="" title=""> <strong>1</strong> </section> <section style="width: 15%;height: 1px;background: rgb(192, 0, 0);margin-top: -35px;overflow: hidden;max-width: 15% !important;" data-width="15%"> <br> </section> </section> <section data-brushtype="text" style="font-size: 16px;letter-spacing: 1.5px;padding: 0px 10px;background: #fff;padding: 8px;transform: rotate(0deg);-webkit-transform: rotate(0deg);-moz-transform: rotate(0deg);-ms-transform: rotate(0deg);-o-transform: rotate(0deg);"> <span style="font-size:17px;"><strong>需求分析</strong></span> </section> </section> </section> <h2 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: rgb(59, 59, 59);font-size: 15px;letter-spacing: 1px;caret-color: red;font-family: Helvetica, Arial, sans-serif;">“秒杀”这个词在电商行业中出现的频率较高,如京东或者淘宝平台的各种“秒杀”活动,最典型的就是“双11抢购”。</span></h2> <h2 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: rgb(59, 59, 59);font-size: 15px;letter-spacing: 1px;caret-color: red;font-family: Helvetica, Arial, sans-serif;">“秒杀”是指在有限的时间内对有限的商品数量进行抢购的一种行为,这是商家以“低价量少”的商品来获取用户的一种营销手段。</span><br></h2> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 17px;color: #C00000;"><strong><span style="letter-spacing: 1px;font-family: Helvetica, Arial, sans-serif;">01. 功能性需求</span></strong></span></h3> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">其实,整个秒杀的业务场景并不复杂,可即查看参与秒杀的商品信息,加上购买和支付的动作,如下图所示。</span></p> <p style="text-align:center;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="0.8692307692307693" src="/upload/2091fa138f8e9a9850df2ab9ec37535d.png" data-type="png" data-w="1040" style="box-sizing: border-box;vertical-align: inherit;border-width: 1px;border-style: solid;border-color: rgb(151, 152, 153);background-color: rgb(255, 255, 255);border-radius: 0px;padding: 4px;width: 448px;height: 389.406px;"></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(192, 0, 0);font-family: Helvetica, Arial, sans-serif;">秒杀业务最大的挑战在于3点:</span></strong></p> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">瞬时:</span></strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">持续时间极短,对于热门且具备极强竞争力的商品通常只有一秒。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">流量巨大:</span></strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">因为价格低廉,商品性价比高,而且正常买是需要很高的价格,所以才会吸引大量的用户来争抢。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">数量有限:</span></strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">因为商品的低价且性价比高,所以只有很有限的商品数量参与秒杀。</span></p></li> </ul> </section> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">同时,在保证高并发流量承接的前提下,为了增强用户的体验和活动规则的公平性,以及防止遭到恶意破坏等,特此增加如下需求:</span></p> <section data-tools="135编辑器" data-id="86005"> <section data-autoskip="1" style="overflow: auto;font-size: 15px;margin-bottom: 16px;line-height: 1.45;padding: 16px;border-radius: 3px;overflow-wrap: normal;background-color: rgb(247, 247, 247);"> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(1)用户在秒杀页面无需一直刷新“抢购”按钮,待秒杀活动开始时,按钮自动点亮。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(2)在公平以及防止恶意破坏的原则下,在下单之前增加验证码的录入,或者答题的相关环节。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(3)库存不能出现问题,即不多扣也不少扣。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(4)整个秒杀活动过程持续10分钟。</span></p> </section> </section> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: #C00000;font-size: 17px;"><strong><span style="letter-spacing: 1px;font-family: Helvetica, Arial, sans-serif;">02. 性能指标预估</span></strong></span></h3> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">通过秒杀的需求描述可得出,当前秒杀活动主要需要预估三块的性能指标:存储容量、并发量、网络带宽。</span></p> <h4 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">1)存储容量</span></strong></h4> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">由于是秒杀活动,且参与的商品基本都是低价高性价比的,数量是非常有限的。所以,在订单存储上基本不用去过多考虑。</span></p> <h4 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">2)并发量</span></strong></h4> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">针对5000万用户平均每人访问2次,则并发量为每秒16.7万左右(5000w*2/10*60),在预留一部分,可以预估到每秒25万左右(也可以进行double下)。</span></p> <h4 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">3)网络带宽</span></strong></h4> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">在带宽方面,需要进行相关优化,采取数据传输越少越好,假设单条传输在0.5KB,则根据并发量预估网络带宽为:977Mb左右(25w*0.5KB=122MB*8bit=977Mb)。</span></p> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color:#c00000;"><strong><span style="letter-spacing: 1px;font-size: 17px;font-family: Helvetica, Arial, sans-serif;">03. 非功能性需求</span></strong></span></h3> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">做任何系统都要考虑非功能性需求,特别是公司的核心系统,</span><span style="color:#c00000;"><strong><span style="font-size: 15px;letter-spacing: 1px;font-family: Helvetica, Arial, sans-serif;">当前秒杀业务系统非功能性需求主要体现在如下几点:</span></strong></span></p> <section data-role="list"> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">高可用</span></strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">,在秒杀活动的整个持续期间内,都能对用户提供服务。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">高性能</span></strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">,让每个用户都能感受到极快的秒杀响应,不能出现大批量用户延迟较高的现象。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">可扩展</span></strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">,当流量比预期更高时,有平滑扩展的策略(也有部分产品设计成友好的拒绝策略)。</span></p></li> </ul> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;"></span></p> </section> </section> <section data-id="98856" data-tools="135编辑器"> <section style="margin: 10px auto;text-align: center;"> <section style="display: flex;flex-direction: column;justify-content: center;align-items: center;" hm_fix="213:645"> <section style="color:#c00000;font-size: 60px;" data-original-title="" title="" data-num="2"> 2 </section> <section style="width: 15%;height: 1px;background: rgb(192, 0, 0);margin-top: -35px;overflow: hidden;max-width: 15% !important;" data-width="15%"> <br> </section> </section> <section data-brushtype="text" style="font-size: 16px;letter-spacing: 1.5px;padding: 0px 10px;background: #fff;padding: 8px;transform: rotate(0deg);-webkit-transform: rotate(0deg);-moz-transform: rotate(0deg);-ms-transform: rotate(0deg);-o-transform: rotate(0deg);"> <strong>概要设计</strong> </section> </section> </section> <h2 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: rgb(59, 59, 59);font-size: 15px;letter-spacing: 1px;caret-color: red;font-family: Helvetica, Arial, sans-serif;">通过对秒杀业务的本身认知以及上面提到的秒杀业务需求,</span><span style="color:#c00000;"><strong><span style="font-size: 15px;letter-spacing: 1px;caret-color: red;font-family: Helvetica, Arial, sans-serif;">本次秒杀系统需要着重设计如下几点:</span></strong></span></h2> <section data-tools="135编辑器" data-id="86005"> <section data-autoskip="1" style="overflow: auto;font-size: 15px;margin-bottom: 16px;line-height: 1.45;padding: 16px;border-radius: 3px;overflow-wrap: normal;background-color: rgb(247, 247, 247);"> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(1)动静分离:如何保证用户在不刷新页面的情况下,依然能进行秒杀相关数据的获取且不会耽误秒杀活动的开始。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(2)流量分层,针对巨大流量,如何进行有效的防控,以免造成后台服务的不堪重负,以及如何避免前端页面的卡死。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(3)高可用:如何确保后台持续提供服务。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(4)扣减库存:如何有效扣减库存。</span></p> </section> </section> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: #C00000;font-size: 17px;"><strong><span style="letter-spacing: 1px;font-family: Helvetica, Arial, sans-serif;">01. 动静分离</span></strong></span></h3> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">动静分离是指,将静态页面与动态页面(或者静态数据与动态数据)解耦分离,用不同系统承载对应流量。这样可以提升整个服务访问性能和可维护性。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">商品秒杀页面的静态数据以及动态数据,均是不同的地方提供,如下图所示。</span></p> <p style="text-align:center;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: rgb(59, 59, 59);font-size: 15px;letter-spacing: 1px;caret-color: red;font-family: Helvetica, Arial, sans-serif;"><img class="rich_pages wxw-img" data-ratio="0.2601851851851852" src="/upload/1fed04ecf9b2eefcff04e2555bbfe732.png" data-type="png" data-w="1080" style="box-sizing: border-box;vertical-align: inherit;width: 100%;"> </span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">静态数据是指,页面中几乎不怎么变化的数据(即不依据用户的Cookie、基本信息、地域,及时间等各种属性来生成的数据),例如:</span></p> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">CSS和JavaScript中的静态文件。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">活动页中的HTML静态文件。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">图片等相关资源文件。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">其他与用户信息无关的静态数据。</span></p></li> </ul> </section> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">对于这种分离出来的静态数据可以进行缓存。在缓存之后,这些静态数据的访问效率就提高了,系统也更快了。可以使用代理服务器进行静态数据的缓存。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">动态数据是指,依据当前用户属性动态生成的数据,在浏览淘宝首页时,每个用户所看到的商品都是不一样的,这就是淘宝的“千人千面”——针对不同用户做不同的推荐;在百度搜索中是依据不同用户的输入条件,以及用户的习惯给出不同的结果页。这其中的数据就是动态数据。</span></p> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: #C00000;font-size: 17px;"><strong><span style="letter-spacing: 1px;font-family: Helvetica, Arial, sans-serif;">02. 流量分层</span></strong></span></h3> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">在“秒杀”业务中,商品价格具有强大的吸引力,所以会受到很多用户的关注,但是商品数量是有限的。所以,在千万的用户中可能只有100人能得到商品,对于系统来说,有90%以上的流量属于无效流量。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">“秒杀”业务希望有大量的用户来关注“秒杀”活动,但是在用户真正下单时又不能将这些流量全部放过,所以,需要设计一套高效的流量管控方案,来有效地控制请求流量,过滤掉没必要的流量。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">对于瞬时流量洪峰可以采用倒三角的分层级逐层控制方式,共分为CDN、反向代理(Nginx)、后端服务及DB这四个层级。接下来,就来看看每一层级是怎么控制流量的,如下图所示。</span></p> <p style="text-align:center;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="0.7956521739130434" data-type="png" data-w="690" src="/upload/ba5076e2325f77af0c3b6c971e5d8f6c.png" style="box-sizing: border-box;vertical-align: inherit;width: 261px;height: 208px;"></p> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: #C00000;font-size: 17px;"><strong><span style="letter-spacing: 1px;font-family: Helvetica, Arial, sans-serif;">03. 高可用</span></strong></span></h3> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">要想在整个“秒杀”活动持续期间内,依然能对用户提供良好的体验,则秒杀系统架构在设计时不能设计成单节点的架构。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">单节点是所有系统设计中的大忌,因为单节点系统意味着系统的不稳定性较高,可能会出现不可用的情况,会给企业带来直接的损失。在系统设计(特别是“秒杀”这类对高并发要求极高的系统)时,必须保证系统的高可用,如下图所示。</span></p> <p style="text-align:center;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="0.526431718061674" src="/upload/608a6e4e9765bcdd64e7083cf225d2b3.png" data-type="png" data-w="908" style="box-sizing: border-box;vertical-align: inherit;width: 392px;height: 206px;"></p> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: #C00000;font-size: 17px;"><strong><span style="letter-spacing: 1px;font-family: Helvetica, Arial, sans-serif;">04. 扣减库存</span></strong></span></h3> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">对于“秒杀”活动,通常,公司是不允许商品超卖(即下单成功的数量不能大于商品存存数量)的。一旦超卖,则会给公司造成损失。如果被恶意流量利用,则损失是巨大的。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">库存对于电商平台来说是一个重要的业务指标,所以在技术上需要合理设计扣减库存,不能出现“超卖”现象。通常,扣减库存常有以下3种方式:</span></p> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">下单扣库存:在用户下单后就扣减库存。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">支付扣库存:用户付完款后再扣减库存。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">预扣库存:在用户下完订单后,系统会为其锁定库存一段时间,在超过锁定时间后会自动释放锁定的库存。</span></p></li> </ul> </section> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color:#c00000;"><strong><span style="letter-spacing: 1px;font-size: 17px;font-family: Helvetica, Arial, sans-serif;">05. 系统架构设计</span></strong></span></h3> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">根据上面讨论,针对当前秒杀架构如下图所示。</span></p> <p style="text-align:center;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="1.0804162724692525" src="/upload/bf0020c9cfd743fc2ab53a743014bc94.png" data-type="png" data-w="1057" style="box-sizing: border-box;vertical-align: inherit;width: 361px;height: 390px;"></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color:#c00000;"><strong><span style="font-size: 15px;letter-spacing: 1px;font-family: Helvetica, Arial, sans-serif;">如上架构比较简洁,主要分为以下5层。</span></strong></span></p> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">用户层:用户端的展现部分,主要涉及商品的相关信息及当前“秒杀”活动的信息。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">CDN层:缓存“秒杀”活动的静态资源文件。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">负载均衡层:拦截请求及分发路由等。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">服务层:“秒杀”活动的具体交易的相关逻辑处理。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">基础设施层:数据存储、大数据计算及消息推送相关操作。</span></p></li> </ul> </section> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">其部署架构图如下:</span></p> <p style="text-align:center;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="0.4965437788018433" src="/upload/8a80900bd775d0afbfd60458dae7a1d0.png" data-type="png" data-w="868" style="box-sizing: border-box;vertical-align: inherit;width: 436px;height: 216px;"></p> <section data-id="98856" data-tools="135编辑器"> <section style="margin: 10px auto;text-align: center;"> <section style="display: flex;flex-direction: column;justify-content: center;align-items: center;"> <section style="color:#c00000;font-size: 60px;" data-original-title="" title="" data-num="3"> 3 </section> <section style="width: 15%;height: 1px;background: rgb(192, 0, 0);margin-top: -35px;overflow: hidden;max-width: 15% !important;" data-width="15%"> <br> </section> </section> <section data-brushtype="text" style="font-size: 16px;letter-spacing: 1.5px;padding: 0px 10px;background: #fff;padding: 8px;transform: rotate(0deg);-webkit-transform: rotate(0deg);-moz-transform: rotate(0deg);-ms-transform: rotate(0deg);-o-transform: rotate(0deg);" hm_fix="240:536"> <span style="font-size:17px;"><strong>详细设计</strong></span> </section> </section> </section> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: rgb(192, 0, 0);"><strong><span style="letter-spacing: 1px;caret-color: red;font-family: Helvetica, Arial, sans-serif;">01. 动静分离设计</span></strong></span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">实施动静分离架构可以采用“分而治之”的办法,即将动态数据和静态数据解耦,分别使用各自的架构系统来承载对应的流量:</span></p> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">对于静态数据,推荐缩短用户请求路径,因为路径越短,访问速度也就越快。另外,即尽可能将静态数据缓存起来。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">对于动态数据,一般用户端需要和服务端进行交互才能获取,所以,请求路径较长,访问速度会慢一点。下图展示了动静分离方案。</span></p></li> </ul> </section> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">静态数据访问速度很快,而动态数据访问速度较慢。那么试想下,可以将需要动态获取的数据给提前生成好,然后使用静态页面加速技术来访问吗?如果这样可以,那动态数据访问的速度就变快了。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">这样是可以的,需要用到比较流行的“页面静态化”技术。页面静态化技术是指,直接缓存HTTP连接,而不仅是缓存数据。如下图所示,代理服务器根据请求的URL直接将HTTP对应的响应头及响应消息体返回,流程简洁且高效。</span></p> <section> <p style="text-align:center;"><img class="rich_pages wxw-img" data-ratio="0.49428208386277" src="/upload/75c4e0156d707b690298645f9d52b5cb.png" data-type="png" data-w="787" style="box-sizing: border-box;vertical-align: inherit;width: 417px;height: 206px;"></p> </section> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color:#c00000;"><strong><span style="letter-spacing: 1px;font-size: 17px;font-family: Helvetica, Arial, sans-serif;">02. 流量分层设计</span></strong></span></h3> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">流量分层主要体现在对于CDN层、反向代理层、后端服务层以及数据层流量进行控制。</span></p> <h4 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">1)CDN层流量控制</span></strong></h4> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);text-shadow: rgb(250, 192, 143) 0.2em 0.2em 0.8em;font-family: Helvetica, Arial, sans-serif;">由动静分离技术可以想到:</span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">应尽量将尽可能多的数据提前生成,然后将其放入CDN节点缓存中(因为CDN层在物理架构上离用户比较近)。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">所以,如果绝大部分的流量都在这一层获取数据,则到达后端的流量会减少很多,如下图所示。</span></p> <p style="text-align:center;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="0.6380165289256199" src="/upload/7fda7bb181573899d48f6d7e47a100b6.png" data-type="png" data-w="605" style="box-sizing: border-box;vertical-align: inherit;width: 366px;height: 234px;"></p> <h4 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">2)反向代理层流量控制</span></strong></h4> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">在动静分离方案中,讲到通过“页面静态化技术”加速动态数据的获取,即提前将动态数据生成好,然后对其进行静态化处理。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">所以,这里就可以依据页面静态化加速技术,通过后端服务Job的方式定时提前生成前端需要静态的数据;然后,将其发送到内容分发服务上;最后,分发服务会将这些静态化页面数据分发到所有的反向代理服务器上,如下图所示。</span></p> <p style="text-align:center;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="0.25031446540880503" src="/upload/12605ac975b92be3a9742897531b83dd.png" data-type="png" data-w="795" style="box-sizing: border-box;vertical-align: inherit;width: 441px;height: 110px;"></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">在“秒杀”业务中,活动详情页上有一个倒计时的模块,用户可以看到当前“秒杀”活动还剩余多少时间开始。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);text-shadow: rgb(250, 192, 143) 0.2em 0.2em 0.8em;font-family: Helvetica, Arial, sans-serif;">这种逻辑简单的功能可以直接使用Nginx来实现:</span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">利用nginx-lua插件,使用lua脚本获取当前Nginx服务器的时间进行计算倒计时。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">另外,商品库存数据也可以通过Nginx直接访问分布式缓存来获取,如下图所示。</span></p> <p style="text-align:center;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="0.23796033994334279" src="/upload/ad4b09c6cef7a906eaa41f62d90e1d61.png" data-type="png" data-w="1059" style="box-sizing: border-box;vertical-align: inherit;width: 100%;"></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">“秒杀”业务中的商品价格很低,对于用户有很大的吸引力,所以可能会有人利用“秒杀器”进行不公平竞争,且有可能存在竞争对手恶意刷请求的情况。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">如果存在这样的情况,那本次活动就是有风险的,万一被恶意流量独占了库存,则会导致正常用户不能抢购商品,也有可能这种恶意的请求会对后端系统造成严重冲击,甚至造成后端系统瘫痪。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);text-shadow: rgb(250, 192, 143) 0.2em 0.2em 0.8em;font-family: Helvetica, Arial, sans-serif;">对于这种恶意请求,最好有一套机制能提前感知,并将恶意请求提前封存。</span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">可以在Nginx层中控制;也可以在Nginx中配置用户的访问频率(例如每分钟只能访问10次);还可以使用Lua脚本编写一些简单业务逻辑的接口,例如,通过调用接口直接封掉指定IP地址或UserAgent的请求。</span></p> <h4 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">3)后端服务层流量控制</span></strong></h4> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);text-shadow: rgb(250, 192, 143) 0.2em 0.2em 0.8em;font-family: Helvetica, Arial, sans-serif;">对于服务层的流量控制,有以下几点建议:</span></p> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">在程序开发上,代码独立,不要与平台其他项目一起。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">在部署时,应用独立部署,分散流量,避免不合适的流量影响主体业务。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">使用独立域名,或者按照一定的URL规则在反向代理层进行路由。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">做好系统保护和限流,进一步减少不必要的流量。</span></p></li> </ul> </section> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">当“到达系统中的请求数”明显大于“系统能够处理的最大请求数”时,可以直接拒绝这些多余的请求,直接返回“秒杀”活动结束的信息。例如,活动开始时的商品库存是100,目前库存只剩50了,如果“每台服务器待处理的请求数”已经超过“商品总库存数(100)”了,则可以直接终止掉多余的请求。</span></p> <h4 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">4)数据库层流量控制</span></strong></h4> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">对于请求到数据中的流量,写入的流量就是真正下单成功的流量,即需要扣减库存的动作。有如下建议:</span></p> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">如果不是临时的活动,则建议使用独立的数据库作为“秒杀”活动的数据库。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">将数据库配置成读写分离。</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">尝试去除行锁。</span></p></li> </ul> </section> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">对于数据库行锁的优化,可以通过将商品进行拆分来实现——增加ID,如下图所示。对于单一的“秒杀”活动这会得到显著效果。</span></p> <p style="text-align:center;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="0.7359307359307359" src="/upload/b77e8030a92f155e2ae60f82e345448d.png" data-type="png" data-w="693" style="box-sizing: border-box;vertical-align: inherit;width: 313px;height: 230px;"></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">从流量分层控制方案可看出,瞬时流量就像被漏斗过滤了似的,应尽量将数据和请求量一层一层地过滤掉。这种流量分层控制核心思想:在不同的层级中尽可能地过滤掉无效的请求,到达“倒三角”最末端的请求才是有效的请求。</span></p> <h4 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">5)高可用</span></strong></h4> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">在系统设计时想要做到高可用,避免单节点的一个小妙招:将服务无状态化。如果无法完全无状态化(如存储系统),则可以通过冗余多个备份节点的方案来避免单节点。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">由于篇幅原因,高可用此处就不再赘述,大家可以查看</span><span style="color:#c00000;"><strong><span style="font-size: 15px;letter-spacing: 1px;font-family: Helvetica, Arial, sans-serif;">《高并发系统实战派》</span></strong></span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">一书里面针对高并发系统的真实设计案例,毫无保留的分享出了企业级高并发系统实战。</span></p> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: #C00000;font-size: 17px;"><strong><span style="letter-spacing: 1px;font-family: Helvetica, Arial, sans-serif;">03. 扣减库存设计</span></strong></span></h3> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">由于在“秒杀”场景中商品一般优惠力度很大,对用户很具有吸引力,所以,在这种场景中使用“下单扣库存”方式更为合适。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">在“秒杀”场景中,大部分用户抱着“抢到就是赚到”的想法,基本都会去付款的,但如果真有竞争对手恶意下单不付款,那我们该怎么办?前面在流量管控中已经说到,可以对请求日志进行实时分析,让风控系统选择出恶意用户,然后将其封停。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">在“秒杀”场景中,通过流量分层控制可以分层管控大量的“读”请求。但是,依然会有很大的流量进入真正的下单逻辑。对于这么大的流量,除前面说的数据库隔离外,还需要进一步优化库存,否则数据库读/写依然是系统的瓶颈。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">接下来看看如何优化大流量“秒杀”场景中的库存数量扣减操作。</span></p> <h4 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">1)利用缓存技术</span></strong></h4> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">在“秒杀”场景中,如果只是一个扣减库存数量这样的简单流程,则可以先将库存数量直接放在缓存中,然后用分布式缓存(如Redis)的超高性能去应对这种瞬时流量洪峰下的系统挑战。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">使用缓存是存在一定风险的,比如,缓存节点出现了异常,那库存数量该怎么算?</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">使用缓存,不仅要考虑分布式缓存高可用(如何设计可以查看我的新书“高并发系统实战派”),还要考虑各种限流容错机制,以确保分布式缓存对外提供服务。</span></p> <h4 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">2)异步处理技术</span></strong></h4> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">如果是复杂的扣减库存(如涉及商品信息本身或牵连其他系统),则建议使用数据库进行库存数量的扣减,可以使用异步的方式来应对这种高并发的库存的更新。</span></p> <section data-tools="135编辑器" data-id="86005"> <section data-autoskip="1" style="overflow: auto;font-size: 15px;margin-bottom: 16px;line-height: 1.45;padding: 16px;border-radius: 3px;overflow-wrap: normal;background-color: rgb(247, 247, 247);"> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">①在用户下单时,不立刻生成订单,而是将所有订单依次放入队列。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">②下单模块依据自身的处理速度,从队列中依次获取订单进行“下单扣库存”操作。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">③在订单生成成功后,用户即可进行支付操作了。</span></p> </section> </section> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">这种方式是针对“秒杀”场景的,依据“先到先得”原则来保证公平公正,所有用户都可以抢购,然后等待订单处理,最后生成订单(如果库存不足,则生成订单失败)。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">这样的逻辑,对用户来说体验不是很差。</span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">具体排队逻辑如下图所示。</span></p> <p style="text-align:center;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;"><img class="rich_pages wxw-img" data-ratio="0.15458015267175573" src="/upload/5f3459f51981ba06a1cf0e25a3df938d.png" data-type="png" data-w="1048" style="box-sizing: border-box;vertical-align: inherit;width: 407px;height: 63px;"></span></p> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><img data-ratio="1" data-type="gif" data-w="1" src="/upload/bf5651c2a95fc53ac68a1807c94852b7.jpg" style="vertical-align:inherit;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;"> </span></p> <section data-id="98856" data-tools="135编辑器"> <section style="margin: 10px auto;text-align: center;"> <section style="display: flex;flex-direction: column;justify-content: center;align-items: center;"> <section style="color:#c00000;font-size: 60px;" data-original-title="" title="" data-num="4"> 4 </section> <section style="width: 15%;height: 1px;background: rgb(192, 0, 0);margin-top: -35px;overflow: hidden;max-width: 15% !important;" data-width="15%"> <br> </section> </section> <section data-brushtype="text" style="font-size: 16px;letter-spacing: 1.5px;padding: 0px 10px;background: #fff;padding: 8px;transform: rotate(0deg);-webkit-transform: rotate(0deg);-moz-transform: rotate(0deg);-ms-transform: rotate(0deg);-o-transform: rotate(0deg);" hm_fix="239:554"> <strong><span style="font-size:17px;">搭建千万级流量“秒杀”系统需要哪些技术</span></strong> </section> </section> </section> <h2 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="color: rgb(59, 59, 59);font-size: 15px;letter-spacing: 1px;caret-color: red;font-family: Helvetica, Arial, sans-serif;">前面介绍了千万级流量“秒杀”系统的基本架构、“秒杀”系统的设计原则、如何做动静分离方案和流量控制,以及扣减库存方面内容。这些都是设计高并发“秒杀”系统必须要考虑的。</span><br></h2> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">“秒杀”系统的流程并不复杂——只是一个“下单扣库存”的动作,但由于其独特的业务特点,所以在进行系统设计时不能大意。对于瞬时流量洪峰的高并发“秒杀”系统,我们需要什么技术呢?下面来总结一下。</span></p> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(1)数据的静态化的技术</span></strong></h3> <p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">用来应对高并发读的请求,主要涉及以下内容,这些在</span><span style="color:#c00000;"><strong><span style="font-size: 15px;letter-spacing: 1px;font-family: Helvetica, Arial, sans-serif;">《高并发系统实战派》</span></strong></span><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">一书中详细分享了真实使用场景已经技术方案:</span></p> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">各层级缓存的处理(即多级缓存的技术)</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">分布式缓存技术</span></p></li> </ul> </section> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(2)负载均衡反向代理技术</span></strong></h3> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">LVS</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">Nginx</span></p></li> </ul> </section> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(3)异步处理技术</span></strong></h3> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">消息队列技术</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">排队系统技术</span></p></li> </ul> </section> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(4)系统架构设计技术</span></strong></h3> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">系统模块化划分</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">微服务架构思想</span></p></li> </ul> </section> <h3 style="margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><strong><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">(5)系统监控技术</span></strong></h3> <section data-role="list"> <section data-role="list"> <ul class="list-paddingleft-1" style="padding-left: 30px;"> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">日志监控</span></p></li> <li><p style="text-align:justify;margin-left: 8px;margin-right: 8px;margin-bottom: 20px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(59, 59, 59);font-family: Helvetica, Arial, sans-serif;">服务监控</span></p></li> </ul> </section> </section> </section>