作者:微信小助手
<p style="white-space: normal;"><span style="color: rgb(171, 25, 66);"><strong><span style="font-size: 15px;text-align: left;"></span></strong></span></p> <p style="white-space: normal;text-align: left;"><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><span style="font-size: 15px;text-align: justify;">最近在工作中,遇到了这样一个业务场景:</span><span style="font-size: 15px;text-align: justify;">我们需要关注一个业务系统数据库中某几张表的数据,当数据发生新增或修改时,将它同步到另一个业务系统数据库中的表中。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">一提到数据库的同步,估计大家第一时间想到的就是基于 binlog 的主从复制了。但是放在我们的场景中,还有两个问题:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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> <ol class="list-paddingleft-2" style="list-style-type: decimal;"> <li style="white-space: normal;text-align: left;font-size: 15px;"> <section style="white-space: normal;text-align: left;"> <span style="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;font-size: 15px;">并不是需要复制所有表的数据,复制对象只有少量的几张表;</span> </section></li> <li style="white-space: normal;text-align: left;font-size: 15px;"> <section style="white-space: normal;text-align: left;"> <span style="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;font-size: 15px;">比较麻烦的是两个业务系统数据库表结构可能不一致。例如,要同步数据库 1 的 A表中的某些字段到数据库 2 的 B 表中,在这一过程中,A 表和 B 表的字段并不是完全相同。</span> </section></li> </ol> <section style="white-space: normal;text-align: left;"> <br> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;">这样的话,我们只能通过代码的方式,首先获取到数据库 1 表中数据的变动,再通过手动映射的方式,插入到数据库 2 的表中。但是,获取变动数据的这一过程,还是离不开 binlog,因此我们就需要在代码中对 binlog 进行一下监控。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">先说结论,我们最终使用了一个开源工具 mysql-binlog-connector-java,用来监控 binlog 变化并获取数据,获取数据后再手动插入到另一个库的表中,基于它来实现了数据的同步。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">这个工具的git项目地址如下:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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 style="white-space: normal;text-align: left;"><span style="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;font-size: 12px;color: rgb(136, 136, 136);">https://github.com/shyiko/mysql-binlog-connector-java</span></p> <p style="white-space: normal;text-align: left;"><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;"><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 style="font-size: 15px;">MySQL</span> 的 binlog。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">binlog 是一个二进制文件,它保存在磁盘中,用来记录数据库表结构变更、表数据修改的二进制日志。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;"><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;"><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 style="font-size: 15px;">MySQL</span><span style="font-size: 15px;"> </span>服务已经启用了binlog:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">show</span> <span class="code-snippet__keyword">variables</span> <span class="code-snippet__keyword">like</span> <span class="code-snippet__string">'log_bin'</span>;</span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;">如果为值为 OFF 表示没有启用,那么需要首先启用 binlog,修改配置文件:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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="ini"><code><span class="code-snippet_outer"><span class="code-snippet__attr">log_bin</span>=mysql-bin</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">binlog-format</span>=ROW</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">server-id</span>=<span class="code-snippet__number">1</span></span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="white-space: normal;text-align: left;"> <li style="white-space: normal;text-align: left;"> <section style="white-space: normal;text-align: left;"> <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;">在配置文件中加入了 log_bin 配置项后,表示启用了 binlog;</span> </section></li> <li style="white-space: normal;text-align: left;"> <section style="white-space: normal;text-align: left;"> <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;">binlog-format 是 binlog 的日志格式,支持三种类型,分别是 STATEMENT、ROW、MIXED,我们在这里使用 ROW 模式;</span> </section></li> <li style="white-space: normal;text-align: left;"> <section style="white-space: normal;text-align: left;"> <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;">server-id 用于标识一个 SQL 语句是从哪一个 server 写入的,这里一定要进行设置,否则我们在后面的代码中会无法正常监听到事件。</span> </section></li> </ul> <section style="white-space: normal;text-align: left;"> <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> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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 style="font-size: 15px;">MySQL</span> 服务。再次查看是否启用 binlog,返回为 ON 表示已经开启成功。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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 项目中,首先引入 Maven 依赖:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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>com.github.shyiko<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>mysql-binlog-connector-java<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">version</span>></span>0.21.0<span class="code-snippet__tag"></<span class="code-snippet__name">version</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> <pre data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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 style="white-space: normal;text-align: left;"><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 style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></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__keyword">public</span> static void main(String[] args) {</span></code><code><span class="code-snippet_outer"> BinaryLogClient client = new BinaryLogClient(<span class="code-snippet__string">"127.0.0.1"</span>, <span class="code-snippet__number">3306</span>, <span class="code-snippet__string">"hydra"</span>, <span class="code-snippet__string">"123456"</span>);</span></code><code><span class="code-snippet_outer"> client.setServerId(<span class="code-snippet__number">2</span>);</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> client.registerEventListener(event -> {</span></code><code><span class="code-snippet_outer"> EventData <span class="code-snippet__keyword">data</span> = event.getData();</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">if</span> (<span class="code-snippet__keyword">data</span> instanceof TableMapEventData) {</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(<span class="code-snippet__string">"Table:"</span>);</span></code><code><span class="code-snippet_outer"> TableMapEventData tableMapEventData = (TableMapEventData) <span class="code-snippet__keyword">data</span>;</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(tableMapEventData.getTableId()+<span class="code-snippet__string">": ["</span>+tableMapEventData.getDatabase() + <span class="code-snippet__string">"-"</span> + tableMapEventData.getTable()+<span class="code-snippet__string">"]"</span>);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">if</span> (<span class="code-snippet__keyword">data</span> instanceof UpdateRowsEventData) {</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(<span class="code-snippet__string">"Update:"</span>);</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(<span class="code-snippet__keyword">data</span>.toString());</span></code><code><span class="code-snippet_outer"> } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (<span class="code-snippet__keyword">data</span> instanceof WriteRowsEventData) {</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(<span class="code-snippet__string">"Insert:"</span>);</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(<span class="code-snippet__keyword">data</span>.toString());</span></code><code><span class="code-snippet_outer"> } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (<span class="code-snippet__keyword">data</span> instanceof DeleteRowsEventData) {</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(<span class="code-snippet__string">"Delete:"</span>);</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(<span class="code-snippet__keyword">data</span>.toString());</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> });</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">try</span> {</span></code><code><span class="code-snippet_outer"> client.connect();</span></code><code><span class="code-snippet_outer"> } <span class="code-snippet__keyword">catch</span> (IOException e) {</span></code><code><span class="code-snippet_outer"> e.printStackTrace();</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <p style="white-space: normal;text-align: left;"><br><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;"><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;">首先,创建一个 BinaryLogClient 客户端对象,初始化时需要传入 <span style="font-size: 15px;">M</span><span style="font-size: 15px;">ySQL</span><span style="font-size: 15px;"> </span>的连接信息。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">创建完成后,给客户端注册一个监听器来实现它对 binlog 的监听和解析。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">在监听器中,我们暂时只对 4 种类型的事件数据进行了处理:除了 WriteRowsEventData、DeleteRowsEventData、UpdateRowsEventData 对应增删改操作类型的事件数据外,还有一个 TableMapEventData 类型的数据。包含了表的对应关系,在后面的例子中再具体说明。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">在这里,客户端监听到的是数据库级别的所有事件,并且可以监听到表的 DML 语句和 DDL 语句,所以我们只需要处理我们关心的事件数据就行,否则会收到大量的冗余数据。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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> <pre data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><p><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> <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="makefile"><code><span class="code-snippet_outer">com.github.shyiko.mysql.binlog.BinaryLogClient openChannelToBinaryLogStream</span></code><code><span class="code-snippet_outer"><br></span></code><code><span class="code-snippet_outer"><span class="code-snippet__section">信息: Connected to 127.0.0.1:3306 at mysql-bin.000002/1046 (sid:2, cid:10)</span></span></code></pre> </section></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">连接 MySQL 的 binlog 成功。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">接下来,我们在数据库中插入一条数据,这里操作的数据库名字是 tenant,表是 dept:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">insert</span> <span class="code-snippet__keyword">into</span> dept <span class="code-snippet__keyword">VALUES</span>(<span class="code-snippet__number">8</span>,<span class="code-snippet__string">"人力"</span>,<span class="code-snippet__string">""</span>,<span class="code-snippet__string">"1"</span>);</span></code></pre> </section> <p style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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 style="white-space: normal;text-align: left;"><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> <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="makefile"><code><span class="code-snippet_outer"><span class="code-snippet__section">Table:</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__section">108: [tenant-dept]</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__section">Insert:</span></span></code><code><span class="code-snippet_outer">WriteRowsEventData{tableId=108, includedColumns={0, 1, 2, 3}, rows=[</span></code><code><span class="code-snippet_outer">[8, 人力, , 1]</span></code><code><span class="code-snippet_outer">]}</span></code></pre> </section> <p style="white-space: normal;text-align: left;"><br><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;"><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;">我们监听到的事件类型数据有两类,第一类是 TableMapEventData,通过它可以获取操作的数据库名称、表名称以及表的 id。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">之所以我们要监听这个事件,是因为之后监听的实际操作中返回数据中包含了表的 id,而没有表名等信息,所以如果我们想知道具体的操作是在哪一张表的话,就要先维护一个id与表的对应关系。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">第二个打印出来的监听事件数据是 WriteRowsEventData,其中记录了 insert 语句作用的表、插入涉及到的列以及实际插入的数据。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">另外,如果我们只需要对特定的一张或几张表进行处理的话,也可以提前设置表的名单,在这里根据表 id 到表名的映射关系,实现数据的过滤。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">接下来,我们再执行一条 update 语句:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">update</span> dept <span class="code-snippet__keyword">set</span> tenant_id=<span class="code-snippet__number">3</span> <span class="code-snippet__keyword">where</span> <span class="code-snippet__keyword">id</span>=<span class="code-snippet__number">8</span> <span class="code-snippet__keyword">or</span> <span class="code-snippet__keyword">id</span>=<span class="code-snippet__number">9</span></span></code></pre> </section> <pre data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br><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></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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 style="white-space: normal;text-align: left;"><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> <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="sql"><code><span class="code-snippet_outer">Table:</span></code><code><span class="code-snippet_outer">108: [tenant-dept]</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">Update</span>:</span></code><code><span class="code-snippet_outer">UpdateRowsEventData{tableId=<span class="code-snippet__number">108</span>, includedColumnsBeforeUpdate={<span class="code-snippet__number">0</span>, <span class="code-snippet__number">1</span>, <span class="code-snippet__number">2</span>, <span class="code-snippet__number">3</span>}, includedColumns={<span class="code-snippet__number">0</span>, <span class="code-snippet__number">1</span>, <span class="code-snippet__number">2</span>, <span class="code-snippet__number">3</span>}, <span class="code-snippet__keyword">rows</span>=[</span></code><code><span class="code-snippet_outer">{<span class="code-snippet__keyword">before</span>=[<span class="code-snippet__number">8</span>, 人力, , <span class="code-snippet__number">1</span>], <span class="code-snippet__keyword">after</span>=[<span class="code-snippet__number">8</span>, 人力, , <span class="code-snippet__number">3</span>]},</span></code><code><span class="code-snippet_outer">{<span class="code-snippet__keyword">before</span>=[<span class="code-snippet__number">9</span>, 人力, , <span class="code-snippet__number">1</span>], <span class="code-snippet__keyword">after</span>=[<span class="code-snippet__number">9</span>, 人力, , <span class="code-snippet__number">3</span>]}</span></code><code><span class="code-snippet_outer">]}</span></code></pre> </section> <p style="white-space: normal;text-align: left;"><br><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;"><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;">在执行 update 语句时,可能会作用于多条数据,因此在实际修改的数据中,可能包含多行记录,这一点体现在上面的 rows 中,包含了 id 为 8 和 9 的两条数据。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">最后,再执行一条 delete 语句:</span></p> <p style="white-space: normal;text-align: left;"><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> <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="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">delete</span> <span class="code-snippet__keyword">from</span> dept <span class="code-snippet__keyword">where</span> tenant_id=<span class="code-snippet__number">3</span></span></code></pre> </section> <p style="white-space: normal;text-align: left;"><br><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;"><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;">控制台打印如下,rows中同样返回了生效的两条数据:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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> <pre data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"> <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="makefile"><code><span class="code-snippet_outer"><span class="code-snippet__section">Table:</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__section">108: [tenant-dept]</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__section">Delete:</span></span></code><code><span class="code-snippet_outer">DeleteRowsEventData{tableId=108, includedColumns={0, 1, 2, 3}, rows=[</span></code><code><span class="code-snippet_outer">[8, 人力, , 3],</span></code><code><span class="code-snippet_outer">[9, 人力, , 3]</span></code><code><span class="code-snippet_outer">]}</span></code></pre> </section><p><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></pre> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;"><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;">以 update 操作为例,我们要对提取的数据后进行一下处理,更改上面例子中的方法:</span></p> <p style="white-space: normal;text-align: left;"><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 style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></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__keyword">if</span> (<span class="code-snippet__keyword">data</span> instanceof UpdateRowsEventData) {</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(<span class="code-snippet__string">"Update:"</span>);</span></code><code><span class="code-snippet_outer"> UpdateRowsEventData updateRowsEventData = (UpdateRowsEventData) <span class="code-snippet__keyword">data</span>;</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">for</span> (Map.Entry<Serializable[], Serializable[]> row : updateRowsEventData.getRows()) {</span></code><code><span class="code-snippet_outer"> List<Serializable> entries = Arrays.asList(row.getValue());</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(entries);</span></code><code><span class="code-snippet_outer"> JSONObject dataObject = getDataObject(entries);</span></code><code><span class="code-snippet_outer"> System.<span class="code-snippet__keyword">out</span>.println(dataObject);</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <p style="white-space: normal;text-align: left;"><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><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;">在将 data 强制转换为 UpdateRowsEventData 后,可以使用 getRows 方法获取到更新的行数据,并且能够取到每一列的值。</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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;"><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;">之后,调用了一个自己实现的 getDataObject 方法,用它来实现数据到列的绑定过程:</span></p> <p style="white-space: normal;text-align: left;"><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> <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="powershell"><code><span class="code-snippet_outer">private static JSONObject getDataObject(List message) {</span></code><code><span class="code-snippet_outer"> JSONObject resultObject = new JSONObject();</span></code><code><span class="code-snippet_outer"> String format = <span class="code-snippet__string">"{\"</span>id\<span class="code-snippet__string">":\"</span><span class="code-snippet__number">0</span>\<span class="code-snippet__string">",\"</span>dept_name\<span class="code-snippet__string">":\"</span><span class="code-snippet__number">1</span>\<span class="code-snippet__string">",\"</span>comment\<span class="code-snippet__string">":\"</span><span class="code-snippet__number">2</span>\<span class="code-snippet__string">",\"</span>tenant_id\<span class="code-snippet__string">":\"</span><span class="code-snippet__number">3</span>\<span class="code-snippet__string">"}"</span>;</span></code><code><span class="code-snippet_outer"> JSONObject json = JSON.parseObject(format);</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">for</span> (String key : json.keySet()) {</span></code><code><span class="code-snippet_outer"> resultObject.put(key, message.get(json.getInteger(key)));</span></code><code><span class="code-snippet_outer"> }</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">return</span> resultObject;</span></code><code><span class="code-snippet_outer">}</span></code></pre> </section> <p style="white-space: normal;text-align: left;"><br><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;"><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;">在 format 字符串中,提前维护了一个数据库表的字段顺序的字符串,标识了每个字段位于顺序中的第几个位置。通过上面这个函数,能够实现数据到列的填装过程,我们再执行一条 update 语句来查看一下结果:</span></p> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><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="sql"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">update</span> dept <span class="code-snippet__keyword">set</span> tenant_id=<span class="code-snippet__number">3</span>,<span class="code-snippet__keyword">comment</span>=<span class="code-snippet__string">"1"</span> <span class="code-snippet__keyword">where</span> <span class="code-snippet__keyword">id</span>=<span class="code-snippet__number">8</span></span></code></pre> </section> <p style="white-space: normal;text-align: left;"><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;"><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;"><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="cs"><code><span class="code-snippet_outer">Table:</span></code><code><span class="code-snippet_outer"><span class="code-snippet__number">108</span>: [tenant-dept]</span></code><code><span class="code-snippet_outer">Update:</span></code><code><span class="code-snippet_outer">[<span class="code-snippet__meta">8, 人力, 1, 3</span>]</span></code><code><span class="code-snippet_outer">{<span class="code-snippet__string">"tenant_id"</span>:<span class="code-snippet__number">3</span>,<span class="code-snippet__string">"dept_name"</span>:<span class="code-snippet__string">"人力"</span>,<span class="code-snippet__string">"comment"</span>:<span class="code-snippet__string">"1"</span>,<span class="code-snippet__string">"id"</span>:<span class="code-snippet__number">8</span>}</span></code></pre> </section> <p data-tool="mdnice编辑器" style="white-space: normal;text-align: left;">可以看到,将修改后的这一条记录中的属性填装到了它对应的列中,之后我们再根据具体的业务逻辑,就可以根据字段名取出数据,将数据同步到其他的表了。</p>
作者:微信小助手
<section style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"> <br> </section> <p style="white-space: normal;"><span style="font-size: 15px;">我是一个苦逼的运维,有一次老板过来找我。</span></p> <section style="white-space: normal;text-align: left;"> <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> <section style="white-space: normal;text-align: center;"> <img data-ratio="0.5171232876712328" src="/upload/db0d0115164425d7e5a96c0baaf5e677.png" data-type="png" data-w="584" height="235px" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 456px !important;visibility: visible !important;" width="456px"> </section> <section style="white-space: normal;text-align: left;"> <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> <section style="white-space: normal;text-align: left;"> <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;">老板:现在有四个 Redis 节点摆在你面前,一主三从,你负责盯着点,主节点挂了你赶紧想办法拿从节点顶上来,交给你了!</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> </section> <section style="white-space: normal;text-align: left;"> <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> <section style="white-space: normal;text-align: center;"> <img class="__bg_gif" data-ratio="0.5666666666666667" src="/upload/ac9fe8f6d2ab6ed6347c49a2190c5810.png" data-type="gif" data-w="540" height="249px" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 440px !important;visibility: visible !important;" width="440px"> </section> <section style="white-space: normal;text-align: left;"> <br> </section> <section style="white-space: normal;text-align: left;"> <span style="font-size: 15px;text-align: justify;">这还不简单!</span> </section> <section style="white-space: normal;text-align: left;"> <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> <section style="white-space: normal;text-align: left;"> <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;">首先我先分别连上这四台 Redis 节点。</span> </section> <section style="white-space: normal;text-align: left;"> <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> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="white-space: normal;text-align: left;"> <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="css"><code><span class="code-snippet_outer"><span class="code-snippet__selector-tag">redis-cli</span> <span class="code-snippet__selector-tag">-h</span> 10<span class="code-snippet__selector-class">.232</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.0</span> <span class="code-snippet__selector-tag">-p</span> 6379</span></code><code><span class="code-snippet_outer"><span class="code-snippet__selector-tag">redis-cli</span> <span class="code-snippet__selector-tag">-h</span> 10<span class="code-snippet__selector-class">.232</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.1</span> <span class="code-snippet__selector-tag">-p</span> 6379</span></code><code><span class="code-snippet_outer"><span class="code-snippet__selector-tag">redis-cli</span> <span class="code-snippet__selector-tag">-h</span> 10<span class="code-snippet__selector-class">.232</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.2</span> <span class="code-snippet__selector-tag">-p</span> 6379</span></code><code><span class="code-snippet_outer"><span class="code-snippet__selector-tag">redis-cli</span> <span class="code-snippet__selector-tag">-h</span> 10<span class="code-snippet__selector-class">.232</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.3</span> <span class="code-snippet__selector-tag">-p</span> 6379</span></code></pre> </section> <pre data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></pre> </section> <pre style="white-space: normal;text-align: left;"> <section style="white-space: normal;text-align: left;"> <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;">然后每隔 1s 分别发送 redis 专属的命令 <strong>PING</strong>。<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></span> </section> <section style="white-space: normal;text-align: left;"> <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></pre> <section style="white-space: normal;text-align: center;"> <img class="__bg_gif" data-ratio="0.5666666666666667" src="/upload/c12a29b5cf54846f5bc54bc5dcc041a6.png" data-type="gif" data-w="540" height="233px" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 412px !important;visibility: visible !important;" width="412px"> </section> <section style="white-space: normal;text-align: left;"> <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> <section style="white-space: normal;text-align: left;"> <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;">我就这样一直不断地发送着 PING 命令,日复一日。</span> </section> <section style="white-space: normal;text-align: left;"> <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> <section style="white-space: normal;text-align: left;"> <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;">终于有一天,发送给主节点的 PING 命令收到了无效回复!</span> </section> <section style="white-space: normal;text-align: left;"> <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> <section style="white-space: normal;text-align: center;"> <img class="__bg_gif" data-ratio="0.5666666666666667" src="/upload/18f5610774305b3b218b1e042ef305ac.png" data-type="gif" data-w="540" height="230px" style="box-sizing: border-box !important;overflow-wrap: break-word !important;width: 407px !important;visibility: visible !important;" width="407px"> </section> <section style="white-space: normal;text-align: left;"> <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> <section style="white-space: normal;text-align: left;"> <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> </section> <section style="white-space: normal;text-align: left;"> <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> <section style="white-space: normal;text-align: left;"> <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> </section> <section style="white-space: normal;text-align: left;"> <br> </section> <p><span style="color: rgb(171, 25, 66);"><strong><span style="color: rgb(171, 25, 66);font-size: 15px;text-align: left;">1. 选择一个从节点,将其变为主节点</span></strong></span></p> <section style="white-space: normal;text-align: left;"> <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </section> <section style="white-space: normal;text-align: left;"> <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;">选哪个节点好呢?先别管那么多了,随便选一个,就 10.232.0.3:6379 这个吧!</span> </section> <section style="white-space: normal;text-align: left;"> <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> </section> <section style="white-space: normal;text-align: left;"> <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> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="white-space: normal;text-align: left;"> <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="css"><code><span class="code-snippet_outer">10<span class="code-snippet__selector-class">.232</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.3</span><span class="code-snippet__selector-pseudo">:6379</span>> <span class="code-snippet__selector-tag">slaveof</span> <span class="code-snippet__selector-tag">no</span> <span class="code-snippet__selector-tag">one</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__selector-tag">OK</span></span></code></pre> </section> <pre data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></pre> </section> <section style="white-space: normal;text-align: left;"> <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> </section> <section style="white-space: normal;text-align: left;"> <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> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="white-space: normal;text-align: left;"> <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="css"><code><span class="code-snippet_outer">10<span class="code-snippet__selector-class">.232</span><span class="code-snippet__selector-class">.0</span><span class="code-snippet__selector-class">.3</span><span class="code-snippet__selector-pseudo">:6379</span>> <span class="code-snippet__selector-tag">info</span></span></code><code><span class="code-snippet_outer">...</span></code><code><span class="code-snippet_outer"><span class="code-snippet__selector-tag">role</span><span class="code-snippet__selector-pseudo">:slave</span></span></code></pre> </section> <pre data-tool="mdnice编辑器" style="white-space: normal;text-align: left;"><br></pre> </section> <section style="white-space: normal;text-align: left;"> <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> </section> <section style="white-space: normal;text-align: left;"> <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> <sec
作者:微信小助手
<h2 data-tool="mdnice编辑器" style="font-weight: bold;padding-top: 30px;padding-bottom: 30px;color: rgb(19, 92, 224);font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;">前言</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">现在已经有很多公司在使用HikariCP了,HikariCP还成为了SpringBoot默认的连接池,伴随着SpringBoot和微服务,HikariCP 必将迎来广泛的普及。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;margin-top: 16px;">下面陈某带大家从源码角度分析一下HikariCP为什么能够被Spring Boot 青睐,文章目录如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="0.43151969981238275" src="/upload/c6b79aa323679e431f3b061a5e37b40.png" data-type="png" data-w="1066" style="display: block;margin-right: auto;margin-left: auto;border-radius: 3px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> <figcaption style="text-align: center;color: #888;opacity: .6;margin-top: 12px;font-size: 12px;"> 目录 </figcaption> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;padding-top: 30px;padding-bottom: 30px;color: rgb(19, 92, 224);font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;">零、类图和流程图</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">开始前先来了解下HikariCP获取一个连接时类间的交互流程,方便下面详细流程的阅读。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;margin-top: 16px;">获取连接时的类间交互:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="0.634765625" src="/upload/be3ab8a522ef2d4a8a180b9072c845d.png" data-type="png" data-w="1024" style="display: block;margin-right: auto;margin-left: auto;border-radius: 3px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> <figcaption style="text-align: center;color: #888;opacity: .6;margin-top: 12px;font-size: 12px;"> 图1 </figcaption> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;padding-top: 30px;padding-bottom: 30px;color: rgb(19, 92, 224);font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;">一、主流程1:获取连接流程</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">HikariCP获取连接时的入口是<code style="font-size: 14px;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;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">HikariDataSource</code>里的<code style="font-size: 14px;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;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">getConnection</code>方法,现在来看下该方法的具体流程:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="0.7890625" src="/upload/a3a6d4f9827d86f9b29d1a810b5f2ee1.png" data-type="png" data-w="1024" style="display: block;margin-right: auto;margin-left: auto;border-radius: 3px;box-shadow: rgb(210, 210, 210) 0em 0em 0.5em 0px;font-size: 17px;"> <figcaption style="text-align: center;color: #888;opacity: .6;margin-top: 12px;font-size: 12px;"> 主流程1 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;color: black;line-height: 2;">上述为HikariCP获取连接时的流程图,由图1可知,每个<code style="font-size: 14px;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;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">datasource</code>对象里都会持有一个<code style="font-size: 14px;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;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">HikariPool</code>对象,记为pool,初始化后的datasource对象pool是空的,所以第一次<code style="font-size: 14px;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;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">getConnection</code>的时候会进行<code style="font-size: 14px;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;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">实例化pool</code>属性(参考<code style="font-size: 14px;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;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">主流程1</code>),初始化的时候需要将当前datasource里的<code style="font-size: 14px;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;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">config属性</code>传过去,用于pool的初始化,最终标记<code style="font-size: 14px;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;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">sealed</code>,然后根据pool对象调用<code style="font-size: 14px;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;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">getConnection</code>方法(参考<code style="font-size: 14px;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;color: rgb(19, 148, 216);padding: 2px 6px;word-break: normal;">流程1.1</code>),获取成功后返回连接对象。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;padding-top: 30px;padding-bottom: 30px;color: rgb(19, 92, 224);font-size: 20px;"><span style="display: none;"></span><span style="border-left: 4px solid;padding-left: 10px;">二、主流程2:初始化池对象</span></h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 0px 0.5em;"> <img data-ratio="1.1531531531531531" src="/upload/ab1dff39f8867cf67ec04e814b303307.png" data-type="png" data-w="
作者:微信小助手
<p style="text-align: center;"><img class="rich_pages" src="/upload/92ba491e0ed1b65ddc514364ea136df8.png" data-cropx1="0" data-cropx2="900.0000000000001" data-cropy1="0" data-cropy2="469.0140845070423" data-galleryid="" data-ratio="0.5211111111111111" data-s="300,640" src="https://mmbiz.qpic.cn/mmbiz_jpg/PxMzT0Oibf4gOa4vJmA3Iz2aqv8C4e699qibG4OFemkoiaP7pAkgs87ibAeWW4KIwXA5Acia5xbOicQbkyHV38n5yicvA/640?wx_fmt=jpeg" data-type="jpeg" data-w="900" style="width: 568px;height: 296px;"></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">今天我们来聊一聊在基于SpringBoot前后端分离开发模式下,如何<span style="font-weight: 700;color: rgb(248, 57, 41);">友好的</span>返回统一的标准格式以及如何优雅的处理全局异常。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">首先我们来看看为什么要返回统一的标准格式?</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">为什么要对SpringBoot返回统一的标准格式</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在默认情况下,SpringBoot的返回格式常见的有三种:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">第一种:返回 String</span></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/hNWCQ9bibbzEGR101C4cWRaztcjpBzcAujhRbku5L5jENGfXkHViafIGjgvNe19ejticfE2WjfibyHk9d4gLKnrkT4iadfnTPE95m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@GetMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/hello"</span>)<br><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> String <span style="color: #4078f2;line-height: 26px;">getStr</span><span style="line-height: 26px;">()</span></span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> <span style="color: #50a14f;line-height: 26px;">"hello,javadaily"</span>;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">此时调用接口获取到的返回值是这样:</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/hNWCQ9bibbzEGR101C4cWRaztcjpBzcAujhRbku5L5jENGfXkHViafIGjgvNe19ejticfE2WjfibyHk9d4gLKnrkT4iadfnTPE95m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">hello,javadaily<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">第二种:返回自定义对象</span></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/hNWCQ9bibbzEGR101C4cWRaztcjpBzcAujhRbku5L5jENGfXkHViafIGjgvNe19ejticfE2WjfibyHk9d4gLKnrkT4iadfnTPE95m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #4078f2;line-height: 26px;">@GetMapping</span>(<span style="color: #50a14f;line-height: 26px;">"/aniaml"</span>)<br><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> Aniaml <span style="color: #4078f2;line-height: 26px;">getAniaml</span><span style="line-height: 26px;">()</span></span>{<br> Aniaml aniaml = <span style="color: #a626a4;line-height: 26px;">new</span> Aniaml(<span style="color: #986801;line-height: 26px;">1</span>,<span style="color: #50a14f;line-height: 26px;">"pig"</span>);<br> <span style="color: #a626a4;line-height: 26px;">return</span> aniaml;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">此时调用接口获取到的返回值是这样:</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/hNWCQ9bibbzEGR101C4cWRaztcjpBzcAujhRbku5L5jENGfXkHViafIGjgvNe19ejticfE2WjfibyHk9d4gLKnrkT4iadfnTPE95m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">{<br> <span style="color: #50a14f;line-height: 26px;">"id"</span>: <span style="color: #986801;line-height: 26px;">1</span>,<br> <span style="color: #50a14f;line-height: 26px;">"name"</span>: <span style="color: #50a14f;line-height: 26px;">"pig"</span><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-weight: 700;color: rgb(248, 57, 41);">第三种:接口异常</span></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/hNWCQ9bibbzEGR101C4cWRaztcjpBzcAujhRbku5L5jENGfXkHViafIGjgvNe19ejticfE2WjfibyHk9d4gLKnrkT4iadfnTPE95m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">@GetMapping(<span style="color: #50a14f;line-height: 26px;">"/error"</span>)<br>public int <span style="line-height: 26px;"><span style="color: #4078f2;line-height: 26px;">error</span></span>(){<br> int i = 9/0;<br> <span style="color: #c18401;line-height: 26px;">return</span> i;<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">此时调用接口获取到的返回值是这样:</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/hNWCQ9bibbzEGR101C4cWRaztcjpBzcAujhRbku5L5jENGfXkHViafIGjgvNe19ejticfE2WjfibyHk9d4gLKnrkT4iadfnTPE95m/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">{<br> <span style="color: #986801;line-height: 26px;">"timestamp"</span>: <span style="color: #50a14f;line-height: 26px;">"2021-07-08T08:05:15.423+00:00"</span>,<br> <span style="color: #986801;line-height: 26px;">"status"</span>: <span style="color: #986801;line-height: 26px;">500</span>,<br> <span style="color: #986801;line-height: 26px;">"error"</span>: <span style="color: #50a14f;line-height: 26px;">"Internal Server Error"</span>,<br> <span style="color: #986801;line-height: 26px;">"path"</span>: <span style="color: #50a14f;line-height: 26px;">"/wrong"</span><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">基于以上种种情况,如果你和前端开发人员联调接口她们就会很懵逼,由于我们没有给他一个统一的格式,前端人员不知道如何处理返回值。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">还有甚者,有的同学比如小张喜欢对结果进行封装,他使用了Result对象,小王也喜欢对结果进行包装,但是他却使用的是Response对象,当出现这种情况时我相信前端人员一定会抓狂的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">所以我们项目中是需要定义一个统一的标准返回格式的。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">定义返回标准格式</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">一个标准的返回格式至少包含3部分:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;ma
作者:微信小助手
<p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">大家好,我是飘渺!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">生产环境网关模块偶发的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">OutOfDirectMemoryError</code> 错误排查起来困难且曲折,2021-02-05 号也出现过此问题,起初以为是 JVM 堆内存过小 (当时是 2g) 导致,后调整到 8g (2 月 5 号调整)。但是经过上次调整后 5 月 7 号又出现此问题,于是猜测可能是由于网关模块存在内存泄露导致。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">症状</span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">报错详情</span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">网关模块偶现 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">OutOfDirectMemoryError</code> 错误,两次问题出现相隔大概 3 个月。两次发生的时机都是正在大批量接收数据 (大约 500w),TPS 60 左右,网关服务波动不大,完全能抗住,按理不应该出现此错误。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">详细报错信息如下:</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/hNWCQ9bibbzEGR101C4cWRQEqVsEAeicmFn0dcjcbPMnVuyicBaqXh9PyhGLBGJkaeTSPBeJwibs5ds4MNEkPtGRIRgria8KD7OM8/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #986801;line-height: 26px;">2021</span>-<span style="color: #986801;line-height: 26px;">05</span>-<span style="color: #986801;line-height: 26px;">06</span> <span style="color: #986801;line-height: 26px;">13</span>:<span style="color: #986801;line-height: 26px;">44</span>:<span style="color: #986801;line-height: 26px;">18</span>|WARN |[reactor-http-epoll-<span style="color: #986801;line-height: 26px;">5</span>]|[AbstractChannelHandlerContext.java : <span style="color: #986801;line-height: 26px;">311</span>]|An exception <span style="color: #50a14f;line-height: 26px;">'io.netty.util.internal.OutOfDirectMemoryError: failed to allocate 16384 byte(s) of direct memory (used: 8568993562, max: 8589934592)'</span> [enable DEBUG level <span style="color: #a626a4;line-height: 26px;">for</span> full stacktrace] was thrown by a user handler<span style="color: #50a14f;line-height: 26px;">'s exceptionCaught() method while handling the following exception:<br>io.netty.util.internal.OutOfDirectMemoryError: failed to allocate 16384 byte(s) of direct memory (used: 8568993562, max: 8589934592)<br> at io.netty.util.internal.PlatformDependent.incrementMemoryCounter(PlatformDependent.java:754)<br> at io.netty.util.internal.PlatformDependent.allocateDirectNoCleaner(PlatformDependent.java:709)<br> at io.netty.buffer.UnpooledUnsafeNoCleanerDirectByteBuf.allocateDirect(UnpooledUnsafeNoCleanerDirectByteBuf.java:30)<br> at io.netty.buffer.UnpooledDirectByteBuf.<init>(UnpooledDirectByteBuf.java:64)<br> at io.netty.buffer.UnpooledUnsafeDirectByteBuf.<init>(UnpooledUnsafeDirectByteBuf.java:41)<br> at io.netty.buffer.UnpooledUnsafeNoCleanerDirectByteBuf.<init>(UnpooledUnsafeNoCleanerDirectByteBuf.java:25)<br> at io.netty.buffer.UnsafeByteBufUtil.newUnsafeDirectByteBuf(UnsafeByteBufUtil.java:625)<br> at io.netty.buffer.PooledByteBufAllocator.newDirectBuffer(PooledByteBufAllocator.java:359)<br> at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:187)<br> at io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:178)<br> at io.netty.channel.unix.PreferredDirectByteBufAllocator.ioBuffer(PreferredDirectByteBufAllocator.java:53)<br> at io.netty.channel.DefaultMaxMessagesRecvByteBufAllocator$MaxMessageHandle.allocate(DefaultMaxMessagesRecvByteBufAllocator.java:114)<br> at io.netty.channel.epoll.EpollRecvByteAllocatorHandle.allocate(EpollRecvByteAllocatorHandle.java:75)<br> at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:777)<br> at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:475)<br> at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:378)<br> at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)<br> at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)<br> at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)<br> at java.lang.Thread.run(Thread.java:748)<br></span></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">JVM 配置</span><span style="display: none;"></span></h3> <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/hNWCQ9bibbzEGR101C4cWRQEqVsEAeicmFn0dcjcbPMnVuyicBaqXh9PyhGLBGJkaeTSPBeJwibs5ds4MNEkPtGRIRgria8KD7OM8/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">-server -Xmx8g -Xms8g -Xmn1024m <br>-XX:PermSize=<span style="color: #986801;line-height: 26px;">512</span>m -Xss256k <br>-XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled <br>-XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=<span style="color: #986801;line-height: 26px;">128</span>m <br>-XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly <br>-XX:CMSInitiatingOccupancyFraction=<span style="color: #986801;line-height: 26px;">70</span> -Djava.awt.headless=<span style="color: #a626a4;line-height: 26px;">true</span> <br>-Djava.net.preferIPv4Stack=<span style="color: #a626a4;line-height: 26px;">true</span><br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;">版本信息</span><span style="display: none;"></span></h3> <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/hNWCQ9bibbzEGR101C4cWRQEqVsEAeicmFn0dcjcbPMnVuyicBaqXh9PyhGLBGJkaeTSPBeJwibs5ds4MNEkPtGRIRgria8KD7OM8/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">spring cloud : Hoxton.SR5<br>spring cloud starter gateway : <span style="color: #986801;line-height: 26px;">2.2</span><span style="color: #986801;line-height: 26px;">.3</span>.RELEASE<br>spring boot starter : <span style="color: #986801;line-height: 26px;">2.3</span><span style="color: #986801;line-height: 26px;">.0</span>.RELEASE<br>netty : <span style="color: #986801;line-height: 26px;">4.1</span><span style="color: #986801;line-height: 26px;">.54</span>.Final<br>reactor-netty: <span style="color: #986801;line-height: 26px;">0.9</span><span style="color: #986801;line-height: 26px;">.7</span>.RELEASE<br></code></pre> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">山重水复疑无路</span></h2> <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: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;">JVM 参数详解:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html</p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">报错的信息是 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">OutOfDirectMemoryError</code>,即堆外内存不足,于是复习了下 JVM 堆外内存的相关知识。</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 堆外内存是在 NIO 中使用的; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 堆外内存通过 -XX:MaxDirectMemorySize 参数控制大小,注意和 -XX:+DisableExplicitGC 参数的搭配使用; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> JDK8 中堆外内存默认和堆内存一样大(-Xmx); </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> JDK8 如果配置 -XX:MaxDirectMemorySize 参数,则堆外内存大小以设置的参数为准; </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">SpringCloudGateway 是基于 <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(255, 93, 108);">WebFlux</code> 框架实现的,而 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">WebFlux</code> 框架底层则使用了高性能的 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">Reactor</code> 模式通信框架 Netty。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">网上查阅相关资料,有些场景是因为堆外内存没有手动 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">release</code> 导致,于是简单查看了网关模块的相关代码发现并无此问题,关键的地方也都调用了相关方法释放内存。堆外内存通过操作堆的命令无法看到,只能监控实例总内存走势判断。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/hNWCQ9bibbzEGR101C4cWRQEqVsEAeicmFn0dcjcbPMnVuyicBaqXh9PyhGLBGJkaeTSPBeJwibs5ds4MNEkPtGRIRgria8KD7OM8/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 释放内存方法</span><br>DataBufferUtils.release(dataBuffer);<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">Dump 堆内存下来也没有发现有什么问题:<img data-ratio="0.521461716937355" src="/upload/10f45765dab5ad82f8180c2295a52657.png" data-type="png" data-w="1724" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"></p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">柳暗花明又一村</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">抱着试一试的想法到 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">SpringCloudGateway</code> 官方仓库 issue 搜索有没有人遇到相同的问题,果不其然,有人提了类似的 issue。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">https://github.com/spring-cloud/spring-cloud-gateway/issues/1704</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">在 issue 中开发人员也给出了回应,<span style="font-weight: 700;color: rgb(248, 57, 41);">确实是 SpringCloudGateway 的 BUG!此问题已在 2.2.6.RELEASE 版本中修复。而我们项目中使用版本为 2.2.3.RELEASE,所以就会出现这个问题。</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">原因是:<span style="font-weight: 700;color: rgb(248, 57, 41);">包装原生的 pool 后没有释放内存。</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-ratio="0.5053272450532724" src="/upload/93a311eba493f91897d8e934873984a1.jpg" data-type="jpeg" data-w="2628" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img data-ratio="0.4494949494949495" src="/upload/6a89a759cd63636f084d5447a314699b.jpg" data-type="jpeg" data-w="2376" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">出乎意料</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">问题原因已经找到,想着在测试环境复现后升级版本再验证即可。可结果却出乎了我的意料。</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 测试环境将堆内存调小尝试进行复现生产问题,在压测将近 1 个小时后出现了同样的问题,复现成功。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 升级 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">SpringCloudGateway</code> 的版本至 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">2.2.6.RELEASE</code>。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> <span style="font-weight: 700;color: rgb(248, 57, 41);">重新压测,问题再次出现。</span> </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">你没看错,问题再次出现,且报错信息一模一样。我很快又陷入了沉思。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">深究原因</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">排除了组件的问题,剩下的就是代码的问题了,最有可能的就是程序中没有显示调用释放内存导致。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">网关模块共定义了三个过滤器,一个全局过滤器 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">RequestGatewayFilter implements GlobalFilter</code>。两个自定义过滤器 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">RequestDecryptGatewayFilterFactory extends AbstractGatewayFilterFactory</code> 和 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">ResponseEncryptGatewayFilterFactory extends AbstractGatewayFilterFactory</code>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">依次仔细排查相关逻辑,在全局过滤器 <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 93, 108);">RequestGatewayFilter</code> 中有一块代码引起了我的注意:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/hNWCQ9bibbzEGR101C4cWRQEqVsEAeicmFn0dcjcbPMnVuyicBaqXh9PyhGLBGJkaeTSPBeJwibs5ds4MNEkPtGRIRgria8KD7OM8/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 伪代码</span><br><span style="color: #4078f2;line-height: 26px;">@Override</span><br><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> Mono<Void> <span style="color: #4078f2;line-height: 26px;">filter</span><span style="line-height: 26px;">(ServerWebExchange exchange, GatewayFilterChain chain)</span> </span>{<br> HttpHeaders headers = request.getHeaders();<br> <span style="color: #a626a4;line-height: 26px;">return</span> DataBufferUtils.join(exchange.getRequest().getBody())<br> .flatMap(dataBuffer -> {<br> DataBufferUtils.retain(dataBuffer);<br> Flux<DataBuffer> cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(<span style="color: #986801;line-height: 26px;">0</span>, dataBuffer.readableByteCount())));<br> <br> ServerHttpRequest mutatedRequest = <span style="color: #a626a4;line-height: 26px;">new</span> ServerHttpRequestDecorator(exchange.getRequest()) {<br> <span style="color: #4078f2;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> Flux<DataBuffer> <span style="color: #4078f2;line-height: 26px;">getBody</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> cachedFlux;<br> }<br><br> <span style="color: #4078f2;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> HttpHeaders <span style="color: #4078f2;line-height: 26px;">getHeaders</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> headers;<br> }<br> };<br> <span style="color: #a626a4;line-height: 26px;">return</span> chain.filter(exchange.mutate().request(mutatedRequest).build());<br> });<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">我们知道,Request 的 Body 是只能读取一次的,如果直接通过在 Filter 中读取,而不封装回去回导致后面的服务无法读取数据。</p> <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: 15px;padding-right: 10px;padding-bottom: 15px;line-height: 1.75;border-radius: 13px;color: rgb(53, 53, 53);background: rgb(245, 245, 245);"> <span style="display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">“</span> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;font-size: 16px;margin-right: 10px;margin-left: 10px;"><span style="font-size: 14px;">此全局过滤器的目的就是把原有的 request 请求中的 body 内容读出来,并且使用</span><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(255, 93, 108);"><span style="font-size: 14px;">ServerHttpRequestDecorator</span></code><span style="font-size: 14px;"> 这个请求装饰器对 request 进行包装,重写 getBody 方法,并把包装后的请求放到过滤器链中传递下去。这样后面的过滤器中再使用 </span><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(255, 93, 108);"><span style="font-size: 14px;">exchange.getRequest ().getBody ()</span></code><span style="font-size: 14px;"> 来获取 body 时,实际上就是调用的重载后的 </span><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(255, 93, 108);"><span style="font-size: 14px;">getBody()</span></code><span style="font-size: 14px;"> 方法,获取的最先已经缓存了的 body 数据。这样就能够实现 body 的多次读取了。</span></p> <span style="float: right;display: block;font-size: 2em;color: rgb(248, 57, 41);font-family: Arial, serif;line-height: 1em;font-weight: 700;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">但是将 DataBuffer 读取出来后并没有手动释内存,会导致堆外内存持续增长。于是添加了一行代码手动释放堆外内存:</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/hNWCQ9bibbzEGR101C4cWRQEqVsEAeicmFn0dcjcbPMnVuyicBaqXh9PyhGLBGJkaeTSPBeJwibs5ds4MNEkPtGRIRgria8KD7OM8/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;">DataBufferUtils.release(dataBuffer);<br></code></pre> <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/hNWCQ9bibbzEGR101C4cWRQEqVsEAeicmFn0dcjcbPMnVuyicBaqXh9PyhGLBGJkaeTSPBeJwibs5ds4MNEkPtGRIRgria8KD7OM8/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(250, 250, 250);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #383a42;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #fafafa;border-radius: 5px;"><span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 伪代码</span><br><span style="color: #4078f2;line-height: 26px;">@Override</span><br><span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> Mono<Void> <span style="color: #4078f2;line-height: 26px;">filter</span><span style="line-height: 26px;">(ServerWebExchange exchange, GatewayFilterChain chain)</span> </span>{<br> HttpHeaders headers = request.getHeaders();<br> <span style="color: #a626a4;line-height: 26px;">return</span> DataBufferUtils.join(exchange.getRequest().getBody())<br> .flatMap(dataBuffer -> {<br> <span style="color: #a626a4;line-height: 26px;">byte</span>[] bytes = <span style="color: #a626a4;line-height: 26px;">new</span> <span style="color: #a626a4;line-height: 26px;">byte</span>[dataBuffer.readableByteCount()];<br> dataBuffer.read(bytes);<br> <span style="color: #a0a1a7;font-style: italic;line-height: 26px;">// 释放堆外内存</span><br> DataBufferUtils.release(dataBuffer);<br> ServerHttpRequest mutatedRequest = <span style="color: #a626a4;line-height: 26px;">new</span> ServerHttpRequestDecorator(exchange.getRequest()) {<br> <span style="color: #4078f2;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> Flux<DataBuffer> <span style="color: #4078f2;line-height: 26px;">getBody</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> Flux.defer(() -> {<br> DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);<br> DataBufferUtils.retain(buffer);<br> <span style="color: #a626a4;line-height: 26px;">return</span> Mono.just(buffer);<br> });<br> }<br><br> <span style="color: #4078f2;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #a626a4;line-height: 26px;">public</span> HttpHeaders <span style="color: #4078f2;line-height: 26px;">getHeaders</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #a626a4;line-height: 26px;">return</span> headers;<br> }<br> };<br> <span style="color: #a626a4;line-height: 26px;">return</span> chain.filter(exchange.mutate().request(mutatedRequest).build());<br> });<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">再次压测未出现堆外内存溢出问题。终究还是自己大意了。。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">后在网络上查询到了类似的案例:https://github.com/reactor/reactor-netty/issues/788</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;color: black;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-size: 18px;color: rgb(34, 34, 34);display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">总结</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;">这个问题排查花费了自己不少的时间,自己也没有想到这么曲折。问题是解决了,但是暴露了自身的很多问题,比如针对不同版本 JVM 内存分配不够熟悉、对 <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(255, 93, 108);">SpringCloudGateway</code> 不够熟悉及太过相信官方开源版本。在直接内存中排查了很久,浪费了不少时间。同时自己也学到了不少东西:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(248, 57, 41);" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 遇到问题主要先去思考,要全面且细致,慢慢去分析,抽丝剥茧; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 一定要细致再细致,耐心再耐心的去还原问题,思考问题; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> JVM 直接内存的使用和配置、场景; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(53, 53, 53);"> 不要对开源组件过分信任,遇到问题时,对开源组件持怀疑态度; </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin-top: 0.8em;margin-bottom: 0.8em;"><span style="font-size: 12px;">作者: 彩虹马<br>原文链接: </span><span style="font-size: 12px;background-color: rgb(255, 255, 255);font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;letter-spacing: 0.8px;word-spacing: 0.8px;">https://my5353.com/poLDV</span></p> <p data-tool="mdnice编辑器" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-top: 8px;padding-bottom: 8px;color: rgb(53, 53, 53);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;letter-spacing: 0.8px;text-align: left;white-space: normal;word-spacing: 0.8px;background-color: rgb(255, 255, 255);line-height: 1.75;">好了,今天的文章就到这里了,希望能对你有所帮助。<br><span style="letter-spacing: 0.8px;word-spacing: 0.8px;">最后,我是飘渺Jam,一名写代码的架构师,做架构的程序员,期待您的关注。</span></p> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzAwMTk4NjM1MA==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/PxMzT0Oibf4gcBzLSUNh2cgXUsuLIsvQYJE1lzZd74qpC3iciaM6gcYIfOVV0KjDDkeN4CTLTn4ETPtaHOAuTWSWA/0?wx_fmt=png" data-nickname="JAVA日知录" data-alias="javadaily" data-signature="写代码的架构师,做架构的程序员! 实战、源码、数据库、架构...只要你来,你想了解的这里都有!" data-from="0"></mpprofile> </section> <section> <br> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <section style="display: flex;justify-content: center;align-items: center;width: 100%;padding: 0px 16px;" data-mid="" mpa-from-tpl="t"> <section style="display: flex;justify-content: space-between;align-items: center;width: 100%;" data-mid="" mpa-from-tpl="t"> <section style="display: flex;justify-content: center;align-items: center;" data-mid="" mpa-from-tpl="t"> <section style="width: 40px;" data-mid="" mpa-from-tpl="t"> <section data-mid="" mpa-from-tpl="t"> <img data-ratio="0.8" src="/upload/cb4d87d1396617decd3c3be3400465af.png" data-type="png" data-w="80" style="display: block;"> </section> <section style="margin-left: 3px;margin-top: 4px;" data-mid="" mpa-from-tpl="t"> <p style="font-size: 12px;font-family: PingFangSC-Regular, PingFang SC;color: #333333;line-height: 17px;" data-mid="">转发</p> </section> </section> <section style="width: 40px;" data-mid="" mpa-from-tpl="t"> <section data-mid="" mpa-from-tpl="t"> <img data-ratio="0.8" src="/upload/cf6883d51e28a53a6692208fa6c2f7ff.png" data-type="png" data-w="80" style="display: block;"> </section> <section style="margin-left: 3px;margin-top: 4px;" data-mid="" mpa-from-tpl="t"> <p style="font-size: 12px;font-family: PingFangSC-Regular, PingFang SC;color: #333333;line-height: 17px;" data-mid="">收藏</p> </section> </section> </section> <section style="display: flex;justify-content: center;align-items: center;" data-mid="" mpa-from-tpl="t"> <section style="width: 40px;" data-mid="" mpa-from-tpl="t"> <section data-mid="" mpa-from-tpl="t"> <img data-ratio="0.825" src="/upload/d1f2a04ed7be7400b3260245438c6f59.png" data-type="png" data-w="80" style="display: block;"> </section> <section style="margin-left: 3px;margin-top: 4px;" data-mid="" mpa-from-tpl="t"> <p style="font-size: 12px;font-family: PingFangSC-Regular, PingFang SC;color: #333333;line-height: 17px;" data-mid="">点赞</p> </section> </section> <section style="width: 40px;" data-mid="" mpa-from-tpl="t"> <section data-mid="" mpa-from-tpl="t"> <img data-ratio="0.8461538461538461" src="/upload/b00a449f5d6f6a5df29ca07a28951849.png" data-type="png" data-w="78" style="display: block;"> </section> <section style="margin-left: 3px;margin-top: 4px;" data-mid="" mpa-from-tpl="t"> <p style="font-size: 12px;font-family: PingFangSC-Regular, PingFang SC;color: #333333;line-height: 17px;" data-mid="">在看</p> </section> </section> </section> </section> </section> </section>
作者:微信小助手
<h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 30px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;text-align: left;white-space: normal;border-bottom: 2px solid rgb(89, 89, 89);color: rgb(89, 89, 89);"><span style="display: inline-block;border-bottom: 2px solid rgb(89, 89, 89);">限流简介</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">现在说到高可用系统,都会说到高可用的保护手段:缓存、降级和限流,本博文就主要说说限流。限流是流量限速(Rate Limit)的简称,是指只允许指定的事件进入系统,超过的部分将被拒绝服务、排队或等待、降级等处理。对于server服务而言,限流为了保证一部分的请求流量可以得到正常的响应,总好过全部的请求都不能得到响应,甚至导致系统雪崩。限流与熔断经常被人弄混,博主认为它们最大的区别在于限流主要在server实现,而熔断主要在client实现,当然了,一个服务既可以充当server也可以充当client,这也是让限流与熔断同时存在一个服务中,这两个概念才容易被混淆。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">那为什么需要限流呢?很多人第一反应就是服务扛不住了所以需要限流。这是不全面的说法,博主认为限流是因为资源的稀缺或出于安全防范的目的,采取的自我保护的措施。限流可以保证使用有限的资源提供最大化的服务能力,按照预期流量提供服务,超过的部分将会拒绝服务、排队或等待、降级等处理。关注公众号‘码猿技术专栏’,回复关键词‘9527’获取Spring Cloud Alibaba实战视频教程!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">现在的系统对限流的支持各有不同,但是存在一些标准。在HTTP RFC 6585标准中规定了『429 Too Many Requests 』,429状态码表示用户在给定时间内发送了太多的请求,需要进行限流(“速率限制”),同时包含一个 Retry-After 响应头用于告诉客户端多长时间后可以再次请求服务.<span style="background-color: rgb(40, 44, 52);color: rgb(0, 0, 0);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"></span></p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding: 0.4em 0.6em;border-radius: 5px;background: rgb(248, 248, 248);color: rgb(0, 0, 0);font-size: 16px;text-align: left;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><code style="padding: 15px 16px 16px;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;">HTTP/1.1 429 Too Many Requests<br>Content-Type: text/html<br>Retry-After: 3600<br><br><br> <br> <span style="line-height: 26px;"><<span style="color: rgb(224, 108, 117);line-height: 26px;">title</span>></span>Too Many Requests<span style="line-height: 26px;"></<span style="color: rgb(224, 108, 117);line-height: 26px;">title</span>></span><br> <br> <br> <span style="line-height: 26px;"><<span style="color: rgb(224, 108, 117);line-height: 26px;">h1</span>></span>Too Many Requests<span style="line-height: 26px;"></<span style="color: rgb(224, 108, 117);line-height: 26px;">h1</span>></span><br> <span style="line-height: 26px;"><<span style="color: rgb(224, 108, 117);line-height: 26px;">p</span>></span>I only allow 50 requests per hour to this Web site per<br> logged in user. Try again soon.<span style="line-height: 26px;"></<span style="color: rgb(224, 108, 117);line-height: 26px;">p</span>></span><br> <br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">很多应用框架同样集成了,限流功能并且在返回的Header中给出明确的限流标识。</p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 533.891px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> X-Rate-Limit-Limit:同一个时间段所允许的请求的最大数目; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> X-Rate-Limit-Remaining:在当前时间段内剩余的请求的数量; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> X-Rate-Limit-Reset:为了得到最大请求数所等待的秒数。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">这是通过响应头告诉调用方服务端的限流频次是怎样的,保证后端的接口访问上限,客户端也可以根据响应的Header调整请求。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 30px;font-weight: bold;font-size: 22px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;text-align: left;white-space: normal;border-bottom: 2px solid rgb(89, 89, 89);color: rgb(89, 89, 89);"><span style="display: inline-block;border-bottom: 2px solid rgb(89, 89, 89);">限流分类</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);"><code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(71, 193, 168);">限流</code>,拆分来看,就两个字<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(71, 193, 168);">限</code>和<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(71, 193, 168);">流</code>,<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(71, 193, 168);">限</code>就是动词限制,很好理解。但是<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(71, 193, 168);">流</code>在不同的场景之下就是不同资源或指标,多样性就在<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(71, 193, 168);">流</code>中体现。在网络流量中可以是字节流,在数据库中可以是TPS,在API中可以是QPS亦可以是并发请求数,在商品中还可以是库存数... ...但是不管是哪一种『流』,这个流必须可以<strong style="color: rgb(71, 193, 168);">被量化,可以被度量,可以被观察到、可以统计出来</strong>。我们把限流的分类基于不同的方式分为不同的类别,如下图。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5409504550050556" src="/upload/c1169a4981ef5e3f57399e991dd05e3b.png" data-type="png" data-w="989" style="margin-right: auto;margin-left: auto;display: block;box-sizing: border-box !important;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 14px;"> 限流分类 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">因为篇幅有限,本文只会挑选几个常见的类型分类进行说明。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;text-align: left;white-space: normal;color: rgb(89, 89, 89);">限流粒度分类</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">根据限流的粒度分类:</p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 533.891px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 单机限流 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 分布式限流 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">现状的系统基本上都是分布式架构,单机的模式已经很少了,这里说的单机限流更加准确一点的说法是单服务节点限流。单机限流是指请求进入到某一个服务节点后超过了限流阈值,服务节点采取了一种限流保护措施。<span style="color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;">关注公众号‘码猿技术专栏</span><span style="color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;">’,回复关键词‘95</span><span style="color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;">27’获取Spring Cloud Alibaba实战视频教程!</span></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5474642392717816" src="/upload/4d7af8a06e6a0312e5b9b3e423c9d303.png" data-type="png" data-w="1538" style="margin-right: auto;margin-left: auto;display: block;box-sizing: border-box !important;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 14px;"> 单机限流示意图 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">分布式限流狭义的说法是在接入层实现多节点合并限流,比如NGINX+redis,分布式网关等,广义的分布式限流是多个节点(可以为不同服务节点)有机整合,形成整体的限流服务。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6317016317016317" src="/upload/fb4c71df7f59f3c487c8ffd877584ef6.png" data-type="png" data-w="1716" style="margin-right: auto;margin-left: auto;display: block;box-sizing: border-box !important;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 14px;"> 分布式限流示意图 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">单机限流防止流量压垮服务节点,缺乏对整体流量的感知。分布式限流适合做细粒度不同的限流控制,可以根据场景不同匹配不同的限流规则。与单机限流最大的区别,分布式限流需要中心化存储,常见的使用redis实现。引入了中心化存储,就需要解决以下问题:</p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 533.891px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">数据一致性</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">在限流模式中理想的模式为时间点一致性。时间点一致性的定义中要求所有数据组件的数据在任意时刻都是完全一致的,但是一般来说信息传播的速度最大是光速,其实并不能达到任意时刻一致,总有一定的时间不一致,对于我们CAP中的一致性来说只要达到读取到最新数据即可,达到这种情况并不需要严格的任意时间一致。这只能是理论当中的一致性模型,可以在限流中达到线性一致性即可。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">时间一致性</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">这里的时间一致性与上述的时间点一致性不一样,这里就是指各个服务节点的时间一致性。一个集群有3台机器,但是在某一个A/B机器的时间为<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(71, 193, 168);">Tue Dec 3 16:29:28 CST 2019</code>,C为<code style="margin-right: 2px;margin-left: 2px;padding: 2px 4px;font-size: 14px;border-radius: 4px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(71, 193, 168);">Tue Dec 3 16:29:28 CST 2019</code>,那么它们的时间就不一致。那么使用ntpdate进行同步也会存在一定的误差,对于时间窗口敏感的算法就是误差点。</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">超时</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">在分布式系统中就需要网络进行通信,会存在网络抖动问题,或者分布式限流中间件压力过大导致响应变慢,甚至是超时时间阈值设置不合理,导致应用服务节点超时了,此时是放行流量还是拒绝流量?</p> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">性能与可靠性</p> <p style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);">分布式限流中间件的资源总是有限的,甚至可能是单点的(写入单点),性能存在上限。如果分布式限流中间件不可用时候如何退化为单机限流模式也是一个很好的降级方案。</p> </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;text-align: left;white-space: normal;color: rgb(89, 89, 89);">限流对象类型分类</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">按照对象类型分类:</p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 533.891px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 基于请求限流 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 基于资源限流 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">基于请求限流,一般的实现方式有<strong style="color: rgb(71, 193, 168);">限制总量</strong>和<strong style="color: rgb(71, 193, 168);">限制QPS</strong>。限制总量就是限制某个指标的上限,比如抢购某一个商品,放量是10w,那么最多只能卖出10w件。微信的抢红包,群里发一个红包拆分为10个,那么最多只能有10人可以抢到,第十一个人打开就会显示『手慢了,红包派完了』。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.6416666666666666" src="/upload/da69194b424774230a308091e7adcf08.jpg" data-type="jpeg" data-w="360" style="margin-right: auto;margin-left: auto;display: block;box-sizing: border-box !important;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 14px;"> 红包抢完了 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">限制QPS,也是我们常说的限流方式,只要在接口层级进行,某一个接口只允许1秒只能访问100次,那么它的峰值QPS只能为100。限制QPS的方式最难的点就是如何预估阈值,如何定位阈值,下文中会说到。<span style="color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;">关注公众号‘码猿技术专栏</span><span style="color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;">’,回复关键词‘95</span><span style="color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;text-align: left;">27’获取Spring Cloud Alibaba实战视频教程!</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">基于资源限流是基于服务资源的使用情况进行限制,需要定位到服务的关键资源有哪些,并对其进行限制,如限制TCP连接数、线程数、内存使用量等。限制资源更能有效地反映出服务当前地清理,但与限制QPS类似,面临着如何确认资源的阈值为多少。这个阈值需要不断地调优,不停地实践才可以得到一个较为满意地值。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;text-align: left;white-space: normal;color: rgb(89, 89, 89);">限流算法分类</h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">不论是按照什么维度,基于什么方式的分类,其限流的底层均是需要算法来实现。下面介绍实现常见的限流算法:</p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 533.891px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 计数器 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 令牌桶算法 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 漏桶算法 </section></li> </ul> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;text-align: left;white-space: normal;">计数器</h4> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;text-align: left;white-space: normal;">固定窗口计数器</h5> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">计数限流是最为简单的限流算法,日常开发中,我们说的限流很多都是说固定窗口计数限流算法,比如某一个接口或服务1s最多只能接收1000个请求,那么我们就会设置其限流为1000QPS。该算法的实现思路非常简单,维护一个固定单位时间内的计数器,如果检测到单位时间已经过去就重置计数器为零。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.48527131782945737" src="/upload/443e2f7cbf5328efdfafbe992ebc736c.png" data-type="png" data-w="1290" style="margin-right: auto;margin-left: auto;display: block;box-sizing: border-box !important;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 14px;"> 固定窗口计数器原理 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">其操作步骤:</p> <ol data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 533.891px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;"> <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);"> 落在每一个时间窗口内的请求就将计数器加1; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如果计数器超过了限流阈值,则后续落在该窗口的请求都会被拒绝。但时间达到下一个时间窗口时,计数器会被重置为0。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">下面实现一个简单的代码。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding: 0.4em 0.6em;border-radius: 5px;background: rgb(248, 248, 248);color: rgb(0, 0, 0);font-size: 16px;text-align: left;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/wbiax4xEAl5xpJuBwIia6AZYnaoT2XA2GEq8YViaZj4vurJC2AOleFek7HYAjHqeXS98tExVyl6PVmQ3rwa5jLUsQ/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 542.812px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">package</span> limit<br><br><span style="color: rgb(198, 120, 221);line-height: 26px;">import</span> (<br> <span style="color: rgb(152, 195, 121);line-height: 26px;">"sync/atomic"</span><br> <span style="color: rgb(152, 195, 121);line-height: 26px;">"time"</span><br>)<br><br><span style="color: rgb(198, 120, 221);line-height: 26px;">type</span> Counter <span style="color: rgb(198, 120, 221);line-height: 26px;">struct</span> {<br> Count <span style="color: rgb(198, 120, 221);line-height: 26px;">uint64</span> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 初始计数器</span><br> Limit <span style="color: rgb(198, 120, 221);line-height: 26px;">uint64</span> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 单位时间窗口最大请求频次</span><br> Interval <span style="color: rgb(198, 120, 221);line-height: 26px;">int64</span> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 单位ms</span><br> RefreshTime <span style="color: rgb(198, 120, 221);line-height: 26px;">int64</span> <span style="color: rgb(92, 99, 112);font-style: italic;line-height: 26px;">// 时间窗口</span><br>}<br><br><span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">func</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">NewCounter</span><span style="line-height: 26px;">(count, limit <span style="color: rgb(198, 120, 221);line-height: 26px;">uint64</span>, interval, rt <span style="color: rgb(198, 120, 221);line-height: 26px;">int64</span>)</span> *<span style="color: rgb(97, 174, 238);line-height: 26px;">Counter</span></span> {<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">return</span> &amp;Counter{<br> Count: count,<br> Limit: limit,<br> Interval: interval,<br> RefreshTime: rt,<br> }<br>}<br><br><span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">func</span> <span style="line-height: 26px;">(c *Counter)</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">RateLimit</span><span style="line-height: 26px;">()</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">bool</span></span> {<br> now := time.Now().UnixNano() / <span style="color: rgb(209, 154, 102);line-height: 26px;">1e6</span><br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span> now &lt; (c.RefreshTime + c.Interval) {<br> atomic.AddUint64(&amp;c.Count, <span style="color: rgb(209, 154, 102);line-height: 26px;">1</span>)<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">return</span> c.Count &lt;= c.Limit<br> } <span style="color: rgb(198, 120, 221);line-height: 26px;">else</span> {<br> c.RefreshTime = now<br> atomic.AddUint64(&amp;c.Count, -c.Count)<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">return</span> <span style="color: rgb(86, 182, 194);line-height: 26px;">true</span><br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">测试代码:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding: 0.4em 0.6em;border-radius: 5px;background: rgb(248, 248, 248);color: rgb(0, 0, 0);font-size: 16px;text-align: left;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/wbiax4xEAl5xpJuBwIia6AZYnaoT2XA2GEq8YViaZj4vurJC2AOleFek7HYAjHqeXS98tExVyl6PVmQ3rwa5jLUsQ/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 542.812px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;overflow-x: auto;color: rgb(171, 178, 191);display: -webkit-box;font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">package</span> limit<br><br><span style="color: rgb(198, 120, 221);line-height: 26px;">import</span> (<br> <span style="color: rgb(152, 195, 121);line-height: 26px;">"fmt"</span><br> <span style="color: rgb(152, 195, 121);line-height: 26px;">"testing"</span><br> <span style="color: rgb(152, 195, 121);line-height: 26px;">"time"</span><br>)<br><br><span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">func</span> <span style="color: rgb(97, 174, 238);line-height: 26px;">Test_Counter</span><span style="line-height: 26px;">(t *testing.T)</span></span> {<br> counter := NewCounter(<span style="color: rgb(209, 154, 102);line-height: 26px;">0</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">5</span>, <span style="color: rgb(209, 154, 102);line-height: 26px;">100</span>, time.Now().Unix())<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">for</span> i := <span style="color: rgb(209, 154, 102);line-height: 26px;">0</span>; i &lt; <span style="color: rgb(209, 154, 102);line-height: 26px;">10</span>; i++ {<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">go</span> <span style="line-height: 26px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">func</span><span style="line-height: 26px;">(i <span style="color: rgb(198, 120, 221);line-height: 26px;">int</span>)</span></span> {<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">for</span> k := <span style="color: rgb(209, 154, 102);line-height: 26px;">0</span>; k &lt;= <span style="color: rgb(209, 154, 102);line-height: 26px;">10</span>; k++ {<br> fmt.Println(counter.RateLimit())<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span> k%<span style="color: rgb(209, 154, 102);line-height: 26px;">3</span> == <span style="color: rgb(209, 154, 102);line-height: 26px;">0</span> {<br> time.Sleep(<span style="color: rgb(209, 154, 102);line-height: 26px;">102</span> * time.Millisecond)<br> }<br> }<br> }(i)<br> }<br> time.Sleep(<span style="color: rgb(209, 154, 102);line-height: 26px;">10</span> * time.Second)<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">看了上面的逻辑,有没有觉得固定窗口计数器很简单,对,就是这么简单,这就是它的一个优点实现简单。同时也存在两个比较严重缺陷。试想一下,固定时间窗口1s限流阈值为100,但是前100ms,已经请求来了99个,那么后续的900ms只能通过一个了,就是一个缺陷,基本上没有应对突发流量的能力。第二个缺陷,在00:00:00这个时间窗口的后500ms,请求通过了100个,在00:00:01这个时间窗口的前500ms还有100个请求通过,对于服务来说相当于1秒内请求量达到了限流阈值的2倍。</p> <h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;text-align: left;white-space: normal;">滑动窗口计数器</h5> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">滑动时间窗口算法是对固定时间窗口算法的一种改进,这词被大众所知实在TCP的流量控制中。固定窗口计数器可以说是滑动窗口计数器的一种特例,滑动窗口的操作步骤:</p> <ol data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 533.891px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 将单位时间划分为多个区间,一般都是均分为多个小的时间段; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 每一个区间内都有一个计数器,有一个请求落在该区间内,则该区间内的计数器就会加一; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 每过一个时间段,时间窗口就会往右滑动一格,抛弃最老的一个区间,并纳入新的一个区间; </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 计算整个时间窗口内的请求总数时会累加所有的时间片段内的计数器,计数总和超过了限制数量,则本窗口内所有的请求都被丢弃。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;font-size: 16px;text-align: left;white-space: normal;line-height: 26px;color: rgb(89, 89, 89);">时间窗口�
作者:微信小助手
<section class="mp_profile_iframe_wrp" data-mpa-powered-by="yiban.io"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzA5MTU0OTY0Ng==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/zc3KLDBfJlmPt0J5PXYOoiaG8wsQPZrLevbxMZSfgQ0YypNYaicnbS0P9UicluuOySLSP4CjTcRUVHCZzYeXQ9WlA/0?wx_fmt=png" data-nickname="Java派" data-alias="javapai" data-signature="专注Java相关技术栈:Spring全家筒、Docker、k8s、Mysql、集群、微服务、中间件等知识。" data-from="0"></mpprofile> </section> <p><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;letter-spacing: 1.6px;text-align: left;word-spacing: 1.6px;background-color: rgb(255, 255, 255);"></span><br></p> <p><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 16px;letter-spacing: 1.6px;text-align: left;word-spacing: 1.6px;background-color: rgb(255, 255, 255);">平时的工作中经常碰到很多疑难问题的处理,在解决问题的同时,有一些工具起到了相当大的作用,在此书写下来,一是作为笔记,可以让自己后续忘记了可快速翻阅,二是分享,希望看到此文的同学们可以拿出自己日常觉得帮助很大的工具,大家一起进步。</span></p> <p><br></p> <pre style="margin-top: 0.5em;margin-bottom: 0.5em;padding: 0.4em 0.6em;border-radius: 8px;background: rgb(255, 255, 255);font-size: 15px;letter-spacing: 0.544px;"> <section style="text-align: left;"> <span style="color: rgb(14, 136, 235);font-size: 18px;font-weight: bold;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0px;">Linux命令类</span> <br> </section></pre> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding: 1px;letter-spacing: 0px;white-space: normal;font-size: 16px;color: black;line-height: 1.6;word-break: break-word;text-align: left;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;">tail</h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.95;letter-spacing: 0.1em;word-spacing: 0.1em;color: rgb(51, 51, 51);">最常用的tail -f</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding: 0.4em 0.6em;border-radius: 5px;background: rgb(248, 248, 248);box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/VQXy63picV0Gmjqibu3vicJ3xRtkmyxuAB498bib5HNqY1xLVVcPuWskAelmm04zPLUXib13DVBtlUSA4zeXSibTfUAg/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 560.812px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;overflow-x: auto;color: rgb(221, 221, 221);display: -webkit-box;font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(39, 40, 34);border-radius: 5px;">tail -300f shopbase.log <span style="color: rgb(117, 113, 94);line-height: 26px;">#倒数300行并进入实时监听文件写入模式</span><br></code></pre> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;">grep</h4> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding: 0.4em 0.6em;border-radius: 5px;background: rgb(248, 248, 248);box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="margin-bottom: -7px;display: block;background: url("https://mmbiz.qpic.cn/mmbiz_png/VQXy63picV0Gmjqibu3vicJ3xRtkmyxuAB498bib5HNqY1xLVVcPuWskAelmm04zPLUXib13DVBtlUSA4zeXSibTfUAg/640?wx_fmt=png") 10px 10px / 40px no-repeat rgb(39, 40, 34);height: 30px;width: 560.812px;border-radius: 5px;"></span><code style="padding: 15px 16px 16px;overflow-x: auto;color: rgb(221, 221, 221);display: -webkit-box;font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size: 12px;background: rgb(39, 40, 34);border-radius: 5px;">grep forest f.txt <span style="color: rgb(117, 113, 94);line-height: 26px;">#文件查找</span><br>grep forest f.txt cpf.txt <span style="color: rgb(117, 113, 94);line-height: 26px;">#多文件查找</span><br>grep <span style="color: rgb(166, 226, 46);line-height: 26px;">'log'</span> /home/admin -r -n <span style="color: rgb(117, 113, 94);line-height: 26px;">#目录下查找所有符合关键字的文件</span><br>cat f.txt | grep -i shopbase <br>grep <span style="color: rgb(166, 226, 46);line-height: 26px;">'shopbase'</span> /home/admin -r -n --include *.{vm,java} <span style="color: rgb(117, 113, 94);line-height: 26px;">#指定文件后缀</span><br>grep <span style="color: rgb(166, 226, 46);line-height: 26px;">'shopbase'</span> /home/admin -r -n --exclude *.{vm,java}&nbs
作者:微信小助手
<h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">背景</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">今天团队在做线下代码评审的时候,发现同学们在代码中出现了 select count(1) 、 select count(*),和具体的select count(字段)的不同写法,本着分析的目的在会议室讨论了起来,那这几种写法究竟孰优孰劣呢,我们一起来看一下。</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">讨论归纳</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">先来看看MySQL官方对SELECT COUNT的定义:</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">传送门:https://dev.mysql.com/doc/refman/5.6/en/aggregate-functions.html#function_count</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><img data-backh="354" data-backw="570" data-ratio="0.621875" src="/upload/e8eed5df1df3943a7bbf746b712e497e.jpg" data-type="jpeg" data-w="640" style="width: 100%;height: auto;"></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">大概可以分下面这几个步骤讨论。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"> <mpcpc js_editor_cpcad="" class="js_cpc_area cpc_iframe" src="/cgi-bin/readtemplate?t=tmpl/cpc_tmpl#1627475595490" data-category_id_list="1|11|16|17|22|24|26|27|28|29|3|31|32|35|36|37|39|41|42|43|45|46|47|48|49|5|50|51|52|53|54|55|6|7|8" data-id="1627475595490"></mpcpc>COUNT(expr)的分析</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">COUNT(expr)函数返回的值是由SELECT语句检索的行中expr表达式非null的计数值,一个BIGINT的值。如果没有匹配到数据,COUNT(expr)将返回0,通常有下面这三种用法:</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、COUNT(字段) 会统计该字段在表中出现的次数,忽略字段为null 的情况。即不统计字段为null 的记录。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、COUNT(*) 则不同,它执行时返回检索到的行数的计数,不管这些行是否包含null值,</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">3、COUNT(1)跟COUNT(*)类似,不将任何列是否null列入统计标准,仅用1代表代码行,所以在统计结果的时候,不会忽略列值为NULL的行。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">所以执行以下数据会出现这样的结果(这边是故意给component字段设置了几个null值):</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">select</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">COUNT</span>(*),<span style="color: rgb(198, 120, 221);line-height: 26px;">COUNT</span>(<span style="color: rgb(209, 154, 102);line-height: 26px;">1</span>),<span style="color: rgb(198, 120, 221);line-height: 26px;">COUNT</span>(component) <span style="color: rgb(198, 120, 221);line-height: 26px;">from</span> worklog;<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><img data-backh="188" data-backw="570" data-ratio="0.3296875" src="/upload/a9ed8822dd41534dfa499c65754fd976.jpg" data-type="jpeg" data-w="640" style="width: 100%;height: auto;"></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">归纳如下:</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">count(*)包括了所有的列,相当于行数,在统计结果的时候,<strong style="color: rgb(53, 179, 120);">不会忽略列值为NULL</strong></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">count(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,<strong style="color: rgb(53, 179, 120);">不会忽略列值为NULL</strong></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">count(字段)只包括字段那一列,在统计结果的时候,会忽略列值为null的计数,<strong style="color: rgb(53, 179, 120);">即某个字段值为NULL时,不统计</strong>。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"> <mpcpc js_editor_cpcad="" class="js_cpc_area cpc_iframe" src="/cgi-bin/readtemplate?t=tmpl/cpc_tmpl#1627475604041" data-category_id_list="1|11|16|17|22|24|26|27|28|29|3|31|32|35|36|37|39|41|42|43|45|46|47|48|49|5|50|51|52|53|54|55|6|7|8" data-id="1627475604041"></mpcpc>关于 COUNT(*) 和 COUNT(1)</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">先看看<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">COUNT(*)</code>,MyISAM 引擎会把一个表的总行数记录了下来,所以在执行 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">COUNT(*)</code>的时候会直接返回数量,执行效率很高。对于InnoDB这样的事务性存储引擎, 因为增加了版本控制(MVCC)的原因,同时有多个事务访问数据并且有更新操作的时候,每个事务需要维护自己的可见性,那么每个事务查询到的行数也是不同的,所以不能缓存具体的行数,他每次都需要count计算一下所有的行数。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">至于 COUNT(1) 和 COUNT(*)有什么区别呢,根据官网的内容(即上述截图倒数第二段),两种实现上其实一样:</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: rgb(53, 179, 120);">InnoDB handles SELECT COUNT(*) and SELECT COUNT(1) operations in the same way. There is no performance difference.</strong></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">因为<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">COUNT(*)</code> 不care返回值是否为空都会将改行纳入计算,所以他count了所有行数,而 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">COUNT(1)</code> 中的 1 ,则是遇到了行的时候为恒真表达式,所以 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">COUNT(*)</code> 还是 COUNT(1) 都是对所有的结果集进行 count,他们本质上没有什么区别。姑且认为 COUNT(*)≈ COUNT(1)。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">关于COUNT(字段)</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">我们再来看看的COUNT(字段),他的查询就简单粗暴了,就是进行全表扫描,然后判断拿到的字段的值是不是为NULL,不为NULL则累加。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">相比<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">COUNT(*)</code>,COUNT(字段)多了一个步骤就是判断所查询的字段是否为NULL,所以他的性能要比<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">COUNT(*)</code>和<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">COUNT(1)</code>慢。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">总结</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">综上,COUNT(1)和 COUNT(*)表示的是直接查询符合条件的数据库表的行数。而COUNT(字段)表示的是查询符合条件的列的值,并判断不为NULL的行数的累计,效率自然会低一点,</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">除了查询得到结果集有区别之外,相比COUNT(1) 和 COUNT(字段)来讲,COUNT(*)是SQL92定义的标准统计数的语法,是官方提供的标准方案,基于此,MySQL数据库对他进行过很多优化。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: rgb(53, 179, 120);">注:SQL92,是数据库的一个ANSI/ISO标准。它定义了一种语言(SQL)以及数据库的行为(事务、隔离级别等)。</strong></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: rgb(53, 179, 120);">下面是对一张具有3400W数据的表的统计过程,comid是整型,可以对比下执行效率差异:</strong></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><img data-backh="370" data-backw="570" data-ratio="0.6484375" src="/upload/21e4402d501cb7e39b48caf052ff5d81.jpg" data-type="jpeg" data-w="640" style="width: 100%;height: auto;"></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">使用建议</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">根据总结的内容,从效率层面说,<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">COUNT(*)≈ COUNT(1) > COUNT(字段)</code>,又因为 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">COUNT(*)</code>是SQL92定义的标准统计数的语法,<strong style="color: rgb(53, 179, 120);">我们建议使用 COUNT(*)。</strong></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">我们再来看看MySQL数据库做了哪些优化:以MySQL中比较常用的执行引擎InnoDB和MyISAM为例子。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: rgb(53, 179, 120);">1、MyISAM不支持事务,MyISAM中的锁是表级锁;</strong></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">因为MyISAM的锁是表级锁,所以同一张表上面的操作是串行执行的,MyISAM把表的总行数单独记录下来,如果只是使用COUNT(*)对表进行查询的时候,可以直接返回这个记录的数值就可以了。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这样表中总行数记录即可提供给COUNT(*)查询使用,又因MyISAM数据库是表级锁,数据库行数不会被并行修改,所以行数是准确无误的。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="color: rgb(53, 179, 120);">2、InnoDB支持事务,其中大部分操作都是行级锁。</strong></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这样就不能愉快的做这种缓存操作了,因为表的行数可能会被并发修改,缓存记录下来的总行数就不准确了。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在InnoDB中,使用<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">COUNT(*)</code>查询行数的时候,不需要进行扫表,只要获取记录行数而已。所以官方在针对InnoDB的 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">SELECT COUNT(*) FROM</code>语句执行过程,会自动选择一个成本较低的索引进行的话,这样就可以大大节省时间。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">InnoDB中索引分为聚簇索引(主键索引)和非聚簇索引(非主键索引),聚簇索引的叶子节点中保存的是整行记录,而非聚簇索引的叶子节点中保存的是该行记录的主键的值,非聚簇索引要比聚簇索引小很多,MySQL会优先选择最小的非聚簇索引来扫表,这样可以保证COUNT(*)的最优效率。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">当查询语句中包含WHERE以及GROUP BY条件,会有一些其他的因素影响,所以要综合考虑。</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">判断数据在否,COUNT怎么用?</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">上面那种很获取COUNT数的场景多用于数据分页,数据统计的场景,有很多的情况则是直接判断数据是否存在,这种情况下,其实是不关心有多少数据。但是我们CoreReview的时候还是会很经常看到这种做法:</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">select</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">COUNT</span>(*) <span style="color: rgb(198, 120, 221);line-height: 26px;">from</span> test_ucsyncdetail <span style="color: rgb(198, 120, 221);line-height: 26px;">where</span> comid > <span style="color: rgb(209, 154, 102);line-height: 26px;">520</span>;<br><br>int count = testDao.CountByComId(comId);<br><br>if(count>0){<br><br>//存在,则执行存在分支的代码<br>}else{<br><br>//不存在,则执行存在分支的代码<br>}<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">更好的写法应该是这样:</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(198, 120, 221);line-height: 26px;">select</span> <span style="color: rgb(209, 154, 102);line-height: 26px;">1</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">from</span> test_ucsyncdetail <span style="color: rgb(198, 120, 221);line-height: 26px;">where</span> comid > <span style="color: rgb(209, 154, 102);line-height: 26px;">520</span> limit1;<br><br>Object tda = testDao.checkExit(comId);<br><br>if(tda !=null){<br><br>//存在,则执行存在分支的代码<br>}else{<br><br>//不存在,则执行存在分支的代码<br>}<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">规避了SQL使用COUNT表达式扫表的操作,而是改用,数据库查询时遇到一条就返回,不会再继续查找和执行,如果存在传输回一条结果为1的数据 ,否则为null,业务代码中直接判断是否非空即可</p>
作者:微信小助手
<section data-role="outer" label="Powered by 135editor.com"> <section data-role="outer" style="box-sizing: border-box;"> <p style="text-align:center;font-family: mp-quote, -apple-system-font, system-ui, Arial, sans-serif;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.287962962962963" src="/upload/1c2615ff8aa10d76ccaee29dfc62a7f4.png" data-type="gif" data-w="1080"><br></p> <p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.75625" data-s="300,640" src="/upload/8f6708a84b91515d29f7161960644ef7.jpg" data-type="jpeg" data-w="1280" style=""></p> <p style="text-align:justify;margin-right: 10px;margin-left: 10px;max-inline-size: 100%;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);letter-spacing: 0.5px;line-height: 1.75em;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><span style="font-size: 12px;color: rgb(136, 136, 136);">题图:《教父》</span></p> <p style="text-align:justify;margin-right: 10px;margin-left: 10px;max-inline-size: 100%;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);letter-spacing: 0.5px;line-height: 1.75em;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"> </p> <p style="text-align:justify;margin-right: 10px;margin-left: 10px;max-inline-size: 100%;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);letter-spacing: 0.5px;line-height: 1.75em;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><br></p> <p style="text-align:justify;margin-right: 10px;margin-left: 10px;max-inline-size: 100%;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><span style="color: rgb(63, 63, 63);font-size: 16px;font-family: 微软雅黑, "Microsoft YaHei";">在知乎,有超过 9200 万人关心那些</span><strong><span style="font-size: 16px;color: rgb(0, 102, 255);font-family: 微软雅黑, "Microsoft YaHei";">越早知道越好的人生经验</span></strong></p> <p style="text-align:justify;margin-right: 10px;margin-left: 10px;max-inline-size: 100%;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><br></p> <p style="text-align:left;margin-right: 10px;margin-left: 10px;max-inline-size: 100%;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><span style="color: rgb(63, 63, 63);font-size: 16px;font-family: 微软雅黑, "Microsoft YaHei";">问题下的高赞回答里,答主们针对个人成长、人际交往、工作和爱情,给出了很多中肯的建议:</span></p> <p style="text-align:justify;margin-right: 10px;margin-left: 10px;max-inline-size: 100%;min-height: 1em;cursor: text;caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;box-sizing: border-box !important;outline: none 0px !important;"><br></p> <p style="text-align:justify;margin-right: 10px;margin-left: 10px;max-inline-size: 100%;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><br></p> <section data-tools="135编辑器" data-id="93009" data-width="96%" style="box-sizing:border-box;margin-left: auto;margin-right: auto;width: 96%;flex: 0 0 96%;"> <section style="display: flex;justify-content:flex-start;align-items: center;"> <section style="text-align: center;"> <section style="font-size: 40px;color: #0066ff;"> <strong>0</strong> <span data-original-title="" title=""><strong>1</strong></span> </section> </section> <section style="text-align: center;margin-left:5px;"> <section data-brushtype="text" style="font-size: 18px;text-align: left;" hm_fix="328:256"> <strong><span style="font-size: 16px;font-family: 微软雅黑, "Microsoft YaHei";">关于个人成长</span></strong> </section> </section> </section> </section> <p style="text-align:justify;margin-right: 10px;margin-left: 10px;max-inline-size: 100%;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><br></p> <p style="text-align:justify;max-inline-size: 100%;margin-right: 10px;margin-left: 10px;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><span style="max-inline-size: 100%;cursor: text;font-family: 微软雅黑, "Microsoft YaHei";box-sizing: border-box !important;outline: none 0px !important;"><strong style="max-inline-size: 100%;cursor: text;box-sizing: border-box !important;outline: none 0px !important;"><span style="max-inline-size: 100%;cursor: text;color: rgb(0, 102, 255);font-size: 16px;box-sizing: border-box !important;outline: none 0px !important;">@along阿龙<span style="max-inline-size: 100%;cursor: text;font-size: 14px;color: rgb(127, 127, 127);">(2.4 万+ 赞同)</span></span></strong></span></p> <p style="max-inline-size: 100%;margin-right: 10px;margin-left: 10px;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><br></p> <section data-role="list"> <ul class="list-paddingleft-2" style="padding-left: 30px;"> <li><p style="max-inline-size: 100%;margin-right: 10px;margin-left: 10px;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><strong style="max-inline-size: 100%;cursor: text;box-sizing: border-box !important;outline: none 0px !important;"><span style="max-inline-size: 100%;cursor: text;font-size: 16px;color: rgb(63, 63, 63);font-family: 微软雅黑, "Microsoft YaHei";box-sizing: border-box !important;outline: none 0px !important;">学会以结果为导向的思维。</span></strong></p></li> </ul> </section> <p style="max-inline-size: 100%;margin-right: 10px;margin-left: 10px;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><br></p> <p style="max-inline-size: 100%;margin-right: 10px;margin-left: 10px;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><span style="max-inline-size: 100%;cursor: text;font-size: 16px;color: rgb(63, 63, 63);font-family: 微软雅黑, "Microsoft YaHei";box-sizing: border-box !important;outline: none 0px !important;">什么是结果导向呢?结果导向指的是做一件事情,以结果为最终目标,以如何完成目标为导向的思考方式,与之相对的就是过程导向:只关心做事情的过程,不在乎结果。</span></p> <p style="max-inline-size: 100%;margin-right: 10px;margin-left: 10px;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><br></p> <p style="max-inline-size: 100%;margin-right: 10px;margin-left: 10px;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><span style="max-inline-size: 100%;cursor: text;font-size: 16px;color: rgb(63, 63, 63);font-family: 微软雅黑, "Microsoft YaHei";box-sizing: border-box !important;outline: none 0px !important;">只有当你以结果为导向,对结果负责,而不是沉浸在「我已经很努力了的过程里面」你才不会遇到困难找借口,总想着逃避。</span></p> <p style="text-align:justify;margin-right: 10px;margin-left: 10px;max-inline-size: 100%;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"> </p> <p style="text-align:justify;margin-right: 10px;margin-left: 10px;max-inline-size: 100%;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><br></p> <p style="text-align:justify;max-inline-size: 100%;margin-right: 10px;margin-left: 10px;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><span style="max-inline-size: 100%;cursor: text;font-family: 微软雅黑, "Microsoft YaHei";box-sizing: border-box !important;outline: none 0px !important;"><strong style="max-inline-size: 100%;cursor: text;box-sizing: border-box !important;outline: none 0px !important;"><span style="max-inline-size: 100%;cursor: text;color: rgb(0, 102, 255);font-size: 16px;box-sizing: border-box !important;outline: none 0px !important;">@君佳<span style="max-inline-size: 100%;cursor: text;font-size: 14px;color: rgb(127, 127, 127);">(1 万+ 赞同)</span></span></strong></span></p> <p style="max-inline-size: 100%;margin-right: 10px;margin-left: 10px;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><br></p> <section data-role="list"> <section data-role="list"> <ul class="list-paddingleft-2" style="padding-left: 30px;"> <li style="font-weight: bold;"><p style="max-inline-size: 100%;margin-right: 10px;margin-left: 10px;min-height: 1em;cursor: text;color: rgb(0, 0, 0);caret-color: rgb(255, 0, 0);line-height: 1.75em;letter-spacing: 0.5px;font-family: 微软雅黑, sans-serif;box-sizing: border-box !important;outline: none 0px !important;"><strong><span style="max-inline-size: 100%;cursor: text;font-size: 16px;color: rgb(63, 63, 63);font-family: 微软雅黑, "Microsoft YaHei";box-sizing: border-box !important;outline: none 0px !important;">任何没门槛的事情都不值得吹嘘。<
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;line-height: 1.6;letter-spacing: 0.034em;color: rgb(63, 63, 63);font-size: 16px;"> <p data-tool="mdnice编辑器" style="padding-bottom: 8px;padding-top: 23px;color: rgb(74, 74, 74);line-height: 1.75em;">今天,在朋友圈发了条动态来征集标题,顺便截了下最近买的一个基金。没想到遇到了好多"养鸡"的小伙伴,还认识了跟我一样韭的兄弟<img data-ratio="1" src="/upload/62536ac0e33cc6ee09831892ed79a569.png" data-type="png" data-w="20" style="display:inline-block;width:20px;vertical-align:text-bottom;"></p> <p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="1.247584541062802" data-s="300,640" src="/upload/dfb9564fa12c2270bde8e1b5fc11fbdc.png" data-type="png" data-w="828" style=""></p> <p style="text-align: left;"><br></p> <p style="text-align: left;">以前我在公司内网搜Redis相关资料的时候,无意中看到gitlab有个项目叫做「<strong>f**kRedis</strong>」,readme里边大概的内容是:「<strong>干*Redis,看下Redis是不是有真的这么牛逼,每次Redis出问题那些人都赖在网络上</strong>」</p> <p style="text-align: left;"><br></p> <p style="text-align: left;">(文章标题由朋友圈小伙伴友情提供)</p> <p style="text-align: left;"><br></p> <p style="text-align: left;">这篇文章主要的内容是<strong>Redis主从架构</strong>相关的,看完就能了解Redis是有什么措施来实现<strong>高可用</strong>的<br><br></p> </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;"> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/b14153cbea4deca169a4d056d4a9df27.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/b979a675d4c53c3e2f6de5787519515b.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/e6ef83cfd570a86d8c4aa4de60dbbbd5.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.8132530120481928" src="/upload/f196c162e5468d24c83340475f7c88c6.jpg" data-type="jpeg" data-w="1328" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/ab1e15738ad2d3d2858e8424c7ad9be.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/4f5f2049a2f33a83a812145bd420178d.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/a4f6c4a5339ac4edd1321005fe23d87c.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.3897058823529412" src="/upload/6370fe57a650ed6d28c9c256d4f4703b.jpg" data-type="jpeg" data-w="1632" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/60d3e1111f677f3b409df9889c59199d.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/1816d041415887adef47f9c857f16642.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.550531914893617" src="/upload/d06201d53018f84146cb015da119e4f5.jpg" data-type="jpeg" data-w="1504" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/1bc6ee04f3d312ed811d7a7e7e261c70.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/d303d95a2fbdaa635f35007eac15675.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/52c4b8055809c450f6de8c755aea779d.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/c12cb894c71e6481f884219caf5c233e.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5079872204472844" src="/upload/34a24a90764dcd427ded60c55542f2e9.jpg" data-type="jpeg" data-w="1878" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/459a6908a99f671f8b8da8a8fd509eba.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6428571428571429" src="/upload/7578dc85ba0868cfa302e82fc8ef2dcc.jpg" data-type="jpeg" data-w="1680" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/d0ef73809511b0d301cf663b11af222f.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/1d0e5a69a49a3276569d108ad5cdd456.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.4965786901270772" src="/upload/312a7d29c534ef1217aedf936e46c126.jpg" data-type="jpeg" data-w="2046" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/e408e11fde317b03247b9405556d38d4.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/63ebd0020bc365de77ae8c89a6c5620a.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/87407fe19472a63e79865f320232be01.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/4d58abcd47b067e187117809a2b54d39.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/14b750c9a7fea6dd375eb7a572eb1ab8.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.7786666666666666" src="/upload/a29a455838faf8b2dc96d34a08905b8.jpg" data-type="jpeg" data-w="750" style="display: block;margin-right: auto;margin-left: auto;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;font-weight: bold;font-size: 22px;border-bottom: 2px solid rgb(89, 89, 89);margin-bottom: 30px;color: rgb(89, 89, 89);"><span style="display: none;"></span><span style="display: inline-block;border-bottom: 2px solid rgb(89, 89, 89);">今日总结</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">Redis实现高可用</strong>:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> AOF/RDB持久化机制 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 主从架构(主服务器挂了,手动由从服务器顶上) </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 引入哨兵机制自动故障转义 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">主从复制原理</strong>:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> PSYNC命令两种模式:完全重同步、部分重同步 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 完全重同步:主从服务器建立连接、主服务器生成RDB文件发给从服务器、主服务器不阻塞(相关修改命令记录至buffer)、将修改命令发给从服务器 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 部分重同步:从服务器断线重连,发送RunId和offset给主服务器,主服务器判断offset和runId,将还未同步给从服务器的offset相关指令进行发送 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">哨兵机制</strong>:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 哨兵可以理解为特殊的Redis服务器,一般会组成哨兵集群 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 哨兵主要工作是监控、告警、配置以及选主 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 当主服务器发生故障时,会「选出」一台从服务器来顶上「客观下线」的服务器,由「领头哨兵」进行切换 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: rgb(89, 89, 89);"><strong style="color: rgb(71, 193, 168);">数据丢失</strong>:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> Redis的主从复制和故障转移阶段都有可能发生数据丢失问题(通过配置尽可能避免) </section></li> </ul> </section>