作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="line-height: 1.6;word-break: break-word;word-wrap: break-word;text-align: left;padding: 5px;font-size: 16px;color: #353535;word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 16px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">毫不夸张的说咱们后端工程师,无论在哪家公司,呆在哪个团队,做哪个系统,遇到的第一个让人头疼的问题绝对是数据库性能问题<strong style="font-weight: 700;color: rgb(248, 57, 41);">。</strong>如果我们有一套成熟的方法论,能让大家快速、准确的去选择出合适的优化方案,我相信能够快速准备解决咱么日常遇到的80%甚至90%的性能问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">从解决问题的角度出发,我们得先了解到<strong style="font-weight: 700;color: rgb(248, 57, 41);">问题的原因;</strong>其次我们得有一套<strong style="font-weight: 700;color: rgb(248, 57, 41);">思考、判断问题的流程方式,</strong>让我们合理的站在哪个层面选择方案;最后从众多的方案里面选择一个适合的方案进行解决问题,<strong style="font-weight: 700;color: rgb(248, 57, 41);">找到一个合适的方案的前提是我们自己对各种方案之间的优缺点、场景有足够的了解</strong>,没有一个方案是完全可以通吃通用的,软件工程没有银弹。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">下文的我工作多年以来,曾经使用过的八大方案,结合了平常自己学习收集的一些资料,以系统、全面的方式整理成了这篇博文,也希望能让一些有需要的同行在工作上、成长上提供一定的帮助。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">为什么数据库会慢?</span></h2> <section data-tool="mdnice编辑器" style="overflow-x: auto;"> <table> <thead> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <th style="border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;font-weight: bold;font-size: 16px;color: rgb(53, 53, 53);background-color: rgb(219, 217, 216);min-width: 85px;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">慢的本质</strong></th> <th style="border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;font-weight: bold;font-size: 16px;color: rgb(53, 53, 53);background-color: rgb(219, 217, 216);min-width: 85px;"><br></th> </tr> </thead> <tbody style="border-width: 0px;border-style: initial;border-color: initial;"> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;font-size: 16px;color: rgb(100, 86, 71);min-width: 85px;">查找的时间复杂度</td> <td style="border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;font-size: 16px;color: rgb(100, 86, 71);min-width: 85px;">查找算法</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;font-size: 16px;color: rgb(100, 86, 71);min-width: 85px;">存储数据结构</td> <td style="border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;font-size: 16px;color: rgb(100, 86, 71);min-width: 85px;">存储数据结构</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;font-size: 16px;color: rgb(100, 86, 71);min-width: 85px;">数据总量</td> <td style="border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;font-size: 16px;color: rgb(100, 86, 71);min-width: 85px;">数据拆分</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;font-size: 16px;color: rgb(100, 86, 71);min-width: 85px;">高负载</td> <td style="border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;font-size: 16px;color: rgb(100, 86, 71);min-width: 85px;">CPU、磁盘繁忙</td> </tr> </tbody> </table> </section> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">无论是关系型数据库还是NoSQL,任何存储系统决定于其查询性能的主要有三种:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">查找的时间复杂度</strong> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">数据总量</strong> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">高负载</strong> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">而决定于<strong style="font-weight: 700;color: rgb(248, 57, 41);">查找时间复杂度</strong>主要有两个因素:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">查找算法</strong> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> <strong style="font-weight: 700;color: rgb(248, 57, 41);">存储数据结构</strong> </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">无论是哪种存储,数据量越少,自然查询性能就越高,随着数据量增多,资源的消耗(CPU、磁盘读写繁忙)、耗时也会越来越高。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">从关系型数据库角度出发,索引结构基本固定是B+Tree,时间复杂度是O(log n),存储结构是行式存储。因此咱们对于关系数据库能优化的一般只有数据量。</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">而高负载造成原因有高并发请求、复杂查询等,导致CPU、磁盘繁忙等,而服务器资源不足则会导致慢查询等问题。该类型问题一般会选择集群、数据冗余的方式分担压力。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="1.001763668430335" src="/upload/bce978e8f74726e5315f0ad1335121b7.png" data-type="png" data-w="567" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">应该站在哪个层面思考优化?</span></h2> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.6909871244635193" src="/upload/b14c8b143cdf448e4b42ed05b9caf9b0.png" data-type="png" data-w="932" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">从上图可见,自顶向下的一共有四层,分别是<strong style="font-weight: 700;color: rgb(248, 57, 41);">硬件、存储系统、存储结构、具体实现。层与层之间是紧密联系的,每一层的上层是该层的载体;因此越往顶层越能决定性能的上限,同时优化的成本也相对会比较高,性价比也随之越低</strong>。以最底层的具体实现为例,那么索引的优化的成本应该是最小的,可以说加了索引后无论是CPU消耗还是响应时间都是立竿见影降低;然而一个简单的语句,无论如何优化加索引也是有局限的,当在具体实现这层没有任何优化空间的时候就得往上一层【存储结构】思考,思考是否从物理表设计的层面出发优化(如分库分表、压缩数据量等),如果是文档型数据库得思考下文档聚合的结果;如果在存储结构这层优化得没效果,得继续往再上一次进行考虑,是否关系型数据库应该不适合用在现在得业务场景?如果要换存储,那么得换怎样得NoSQL?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">所以咱们优化的思路,出于性价比的优先考虑具体实现,实在没有优化空间了再往上一层考虑。当然如果公司有钱,直接使用钞能力,绕过了前面三层,这也是一种便捷的应急处理方式。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">该篇文章不讨论顶与底的两个层面的优化,主要从存储结构、存储系统中间两层的角度出发进行探讨</strong>。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">八大方案总结</span></h2> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.7739251040221914" src="/upload/b6d1e15b6039b490809e62d166f204b3.png" data-type="png" data-w="721" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">数据库的优化方案核心本质有三种:<strong style="font-weight: 700;color: rgb(248, 57, 41);">减少数据量</strong>、<strong style="font-weight: 700;color: rgb(248, 57, 41);">用空间换性能</strong>、<strong style="font-weight: 700;color: rgb(248, 57, 41);">选择合适的存储系统,</strong>这也对应了开篇讲解的慢的三个原因<strong style="font-weight: 700;color: rgb(248, 57, 41);">:数据总量、高负载、*<em style="font-style: italic;color: rgb(248, 57, 41);">查找的时间复杂度。*</em></strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">这里大概解释下收益类型:短期收益,处理成本低,能紧急应对,久了则会有技术债务;长期收益则跟短期收益相反,短期内处理成本高,但是效果能长久使用,扩展性会更好。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">静态数据意思是,相对改动频率比较低的,也无需过多联表的,where过滤比较少。动态数据与之相反,更新频率高,通过动态条件筛选过滤。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;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: 0.8em 0;font-size: 16px;color: #353535;">减少数据量类型共有四种方案:<strong style="font-weight: 700;color: rgb(248, 57, 41);">数据序列化存储、数据归档、中间表生成、分库分表。</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">就如上面所说的,无论是哪种存储,数据量越少,自然查询性能就越高,随着数据量增多,资源的消耗(CPU、磁盘读写繁忙)、耗时也会越来越高。目前市面上的NoSQL基本上都支持分片存储,所以其天然分布式写的能力从数据量上能得到非常的解决方案。而关系型数据库,查找算法与存储结构是可以优化的空间比较少,因此咱们一般思考出发点只有从<strong style="font-weight: 700;color: rgb(248, 57, 41);">如何减少数据量</strong>的这个角度进行选择优化,因此本类型的优化方案主要针对<strong style="font-weight: 700;color: rgb(248, 57, 41);">关系型数据库</strong>进行处理。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="1" src="/upload/47b6d1731b4b37acf125332baa24f3d8.png" data-type="png" data-w="386" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">数据归档</span></h2> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.14218455743879474" src="/upload/ecb35382bc62e7e028d513816928fee.png" data-type="png" data-w="1062" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">注意点:别一次性迁移数量过多,建议低频率多次限量迁移。像MySQL由于删除数据后是不会释放空间的,可以执行命令OPTIMIZE TABLE释放存储空间,但是会锁表,如果存储空间还满足,可以不执行。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">建议优先考虑该方案,主要通过数据库作业把非热点数据迁移到历史表,如果需要查历史数据,可新增业务入口路由到对应的历史表(库)。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.923469387755102" src="/upload/a33727ee1bf578195f131508cf7764f6.png" data-type="png" data-w="588" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">中间表(结果表)</span></h2> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.13745387453874539" src="/upload/9b188bfbbea1869f3b96d79a7b8e37b1.png" data-type="png" data-w="1084" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">中间表(结果表)其实就是利用调度任务把复杂查询的结果跑出来存储到一张额外的物理表,因为这张物理表存放的是通过跑批汇总后的数据,因此可以理解成根据原有的业务进行了高度的数据压缩。以报表为例,如果一个月的源数据有数十万,我们通过调度任务以月的维度生成,那么等于把原有的数据压缩了几十万分之一;接下来的季报和年报可以根据月报*N来进行统计,以这种方式处理的数据,就算三年、五年甚至十年数据量都可以在接受范围之内,而且可以精确计算得到。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">那么数据的压缩比率是否越低越好?下面有一段口诀:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: disc;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 字段越多,粒度越细,灵活性越高,可以以中间表进行不同业务联表处理。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 字段越少,粒度越粗,灵活性越低,一般作为结果表查询出来。 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">数据序列化存储</span></h2> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.1735632183908046" src="/upload/b775ab72828f0b3915b0daa9d6e10945.png" data-type="png" data-w="870" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.766016713091922" src="/upload/33617f3fa8a2d0b19d913605353511d9.png" data-type="png" data-w="718" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">在数据库以序列化存储的方式,对于一些不需要结构化存储的业务来说是一种很好减少数据量的方式,特别是对于一些M*N的数据量的业务场景,如果以M作为主表优化,那么就可以把数据量维持最多是M的量级。另外像订单的地址信息,这种业务一般是不需要根据里面的字段检索出来,也比较适合。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">这种方案我认为属于一种临时性的优化方案,无论是从序列化后丢失了部份字段的查询能力,还是这方案的可优化性都是有限的。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;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: 0.8em 0;font-size: 16px;color: #353535;">分库分表作为数据库优化的一种非常经典的优化方案,特别是在以前NoSQL还不是很成熟的年代,这个方案就如救命草一般的存在。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">如今也有不少同行也会选择这种优化方式,但是从我角度来看,分库分表是一种优化成本很大的方案。这里我有几个建议:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: decimal;color: #f83929;font-size: 16px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 分库分表是实在没有办法的办法,应放到最后选择。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 优先选择NoSQL代替,因为NoSQL诞生基本上为了扩展性与高性能。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 究竟分库还是分表?量大则分表,并发高则分库 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;font-weight: 500;color: #353535;"> 不考虑扩容,一部做到位。因为技术更新太快了,每3-5年一大变。 </section></li> </ol> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">拆分方式</strong></span><span style="display: none;"></span></h3> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.43582089552238806" src="/upload/b4358d623732a6de6845af6d4752eb8.png" data-type="png" data-w="670" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">只要涉及到这个拆,那么无论是微服务也好,分库分表也好,拆分的方式主要分两种:<strong style="font-weight: 700;color: rgb(248, 57, 41);">垂直拆分、水平拆分</strong>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">垂直拆分更多是从<strong style="font-weight: 700;color: rgb(248, 57, 41);">业务角度</strong>进行拆分,主要是为了<strong style="font-weight: 700;color: rgb(248, 57, 41);">降低业务耦合度;</strong>此外以SQL Server为例,一页是8KB存储,如果在一张表里字段越多,一行数据自然占的空间就越大,那么一页数据所存储的行数就自然越少,那么每次查询所需要IO则越高因此性能自然也越慢;因此反之,减少字段也能很好提高性能。之前我听说某些同行的表有80个字段,几百万的数据就开始慢了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">水平拆分更多是从<strong style="font-weight: 700;color: rgb(248, 57, 41);">技术角度</strong>进行拆分,拆分后每张表的结构是一模一样的,简而言之就是把原有一张表的数据,通过<strong style="font-weight: 700;color: rgb(248, 57, 41);">技术手段</strong>进行分片到多张表存储,从根本上解决了数据量的问题。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.4485798237022527" src="/upload/eed74476753fc09d9690d183aef5dc55.png" data-type="png" data-w="1021" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.6808009422850412" src="/upload/cb1f46d445874666450f01add63b7fc9.png" data-type="png" data-w="849" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">路由方式</strong></span><span style="display: none;"></span></h3> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.5060728744939271" src="/upload/c6e075ae552849457cdd3a1ea63b6a1a.png" data-type="png" data-w="988" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">进行水平拆分后,根据分区键(sharding key)原来应该在同一张表的数据拆解写到不同的物理表里,那么查询也得根据分区键进行定位到对应的物理表从而把数据给查询出来。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">路由方式一般有三种<strong style="font-weight: 700;color: rgb(248, 57, 41);">区间范围、Hash、分片映射表</strong>,每种路由方式都有自己的优点和缺点,可以根据对应的业务场景进行选择。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">区间范围</strong>根据某个元素的区间的进行拆分,以时间为例子,假如有个业务我们希望以月为单位拆分那么表就会拆分像 table_2022-04,这种对于文档型、ElasticSearch这类型的NoSQL也适用,无论是定位查询,还是日后清理维护都是非常的方便的。那么缺点也明显,会因为业务独特性导致数据不平均,甚至不同区间范围之间的数据量差异很大。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">Hash</strong>也是一种常用的路由方式,根据Hash算法取模以数据量均匀分别存储在物理表里,缺点是对于带分区键的查询依赖特别强,如果不带分区键就无法定位到具体的物理表导致相关所有表都查询一次,而且在分库的情况下对于Join、聚合计算、分页等一些RDBMS的特性功能还无法使用。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.7084468664850136" src="/upload/967757dc96e229ad683b4b19495ff642.png" data-type="png" data-w="734" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">一般分区键就一个,假如有时候业务场景得用不是分区键的字段进行查询,那么难道就必须得全部扫描一遍?其实可以使用<strong style="font-weight: 700;color: rgb(248, 57, 41);">分片映射表</strong>的方式,简单来说就是额外有一张表记录额外字段与分区键的映射关系。举个例子,有张订单表,原本是以UserID作为分区键拆分的,现在希望用OrderID进行查询,那么得有额外得一张物理表记录了OrderID与UserID的映射关系。因此得先查询一次映射表拿到分区键,再根据分区键的值路由到对应的物理表查询出来。可能有些朋友会问,那这映射表是否多一个映射关系就多一张表,还是多个映射关系在同一张表。我优先建议单独处理,如果说映射表字段过多,那跟不进行水平拆分时的状态其实就是一致的,这又跑回去的老问题。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;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: 0.8em 0;font-size: 16px;color: #353535;">该类型的两个方案都是用来应对高负载的场景,方案有以下两种:<strong style="font-weight: 700;color: rgb(248, 57, 41);">分布式缓存、一主多从。</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">与其说这个方案叫用空间换性能,我认为用空间换资源更加贴切一些。因此两个方案的本质主要通<strong style="font-weight: 700;color: rgb(248, 57, 41);">数据冗余、集群</strong>等方式分担负载压力。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">对于关系型数据库而言,因为他的ACID特性让它天生不支持写的分布式存储,<strong style="font-weight: 700;color: rgb(248, 57, 41);">但是它依然天然的支持分布式读</strong>。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="1" src="/upload/db0a36c7e573d71a60220f3eb6cd1cc7.png" data-type="png" data-w="415" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">分布式缓存</span></h2> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;border-radius: 16px;overflow: hidden;"> <img class="rich_pages wxw-img" data-ratio="0.2551440329218107" src="/upload/8720c7c8683803a9dd623d34fce381dd.png" data-type="png" data-w="972" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">缓存层级可以分好几种:<strong style="font-weight: 700;color: rgb(248, 57, 41);">客户端缓存</strong>、<strong style="font-weight: 700;color: rgb(248, 57, 41);">API服务本地缓存</strong>和<strong style="font-weight: 700;color: rgb(248, 57, 41);">分布式缓存</strong>,咱们这次只聊分布式缓存。一般我们选择分布式缓存系统都会优先选择NoSQL的键值型数据库,例如Memcached、Redis,如今Redis的数据结构多样性,高性能,易扩展性也逐渐占据了分布式缓存的主导地位。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">缓存策略也主要有很多种:<strong style="font-weight: 700;color: rgb(248, 57, 41);">Cache-Aside</strong>、<strong style="font-weight: 700;color: rgb(248, 57, 41);">Read/Wirte-Through</strong>、<strong style="font-weight: 700;color: rgb(248, 57, 41);">Write-Back</strong>,咱们用得比较多的方式主要<strong style="font-weight: 700;color: rgb(248, 57, 41);">Cache-Aside,</strong>具体流程可看下图:</p> <figure data-tool="mdnice编辑器" style="margin: 0;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.6621848739495798" src="/upload/72b67871fb63e065db08c1be2fa412a0.png" data-type="png" data-w="595" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;" class="rich_pages wxw-img"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">我相信大家对分布式缓存相对都比较熟悉了,但是我在这里还是有几个注意点希望提醒一下大家:</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">避免滥用缓存</strong></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">缓存应该是按需使用,从28法则来看,80%的性能问题由主要的20%的功能引起</strong>。滥用缓存的后果会导致维护成本增大,而且有一些数据一致性的问题也不好定位。特别像一些动态条件的查询或者分页,key的组装是多样化的,量大又不好用keys指令去处理,当然我们可以用额外的一个key把记录数据的key以集合方式存储,删除时候做两次查询,先查Key的集合,然后再遍历Key集合把对应的内容删除。这一顿操作下来无疑是非常废功夫的,谁弄谁知道。</p> <figure data-tool="mdnice编辑器" style="margin: 0;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.19043321299638988" src="/upload/89422923718d8749f4d02740f734e431.png" data-type="png" data-w="1108" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;" class="rich_pages wxw-img"> </figure> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">避免缓存击穿</strong></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">当缓存没有数据,就得跑去数据库查询出来,这就是<strong style="font-weight: 700;color: rgb(248, 57, 41);">缓存穿透</strong>。假如某个时间临界点数据是空的例如周排行榜,穿透过去的无论查找多少次数据库仍然是空,而且该查询消耗CPU相对比较高,并发一进来因为缺少了缓存层的对高并发的应对,这个时候就会<strong style="font-weight: 700;color: rgb(248, 57, 41);">因为并发导致数据库资源消耗过高</strong>,这就是<strong style="font-weight: 700;color: rgb(248, 57, 41);">缓存击穿</strong>。数据库资源消耗过高就会导致其他查询超时等问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">该问题的解决方案也简单,对于查询到数据库的空结果也缓存起来,但是给一个相对快过期的时间。有些同行可能又会问,这样不就会造成了数据不一致了么?一般有数据同步的方案像分布式缓存、后续会说的一主多从、CQRS,只要存在<strong style="font-weight: 700;color: rgb(248, 57, 41);">数据同步</strong>这几个字,那就意味着会存在数据一致性的问题,<strong style="font-weight: 700;color: rgb(248, 57, 41);">因此如果使用上述方案,对应的业务场景应允许容忍一定的数据不一致。</strong></p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 20px;"><span style="display: none;"></span><span style="font-size: 16px;color: #222;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">不是所有慢查询都适用</strong></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">一般来说,慢的查询都意味着比较吃资源的(CPU、磁盘I/O)。举个例子,假如某个查询功能需要3秒时间,串行查询的时候并没什么问题,我们继续假设这功能每秒大概QPS为100,那么在第一次查询结果返回之前,接下来的所有查询都应该穿透到数据库,也就意味着这几秒时间有300个请求到数据库,如果这个时候数据库CPU达到了100%,那么接下来的所有查询都会超时,也就是无法有第一个查询结果缓存起来,从而还是形成了缓存击穿。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">一主多从</span></h2> <figure data-tool="mdnice编辑器" style="margin: 0;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.34579439252336447" src="/upload/d08abcd456e911cd1251e47400b0f801.png" data-type="png" data-w="856" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;" class="rich_pages wxw-img"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">常用的分担数据库压力还有一种常用做法,就是读写分离、一主多从。咱们都是知道关系型数据库天生是不具备分布式分片存储的,也就是不支持分布式写,但是它天然的支持分布式读。一主多从是部署多台从库只读实例,通过冗余主库的数据来分担读请求的压力,路由算法可有代码实现或者中间件解决,具体可以根据团队的运维能力与代码组件支持视情况选择。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">一主多从在还没找到根治方案前是一个非常好的应急解决方案,特别是在现在云服务的年代,扩展从库是一件非常方便的事情,而且一般情况只需要运维或者DBA解决就行,无需开发人员接入。当然这方案也有缺点,因为数据无法分片,所以主从的数据量完全冗余过去,也会导致高的硬件成本。从库也有其上限,从库过多了会主库的多线程同步数据的压力。</p> <figure data-tool="mdnice编辑器" style="margin: 0;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.6688829787234043" src="/upload/6ce6c5a6fb1148ac7d970c97013e6594.png" data-type="png" data-w="752" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;" class="rich_pages wxw-img"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;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: 0.8em 0;font-size: 16px;color: #353535;">NoSQL主要以下五种类型:<strong style="font-weight: 700;color: rgb(248, 57, 41);">键值型、文档型、列型、图型、搜素引擎,</strong>不同的存储系统直接决定了<strong style="font-weight: 700;color: rgb(248, 57, 41);">查找算法</strong>、<strong style="font-weight: 700;color: rgb(248, 57, 41);">存储数据结构</strong>,也应对了需要解决的不同的业务场景。NoSQL的出现也解决了关系型数据库之前面临的难题(性能、高并发、扩展性等)。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">例如,ElasticSearch的查找算法是倒排索引,可以用来代替关系型数据库的低性能、高消耗的Like搜索(全表扫描)。而Redis的Hash结构决定了时间复杂度为O(1),还有它的内存存储,结合分片集群存储方式以至于可以支撑数十万QPS。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">因此本类型的方案主要有两种:<strong style="font-weight: 700;color: rgb(248, 57, 41);">CQRS、替换(选择)存储,</strong>这两种方案的最终本质基本是一样的主要使用合适存储来弥补关系型数据库的缺点,只不过切换过渡的方式会有点不一样。</p> <figure data-tool="mdnice编辑器" style="margin: 0;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="1.1602787456445993" src="/upload/92e53044131eec421d5040e36dfcf31c.png" data-type="png" data-w="861" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;" class="rich_pages wxw-img"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">CQRS</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;"><strong style="font-weight: 700;color: rgb(248, 57, 41);">CQS(命令查询分离)指同一个对象中作为查询或者命令的方法,每个方法或者返回的状态,要么改变状态,但不能两者兼备</strong></p> <figure data-tool="mdnice编辑器" style="margin: 0;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.3802083333333333" src="/upload/2311953c4ded731af91ffc3b8ca457ff.png" data-type="png" data-w="960" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;" class="rich_pages wxw-img"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">讲解CQRS前得了解CQS,有些小伙伴看了估计还没不是很清晰,我这里用通俗的话解释:某个对象的数据访问的方法里,要么只是查询,要么只是写入(更新)。而CQRS(命令查询职责分离)基于CQS的基础上,用物理数据库来写入(更新),而用另外的存储系统来查询数据。因此我们在某些业务场景进行存储架构设计时,可以通过关系型数据库的ACID特性进行数据的更新与写入,用NoSQL的高性能与扩展性进行数据的查询处理,这样的好处就是关系型数据库和NoSQL的优点都可以兼得,同时对于某些业务不适于一刀切的替换存储的也可以有一个平滑的过渡。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">从代码实现角度来看,不同的存储系统只是调用对应的接口API,因此CQRS的难点主要在于如何进行数据同步。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(248, 57, 41);">数据同步方式</span></h2> <figure data-tool="mdnice编辑器" style="margin: 0;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.30808080808080807" src="/upload/854d0055ee530706c62f17885e8326f8.png" data-type="png" data-w="990" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;" class="rich_pages wxw-img"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">一般讨论到数据同步的方式主要是分<strong style="font-weight: 700;color: rgb(248, 57, 41);">推</strong>和<strong style="font-weight: 700;color: rgb(248, 57, 41);">拉:</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">推指的是由数据变更端通过直接或者间接的方式把数据变更的记录发送到接收端,从而进行数据的一致性处理,这种主动的方式优点是实时性高。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">拉指的是接收端定时的轮询数据库检查是否有数据需要进行同步,这种被动的方式从实现角度来看比推简单,因为推是需要数据变更端支持变更日志的推送的。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">而推的方式又分两种:CDC(变更数据捕获)和领域事件。对于一些旧的项目来说,某些业务的数据入口非常多,无法完整清晰的梳理清楚,这个时候CDC就是一种非常好的方式,只要从最底层数据库层面把变更记录取到就可。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">对于已经服务化的项目来说领域事件是一种比较舒服的方式,因为CDC是需要数据库额外开启功能或者部署额外的中间件,而领域事件则不需要,从代码可读性来看会更高,也比较开发人员的维护思维模式。</p> <figure data-tool="mdnice编辑器" style="margin: 0;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.4581850533807829" src="/upload/a9c7659cbce2abeae1ae717d92bc4a04.png" data-type="png" data-w="1124" style="border-radius: 6px;display: block;margin: 20px auto;max-width: 95%;object-fit: contain;" class="rich_pages wxw-img"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;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: 0.8em 0;font-size: 16px;color: #353535;">因为从本质来看该模式与CQRS的核心本质是一样的,主要是要对NoSQL的优缺点有一个全面认识,这样才能在对应业务场景选择与判断出一个合适的存储系统。这里我像大家介绍一本书马丁.福勒《NoSQL精粹》,这本书我重复看了好几遍,也很好全面介绍各种NoSQL优缺点和使用场景。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">当然替换存储的时候,我这里也有个建议:加入一个中间版本,该版本做好数据同步与业务开关,数据同步要保证全量与增加的处理,随时可以重来,业务开关主要是为了后续版本的更新做的一个临时型的功能,主要避免后续版本更新不顺利或者因为版本更新时导致的数据不一致的情况出现。在跑了一段时间后,验证了两个不同的存储系统数据是一致的后,接下来就可以把数据访问层的底层调用替换了。如此一来就可以平滑的更新切换。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;color: #222;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: 0.8em 0;font-size: 16px;color: #353535;">本文到这里就把八大方案介绍完了,在这里再次提醒一句,每个方案都有属于它的应对场景,咱们只能根据业务场景选择对应的解决方案,没有通吃,没有银弹。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 1.75;margin: 0.8em 0;font-size: 16px;color: #353535;">这八个方案里,大部分都存在数据同步的情况,只要存在数据同步,无论是一主多从、分布式缓存、CQRS都好,都会有数据一致性的问题导致,因此这些方案更多适合一些只读的业务场景。当然有些写后既查的场景,可以通过过渡页或者广告页通过用户点击关闭切换页面的方式来缓解数据不一致性的情况。</p> </section>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;word-break: break-word;word-wrap: break-word;text-align: left;font-size: 14px;padding: 10px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">大家好,我是老三,上一篇我们聊了 <a href="https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247491980&idx=1&sn=22c357da998773d57115d71c3f5708c3&scene=21#wechat_redirect" style="text-decoration: none;color: #1e6bb8;word-wrap: break-word;font-weight: bold;border-bottom: 1px solid #1e6bb8;" data-linktype="2">如何防止订单重复支付</a> 这篇和大家聊聊如何防止重复下单,文章很短,大概只需要几分钟阅读。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left: 3px solid rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;margin: 0px;color: black;line-height: 26px;">关注公众号「<strong style="font-weight: bold;color: black;">三分恶</strong>」,回复「<strong style="font-weight: bold;color: black;">666</strong>」,领取七百多页独家原创的面试手册!</p> <figure style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5145098039215686" src="/upload/df52b8305cb9bac55ff97513708dede8.png" data-type="png" data-w="2550" style="display: block;margin: 0 auto;max-width: 100%;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 面渣逆袭手册 </figcaption> </figure> </blockquote> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 24px;">用户下单流程</h1> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">我们从用户浏览商品开始,看看用户下单的简要过程:</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="1.0091047040971168" src="/upload/7b0a754432ee1f2af50f3c6fed5ea270.jpg" data-type="jpeg" data-w="1318" style="display: block;margin: 0 auto;max-width: 100%;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 用户下单简要过程 </figcaption> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 浏览商品:用户查看商品详情 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 加购/结算:用户可以选择直接购买商品,也可以先加入购物车,用户购买的这一步就是结算 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 确认下单:结算完成,就进入了下单页面, <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #ff6441;">提交订单</code>,这一步就会生成一个订单,然后进入付款页面 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">我们可以看到,下单是发生在结算之后,下单之后,会生成唯一的订单号,接下来,客户端需要用这个订单号去完成支付。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">那接下来先看看,为什么发生重复下单?</p> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 24px;">为什么会重复下单</h1> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">为什么会重复下单,对于订单服务而言,就是接到了多个下单的请求,原因可能有很多,最常见的是这两种:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 用户重复提交 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 网络原因导致的超时重试 </section></li> </ul> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3269230769230769" src="/upload/9e854099a6c015ca03c599dd9c20ed97.png" data-type="png" data-w="1144" style="display: block;margin: 0 auto;max-width: 100%;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 重复下单原因 </figcaption> </figure> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 24px;">如何防止重复下单</h1> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">防止用户提交,最常规的做法,就是客户端点击下单之后,在收到服务端响应之前,按钮置灰。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">当然,防止重复下单,肯定不能只依靠客户端,可能会因为一些网络的抖动,导致仍然有重复的请求到达服务端,所以还是要在服务端做防重/幂等的处理。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">PS:这里额外插入一点我对防重和幂等的理解:防重指的是防止重复提交,幂等指的是多次请求如一次,简单说,就是防重可以给对重复请求抛异常,幂等是对重复的请求响应第一次的结果,在我们讨论的这个场景里,幂等就是响应唯一的订单号。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4307116104868914" src="/upload/308c18eb21d29c1948b663583fab57b2.png" data-type="png" data-w="534" style="display: block;margin: 0 auto;max-width: 100%;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 防重和幂等 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">防重第一步,需要识别请求是否重复,这一步,需要客户端配合实现。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">为什么呢?大家想一下,下单的时候,服务端怎么去判断这个下单请求是否唯一呢?金额?商品?优惠券?……万一用户就是喜欢,又下了一个一模一样的单呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">所以,需要客户端在请求下单接口的时候,需要生成一个唯一的请求号:<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #ff6441;">requestId</code>,服务端拿这个请求号,判断是否重复请求。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">那么,接下来,压力就给到服务端了,看看服务端怎么实现防重/幂等吧!</p> <h2 data-tool="mdnice编辑器" style="padding: 12px 0px;font-size: 22px;text-align: center;font-weight: bold;color: black;line-height: 1.1em;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));">利用数据库实现幂等</span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">可以在订单表<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #ff6441;">t_order</code>里添加一个字段:<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #ff6441;">requestId</code>,添加唯一索引:</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5594405594405595" src="/upload/78e150b0b0b98faad52ffc00f3128212.png" data-type="png" data-w="572" style="display: block;margin: 0 auto;max-width: 100%;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 唯一请求字段 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">这样一来,如果是重复的请求,在落库的时候就会报错,为了保证幂等性,我们可以catch住这个异常,根据requestId获取订单号,然后向客户端响应订单号。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">大概的代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;max-width: 100%;border-radius: 4px;margin: 10px auto 0 auto;"><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;">PlaceOrderResVO <span style="color: #61aeee;line-height: 26px;">placeOrder</span><span style="line-height: 26px;">(PlaceOrderReqVO reqVO)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">try</span> {<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//下单业务逻辑</span><br> ……<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//生成订单号</span><br> String oid=generateOid();<br> ……<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//订单落库</span><br> Order order = orderMapper.saveOrder(orderDO); <br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//响应订单</span><br> resVO.setOid(order.getOid());<br> <span style="color: #c678dd;line-height: 26px;">return</span> resVO;<br> } <span style="color: #c678dd;line-height: 26px;">catch</span>(UniqueKeyViolationException e) {<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 发生了重复异常</span><br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 根据请求号获取订单</span><br> Order order = getOrderByRequestId(reqVO.getRequestId());<br> resVO.setOid(order.getOid());<br> <span style="color: #c678dd;line-height: 26px;">return</span> resVO;<br> } <span style="color: #c678dd;line-height: 26px;">catch</span> (Exception e) {<br> }<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">当然,这里不太好的地方是,拿异常来做业务判断。</p> <h2 data-tool="mdnice编辑器" style="padding: 12px 0px;font-size: 22px;text-align: center;font-weight: bold;color: black;line-height: 1.1em;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));">利用Redis防重</span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">另外一个办法,就是下单请求的时候要加锁了,通常我们的服务都是集群部署,所以一般都是用Redis实现分布式锁。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">大概的逻辑:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 就是以 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #ff6441;">requestId</code>为维度,进行加锁,如果获取锁失败,就抛一个自定义的重复下单异常。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 如果获取到锁,先check一下,是否已经下单,为了提高性能,下单完成后,也把下单的结果放在Redis缓存里。 </section></li> </ul> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.2682291666666667" src="/upload/ced671993709699a9f1609d5b11fc147.png" data-type="png" data-w="1536" style="display: block;margin: 0 auto;max-width: 100%;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> redis防重逻辑 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">大概的代码如下:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;max-width: 100%;border-radius: 4px;margin: 10px auto 0 auto;"><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> PlaceOrderResVO <span style="color: #61aeee;line-height: 26px;">placeOrder</span><span style="line-height: 26px;">(PlaceOrderReqVO reqVO)</span> </span>{<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//加锁</span><br> RLock orderLock = redissonClient.getLock(RedisConstant.PLACE_ORDER_LOCK_KEY + reqVO.getRequestId());<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//获取锁失败,抛出重复下单异常</span><br> <span style="color: #c678dd;line-height: 26px;">if</span>(orderLock.isExistes){<br> <span style="color: #c678dd;line-height: 26px;">throw</span> <span style="color: #c678dd;line-height: 26px;">new</span> OrderRepeatException();<br> }<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 加锁</span><br> orderLock.lock();<br> <span style="color: #c678dd;line-height: 26px;">try</span> {<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//检查是否已经下单</span><br> RBucket<PlaceOrderResVO> orderCache = redissonClient.getBucket(RedisConstant.PLACE_ORDER_LOCK_KEY+reqVO.getRequestId());<br> <span style="color: #c678dd;line-height: 26px;">if</span>(orderCache.isExistes){<br> <span style="color: #c678dd;line-height: 26px;">return</span> orderCache.get();<br> }<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//下单业务逻辑</span><br> ……<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//落库</span><br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//订单落库</span><br> Order order = orderMapper.saveOrder(orderDO); <br> ……<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//缓存结果</span><br> orderCache.put(resVO);<br> <span style="color: #c678dd;line-height: 26px;">return</span> resVO;<br> } <br> } <span style="color: #c678dd;line-height: 26px;">catch</span> (Exception e) {<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//……</span><br> } <span style="color: #c678dd;line-height: 26px;">finally</span> {<br> orderLock.unlock();<br> }<br> <span style="color: #c678dd;line-height: 26px;">return</span> resVO;<br> }<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">这里再说明一下:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: square;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 为什么获取不到锁的时候要抛异常呢? </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">因为下单里面其实还有一些其它的业务流程,比如锁库存、清优惠券……而此时,获取到锁的请求的下单流程还没有结束,下单的结果还获取不到,没法完成响应,也就没办法做幂等。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">客户端,也可以根据响应的状态码,进行特殊处理,比如这个异常先不提示,但是允许用户再次点击下单按钮,来提升用户的体验。</p> <hr data-tool="mdnice编辑器" style="height: 1px;margin: 10px 0px;border-right: none;border-bottom: none;border-left: none;border-top: 1px solid black;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">好了,这一篇简单的小文章就这样结束了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;">最近工作实在太忙了,基本上每天都是九点多、十点多下班,写新的文章,还有《面渣逆袭手册》的维护,都会努力抽时间去做,谢谢大家的理解和支持!</p> <br data-tool="mdnice编辑器"> <hr data-tool="mdnice编辑器" style="height: 1px;margin: 10px 0px;border-right: none;border-bottom: none;border-left: none;border-top: 1px solid black;"> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;font-size: 14px;"><big><strong>参考:</strong></big></p> <p><span style="font-size: 10px;">[1]. 重复下单:https://blog.csdn.net/yangguosb/article/details/106095858)</span></p> <p><span style="font-size: 10px;">[2]. 用幂等防止重复订单:https://cloud.tencent.com/developer/article/1121727</span></p> <span style="font-size: 15px;display: block;text-align: center;margin-top: 50px;color: #999;border-bottom: 1px solid #eee;">- END -</span> </section> <p><br></p>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="word-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-size: 16px;padding: 5px;color: rgb(51, 51, 51);letter-spacing: 1px;line-height: 30px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 24px;" data-mpa-powered-by="yiban.io"> <p style="text-align: center;margin-bottom: 0em;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.4393939393939394" data-s="300,640" src="/upload/451328793afaa54df1a4b7f62026e2e0.png" data-type="png" data-w="2376" style=""></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">你好,我是悟空,我的好朋友艾小仙同学发现了一个延迟注册的坑,这篇的排查思路还挺有意思的,推荐大家学习一波~</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">Eureka 有个延迟注册的功能,也就是在服务启动成功之后不立刻注册到 Eureka Server,而是延迟一段时间再去注册,这样做的主要目的是因为虽然服务启动成功了,可能还有一些框架或者业务的代码没有初始化完成,可能会导致调用的报错,所以需要延迟注册。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">但是发现,然并卵啊,好像这个延迟注册并没有生效,也是开始了排查之路。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(255, 90, 5);">延迟注册</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">首先,延迟注册的功能主要依赖这两个参数,<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">eureka.client.initial-instance-info-replication-interval-seconds</code>代表第一次初始化延迟注册的时间间隔,<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">eureka.client.instance-info-replication-interval-seconds</code>则代表后续同步注册的时间间隔。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;max-width: 100%;border-radius: 4px;margin: 10px auto 0 auto;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CJ35Z2cnZA2MoRqGkl9iaAyu9L7ibh2bt9L4UQwRmYHK5fiafuxy7m06dhBI4v473ricgWY47qCkECFpqHhEBNXRO0K6VuVVxUp0/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">eureka.client.initial-instance-info-replication-interval-seconds=40 //默认40秒<br>eureka.client.instance-info-replication-interval-seconds=30 //默认30秒</code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">我们从源码先来看是怎么做到延迟注册的,先看 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">DiscoveryClient</code> 的 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">initScheduledTasks</code> ,这里创建了同步注册到 Eureka Server 的定时任务。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.30210016155088854" src="/upload/960c1ff6b08b5fa9c7a8e2878a69581d.jpg" data-type="jpeg" data-w="1238" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">之后调用 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">start</code> 方法创建定时任务,并且延迟 40 秒执行,也就是我们达到的延迟注册的效果。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.09339407744874716" src="/upload/1d6f06cb33f900e54f3f7f8e32367e49.jpg" data-type="jpeg" data-w="1756" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.24754901960784315" src="/upload/da7f51e51d0012aa2164434ee97dfe24.jpg" data-type="jpeg" data-w="1632" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">默认的第一次注册,也就是延迟注册的时间是 40 秒,之后每 30 秒会同步注册信息。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.43944265809217575" src="/upload/535e6aafb4cdca8d7baa01f466b04f39.jpg" data-type="jpeg" data-w="1866" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">但是,即便我们配置了这俩属性,发现好像没什么卵用,接下来我们要排查下到底是为啥捏?</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(255, 90, 5);">第一个问题</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">我发现在 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">InstanceInfoReplica</code> 中存在这样一段终止当前线程池任务,并且直接调用 run 方法的存在,猜测失效就是他直接调用导致延迟任务没有生效,因为这个方法的直接调用导致延迟注册压根就没效果嘛。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.578468130690948" src="/upload/d1de520a6861fcb4ef3c5c493e1aa296.jpg" data-type="jpeg" data-w="1867" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">看起来他存在两个调用,第一个是<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">registerHealthCheck</code>,当存在这个健康检查什么玩意儿的时候就会去调用<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">onDemandUpdate</code>。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3987138263665595" src="/upload/16ef353fa15f62cb351368983832fc95.jpg" data-type="jpeg" data-w="1866" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">经过排查我们发现,只要配置了<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">eureka.client.healthcheck.enabled=true</code>,就会创建 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">HealthCheckHandler</code>的实例出来,默认情况下他是<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">false</code>的,所以应该是对我们没有影响的。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4904632152588556" src="/upload/3618c1173d15bb84a049e8824df85ff4.jpg" data-type="jpeg" data-w="2202" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">这里需要特别说明一下 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">eureka.client.healthcheck.enabled </code>的作用,默认 Eureka 根据心跳来决定应用的状态,如果是这个属性配置成 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">true</code>的话,则是会根据 Spring Boot Actuator 来决定,而不是心跳了。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">比如我们可以实现 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">HealthIndicator</code>接口,自己写一个<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">Controller</code>来动态改变服务的状态</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;max-width: 100%;border-radius: 4px;margin: 10px auto 0 auto;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CJ35Z2cnZA2MoRqGkl9iaAyu9L7ibh2bt9L4UQwRmYHK5fiafuxy7m06dhBI4v473ricgWY47qCkECFpqHhEBNXRO0K6VuVVxUp0/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #61aeee;line-height: 26px;">@RestController</span><br><span style="color: #c678dd;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span> <span style="color: #e6c07b;line-height: 26px;">ControllerTest</span> </span>{<br> <span style="color: #61aeee;line-height: 26px;">@Autowired</span><br> <span style="color: #c678dd;line-height: 26px;">private</span> HealthChecker healthChecker;<br><br> <span style="color: #61aeee;line-height: 26px;">@RequestMapping</span>(<span style="color: #98c379;line-height: 26px;">"/change"</span>)<br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> String <span style="color: #61aeee;line-height: 26px;">test</span><span style="line-height: 26px;">(Boolean flag)</span> </span>{<br> healthChecker.setUp(<span style="color: #c678dd;line-height: 26px;">new</span> AtomicBoolean(flag));<br> <span style="color: #c678dd;line-height: 26px;">return</span> <span style="color: #98c379;line-height: 26px;">"success"</span>;<br> }<br><br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">实现<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">HealthChecker</code>,这样会发现启动、下线服务 Eureka Server 的状态不会变成 Down,只有通过调用接口手动改变应用状态 Server 的状态才会发生改变,大家可以自行测试。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;max-width: 100%;border-radius: 4px;margin: 10px auto 0 auto;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CJ35Z2cnZA2MoRqGkl9iaAyu9L7ibh2bt9L4UQwRmYHK5fiafuxy7m06dhBI4v473ricgWY47qCkECFpqHhEBNXRO0K6VuVVxUp0/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #61aeee;line-height: 26px;">@Component</span><br><span style="color: #c678dd;line-height: 26px;">public</span> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">class</span> <span style="color: #e6c07b;line-height: 26px;">HealthChecker</span> <span style="color: #c678dd;line-height: 26px;">extends</span> <span style="color: #e6c07b;line-height: 26px;">EurekaHealthIndicator</span> <span style="color: #c678dd;line-height: 26px;">implements</span> <span style="color: #e6c07b;line-height: 26px;">HealthIndicator</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">private</span> AtomicBoolean up = <span style="color: #c678dd;line-height: 26px;">new</span> AtomicBoolean(<span style="color: #c678dd;line-height: 26px;">true</span>);<br><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #61aeee;line-height: 26px;">HealthChecker</span><span style="line-height: 26px;">(EurekaClient eurekaClient, EurekaInstanceConfig instanceConfig, EurekaClientConfig clientConfig)</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">super</span>(eurekaClient, instanceConfig, clientConfig);<br> }<br><br> <span style="color: #61aeee;line-height: 26px;">@Override</span><br> <span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> Health <span style="color: #61aeee;line-height: 26px;">health</span><span style="line-height: 26px;">()</span> </span>{<br> <span style="color: #c678dd;line-height: 26px;">if</span>(up.get()){<br> <span style="color: #c678dd;line-height: 26px;">return</span> Health.up().build();<br> }<span style="color: #c678dd;line-height: 26px;">else</span>{<br> <span style="color: #c678dd;line-height: 26px;">return</span> Health.down().build();<br> }<br> }<br><br></code></pre> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(255, 90, 5);">第二个问题</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">第一个问题我们找到了,发现他不是导致我们问题的根因,于是继续排查。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">发现第二个调用,在<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">DiscoveryClient</code>注册了状态事件变更的监听,如果状态发生变更,也会去调用 onDemandUpdate ,影响延迟注册的效果。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">这里存在一个配置项<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">onDemandUpdateStatusChange</code>,默认是<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">true</code>,所以应该是他没错了。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.514745308310992" src="/upload/c205cbf1a9cf548fe41a9644119135a5.jpg" data-type="jpeg" data-w="1492" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">进入<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">StatusChangeListener</code>,找到了一个调用。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3358085808580858" src="/upload/fd6da25ff8e443159b3a0535fd2c4980.jpg" data-type="jpeg" data-w="2424" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">就是通过<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">setInstanceStatus</code>方法触发的事件通知。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.616710875331565" src="/upload/c8bf45dd5c955bfec1bf71cf400b9fd6.jpg" data-type="jpeg" data-w="1508" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">这里存在 6 个调用,一一排查,通过源码找啊找,最终定位到服务启动自动装配的地方,在这里去修改服务状态为 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">UP</code>,然后触发事件通知,启动 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">start</code> 方法调用<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">register</code>方法。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.3956043956043956" src="/upload/b85c3c204ecc2e59cd0ca738b7ca2f57.jpg" data-type="jpeg" data-w="2730" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">继续调用,修改应用为上线<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">UP</code>状态。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.2801047120418848" src="/upload/755c9b13a7ee9e82258b568635d2f8b8.jpg" data-type="jpeg" data-w="3056" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">由此我们知道,只要服务启动成功,就会触发事件通知,所以这个基本上是启动成功立刻就会去注册到 Eureka Server,这就会导致延迟注册的失效,从启动日志也能直观的看到这个效果。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.15680473372781065" src="/upload/7a226ee332d543d87833a3c14326509f.jpg" data-type="jpeg" data-w="3380" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(255, 90, 5);">验证</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">为了验证我的猜想,我把这两个配置同时配置成<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">false</code>,并且把延迟注册的时间调整到非常大。</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;max-width: 100%;border-radius: 4px;margin: 10px auto 0 auto;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/CJ35Z2cnZA2MoRqGkl9iaAyu9L7ibh2bt9L4UQwRmYHK5fiafuxy7m06dhBI4v473ricgWY47qCkECFpqHhEBNXRO0K6VuVVxUp0/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">eureka.client.healthcheck.enabled=false<br>eureka.client.onDemandUpdateStatusChange=false<br>eureka.client.initial-instance-info-replication-interval-seconds=9999999 //默认40秒<br>eureka.client.instance-info-replication-interval-seconds=999999 //默认30秒</code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">但是,但是!!!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">发现过了几十秒之后,还是注册到 Server 了,真的是醉了。。。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">那就继续看吧。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">再看下注册方法,可能不止一个地方存在调用,我们发现果然如此,有 3 个地方都调用了注册方法。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.29162656400384984" src="/upload/c65f7bb733f8852c27eb88a09fbd964f.jpg" data-type="jpeg" data-w="2078" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">第一个调用在<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">DiscoveryClient</code>注入的时候,这个看了下,<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">clientConfig.shouldEnforceRegistrationAtInit()</code>默认是<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">false</code>,方法不会进来,不管他了。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.30328738069989397" src="/upload/34f579554ae7cbd367b12c7b13030c77.jpg" data-type="jpeg" data-w="1886" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">那么继续看第二个调用,第二个调用你看<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">renew</code>方法,这一看我们就知道了,这不就是心跳吗?!</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">发送心跳如果返回<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">NOT_FOUND</code>,就会去注册了啊。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.40268456375838924" src="/upload/9a9451d7a42326204b8aa692bdb82418.jpg" data-type="jpeg" data-w="2682" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.40827586206896554" src="/upload/514c7127de7cf47e9b1003942ce5ec0f.jpg" data-type="jpeg" data-w="1450" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">感觉已经接近真相了,去找下 Server 心跳的源码,根据调用的路径找到源码位于<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">InstanceResource</code>中。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">可以看到第一次注册的时候从注册表拿到的实例信息是空的,所以直接返回了 false,就会返回 NOT FOUND 了。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.4776647501105705" src="/upload/b049f6f930bdff06a7aaf284f93b6ad4.jpg" data-type="jpeg" data-w="2261" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">看<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">registry.renew</code>方法,最终会调用到<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">AbstractInstanceRegistry</code>中,初始化的时候注册表<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">registry</code>肯定没有当前实例的信息,所以拿到是空的,返回了false,最终就返回了<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">NOT_FOUND</code>。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.289760348583878" src="/upload/9fc5408c7e27f90adba9e9bb8a1f6632.jpg" data-type="jpeg" data-w="2754" style="display: block;margin: 0 auto;max-width: 100%;margin-top: 10px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">因此,虽然我们把这两个参数都设置成了<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">false</code>,但是由于心跳默认 30 秒一次,所以最终我们发现配置的超级大的延迟注册的时间并没有完全生效。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 22px;text-align: left;margin: 20px 10px 0px 0px;"><span style="display: none;"></span><span style="font-size: 18px;font-weight: 700;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(255, 90, 5);">总结</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">OK,到此,延迟注册不生效的原因找到了,我们做一个总结。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">默认情况下,配置了延迟注册的时间并不会生效,因为事件监听默认是<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">true</code>,服务启动之后就会立刻注册到 Eureka Server。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">如果需要延迟注册生效,必须 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">eureka.client.healthcheck.enabled </code>、<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">eureka.client.onDemandUpdateStatusChange</code> 都为<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);word-break: break-all;color: rgba(255, 90, 5);font-family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;">false</code>。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 0;font-size: 16px;letter-spacing: 0.2em;line-height: 1.75;word-break: break-all;color: rgb(51,51,51);padding: 10px 0 10px 0;">即便我们把所有途径都封死了,但是发送心跳的线程仍然会去注册,所以这个延迟注册的时间最多也不会超过 30 秒,即便配置的延迟时间超过 30 秒。<span style="letter-spacing: 0.2em;"></span></p> </section>
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding: 0px 10px;line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;overflow-wrap: break-word;text-align: left;word-break: break-all;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 0px;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">不知不觉,面渣逆袭系列已经肝了差不多十篇,每一篇都是上万字,几十图,基本上涵盖了面试的主要知识点,这期MySQL结束之后,这个系列可能会暂时告一段落,作为面渣逆袭系列第一阶段的收官之作,大家多多<strong>点赞</strong>、<strong>收藏</strong>哦!</p> <section> <br> </section> <h1 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;color: black;font-size: 1.7em;font-weight: normal;border-bottom: 2px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="background: hsl(216, 100%, 68%);color: white;padding: 3px 10px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">基础</span></h1> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.5143769968051118" src="/upload/cae002c6aeec2cd6b137f862b5d405eb.png" data-type="png" data-w="313" style="display: block;max-width: 100%;width: 90%;margin: 0 auto;box-shadow: #CCC 0 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> MySQ Logo </figcaption> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">作为SQL Boy,基础部分不会有人不会吧?面试也不怎么问,基础掌握不错的小伙伴可以<strong style="font-weight: bold;color: hsl(216, 80%, 44%);">跳过</strong>这一部分。当然,可能会现场写一些SQL语句,SQ语句可以通过牛客、LeetCode、LintCode之类的网站来练习。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">1. 什么是内连接、外连接、交叉连接、笛卡尔积呢?</span></h2> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: disc;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 内连接(inner join):取得两张表中满足存在连接匹配关系的记录。 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 外连接(outer join):不只取得两张表中满足存在连接匹配关系的记录,还包括某张表(或两张表)中不满足匹配关系的记录。 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 交叉连接(cross join):显示两张表所有记录一一对应,没有匹配关系进行筛选,它是笛卡尔积在SQL中的实现,如果A表有m行,B表有n行,那么A和B交叉连接的结果就有m*n行。 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 笛卡尔积:是数学中的一个概念,例如集合A={a,b},集合B={1,2,3},那么A✖️B={<a,o>,<a,1>,<a,2>,<b,0>,<b,1>,<b,2>,}。 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">2. 那MySQL 的内连接、左连接、右连接有有什么区别?</span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">MySQL的连接主要分为内连接和外连接,外连接常用的有左连接、右连接。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.7867494824016563" src="/upload/b8a7e6edfe900ca8b84f353c1695fcd2.png" data-type="png" data-w="966" style="display: block;max-width: 100%;width: 90%;margin: 0 auto;box-shadow: #CCC 0 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> MySQL-joins-来源菜鸟教程 </figcaption> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: disc;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> inner join 内连接,在两张表进行连接查询时,只保留两张表中完全匹配的结果集 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> left join 在两张表进行连接查询时,会返回左表所有的行,即使在右表中没有匹配的记录。 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> right join 在两张表进行连接查询时,会返回右表所有的行,即使在左表中没有匹配的记录。 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">3.说一下数据库的三大范式?</span></h2> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.608955223880597" src="/upload/e6481e7c7913f74949887140f5f9ec50.png" data-type="png" data-w="670" style="display: block;max-width: 100%;width: 90%;margin: 0 auto;box-shadow: #CCC 0 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 数据库三范式 </figcaption> </figure> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: disc;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 第一范式:数据表中的每一列(每个字段)都不可以再拆分。例如用户表,用户地址还可以拆分成国家、省份、市,这样才是符合第一范式的。 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。例如订单表里,存储了商品信息(商品价格、商品类型),那就需要把商品ID和订单ID作为联合主键,才满足第二范式。 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 第三范式:在满足第二范式的基础上,表中的非主键只依赖于主键,而不依赖于其他非主键。例如订单表,就不能存储用户信息(姓名、地址)。 </section></li> </ul> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="1" src="/upload/d28ee12a8a622a52788119feb755854f.png" data-type="gif" data-w="300" style="display: block;max-width: 100%;width: 90%;margin: 0 auto;box-shadow: #CCC 0 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 你设计遵守范式吗? </figcaption> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">三大范式的作用是为了控制数据库的冗余,是对空间的节省,实际上,一般互联网公司的设计都是反范式的,通过冗余一些数据,避免跨表跨库,利用空间换时间,提高性能。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">4.varchar与char的区别?</span></h2> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.27370689655172414" src="/upload/37550c667c359a1236921ea35ea9f2f6.png" data-type="png" data-w="928" style="display: block;max-width: 100%;width: 90%;margin: 0 auto;box-shadow: #CCC 0 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> varchar </figcaption> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">char</strong>:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: disc;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> char表示定长字符串,长度是固定的; </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 如果插入数据的长度小于char的固定长度时,则用空格填充; </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 因为长度固定,所以存取速度要比varchar快很多,甚至能快50%,但正因为其长度固定,所以会占据多余的空间,是空间换时间的做法; </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 对于char来说,最多能存放的字符个数为255,和编码无关 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">varchar</strong>:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: disc;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> varchar表示可变长字符串,长度是可变的; </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 插入的数据是多长,就按照多长来存储; </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> varchar在存取方面与char相反,它存取慢,因为长度不固定,但正因如此,不占据多余的空间,是时间换空间的做法; </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 对于varchar来说,最多能存放的字符个数为65532 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">日常的设计,对于长度相对固定的字符串,可以使用char,对于长度不确定的,使用varchar更合适一些。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">5.blob和text有什么区别?</span></h2> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: disc;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> blob用于存储二进制数据,而text用于存储大字符串。 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> blob没有字符集,text有一个字符集,并且根据字符集的校对规则对值进行排序和比较 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">6.DATETIME和TIMESTAMP的异同?</span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">相同点</strong>:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: decimal;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 两个数据类型存储时间的表现格式一致。均为 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: hsl(216, 100%, 68%);">YYYY-MM-DD HH:MM:SS</code> </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 两个数据类型都包含「日期」和「时间」部分。 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 两个数据类型都可以存储微秒的小数秒(秒后6位小数秒) </section></li> </ol> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">区别</strong>:</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.7874564459930313" src="/upload/9ffcdf85c90bbc4e4138bfe315fba73c.png" data-type="png" data-w="574" style="display: block;max-width: 100%;width: 90%;margin: 0 auto;box-shadow: #CCC 0 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> DATETIME和TIMESTAMP的区别 </figcaption> </figure> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: decimal;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">日期范围</strong>:DATETIME 的日期范围是 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: hsl(216, 100%, 68%);">1000-01-01 00:00:00.000000</code> 到 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: hsl(216, 100%, 68%);">9999-12-31 23:59:59.999999</code>;TIMESTAMP 的时间范围是<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: hsl(216, 100%, 68%);">1970-01-01 00:00:01.000000</code> UTC<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: hsl(216, 100%, 68%);"> 到 ``2038-01-09 03:14:07.999999</code> UTC</p> </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">存储空间</strong>:DATETIME 的存储空间为 8 字节;TIMESTAMP 的存储空间为 4 字节</p> </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">时区相关</strong>:DATETIME 存储时间与时区无关;TIMESTAMP 存储时间与时区有关,显示的值也依赖于时区</p> </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">默认值</strong>:DATETIME 的默认值为 null;TIMESTAMP 的字段默认不为空(not null),默认值为当前时间(CURRENT_TIMESTAMP)</p> </section></li> </ol> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">7.MySQL中 in 和 exists 的区别?</span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">MySQL中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。我们可能认为exists比in语句的效率要高,这种说法其实是不准确的,要区分情景:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: decimal;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 如果查询的两个表大小相当,那么用in和exists差别不大。 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in。 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> not in 和not exists:如果查询语句使用了not in,那么内外表都进行全表扫描,没有用到索引;而not extsts的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。 </section></li> </ol> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">8.MySQL里记录货币用什么字段类型比较好?</span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">货币在数据库中MySQL常用Decimal和Numric类型表示,这两种类型被MySQL实现为同样的类型。他们被用于保存与货币有关的数据。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">例如salary DECIMAL(9,2),9(precision)代表将被用于存储值的总的小数位数,而2(scale)代表将被用于存储小数点后的位数。存储在salary列中的值的范围是从-9999999.99到9999999.99。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">DECIMAL和NUMERIC值作为字符串存储,而不是作为二进制浮点数,以便保存那些值的小数精度。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">之所以不使用float或者double的原因:因为float和double是以二进制存储的,所以有一定的误差。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">9.MySQL怎么存储emoji😊?</span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">MySQL可以直接使用字符串存储emoji。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">但是需要注意的,utf8 编码是不行的,MySQL中的utf8是阉割版的 utf8,它最多只用 3 个字节存储字符,所以存储不了表情。那该怎么办?</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">需要使用utf8mb4编码。</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/iahdQicCC5VBSsGkibhVicLUiaOMaR8fdicOEMC1UxVKS1icZrGaECGoK6NX4jHJ94R8iaJD1555Pj5rpbZK5b48mrZJOiccqakM9hjWB/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">alter</span> <span style="color: #c678dd;line-height: 26px;">table</span> blogs <span style="color: #c678dd;line-height: 26px;">modify</span> <span style="color: #c678dd;line-height: 26px;">content</span> <span style="color: #e6c07b;line-height: 26px;">text</span> <span style="color: #e6c07b;line-height: 26px;">CHARACTER</span> <span style="color: #c678dd;line-height: 26px;">SET</span> utf8mb4 <span style="color: #c678dd;line-height: 26px;">COLLATE</span> utf8mb4_unicode_ci <span style="color: #c678dd;line-height: 26px;">not</span> <span style="color: #56b6c2;line-height: 26px;">null</span>;<br></code></pre> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">10.drop、delete与truncate的区别?</span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">三者都表示删除,但是三者有一些差别:</p> <section data-tool="mdnice编辑器" style="overflow-x: auto;"> <table width="NaN"> <thead> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <th style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;background-color: rgb(240, 240, 240);color: rgb(51, 51, 51);font-weight: normal;min-width: 85px;"><br></th> <th style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;background-color: rgb(240, 240, 240);color: rgb(51, 51, 51);font-weight: normal;min-width: 85px;">delete</th> <th style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;background-color: rgb(240, 240, 240);color: rgb(51, 51, 51);font-weight: normal;min-width: 85px;">truncate</th> <th style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;background-color: rgb(240, 240, 240);color: rgb(51, 51, 51);font-weight: normal;min-width: 85px;">drop</th> </tr> </thead> <tbody style="border-width: 0px;border-style: initial;border-color: initial;"> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">类型</td> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">属于DML</td> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">属于DDL</td> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">属于DDL</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">回滚</td> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">可回滚</td> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">不可回滚</td> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">不可回滚</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">删除内容</td> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">表结构还在,删除表的全部或者一部分数据行</td> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">表结构还在,删除表中的所有数据</td> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">从数据库中删除表,所有数据行,索引和权限也会被删除</td> </tr> <tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">删除速度</td> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">删除速度慢,需要逐行删除</td> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">删除速度快</td> <td style="font-size: 16px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 5px 10px;text-align: left;color: rgb(102, 102, 102);min-width: 85px;">删除速度最快</td> </tr> </tbody> </table> </section> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">因此,在不再需要一张表的时候,用drop;在想删除部分数据行时候,用delete;在保留表而删除所有数据的时候用truncate。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">11.UNION与UNION ALL的区别?</span></h2> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: disc;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 如果使用UNION ALL,不会合并重复的记录行 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 效率 UNION 高于 UNION ALL </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">12.count(1)、count(*) 与 count(列名) 的区别?</span></h2> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.20081967213114754" src="/upload/94c9a7c1593f53757f2f05cbc4abc45f.png" data-type="png" data-w="976" style="display: block;max-width: 100%;width: 90%;margin: 0 auto;box-shadow: #CCC 0 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 三种计数方式 </figcaption> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">执行效果</strong>:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: disc;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL</p> </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">count(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL</p> </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;">count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计。</p> </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">执行速度</strong>:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: disc;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 列名为主键,count(列名)会比count(1)快 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 列名不为主键,count(1)会比count(列名)快 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 如果表多个列并且没有主键,则 count(1) 的执行效率优于 count(*) </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 如果有主键,则 select count(主键)的执行效率是最优的 </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> 如果表只有一个字段,则 select count(*)最优。 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: normal;color: #333;font-size: 1.4em;border-bottom: 1px solid hsl(216, 100%, 68%);"><span style="display: none;"></span><span style="border-bottom: 1px solid hsl(216, 100%, 68%);">13.一条SQL查询语句的执行顺序?</span></h2> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.16828929068150209" src="/upload/7c8fb99f71d766e63167257d5689bf44.png" data-type="png" data-w="1438" style="display: block;max-width: 100%;width: 90%;margin: 0 auto;box-shadow: #CCC 0 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"> 查询语句执行顺序 </figcaption> </figure> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;color: black;list-style-type: decimal;padding-left: 2em;" class="list-paddingleft-1"> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">FROM</strong>:对FROM子句中的左表<left_table>和右表<right_table>执行笛卡儿积(Cartesianproduct),产生虚拟表VT1</p> </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">ON</strong>:对虚拟表VT1应用ON筛选,只有那些符合<join_condition>的行才被插入虚拟表VT2中</p> </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">JOIN</strong>:如果指定了OUTER JOIN(如LEFT OUTER JOIN、RIGHT OUTER JOIN),那么保留表中未匹配的行作为外部行添加到虚拟表VT2中,产生虚拟表VT3。如果FROM子句包含两个以上表,则对上一个连接生成的结果表VT3和下一个表重复执行步骤1)~步骤3),直到处理完所有的表为止</p> </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">WHERE</strong>:对虚拟表VT3应用WHERE过滤条件,只有符合<where_condition>的记录才被插入虚拟表VT4中</p> </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">GROUP BY</strong>:根据GROUP BY子句中的列,对VT4中的记录进行分组操作,产生VT5</p> </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: #666;"><strong style="font-weight: bold;color: hsl(216, 80%, 44%);">CUBE|ROLLUP</strong>:对表VT5进行CUBE或ROLLUP操作,产生表VT6</p> </section></li> <li style="color: #666;"> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <p style
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding: 0 10px;word-spacing: 0px;word-wrap: break-word;text-align: left;line-height: 1.6;letter-spacing: .034em;color: rgb(63, 63, 63);font-size: 16px;word-break: all;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;"> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;background: rgb(239, 239, 239);color: rgb(106, 115, 125);margin-bottom: 20px;margin-top: 20px;padding: 15px 20px;line-height: 27px;display: block;"> <p style="padding-bottom: 8px;padding-top: 23px;margin: 0px;line-height: 26px;padding: 0px;font-size: 15px;color: rgb(89,89,89);">导语 :Prometheus是一个开源的完整监控解决方案,本文将从指标抓取到查询及可视化展示,以及最后的监控告警,对Prometheus做一个基本的认识。</p> </blockquote> <h2 data-tool="mdnice编辑器" style="padding: 0px;font-weight: bold;color: black;font-size: 22px;display: block;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/eQPyBffYbue7CTjdvnyapIibq5t1xBJCxyTpnd0ibiaZic3rbdDmfHlMRlaKqibtoRTG0EMhsH3rgkDKJofJ2RQFyLg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;margin-bottom: 10px;"><span style="display: none;"></span><span style="text-align: center;display: inline-block;height: 38px;line-height: 42px;color: rgb(60, 112, 198);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">一、简介</span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">Prometheus是古希腊神话里泰坦族的一名神明,名字的意思是“先见之明”,下图中是Prometheus被宙斯惩罚,饱受肝脏日食夜长之苦。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="0.6198347107438017" src="/upload/6b59d905b7280af527f3abfd72cccbbc.png" data-type="png" data-w="605" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;">下面就是我们CRUD Boy所了解的Prometheus,下面是其官网封面图引导语:<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">From metrics to insight</code>,从指标到洞察力,通过指标去洞察你的系统,为我们的系统提供指标收集和监控的开源解决方案。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">也就是说,Prometheus是一个数据监控的解决方案,让我们能随时掌握系统运行的状态,快速定位问题和排除故障。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="0.2555066079295154" src="/upload/636784d8542e211e150c7f82a8db232b.png" data-type="png" data-w="454" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;">Prometheus发展速度很快,12年开发完成,16年加入CNCF,成为继K8s 之后第二个CNCF托管的项目,目前Github 42k的🌟,而且社区很活跃,维护频率很高,基本稳定在1个月1个小版本的迭代速度。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.2101851851851852" src="/upload/5a240458cb0d93a73d506b02735f08ef.png" data-type="png" data-w="1080" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;"> </figure> <h2 data-tool="mdnice编辑器" style="padding: 0px;font-weight: bold;color: black;font-size: 22px;display: block;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/eQPyBffYbue7CTjdvnyapIibq5t1xBJCxyTpnd0ibiaZic3rbdDmfHlMRlaKqibtoRTG0EMhsH3rgkDKJofJ2RQFyLg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;margin-bottom: 10px;"><span style="display: none;"></span><span style="text-align: center;display: inline-block;height: 38px;line-height: 42px;color: rgb(60, 112, 198);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">二、整体生态</span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">Prometheus提供了从指标暴露,到指标抓取、存储和可视化,以及最后的监控告警等一系列组件。</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.6648148148148149" src="/upload/f12bb4ed4089e930e5e6fc02e1f8971f.png" data-type="png" data-w="1080" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;"> </figure> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>(一)指标暴露<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">每一个被Prometheus监控的服务都是一个Job,Prometheus为这些Job 提供了官方的SDK ,利用这个SDK可以自定义并导出自己的业务指标,也可以使用Prometheus官方提供的各种常用组件和中间件的Exporter(比如常用的MySQL,Consul等等)。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">对于短时间执行的脚本任务或者不好直接 Pull指标的服务,Prometheus提供了PushGateWay网关给这些任务将服务指标主动推Push到网关,Prometheus再从这个网关里Pull指标。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>(二)指标抓取<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">上面提到了Push和Pull,其实这是两种指标抓取模型。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <strong style="font-weight: bold;line-height: 1.75em;color: rgb(74,74,74);">Pull模型:</strong> 监控服务主动拉取被监控服务的指标。 </section></li> </ul> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.40375586854460094" src="/upload/f88d6c54cb66863de83f3d8ef7007902.png" data-type="png" data-w="639" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;"> </figure> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">被监控服务一般通过主动暴露<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">metrics</code>端口或者通过<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Exporter</code>的方式暴露指标,监控服务依赖服务发现模块发现被监控服务,从而去定期的抓取指标。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"> <strong style="font-weight: bold;line-height: 1.75em;color: rgb(74,74,74);">Push模型:</strong> 被监控服务主动将指标推送到监控服务,可能需要对指标做协议适配,必须得符合监控服务要求的指标格式。 </section></li> </ul> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;"><img class="rich_pages wxw-img" data-ratio="0.37254901960784315" src="/upload/51ec3badae441fc42a7e54ffadca3c20.png" data-type="png" data-w="408" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;">对于<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Prometheus</code>中的指标抓取,采用的是Pull模型,默认是一分钟去拉取一次指标,通过<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Prometheus.yaml</code>配置文件中的<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">scrape_interval</code>配置项配置,<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Prometheus</code>对外都是用的Pull模型,一个是<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Pull Exporter</code>的暴露的指标,一个是<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Pull PushGateway</code>暴露的指标。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>(三)指标存储和查询<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">指标抓取后会存储在内置的时序数据库中,<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Prometheus</code>也提供了PromQL 查询语言给我们做指标的查询,我们可以在<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Prometheus</code>的WebUI上通过 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">PromQL</code>,可视化查询我们的指标,也可以很方便的接入第三方的可视化工具,例如<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">grafana</code>。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>(四)监控告警<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">prometheus</code>提供了<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">alertmanageer</code>基于promql来做系统的监控告警,当promql查询出来的指标超过我们定义的阈值时,<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">prometheus</code>会发送一条告警信息到<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">alertmanager</code>,manager会将告警下发到配置好的邮箱或者微信。</p> <h2 data-tool="mdnice编辑器" style="padding: 0px;font-weight: bold;color: black;font-size: 22px;display: block;text-align: center;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/eQPyBffYbue7CTjdvnyapIibq5t1xBJCxyTpnd0ibiaZic3rbdDmfHlMRlaKqibtoRTG0EMhsH3rgkDKJofJ2RQFyLg/640?wx_fmt=png");background-position: center center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;margin-bottom: 10px;"><span style="display: none;"></span><span style="text-align: center;display: inline-block;height: 38px;line-height: 42px;color: rgb(60, 112, 198);background-position: left center;background-repeat: no-repeat;background-attachment: initial;background-origin: initial;background-clip: initial;background-size: 63px;margin-top: 38px;font-size: 18px;margin-bottom: 10px;">三、工作原理</span></h2> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Prometheus</code>的从被监控服务的注册到指标抓取到指标查询的流程分为五个步骤:</p> <figure data-tool="mdnice编辑器" style="margin: 0;margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img class="rich_pages wxw-img" data-ratio="0.34801762114537443" src="/upload/64fd68c00a9ae88c6f94b2f530f7c142.png" data-type="png" data-w="454" style="display: block;margin: 0 auto;max-width: 100%;border-radius: 4px;margin-bottom: 25px;"> </figure> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;padding: 0px;font-weight: bold;color: black;font-size: 18px;"><span style="display: none;"></span>(一)服务注册<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;">被监控服务在<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Prometheus</code>中是一个Job存在,被监控服务的所有实例在 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Prometheus</code>中是一个target的存在,所以被监控服务的注册就是在 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(60, 112, 198);">Prometheus</code>中注册一个Job和其所有的target,这个注册分为:<strong style="font-weight: bold;line-height: 1.75em;color: rgb(74,74,74);">静态注册和动态注册</strong>。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-bottom: 8px;margin: 0;padding-top: 23px;color: rgb(74,74,74);line-height: 1.75em;"><strong style="font-weight: bold;line-height: 1.75em;color: rgb(74,74,74);">静态注册:</strong> 静态的将服务的IP和抓取指标的端口号配置在<code style="font-size
作者:微信小助手
<p data-mpa-powered-by="yiban.io"><br></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="color: black;padding: 0 10px;word-spacing: 0px;word-break: break-word;word-wrap: break-word;text-align: left;font-size: 16px;letter-spacing: 2px;line-height: 1.75em;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;"> <p style="margin: 0px 0px 0em;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 16px;color: rgb(0, 0, 0);letter-spacing: normal;background-color: rgb(255, 255, 255);text-align: center;visibility: visible;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6573208722741433" data-s="300,640" src="/upload/9fa853693d65fd793f4869fa7113de5d.png" data-type="png" data-w="3210" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;vertical-align: bottom;height: auto !important;visibility: visible !important;width: 677px !important;"></p> <p data-tool="mdnice编辑器" style="margin: 10px 0px;padding: 0px 0px 8px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;font-size: 16px;color: black;font-family: -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 2px;background-color: rgb(255, 255, 255);line-height: 26px;word-spacing: 2px;text-align: right;visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(136, 136, 136);visibility: visible;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(136, 136, 136);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 12px;letter-spacing: 0.544px;word-spacing: 0px;caret-color: rgb(178, 178, 178);visibility: visible;">JavaGuide在线网站:javaguide.cn<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"></span></span><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(136, 136, 136);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 12px;letter-spacing: 0.544px;word-spacing: 0px;caret-color: rgb(178, 178, 178);visibility: visible;">来源:https://developer.aliyun.com/article/531067</span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">昨晚在阿里云社区看到一份阿里云官方 Redis 开发规范,是一位阿里云数据库技术专家(Redis方向)写的,感觉有很多地方值得参考。我对原文排版和内容进行了简单完善,这里分享一下。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;min-height: 32px;line-height: 28px;color: #2980B9;display: inline-block;border-bottom-width: 1px;border-bottom-style: solid;border-color: #3498DB;padding-top: 5px;padding-right: 0.5em;padding-left: 0.5em;margin-bottom: -3px;font-size: 22px;margin: 1em auto;padding: 0.5em 0;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>一、键值设计</h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>1. key 名设计<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;"><strong style="font-weight: bold;color: #3498DB;">(1)【建议】: 可读性和可管理性</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">以业务名(或数据库名)为前缀(防止 key 冲突),用冒号分隔,比如业务名:表名:id</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELswnEhChUF51NMrz7bI5oeDNdRreIqAriagHibyGpmuyo4RtYZcvNtjFkm4l85tFg1Cdagic1y9JyVzQrpDPLwaKiax/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">ugc:video:1<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;"><strong style="font-weight: bold;color: #3498DB;">(2)【建议】:简洁性</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">保证语义的前提下,控制 key 的长度,当 key 较多时,内存占用也不容忽视,例如:</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/qbvaL9taELswnEhChUF51NMrz7bI5oeDNdRreIqAriagHibyGpmuyo4RtYZcvNtjFkm4l85tFg1Cdagic1y9JyVzQrpDPLwaKiax/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">user:{uid}:friends:messages:{mid}简化为u:{uid}:fr:m:{mid}。<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;"><strong style="font-weight: bold;color: #3498DB;">(3)【强制】:不要包含特殊字符</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">反例:包含空格、换行、单双引号以及其他转义字符</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">详细解析:<a href="https://mp.weixin.qq.com/s?spm=a2c6h.12873639.article-detail.7.753b1feeTX187Q&__biz=Mzg2NTEyNzE0OA==&mid=2247483663&idx=1&sn=7c4ad441eaec6f0ff38d1c6a097b1fa4&chksm=ce5f9e8cf928179a2c74227da95bec575bdebc682e8630b5b1bb2071c0a1b4be6f98d67c37ca&scene=21#wechat_redirect" style="text-decoration: none;word-wrap: break-word;color: #0084ff;font-weight: normal;border-bottom: 1px solid #0084ff;" data-linktype="2">Redis 开发规范解析(一)--键名设计</a> 。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>2. value 设计<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;"><strong style="font-weight: bold;color: #3498DB;">(1)【强制】:拒绝 bigkey(防止网卡流量、慢查询)</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">string 类型控制在 10KB 以内,hash、list、set、zset 元素个数不要超过 5000。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">反例:一个包含 200 万个元素的 list。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">非字符串的 bigkey,不要使用 del 删除,使用 hscan、sscan、zscan 方式渐进式删除,同时要注意防止 bigkey 过期时间自动删除问题(例如一个 200 万的 zset 设置 1 小时过期,会触发 del 操作,造成阻塞,而且该操作不会不出现在慢查询中(latency 可查)),<span style="font-weight: bold;color: #2C3E50;">查找方法</span><sup style="line-height: 0;font-weight: bold;color: #2C3E50;">[1]</sup>和<span style="font-weight: bold;color: #2C3E50;">删除方法</span><sup style="line-height: 0;font-weight: bold;color: #2C3E50;">[2]</sup> 。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">详细解析:<a href="https://mp.weixin.qq.com/s?spm=a2c6h.12873639.article-detail.10.753b1feeTX187Q&__biz=Mzg2NTEyNzE0OA==&mid=2247483677&idx=1&sn=5c320b46f0e06ce9369a29909d62b401&chksm=ce5f9e9ef928178834021b6f9b939550ac400abae5c31e1933bafca2f16b23d028cc51813aec&scene=21#wechat_redirect" style="text-decoration: none;word-wrap: break-word;color: #0084ff;font-weight: normal;border-bottom: 1px solid #0084ff;" data-linktype="2">Redis 开发规范解析(二)--老生常谈 bigkey</a></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;"><strong style="font-weight: bold;color: #3498DB;">(2)【推荐】:选择适合的数据类型。</strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">例如:实体类型(要合理控制和使用数据结构内存编码优化配置,例如 ziplist,但也要注意节省内存和性能之间的平衡)</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">反例:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELswnEhChUF51NMrz7bI5oeDNdRreIqAriagHibyGpmuyo4RtYZcvNtjFkm4l85tFg1Cdagic1y9JyVzQrpDPLwaKiax/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #e6c07b;line-height: 26px;">set</span> user:1:name tom<br><span style="color: #e6c07b;line-height: 26px;">set</span> user:1:age 19<br><span style="color: #e6c07b;line-height: 26px;">set</span> user:1:favor football<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">正例:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELswnEhChUF51NMrz7bI5oeDNdRreIqAriagHibyGpmuyo4RtYZcvNtjFkm4l85tFg1Cdagic1y9JyVzQrpDPLwaKiax/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">hmset user:1 name tom age 19 favor football<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>3.【推荐】:控制 key 的生命周期,redis 不是垃圾桶。<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">建议使用 expire 设置过期时间(条件允许可以打散过期时间,防止集中过期),不过期的数据重点关注 idletime。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;margin: 20px 5px;quotes: none;display: block;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;border-left: 2.3px solid rgb(52, 152, 219);color: rgb(97, 97, 97);background: rgb(236, 240, 241);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;margin: 0px;color: black;line-height: 26px;"><a href="https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247525881&idx=1&sn=33d4565668cff95f5ee239c6e1d660fe&chksm=cea12a32f9d6a324432c964bf536181f2d55ee50afba9a53d9b09fa0e1a7126a03bb1dde4c60&token=698289097&lang=zh_CN&scene=21#wechat_redirect" style="text-decoration: none;word-wrap: break-word;color: #0084ff;font-weight: normal;border-bottom: 1px solid #0084ff;" data-linktype="2">《Java 面试指北》</a>来啦!这是一份教你如何更高效地准备面试的小册,涵盖常见八股文(系统设计、常见框架、分布式、高并发 ......)、优质面经等内容。</p> </blockquote> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;min-height: 32px;line-height: 28px;color: #2980B9;display: inline-block;border-bottom-width: 1px;border-bottom-style: solid;border-color: #3498DB;padding-top: 5px;padding-right: 0.5em;padding-left: 0.5em;margin-bottom: -3px;font-size: 22px;margin: 1em auto;padding: 0.5em 0;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>二、命令使用</h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>1.【推荐】 O(N)命令关注 N 的数量<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">例如 hgetall、lrange、smembers、zrange、sinter 等并非不能使用,但是需要明确 N 的值。有遍历的需求可以使用 hscan、sscan、zscan 代替。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>2.【推荐】:禁用命令<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">禁止线上使用 keys、flushall、flushdb 等,通过 redis 的 rename 机制禁掉命令,或者使用 scan 的方式渐进式处理。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>3.【推荐】合理使用 select<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">redis 的多数据库较弱,使用数字进行区分,很多客户端支持较差,同时多业务用多数据库实际还是单线程处理,会有干扰。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>4.【推荐】使用批量操作提高效率<span style="display: none;"></span></h3> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;font-size: 15px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> 原生命令:例如 mget、mset。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> 非原生命令:可以使用 pipeline 提高效率。 </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">但要注意控制一次批量操作的 <strong style="font-weight: bold;color: #3498DB;">元素个数</strong>(例如 500 以内,实际也和元素字节数有关)。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">注意两者不同:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;font-size: 15px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> 原生是原子操作,pipeline 是非原子操作。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> pipeline 可以打包不同的命令,原生做不到 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> pipeline 需要客户端和服务端同时支持。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>5.【建议】Redis 事务功能较弱,不建议过多使用<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">Redis 的事务功能较弱(不支持回滚),而且集群版本(自研和官方)要求一次事务操作的 key 必须在一个 slot 上(可以使用 hashtag 功能解决)</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>6.【建议】Redis 集群版本在使用 Lua 上有特殊要求:<span style="display: none;"></span></h3> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;font-size: 15px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> 所有 key 都应该由 KEYS 数组来传递,redis.call/pcall 里面调用的 redis 命令,key 的位置,必须是 KEYS array, 否则直接返回 error,"-ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS array" </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> 所有 key,必须在 1 个 slot 上,否则直接返回 error, "-ERR eval/evalsha command keys must in same slot" </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>7.【建议】必要情况下使用 monitor 命令时,要注意不要长时间使用。<span style="display: none;"></span></h3> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;min-height: 32px;line-height: 28px;color: #2980B9;display: inline-block;border-bottom-width: 1px;border-bottom-style: solid;border-color: #3498DB;padding-top: 5px;padding-right: 0.5em;padding-left: 0.5em;margin-bottom: -3px;font-size: 22px;margin: 1em auto;padding: 0.5em 0;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>三、客户端使用</h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>1.【推荐】避免多个应用使用一个 Redis 实例<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">正例:不相干的业务拆分,公共数据做服务化。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>2.【推荐】使用带有连接池的数据库<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">使用带有连接池的数据库,可以有效控制连接,同时提高效率,标准使用方式:</p> <pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/qbvaL9taELswnEhChUF51NMrz7bI5oeDNdRreIqAriagHibyGpmuyo4RtYZcvNtjFkm4l85tFg1Cdagic1y9JyVzQrpDPLwaKiax/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">执行命令如下:<br>Jedis jedis = null;<br>try {<br> jedis = jedisPool.getResource();<br> //具体的命令<br> jedis.executeCommand()<br>} catch (Exception e) {<br> logger.error(<span style="color: #98c379;line-height: 26px;">"op key {} error: "</span> + e.getMessage(), key, e);<br>} finally {<br> //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。<br> <span style="color: #c678dd;line-height: 26px;">if</span> (jedis != null)<br> jedis.close();<br>}<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">下面是 JedisPool 优化方法的文章:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;font-size: 15px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> <span style="font-weight: bold;color: #2C3E50;">Jedis 常见异常汇总</span> <sup style="line-height: 0;font-weight: bold;color: #2C3E50;">[3]</sup> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> <span style="font-weight: bold;color: #2C3E50;">JedisPool 资源池优化</span> <sup style="line-height: 0;font-weight: bold;color: #2C3E50;">[4]</sup> </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>3.【建议】高并发下建议客户端添加熔断功能(例如 netflix hystrix)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">在通过 Redis 客户端操作 Redis 中的数据时,我们会在其中加入熔断器的逻辑。比如,当节点处于熔断状态时,直接返回空值以及熔断器三种状态之间的转换,具体的示例代码像下面这样:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">这样,当某一个 Redis 节点出现问题,Redis 客户端中的熔断器就会实时监测到,并且不再请求有问题的 Redis 节点,避免单个节点的故障导致整体系统的雪崩。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>4.【推荐】确保登录安全<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">设置合理的密码,如有必要可以使用 SSL 加密访问(阿里云 Redis 支持)</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>5.【建议】选择合适的内存淘汰策略<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">根据自身业务类型,选好 maxmemory-policy(最大内存淘汰策略),设置好过期时间。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">默认策略是 volatile-lru,即超过最大内存后,在过期键中使用 lru 算法进行 key 的剔除,保证不过期数据不被删除,但是可能会出现 OOM 问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;"><strong style="font-weight: bold;color: #3498DB;">其他策略如下</strong> :</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;font-size: 15px;" class="list-paddingleft-1"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> allkeys-lru:根据 LRU 算法删除键,不管数据有没有设置超时属性,直到腾出足够空间为止。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> allkeys-random:随机删除所有键,直到腾出足够空间为止。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> volatile-random:随机删除过期键,直到腾出足够空间为止。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> volatile-ttl:根据键值对象的 ttl 属性,删除最近将要过期数据。如果没有,回退到 noeviction 策略。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;margin: 10px 0;"> noeviction:不会剔除任何数据,拒绝所有写入操作并返回客户端错误信息"(error) OOM command not allowed when used memory",此时 Redis 只响应读操作。 </section></li> </ul> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;margin: 20px 5px;quotes: none;display: block;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;border-left: 2.3px solid rgb(52, 152, 219);color: rgb(97, 97, 97);background: rgb(236, 240, 241);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 15px;margin: 0px;color: black;line-height: 26px;"><a href="https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247525881&idx=1&sn=33d4565668cff95f5ee239c6e1d660fe&chksm=cea12a32f9d6a324432c964bf536181f2d55ee50afba9a53d9b09fa0e1a7126a03bb1dde4c60&token=698289097&lang=zh_CN&scene=21#wechat_redirect" style="text-decoration: none;word-wrap: break-word;color: #0084ff;font-weight: normal;border-bottom: 1px solid #0084ff;" data-linktype="2">《Java 面试指北》</a>来啦!这是一份教你如何更高效地准备面试的小册,涵盖常见八股文(系统设计、常见框架、分布式、高并发 ......)、优质面经等内容。</p> </blockquote> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;min-height: 32px;line-height: 28px;color: #2980B9;display: inline-block;border-bottom-width: 1px;border-bottom-style: solid;border-color: #3498DB;padding-top: 5px;padding-right: 0.5em;padding-left: 0.5em;margin-bottom: -3px;font-size: 22px;margin: 1em auto;padding: 0.5em 0;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>四、相关工具</h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>1.【推荐】:数据同步<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">redis 间数据同步可以使用:redis-port</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>2.【推荐】:big key 搜索<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;"><a href="https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247499195&idx=2&sn=13fe477aac7cbb8cd03d91511ea98ba0&chksm=cea1b270f9d63b667b72c142321dfccdb64deb11f4fd082e1cfedf7d45768e125a9db7fa6918&token=973133388&lang=zh_CN&scene=21#wechat_redirect" style="text-decoration: none;word-wrap: break-word;color: #0084ff;font-weight: normal;border-bottom: 1px solid #0084ff;" data-linktype="2">Redis 为什么变慢了?一文讲透如何排查 Redis 性能问题 | 万字长文</a></p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>3.【推荐】:热点 key 寻找<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">京东开源的 <span style="font-weight: bold;color: #2C3E50;">hotkey</span><sup style="line-height: 0;font-weight: bold;color: #2C3E50;">[5]</sup> 支持毫秒级探测热点数据,毫秒级推送至服务器集群内存,大幅降低热 key 对数据层查询压力。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;min-height: 32px;line-height: 28px;color: #2980B9;display: inline-block;border-bottom-width: 1px;border-bottom-style: solid;border-color: #3498DB;padding-top: 5px;padding-right: 0.5em;padding-left: 0.5em;margin-bottom: -3px;font-size: 22px;margin: 1em auto;padding: 0.5em 0;text-align: center;width: 85%;font-weight: bold;display: flex;flex-direction: column;justify-content: center;"><span style="display: none;"></span>五 附录:删除 bigkey</h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">下面操作可以使用 pipeline 加速。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-size: 16px;margin: 1em 4px;">redis 4.0 已经支持 key 的异步删除,欢迎使用。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>1. Hash 删除: hscan + hdel<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/qbvaL9taELswnEhChUF51NMrz7bI5oeDNdRreIqAriagHibyGpmuyo4RtYZcvNtjFkm4l85tFg1Cdagic1y9JyVzQrpDPLwaKiax/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">void</span> <span style="color: #61aeee;line-height: 26px;">delBigHash</span><span style="line-height: 26px;">(String host, <span style="color: #c678dd;line-height: 26px;">int</span> port, String password, String bigHashKey)</span> </span>{<br> Jedis jedis = <span style="color: #c678dd;line-height: 26px;">new</span> Jedis(host, port);<br> <span style="color: #c678dd;line-height: 26px;">if</span> (password != <span style="color: #c678dd;line-height: 26px;">null</span> && !<span style="color: #98c379;line-height: 26px;">""</span>.equals(password)) {<br> jedis.auth(password);<br> }<br> ScanParams scanParams = <span style="color: #c678dd;line-height: 26px;">new</span> ScanParams().count(<span style="color: #d19a66;line-height: 26px;">100</span>);<br> String cursor = <span style="color: #98c379;line-height: 26px;">"0"</span>;<br> <span style="color: #c678dd;line-height: 26px;">do</span> {<br> ScanResult<Entry<String, String>> scanResult = jedis.hscan(bigHashKey, cursor, scanParams);<br> List<Entry<String, String>> entryList = scanResult.getResult();<br> <span style="color: #c678dd;line-height: 26px;">if</span> (entryList != <span style="color: #c678dd;line-height: 26px;">null</span> && !entryList.isEmpty()) {<br> <span style="color: #c678dd;line-height: 26px;">for</span> (Entry<String, String> entry : entryList) {<br> jedis.hdel(bigHashKey, entry.getKey());<br> }<br> }<br> cursor = scanResult.getStringCursor();<br> } <span style="color: #c678dd;line-height: 26px;">while</span> (!<span style="color: #98c379;line-height: 26px;">"0"</span>.equals(cursor));<br><br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//删除bigkey</span><br> jedis.del(bigHashKey);<br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>2. List 删除: ltrim<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/qbvaL9taELswnEhChUF51NMrz7bI5oeDNdRreIqAriagHibyGpmuyo4RtYZcvNtjFkm4l85tFg1Cdagic1y9JyVzQrpDPLwaKiax/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">void</span> <span style="color: #61aeee;line-height: 26px;">delBigList</span><span style="line-height: 26px;">(String host, <span style="color: #c678dd;line-height: 26px;">int</span> port, String password, String bigListKey)</span> </span>{<br> Jedis jedis = <span style="color: #c678dd;line-height: 26px;">new</span> Jedis(host, port);<br> <span style="color: #c678dd;line-height: 26px;">if</span> (password != <span style="color: #c678dd;line-height: 26px;">null</span> && !<span style="color: #98c379;line-height: 26px;">""</span>.equals(password)) {<br> jedis.auth(password);<br> }<br> <span style="color: #c678dd;line-height: 26px;">long</span> llen = jedis.llen(bigListKey);<br> <span style="color: #c678dd;line-height: 26px;">int</span> counter = <span style="color: #d19a66;line-height: 26px;">0</span>;<br> <span style="color: #c678dd;line-height: 26px;">int</span> left = <span style="color: #d19a66;line-height: 26px;">100</span>;<br> <span style="color: #c678dd;line-height: 26px;">while</span> (counter < llen) {<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//每次从左侧截掉100个</span><br> jedis.ltrim(bigListKey, left, llen);<br> counter += left;<br> }<br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//最终删除key</span><br> jedis.del(bigListKey);<br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>3. Set 删除: sscan + srem<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/qbvaL9taELswnEhChUF51NMrz7bI5oeDNdRreIqAriagHibyGpmuyo4RtYZcvNtjFkm4l85tFg1Cdagic1y9JyVzQrpDPLwaKiax/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">public void delBigSet(String host, int port, String password, String bigSetKey) {<br> Jedis jedis = new Jedis(host, port);<br> <span style="color: #c678dd;line-height: 26px;">if</span> (password != null && !<span style="color: #98c379;line-height: 26px;">""</span>.equals(password)) {<br> jedis.auth(password);<br> }<br> ScanParams scanParams = new ScanParams().count(100);<br> String cursor = <span style="color: #98c379;line-height: 26px;">"0"</span>;<br> <span style="color: #c678dd;line-height: 26px;">do</span> {<br> ScanResult<String> scanResult = jedis.sscan(bigSetKey, cursor, scanParams);<br> List<String> memberList = scanResult.getResult();<br> <span style="color: #c678dd;line-height: 26px;">if</span> (memberList != null && !memberList.isEmpty()) {<br> <span style="color: #c678dd;line-height: 26px;">for</span> (String member : memberList) {<br> jedis.srem(bigSetKey, member);<br> }<br> }<br> cursor = scanResult.getStringCursor();<br> } <span style="color: #c678dd;line-height: 26px;">while</span> (!<span style="color: #98c379;line-height: 26px;">"0"</span>.equals(cursor));<br><br> //删除bigkey<br> jedis.del(bigSetKey);<br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: none;"></span>4. SortedSet 删除: zscan + zrem<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/qbvaL9taELswnEhChUF51NMrz7bI5oeDNdRreIqAriagHibyGpmuyo4RtYZcvNtjFkm4l85tFg1Cdagic1y9JyVzQrpDPLwaKiax/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="line-height: 26px;"><span style="color: #c678dd;line-height: 26px;">public</span> <span style="color: #c678dd;line-height: 26px;">void</span> <span style="color: #61aeee;line-height: 26px;">delBigZset</span><span style="line-height: 26px;">(String host, <span style="color: #c678dd;line-height: 26px;">int</span> port, String password, String bigZsetKey)</span> </span>{<br> Jedis jedis = <span style="color: #c678dd;line-height: 26px;">new</span> Jedis(host, port);<br> <span style="color: #c678dd;line-height: 26px;">if</span> (password != <span style="color: #c678dd;line-height: 26px;">null</span> && !<span style="color: #98c379;line-height: 26px;">""</span>.equals(password)) {<br> jedis.auth(password);<br> }<br> ScanParams scanParams = <span style="color: #c678dd;line-height: 26px;">new</span> ScanParams().count(<span style="color: #d19a66;line-height: 26px;">100</span>);<br> String cursor = <span style="color: #98c379;line-height: 26px;">"0"</span>;<br> <span style="color: #c678dd;line-height: 26px;">do</span> {<br> ScanResult<Tuple> scanResult = jedis.zscan(bigZsetKey, cursor, scanParams);<br> List<Tuple> tupleList = scanResult.getResult();<br> <span style="color: #c678dd;line-height: 26px;">if</span> (tupleList != <span style="color: #c678dd;line-height: 26px;">null</span> && !tupleList.isEmpty()) {<br> <span style="color: #c678dd;line-height: 26px;">for</span> (Tuple tuple : tupleList) {<br> jedis.zrem(bigZsetKey, tuple.getElement());<br> }<br> }<br> cursor = scanResult.getStringCursor();<br> } <span style="color: #c678dd;line-height: 26px;">while</span> (!<span style="color: #98c379;line-height: 26px;">"0"</span>.equals(cursor));<br><br> <span style="color: #5c6370;font-style: italic;line-height: 26px;">//删除bigkey</span><br> jedis.del(bigZsetKey);<br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #3498DB;padding-left: 10px;border-left: 2px solid #3498DB;"><span style="display: block;">参考资料</span></h3> <section data-tool="mdnice编辑器"> <span style="display: flex;"><span style="display: inline;width: 10%;background: none;font-size: 80%;opacity: 0.6;line-height: 26px;font-family: ptima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;">[1]</span><p style="padding-top: 8px;padding-bottom: 8px;display: inline;font-size: 14px;width: 90%;padding: 0px;margin: 0;line-height: 26px;color: black;word-break: break-all;width: calc(100%-50);">查找方法: <em style="font-style: italic;color: #2980B9;">https://developer.aliyun.com/article/531067#cc1</em></p></span> <span style="display: flex;"><span style="display: inline;width: 10%;background: none;font-size: 80%;opacity: 0.6;line-height: 26px;font-family: ptima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;">[2]</span><p style="padding-top: 8px;padding-bottom: 8px;display: inline;font-size: 14px;width: 90%;padding: 0px;margin: 0;line-height: 26px;color: black;word-break: break-all;width: calc(100%-50);">删除方法: <em style="font-style: italic;color: #2980B9;">https://developer.aliyun.com/article/531067#cc2</em></p></span> <span style="display: flex;"><span style="display: inline;width: 10%;background: none;font-size: 80%;opacity: 0.6;line-height: 26px;font-family: ptima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;">[3]</span><p style="padding-top: 8px;padding-bottom: 8px;display: inline;font-size: 14px;width: 90%;padding: 0px;margin: 0;line-height: 26px;color: black;word-break: break-all;width: calc(100%-50);">Jedis 常见异常汇总: <em style="font-style: italic;color: #2980B9;">https://yq.aliyun.com/articles/236384?spm=a2c6h.12873639.article-detail.11.753b1feeTX187Q</em></p></span> <span style="display: flex;"><span style="display: inline;width: 10%;background: none;font-size: 80%;opacity: 0.6;line-height: 26px;font-family: ptima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;">[4]</span><p style="padding-top: 8px;padding-bottom: 8px;display: inline;font-size: 14px;width: 90%;padding: 0px;margin: 0;line-height: 26px;color: black;word-break: break-all;width: calc(100%-50);">JedisPool 资源池优化: <em style="font-style: italic;color: #2980B9;">https://yq.aliyun.com/articles/236383?spm=a2c6h.12873639.article-detail.12.753b1feeTX187Q</em></p></span> <span style="display: flex;"><span style="display: inline;width: 10%;background: none;font-size: 80%;opacity: 0.6;line-height: 26px;font-family: ptima-Regular, Optima, PingFangSC-light, PingFangTC-light, 'PingFang SC', Cambria, Cochin, Georgia, Times, 'Times New Roman', serif;">[5]</span><p style="padding-top: 8px;padding-bottom: 8px;display: inline;font-size: 14px;width: 90%;padding: 0px;margin: 0;line-height: 26px;color: black;word-break: break-all;width: calc(100%-50);">hotkey: <em style="font-style: italic;color: #2980B9;">https://gitee.com/jd-platform-opensource/hotkey</em></p></span> </section> </section>
作者:微信小助手
<section style="margin: 0px 0px 20px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;text-align: left;"> <strong style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;visibility: visible;">目录</span></strong> </section> <ul class="list-paddingleft-1" style="margin: 0px;padding: 0px 0px 0px 1.2em;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <li style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;letter-spacing: 2px;">前言</span></p></li> <li style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;letter-spacing: 2px;visibility: visible;font-size: 15px;">利器一:规范先行</span></p></li> <li style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;letter-spacing: 2px;visibility: visible;font-size: 15px;">利器二:服务至上</span></p></li> <li style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;letter-spacing: 2px;visibility: visible;font-size: 15px;">利器三:度量为王</span></p></li> <li style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;letter-spacing: 2px;visibility: visible;font-size: 15px;">写在最后</span></p></li> </ul> <p><br></p> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="text-align: center;justify-content: center;margin: 10px 0%;display: flex;flex-flow: row nowrap;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: auto;vertical-align: top;min-width: 10%;max-width: 100%;flex: 0 0 auto;height: auto;border-bottom: 1px solid rgb(126, 169, 195);border-bottom-right-radius: 0px;line-height: 0;align-self: flex-start;box-sizing: border-box;"> <section style="margin: 0px 0%;box-sizing: border-box;" powered-by="xiumi.us"> <section style="letter-spacing: 0px;line-height: 1.8;font-size: 15px;color: rgb(126, 169, 195);padding: 0px 5px;box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">前言</strong></p> </section> </section> <section style="margin: 0px 0% -3px;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: 5px;height: 5px;vertical-align: top;overflow: hidden;border-width: 0px;border-radius: 202px;border-style: none;border-color: rgb(62, 62, 62);background-color: rgb(126, 169, 195);box-sizing: border-box;"> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </section> </section> </section> </section> </section> <section> <br> </section> <section> <span style="font-size: 15px;">如果你是一名优秀的应用系统开发人员,想必应该非常清楚在应用系统运行期间,打印日志有多么重要。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">它不但能够记录应用系统运行情况及轨迹,还有助于提升故障排查及定位问题的效率,甚至还可以对其进行分析及监控,洞察系统隐患,提前预警防范。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">但并不是说只要打印尽可能多的日志,就能轻松获得这些能力。设想一下,如果你肆无忌惮地打印了一堆毫无价值的日志,那请问日志又何以能够来为你提供价值呢。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">由此可见,这里的核心关键点并不在于日志的多少,而在于日志打印是否规范且合理。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">不规范合理的日志,不但无法发挥作用产生价值,还会增加故障定位难度、降低解决效率,以及额外增加日志存储成本,消耗应用系统性能。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">在极端情况下,甚至还会对应用系统造成致命性打击,引发应用系统瘫痪的可能。<br></span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">讲到这,我想你应该明白我想说的——应用系统日志打印确实非常重要,但日志打印规范将更为重要,它就像一把双刃剑,只有合理运用才能发挥其特有的作用及价值。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">但在组织中,如果你想让你周围的人都能明白这个道理可并不容易,它需要一个漫长的传播过程,而在这个过程中,你不仅需要坚持不断地宣导来逐步增强他们的认知,还应借助必要的治理手段及工具平台进行辅助,只有利其所器,才能善其所事。</span> </section> <section> <br> </section> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="text-align: center;justify-content: center;margin: 10px 0%;display: flex;flex-flow: row nowrap;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: auto;vertical-align: top;min-width: 10%;max-width: 100%;flex: 0 0 auto;height: auto;border-bottom: 1px solid rgb(126, 169, 195);border-bottom-right-radius: 0px;line-height: 0;align-self: flex-start;box-sizing: border-box;"> <section style="margin: 0px 0%;box-sizing: border-box;" powered-by="xiumi.us"> <section style="letter-spacing: 0px;line-height: 1.8;font-size: 15px;color: rgb(126, 169, 195);padding: 0px 5px;box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">利器一:规范先行</strong></p> </section> </section> <section style="margin: 0px 0% -3px;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: 5px;height: 5px;vertical-align: top;overflow: hidden;border-width: 0px;border-radius: 202px;border-style: none;border-color: rgb(62, 62, 62);background-color: rgb(126, 169, 195);box-sizing: border-box;"> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </section> </section> </section> </section> </section> <section> <section> <br> </section> <section> <span style="font-size: 15px;">在你想启动规范化日志打印前,建议先制定一份日志打印规范,它可能无法面面俱到,但没有关系,它的目的仅是为了先突显日志打印规范的重要性,并且让这件事情能够正式进入正轨。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">如果组织中大部分都是 Java 应用,那么规范内容可以主要围绕 Java 应用来写,虽然无法覆盖所有开发语言,但其核心原则仍是可以借鉴的。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">另外,前期请务必不要将其复杂化,否则它将无法具备普适性,也无法被接受和传播。</span> </section> <section> <br> </section> <h4 data-tool="mdnice编辑器" style="margin: 0px 0px 15px;padding: 0px;outline: 0px;font-weight: 400;font-size: 16px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;color: rgb(34, 34, 34);font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;letter-spacing: 0.544px;orphans: 2;text-align: justify;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-style: initial;text-decoration-color: initial;caret-color: rgb(51, 51, 51);font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;background-color: rgb(255, 255, 255);line-height: 2em;"><strong style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-size: 15px;color: rgb(0, 122, 170);">| Java 应用系统日志打印规范</span></strong></h4> <section> <strong><span style="font-size: 15px;">①Java 日志框架</span></strong> </section> <section> <span style="font-size: 15px;"><br></span> </section> <section> <span style="font-size: 15px;">常用的 Java 日志框架可选择 Log4j/Logback/Log4j2 等,但为了避免后续更换日志框架所带来的额外改造成本,建议将接口层和实现层进行分离。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">将 SLF4J 作为接口层,将 Log4j/Logback/Log4j2 作为实现层,两者通过桥接的方式进行集成。</span> </section> <section> <br> </section> <section> <strong><span style="font-size: 15px;">②Java 日志规范</span></strong> </section> <section> <br> </section> <section> <span style="font-size: 15px;color: rgb(0, 122, 170);">规范一:</span> <span style="font-size: 15px;">【强制】级别只允许使用 ERROR、WARN、INFO、DEBUG。</span> </section> <section> <span style="font-size: 15px;"><br></span> </section> <p style="margin-bottom: 8px;"><span style="font-size: 15px;">定义如下:</span></p> <section style="text-align: center;margin-bottom: 8px;"> <img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5157170923379175" data-s="300,640" src="/upload/8c19d134c7016bdc8b62f3d284b2d591.png" data-type="png" data-w="1018" style=""> </section> <section> <span style="font-size: 15px;color: rgb(0, 122, 170);">规范二:</span> <span style="font-size: 15px;">【强制】禁止使用 Logback/Log4j2 等的 API,应使用 SLF4J 的 API。<br></span> </section> <section> <br> </section> <section> <span style="font-size: 15px;color: rgb(0, 122, 170);">规范三:</span> <span style="font-size: 15px;">【强制】在接口/方法的入口/出口处,打印请求及响应参数日志。<br></span> </section> <section> <br> </section> <section> <span style="font-size: 15px;color: rgb(0, 122, 170);">规范四:</span> <span style="font-size: 15px;">【强制】ERROR 级别日志需打印堆栈,而非 ERROR 级别日志则不需要。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;color: rgb(0, 122, 170);">规范五:</span> <span style="font-size: 15px;">【强制】禁止在代码循环体中直接打印非 DEBUG 级别的日志。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;color: rgb(0, 122, 170);">规范六:</span> <span style="font-size: 15px;">【强制】禁止日志打印内容中仅打印特殊字符或数字的情况。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;color: rgb(0, 122, 170);">规范七:</span> <span style="font-size: 15px;">【建议】日志内容中应包含关键特征类信息,例如:用户标识或流水号。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;color: rgb(0, 122, 170);">规范八:</span> <span style="font-size: 15px;">【建议】应采用异步打印模式,且打印时建议关闭打印位置信息。<br></span> </section> <section> <br> </section> <section> <span style="font-size: 15px;color: rgb(0, 122, 170);">规范九:</span> <span style="font-size: 15px;">【建议】日志打印若出现堵塞,建议至少丢弃 INFO 级别以上的日志。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;color: rgb(0, 122, 170);">规范十:</span> <span style="font-size: 15px;">【建议】每条日志在语义上可独立被理解,减少上下文关联理解。</span> </section> <section> <br> </section> <p style="margin-bottom: 8px;"><span style="font-size: 15px;color: rgb(0, 122, 170);">Java 日志字段:</span></p> <section style="text-align: center;margin-bottom: 8px;"> <img class="rich_pages wxw-img" data-galleryid="" data-ratio="1.195138888888889" data-s="300,640" src="/upload/27d14228012d36d184fbef01b7a91bd7.png" data-type="png" data-w="1440" style=""> </section> <section> <span style="font-size: 15px;">注:位置信息包括类(class)/文件(file)/行号(line)/方法(method),若打印位置信息,则对性能有所影响。<br></span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">以上仅是一些规范参考,你可以根据组织中的实际情况来进行调整,但规范仅仅只是规范,有了它并不代表你已达成目标,只能说明你已为日志打印规范化这件事,迈出了第一步。</span> </section> <section> <br> </section> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="text-align: center;justify-content: center;margin: 10px 0%;display: flex;flex-flow: row nowrap;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: auto;vertical-align: top;min-width: 10%;max-width: 100%;flex: 0 0 auto;height: auto;border-bottom: 1px solid rgb(126, 169, 195);border-bottom-right-radius: 0px;line-height: 0;align-self: flex-start;box-sizing: border-box;"> <section style="margin: 0px 0%;box-sizing: border-box;" powered-by="xiumi.us"> <section style="letter-spacing: 0px;line-height: 1.8;font-size: 15px;color: rgb(126, 169, 195);padding: 0px 5px;box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">利器二:服务至上</strong></p> </section> </section> <section style="margin: 0px 0% -3px;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: 5px;height: 5px;vertical-align: top;overflow: hidden;border-width: 0px;border-radius: 202px;border-style: none;border-color: rgb(62, 62, 62);background-color: rgb(126, 169, 195);box-sizing: border-box;"> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </section> </section> </section> </section> </section> <section> <br> </section> <section> <span style="font-size: 15px;">当制定完应用系统日志打印规范后,请不要幻想有任何人会来自觉地遵守它,一是不知它的存在,二是他们无从下手,三是大家都挺“忙”的。我把它总结为六字真言,分别是“不知”、“不会”、“不想”。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">我曾见过组织中的有些规范,特别是技术规范,在制定完成后就会被长久地封存起来,没有人知道,也没有人想知道。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">所以,要落实好规范,你还得构思一套战术才行。否则,那些无法落实的规范就和废纸毫无两样。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">在很多人眼里,可能会将规范视为是一种约束,而又错误地将约束理解为贬义词,从而避而远之。这种误解的发生,其原因并不出在他们本身,而更多的出在那些制定规范的人身上。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">有些规范制定者不但没有身在其中,甚至也没有去诠释规范所能带来的价值,而仅仅只是强行推行那份冷冰冰的规范,请问此时谁会乐意在不知其所以然的情况下,无缘无故地背上这沉重的“负担”。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">因此,你必须得为这份规范赋予更多的“温度”,而主动服务可能会是一种比较好的“升温”方式。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;"></span> <span style="font-size: 15px;">但在行动前,切忌不要站在他们的对立面,并请做好放低姿态的觉悟,你要让对方深刻的意识到你和他们是同一阵营的。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">在发布规范后的初期,你可以尝试挑选几个日志打印情况最为糟糕的应用系统,扮演为“VIP 私人助理”来与对方进一步传达规范内容及作用,并为他们逐一列举出当前存在的日志打印问题,以及这些问题会对系统造成哪些影响。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">这种方式不但能够避免仅用文字传达所产生的理解偏差,及时有效地为对方解答各种疑问,使他们能够更深一步地理解规范内容及作用,还能够让规范制定者更进一步地了解对方的顾虑及困难,并从同理心视角出发,为对方提供更好的建议及解决思路。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">就这样 5 个、10 个、15 个应用系统......在精力有限的前提下逐步扩大辐射范围,事实证明,这种主动服务+循序渐进的方式对提升规范的接受度将会有所帮助。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">不过在过程中你仍然需要不断回看规范的合理性及适用性,并对规范作出及时且有效的调整。</span> </section> <section> <br> </section> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="text-align: center;justify-content: center;margin: 10px 0%;display: flex;flex-flow: row nowrap;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: auto;vertical-align: top;min-width: 10%;max-width: 100%;flex: 0 0 auto;height: auto;border-bottom: 1px solid rgb(126, 169, 195);border-bottom-right-radius: 0px;line-height: 0;align-self: flex-start;box-sizing: border-box;"> <section style="margin: 0px 0%;box-sizing: border-box;" powered-by="xiumi.us"> <section style="letter-spacing: 0px;line-height: 1.8;font-size: 15px;color: rgb(126, 169, 195);padding: 0px 5px;box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">利器三:度量为王</strong></p> </section> </section> <section style="margin: 0px 0% -3px;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: 5px;height: 5px;vertical-align: top;overflow: hidden;border-width: 0px;border-radius: 202px;border-style: none;border-color: rgb(62, 62, 62);background-color: rgb(126, 169, 195);box-sizing: border-box;"> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </section> </section> </section> </section> </section> <section> <br> </section> <section> <span style="font-size: 15px;">当规范逐渐被更多的人接受后,你的使命并没有完成,而真正的考验才刚刚开始。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">一是接受并不代表整改,二是如何验证整改有效性,三是整改是否可持续性。如果这些问题都不在你的考虑范围内,那你可能会前功尽弃。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">若想要解决以上这些问题,借助度量或许会是一个不错的选择。管理大师德鲁克曾说过:“没有度量,就没有管理”,它同样适用于规范的落实工作,你可以根据日志打印规范来制定一些度量指标,并配套研发相应的度量工具平台。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">通过度量工具平台“可视化”和“自助化”的两种特性,让开发人员能够及时发现日志打印规范的问题,还能够让他们自主验证日志打印规范整改后的效果,从而让他们感受到一种“看得见”+“摸得着”的安全感。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">其中,度量指标的设计将会尤其重要,往往一个不合理的指标,会让整个事情朝着预想中的反方向发展。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">所以,在初期并不建议你设计过多的度量指标,并建议从度量难度、影响程度、达成难度、可解释性四个方面进行综合性评估,以确定较为合理的指标。</span> </section> <section> <br> </section> <p style="margin-bottom: 8px;"><span style="font-size: 15px;color: rgb(0, 122, 170);">如下是当时初期选择的 8 个指标:</span></p> <section style="text-align: center;margin-bottom: 8px;"> <img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6143277723258096" data-s="300,640" src="/upload/b927feb51fd902b3c8edb4f9f084c335.png" data-type="png" data-w="1019" style=""> </section> <section> <span style="font-size: 15px;">注:以上仅列出指标,指标要求建议你可根据实际情况进行动态调整,但过高的指标要求会变得毫无意义。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">可能会有人提出,对于规模较大且日志条数较多的应用系统,是否可放宽指标要求,这听上去好像蛮有道理的,但我却并不这么认为。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">规模越大意味着所承载的职责和能力也就越大,一旦发生故障影响面也就越大,所以反倒更应该达到指标要求。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">这些指标虽然有一定的指导性,但似乎并不能满足开发人员的“胃口”,因为这些指标仍然无法直接暴露问题根源,也无法让他们可快速定位及明确优化方向。因此,你还得赋予指标一定的分析能力。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">例如:订单系统单日 ERROR 级别日志 888 条(占日志总量 0.05%),(TOP1)90% 在 com.OrderService 的第 88 行。(TOP2)10% 在 com.PayService 的第 188 行。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">就这样,你可以逐步完善指标体系及配套的分析能力,但请在设计每一个指标时,遵循先进行系统现状摸排,再进行小范围试点运行,最后进行持续观测并调优,从而确保每一个指标的设计都具备一定的合理性和可解释性。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;color: rgb(0, 122, 170);">如下列出了一些指标,仅供参考:</span> </section> <p style="text-align: center;margin-bottom: 0em;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6116027531956736" data-s="300,640" src="/upload/73897ab56783715ede558da843b4b9e4.png" data-type="png" data-w="1017" style=""></p> <section style="text-align: center;margin-bottom: 8px;"> <img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.408062930186824" data-s="300,640" src="/upload/e84819a7eb5b0657d3986a70455489d5.png" data-type="png" data-w="1017" style=""> </section> <section> <span style="font-size: 15px;">除此之外,你还可以将不同应用系统的指标进行横向对比,并采用排行榜的形式在科技内部进行公开,它将会产生一种改变行为的驱动力。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">可以有效激发“想要赢”和“不想失败”的心理活动,这就好比某些产品也会采用排行榜的方式来激励用户一样。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">通过设计指标体系+研发度量平台+公开排行榜单这三个手段的组合,在一定程度上可以驱动开发人员持续性整改日志打印的问题。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">但万事无绝对,那些始终无动于衷的人依然会存在,不过请不要强行要求对方,毕竟有时候存在即合理。</span> </section> <section> <br> </section> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="text-align: center;justify-content: center;margin: 10px 0%;display: flex;flex-flow: row nowrap;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: auto;vertical-align: top;min-width: 10%;max-width: 100%;flex: 0 0 auto;height: auto;border-bottom: 1px solid rgb(126, 169, 195);border-bottom-right-radius: 0px;line-height: 0;align-self: flex-start;box-sizing: border-box;"> <section style="margin: 0px 0%;box-sizing: border-box;" powered-by="xiumi.us"> <section style="letter-spacing: 0px;line-height: 1.8;font-size: 15px;color: rgb(126, 169, 195);padding: 0px 5px;box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">写在最后</strong></p> </section> </section> <section style="margin: 0px 0% -3px;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: 5px;height: 5px;vertical-align: top;overflow: hidden;border-width: 0px;border-radius: 202px;border-style: none;border-color: rgb(62, 62, 62);background-color: rgb(126, 169, 195);box-sizing: border-box;"> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </section> </section> </section> </section> </section> <section> <section> <br> </section> </section> <section> <span style="font-size: 15px;">日志打印规范固然重要,但也请不要过分追捧,它的核心价值还是在于能够帮助开发人员更好地记录应用系统的“案发现场”,并可为应用系统提供可持续改进的“线索”。</span> </section> <section> <br> </section> <section> <span style="font-size: 15px;">但请牢记,日志打印规范虽不是万能的,但没有日志打印规范却是万万不能的。</span> </section> </section>
作者:微信小助手
<p data-mpa-powered-by="yiban.io"><br></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, 'system-ui', 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-align: justify;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);visibility: visible;"><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"></p> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-align: justify;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="margin: 10px 0px 0px;padding: 0px;outline: 0px;max-width: 100%;visibility: visible;text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin: 0px;padding: 20px 20px 20px 25px;outline: 0px;max-width: 100%;display: inline-block;width: 795px;vertical-align: top;border-style: solid;border-width: 1px;border-color: rgb(231, 217, 203);visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="color: rgb(34, 34, 34);margin: 0px;padding: 0px;outline: 0px;max-width: 100%;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;text-align: left;line-height: 1.5;letter-spacing: 0.5px;visibility: visible;"> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(102, 117, 149);font-size: 19px;visibility: visible;"><span style="font-size: 13px;"><span style="font-size: 13px;margin: 0px;padding: 0px;outline: 0px;max-width: 100%;color: rgb(62, 62, 62);visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;">文|</span><span style="font-size: 13px;outline: 0px;color: rgb(62, 62, 62);visibility: visible;">李乔</span><span style="font-size: 13px;outline: 0px;color: rgb(136, 136, 136);visibility: visible;">(花名:南桥 )</span><span style="font-size: 13px;outline: 0px;visibility: visible;color: rgb(0, 0, 0);">、李宗杰<span style="font-size: 13px;color: rgb(136, 136, 136);">(花名:白鹰 )</span></span></span></p> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(102, 117, 149);font-size: 19px;visibility: visible;"><span style="font-size: 13px;"><span style="font-size: 13px;outline: 0px;visibility: visible;color: rgb(0, 0, 0);"><span style="font-size: 13px;color: rgb(136, 136, 136);"><br></span></span></span></p> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;color: rgb(102, 117, 149);font-size: 19px;line-height: normal;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;text-align: center;"> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: -apple-system, system-ui, 'PingFang SC', 'Hiragino Sans GB', 'Segoe UI', system-ui, Roboto, 'Droid Sans', 'Helvetica Neue', sans-serif;white-space: pre-wrap;background-color: rgb(255, 255, 255);font-size: 12px;visibility: visible;color: rgb(0, 128, 255);">李乔:蚂蚁集团高级开发工程师</span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;color: rgb(102, 117, 149);font-size: 19px;line-height: normal;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;text-align: center;"> <span style="font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.5px;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;float: none;margin: 0px;padding: 0px;outline: 0px;max-width: 100%;font-family: -apple-system, system-ui, 'PingFang SC', 'Hiragino Sans GB', 'Segoe UI', system-ui, Roboto, 'Droid Sans', 'Helvetica Neue', sans-serif;white-space: pre-wrap;background-color: rgb(255, 255, 255);visibility: visible;font-size: 13px;color: rgb(136, 136, 136);display: inline !important;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="color: rgb(136, 136, 136);font-family: -apple-system, system-ui, 'PingFang SC', 'Hiragino Sans GB', 'Segoe UI', system-ui, Roboto, 'Droid Sans', 'Helvetica Neue', sans-serif;font-size: 13px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.5px;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: pre-wrap;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">负责蚂蚁境外银行支付结算系统开发</span></span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;color: rgb(102, 117, 149);font-size: 19px;line-height: normal;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;text-align: center;"> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: -apple-system, system-ui, 'PingFang SC', 'Hiragino Sans GB', 'Segoe UI', system-ui, Roboto, 'Droid Sans', 'Helvetica Neue', sans-serif;white-space: pre-wrap;background-color: rgb(255, 255, 255);font-size: 12px;visibility: visible;color: rgb(0, 128, 255);"><span style="color: rgb(136, 136, 136);font-family: -apple-system, system-ui, 'PingFang SC', 'Hiragino Sans GB', 'Segoe UI', system-ui, Roboto, 'Droid Sans', 'Helvetica Neue', sans-serif;font-size: 14px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.5px;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: pre-wrap;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;"><br></span></span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;color: rgb(102, 117, 149);font-size: 19px;line-height: normal;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;text-align: center;"> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: -apple-system, system-ui, 'PingFang SC', 'Hiragino Sans GB', 'Segoe UI', system-ui, Roboto, 'Droid Sans', 'Helvetica Neue', sans-serif;white-space: pre-wrap;background-color: rgb(255, 255, 255);font-size: 12px;visibility: visible;color: rgb(0, 128, 255);">李宗杰:<span style="color: rgb(0, 128, 255);font-family: -apple-system, system-ui, 'PingFang SC', 'Hiragino Sans GB', 'Segoe UI', system-ui, Roboto, 'Droid Sans', 'Helvetica Neue', sans-serif;font-size: 12px;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.5px;orphans: 2;text-align: left;text-indent: 0px;text-transform: none;white-space: pre-wrap;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;display: inline !important;float: none;">蚂蚁集团技术专家</span></span> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;color: rgb(102, 117, 149);font-size: 19px;line-height: normal;visibility: visible;text-align: center;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;background-color: rgb(255, 255, 255);font-family: -apple-system, system-ui, 'PingFang SC', 'Hiragino Sans GB', 'Segoe UI', system-ui, Roboto, 'Droid Sans', 'Helvetica Neue', sans-serif;white-space: pre-wrap;color: rgb(136, 136, 136);visibility: visible;font-size: 13px;box-sizing: border-box !important;overflow-wrap: break-word !important;">负责蚂蚁分布式事务中间件研发</span> </section> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;visibility: visible;"><br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible;"></p> </section> </section> </section> </section> <section powered-by="xiumi.us" style="color: rgb(34, 34, 34);margin: 0px;padding: 0px;outline: 0px;max-width: 100%;font-size: 16px;display: inline-block;width: 795px;vertical-align: top;line-height: 0;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;text-align: right;justify-content: flex-end;transform: translate3d(-20px, 0px, 0px);visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;display: inline-block;width: 72px;vertical-align: top;height: auto;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;transform: rotateZ(315deg);visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin: 0px 0px 8px;padding: 0px;outline: 0px;max-width: 100%;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;background-color: rgb(10, 130, 229);height: 1px;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <br> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <svg viewbox="0 0 1 1" style="float: left;line-height: 0;width: 0px;vertical-align: top;visibility: visible;"></svg> </section> </section> </section> </section> </section> </section> <section powered-by="xiumi.us" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;transform: perspective(0px);transform-style: flat;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin: 5px 0px 10px;padding: 0px;outline: 0px;max-width: 100%;display: flex;flex-flow: row nowrap;text-align: left;justify-content: flex-start;transform: translate3d(-20px, 0px, 0px) rotateX(180deg) rotateY(180deg);visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin: 0px 5px 0px 0px;padding: 0px;outline: 0px;max-width: 100%;display: inline-block;vertical-align: top;width: auto;line-height: 0;flex: 0 0 0%;height: auto;align-self: flex-start;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;text-align: center;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;display: inline-block;width: 10px;height: 10px;vertical-align: top;overflow: hidden;border-style: solid;border-width: 5px 0px 0px 5px;border-radius: 47px 0px 0px;border-color: rgb(231, 217, 203) rgb(62, 62, 62) rgb(62, 62, 62) rgb(231, 217, 203);visibility: visible;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <svg viewbox="0 0 1 1" style="float: left;line-height: 0;width: 0px;vertical-align: top;visibility: visible;"></svg> </section> </section> </section> <section powered-by="xiumi.us" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;text-align: center;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;display: inline-block;width: 10px;height: 10px;vertical-align: top;overflow: hidden;background-color: rgb(231, 217, 203);visibility: visible;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <svg viewbox="0 0 1 1" style="float: left;line-height: 0;width: 0px;vertical-align: top;visibility: visible;"></svg> </section> </section> </section> </section> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;display: inline-block;vertical-align: top;width: auto;line-height: 0;flex: 0 0 0%;height: auto;align-self: flex-start;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section powered-by="xiumi.us" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;text-align: center;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;display: inline-block;width: 10px;height: 10px;vertical-align: top;overflow: hidden;border-style: solid;border-width: 5px 0px 0px 5px;border-radius: 47px 0px 0px;border-color: rgb(231, 217, 203) rgb(62, 62, 62) rgb(62, 62, 62) rgb(231, 217, 203);visibility: visible;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <svg viewbox="0 0 1 1" style="float: left;line-height: 0;width: 0px;vertical-align: top;visibility: visible;"></svg> </section> </section> </section> <section powered-by="xiumi.us" style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;text-align: center;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;display: inline-block;width: 10px;height: 10px;vertical-align: top;overflow: hidden;background-color: rgb(231, 217, 203);visibility: visible;"> <section style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;visibility: visible;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <svg viewbox="0 0 1 1" style="float: left;line-height: 0;width: 0px;vertical-align: top;visibility: visible;"></svg> </section> </section> </section> </section> </section> </section> </section> </section> <p style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;clear: both;min-height: 1em;color: rgb(34, 34, 34);font-family: system-ui, -apple-system, 'system-ui', 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;font-style: normal;font-variant-ligatures: normal;font-variant-caps: normal;font-weight: 400;letter-spacing: 0.544px;orphans: 2;text-indent: 0px;text-transform: none;white-space: normal;widows: 2;word-spacing: 0px;-webkit-text-stroke-width: 0px;text-decoration-thickness: initial;text-decoration-style: initial;text-decoration-color: initial;background-color: rgb(255, 255, 255);font-size: 16px;text-align: center;visibility: visible;"><br></p> <section style="box-sizing: border-box;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="font-style: normal;font-size: 14px;line-height: 2;letter-spacing: 0.5px;padding: 0px 10px;font-family: PingFangSC-light;box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;white-space: normal;margin: 0px;padding: 0px;box-sizing: border-box;"><span style="font-size: 32px;color: rgba(160, 160, 160, 0.83);box-sizing: border-box;"><strong style="box-sizing: border-box;">PART. 1</strong></span></p> </section> <section style="font-style: normal;font-size: 24px;text-align: center;color: rgb(10, 130, 229);line-height: 2;letter-spacing: 0.5px;padding: 0px 10px;box-sizing: border-box;" powered-by="xiumi.us"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">背景</strong></p> </section> <section style="font-style: normal;text-align: unset;font-size: 15px;line-height: 2;letter-spacing: 0.5px;padding: 0px 10px;box-sizing: border-box;" powered-by="xiumi.us"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">蚂蚁国际境外银行业务正在部分迁移至阿里云,原内部使用的 SOFA 技术栈无法在阿里云上得到支持。为了满足银行业务快速发展、简化银行系统技术栈的目标,我们采用了 Spring+Dubbo 等一套开源的技术方案重新构建起了新的技术栈。蚂蚁集团作为金融机构,内部应用采用了微服务架构,数据间的一致性极其重要,但蚂蚁内部原有的分布式事务框架,在阿里云上也无法提供技术支持。</p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">Seata 是分布式事务解决方案,囊括了阿里集团的 TXC<span style="color: rgba(160, 160, 160, 0.83);box-sizing: border-box;"><em style="box-sizing: border-box;">(阿里云版本称为 GTS)</em></span>和蚂蚁集团的 TCC/SAGA 等多种模式,是一款经过多年双十一大规模流量验证的金融级分布式事务框架。因此在综合比较各个现有的分布式事务框架之后,我们选择了 Seata。</p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">本文介绍了蚂蚁集团境外银行技术部在国际站点建设过程中,使用开源的 Seata 1.4.2 版本进行分布式事务管理的详细方案。同时本文也介绍如何在客户端实现对事务悬挂、幂等、空提交以及空回滚等情形的处理方法。</p> </section> <section style="font-style: normal;font-size: 14px;line-height: 2;letter-spacing: 0.5px;padding: 0px 10px;font-family: PingFangSC-light;box-sizing: border-box;" powered-by="xiumi.us"> <p style="text-align: center;white-space: normal;margin: 0px;padding: 0px;box-sizing: border-box;"><br></p> <p style="text-align: center;white-space: normal;margin: 0px;padding: 0px;box-sizing: border-box;"><span style="font-size: 32px;color: rgba(160, 160, 160, 0.83);box-sizing: border-box;"><strong style="box-sizing: border-box;">PART. 2</strong></span></p> </section> <section style="font-style: normal;font-size: 24px;text-align: center;color: rgb(10, 130, 229);line-height: 2;letter-spacing: 0.5px;padding: 0px 10px;box-sizing: border-box;" powered-by="xiumi.us"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">调研</strong></p> </section> <section style="font-style: normal;text-align: unset;font-size: 15px;line-height: 2;letter-spacing: 0.5px;padding: 0px 10px;box-sizing: border-box;" powered-by="xiumi.us"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">Seata 经过四年建设后,已经形成了一个非常庞大的技术体系。但不管其如何演进,Seata 整体保持了架构的稳定性与使用接口的向后兼容性。</p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-style: normal;text-align: center;justify-content: center;margin: 10px 0%;display: flex;flex-flow: row nowrap;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;vertical-align: middle;width: 33px;box-shadow: rgb(0, 0, 0) 0px 0px 0px;flex: 0 0 auto;height: auto;align-self: center;line-height: 0;background-color: rgba(255, 255, 255, 0);box-sizing: border-box;"> <section style="transform: rotateZ(37deg);box-sizing: border-box;" powered-by="xiumi.us"> <section style="text-align: left;transform: translate3d(8px, 0px, 0px);margin: 2px 0% -3px;box-sizing: border-box;"> <section style="display: inline-block;width: 4px;height: 22px;vertical-align: top;overflow: hidden;background-color: rgb(160, 160, 160);border-width: 0px;border-radius: 16px;border-style: none;border-color: rgb(62, 62, 62);box-shadow: rgb(95, 156, 239) 9px -6px 0px;box-sizing: border-box;"> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </section> </section> </section> </section> <section style="display: inline-block;vertical-align: middle;width: auto;align-self: center;min-width: 10%;max-width: 100%;flex: 0 0 auto;height: auto;box-sizing: border-box;"> <section style="margin: 0px 0%;text-align: left;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 0px 10px;font-size: 18px;text-align: center;color: rgb(102, 102, 102);line-height: 1;letter-spacing: 0.5px;box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">2.1--Seata 架构</strong></p> </section> </section> </section> </section> <section style="font-style: normal;text-align: unset;font-size: 15px;line-height: 2;letter-spacing: 0.5px;padding: 0px 10px;box-sizing: border-box;" powered-by="xiumi.us"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">Seata 官网给出了其如下架构图:</p> </section> <section style="font-style: normal;text-align: center;margin: 10px 0px;box-sizing: border-box;" powered-by="xiumi.us"> <section style="max-width: 100%;vertical-align: middle;display: inline-block;line-height: 0;width: 95%;height: auto;box-shadow: rgb(160, 160, 160) 0px 0px 3px;box-sizing: border-box;"> <img class="rich_pages wxw-img" data-ratio="0.5866543" src="/upload/144e4051519eeacbea325b2a143ad8d3.png" data-type="png" data-w="1079" style="vertical-align: middle;max-width: 100%;width: 100%;box-sizing: border-box;"> </section> </section> <p style="font-style: normal;white-space: normal;margin: 0px;padding: 0px;box-sizing: border-box;" powered-by="xiumi.us"><br style="box-sizing: border-box;"></p> <section style="font-style: normal;text-align: unset;font-size: 15px;line-height: 2;letter-spacing: 0.5px;padding: 0px 10px;box-sizing: border-box;" powered-by="xiumi.us"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">总体由如下角色构成:</strong></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><span style="color: rgb(0, 147, 225);box-sizing: border-box;">●</span>TC: Transaction Coordinator</p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">事务协调器:维护全局事务和分支事务的状态,驱动全局事务提交或者回滚。</p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><span style="color: rgb(0, 147, 225);box-sizing: border-box;">●</span>TM: Transaction Manager</p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">事务管理器:定义全局事务的范围,提交或者回滚全局事务。</p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><span style="color: rgb(0, 147, 225);box-sizing: border-box;">●</span>RM:Resource Manager</p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">资源管理器:和分支事务在同一个应用,进行分支事务的注册,报告分支事务的状态,驱动分支事务的提交或者回滚。</p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">TC 与 TM 以及各个 RM 之间使用 netty 框架进行长链接通信,通信协议是在四层 TCP 协议之上自定义的一套二进制双向通信协议,所以 Seata 总体的通信效率非常高。</p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-style: normal;text-align: center;justify-content: center;margin: 10px 0%;display: flex;flex-flow: row nowrap;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;vertical-align: middle;width: 33px;box-shadow: rgb(0, 0, 0) 0px 0px 0px;flex: 0 0 auto;height: auto;align-self: center;line-height: 0;background-color: rgba(255, 255, 255, 0);box-sizing: border-box;"> <section style="transform: rotateZ(37deg);box-sizing: border-box;" powered-by="xiumi.us"> <section style="text-align: left;transform: translate3d(8px, 0px, 0px);margin: 2px 0% -3px;box-sizing: border-box;"> <section style="display: inline-block;width: 4px;height: 22px;vertical-align: top;overflow: hidden;background-color: rgb(160, 160, 160);border-width: 0px;border-radius: 16px;border-style: none;border-color: rgb(62, 62, 62);box-shadow: rgb(95, 156, 239) 9px -6px 0px;box-sizing: border-box;"> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </section> </section> </section> </section> <section style="display: inline-block;vertical-align: middle;width: auto;align-self: center;min-width: 10%;max-width: 100%;flex: 0 0 auto;height: auto;box-sizing: border-box;"> <section style="margin: 0px 0%;text-align: left;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 0px 10px;font-size: 18px;text-align: center;color: rgb(102, 102, 102);line-height: 1;letter-spacing: 0.5px;box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">2.2--事务模式</strong></p> </section> </section> </section> </section> <section style="font-style: normal;text-align: unset;font-size: 15px;line-height: 2;letter-spacing: 0.5px;padding: 0px 10px;box-sizing: border-box;" powered-by="xiumi.us"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">Seata 提供了 TCC、AT、SAGA 和 XA 四种事务模式:</p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br style="box-sizing: border-box;"></p> </section> <section style="font-style: normal;margin: 10px 0%;text-align: left;justify-content: flex-start;display: flex;flex-flow: row nowrap;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: auto;vertical-align: top;border-left: 5px solid rgba(0, 128, 255, 0.36);border-bottom-left-radius: 0px;padding: 0px 0px 0px 9px;min-width: 10%;max-width: 100%;flex: 0 0 auto;height: auto;align-self: flex-start;box-sizing: border-box;"> <section style="margin: 2px 0%;box-sizing: border-box;" powered-by="xiumi.us"> <section style="color: rgb(0, 128, 255);font-size: 15px;line-height: 2;letter-spacing: 0.5px;text-align: justify;box-sizing: border-box;"> <p style="white-space: normal;margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">TCC 模式</strong></p> </section> </section> </section> </section> <section style="font-style: normal;text-align: unset;font-size: 15px;line-height: 2;letter-spacing: 0.5px;padding: 0px 10px;box-sizing: border-box;" powered-by="xiumi.us"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><span style="box-sizing: border-box;">参与者需要实现 Try/Confirm/Cancel 接口,在一阶段实现数据资源的预处理,在二阶段实现提交和回滚逻辑完成两阶段的提交。优点是通过业务逻辑实现数据可见性和隔离性,快速释放本地事务,提高对同一个资源的并发度,缺点是引入了中间数据的预处理过程,增加了业务复杂度。因此 TCC 模式具有很好的性能与隔离性,尤其适合在银行金融场景下同一个账户的并发交易处理。</span><br style="box-sizing: border-box;"></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br></p> </section> <section style="font-style: normal;margin: 10px 0%;text-align: left;justify-content: flex-start;display: flex;flex-flow: row nowrap;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: auto;vertical-align: top;border-left: 5px solid rgba(0, 128, 255, 0.36);border-bottom-left-radius: 0px;padding: 0px 0px 0px 9px;min-width: 10%;max-width: 100%;flex: 0 0 auto;height: auto;align-self: flex-start;box-sizing: border-box;"> <section style="margin: 2px 0%;box-sizing: border-box;" powered-by="xiumi.us"> <section style="color: rgb(0, 128, 255);font-size: 15px;line-height: 2;letter-spacing: 0.5px;text-align: justify;box-sizing: border-box;"> <p style="white-space: normal;margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">AT 模式</strong></p> </section> </section> </section> </section> <section style="font-style: normal;text-align: unset;font-size: 15px;line-height: 2;letter-spacing: 0.5px;padding: 0px 10px;box-sizing: border-box;" powered-by="xiumi.us"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><span style="box-sizing: border-box;">在一阶段时通过解析 SQL,生成二阶段回滚日志:二阶段提交时,删除回滚日志;二阶段回滚时,通过回滚日志来恢复记录到一阶段之前的状态。类似于 TCC 的两阶段,不过二阶段的 Commit/Rollback 由框架自动生成,自动实现了二阶段操作,易用性好于 TCC。但 AT 模式通过全局锁来实现数据的隔离性,因此对于同一个资源的事务处理只能串行操作,所以性能逊于 TCC。当然如果不存在并发使用同一个资源的场景,则 AT 模式可以很好的兼顾性能和隔离性,以及更好的开发效率。</span><br style="box-sizing: border-box;"></p> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><br></p> </section> <section style="font-style: normal;margin: 10px 0%;text-align: left;justify-content: flex-start;display: flex;flex-flow: row nowrap;box-sizing: border-box;" powered-by="xiumi.us"> <section style="display: inline-block;width: auto;vertical-align: top;border-left: 5px solid rgba(0, 128, 255, 0.36);border-bottom-left-radius: 0px;padding: 0px 0px 0px 9px;min-width: 10%;max-width: 100%;flex: 0 0 auto;height: auto;align-self: flex-start;box-sizing: border-box;"> <section style="margin: 2px 0%;box-sizing: border-box;" powered-by="xiumi.us"> <section style="color: rgb(0, 128, 255);font-size: 15px;line-height: 2;letter-spacing: 0.5px;text-align: justify;box-sizing: border-box;"> <p style="white-space: normal;margin: 0px;padding: 0px;box-sizing: border-box;"><strong style="box-sizing: border-box;">SAGA 模式</strong></p> </section> </section> </section> </section> <section style="font-style: normal;text-align: unset;font-size: 15px;line-height: 2;letter-spacing: 0.5px;padding: 0px 10px;box-sizing: border-box;" powered-by="xiumi.us"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><span style="box-sizing: border-box;">是一种长事务解决方案,其一阶段正向服务和二阶段补偿服务都由业务开发实现,它在微服�
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding: 0px 10px;line-height: 1.6;word-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 0px;" data-mpa-powered-by="yiban.io"> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong><span style="font-size: 18px;">大家好,我是 苏三。</span></strong></p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">最近有很多小伙伴给我留言,能不能总结下异步编程,今天就和大家简单聊聊这个话题。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">早期的系统是同步的,容易理解,我们来看个例子</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;"><strong style="font-weight: bold;color: #35b378;">同步编程</strong></p> </section> <p style="text-align: left;margin-bottom: 0em;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="1.0350109409190371" data-s="300,640" src="/upload/fa95c630b7b6393d65cf58ff9603752f.jpg" data-type="jpeg" data-w="914" style="width: 502px;height: 520px;"></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding: 0px 10px;line-height: 1.6;word-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 0px;"> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">当用户创建一笔电商交易订单时,要经历的业务逻辑流程还是很长的,每一步都要耗费一定的时间,那么整体的RT就会比较长。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">于是,聪明的人们开始思考能不能将一些非核心业务从主流程中剥离出来,于是有了<code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #35b378;">异步编程</code>雏形。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;display: block;font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin: 10px 5px;border-left: 3px solid rgb(53, 179, 120);border-right: 0px solid rgb(53, 179, 120);color: rgb(97, 97, 97);quotes: none;background: rgb(251, 249, 253);"> <p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;">异步编程是让程序并发运行的一种手段。它允许多个事件同时发生,当程序调用需要长时间运行的方法时,它不会阻塞当前的执行流程,程序可以继续运行。</p> </blockquote> </section> <p style="text-align: left;margin-bottom: 0em;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="1.1508620689655173" data-s="300,640" src="/upload/f446b6b36c56372f977eff7069594f8f.jpg" data-type="jpeg" data-w="928" style="width: 483px;height: 556px;"></p> <section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding: 0px 10px;line-height: 1.6;word-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-size: 15px;letter-spacing: 0.05em;color: rgb(89, 89, 89);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;margin-bottom: 0px;"> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">核心思路:采用多线程优化性能,将串行操作变成并行操作。异步模式设计的程序可以显著减少线程等待,从而在高吞吐量场景中,极大提升系统的整体性能,显著降低时延。</p> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">接下来,我们来讲下异步有哪些编程实现方式</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-size: 20px;margin: 1.2em 0 1em;padding: 0;font-weight: bold;color: #35b378;"><span style="display: none;"></span>一、线程 Thread<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;margin: 1em 4px;">直接继承 <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #35b378;">Thread类</code> 是创建异步线程最简单的方式。</p> <p data-tool="mdnice编辑器" style="font-
作者:微信小助手
<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com"> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="letter-spacing: 0px;font-size: 14px;">上周看到一篇因为在金额计算中没有使用</span> <code style="letter-spacing: 0px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(228, 105, 24);background-color: rgb(239, 239, 239);font-size: 0.875em;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal</span></code> <span style="letter-spacing: 0px;font-size: 14px;">而导致故障的文章,但是除非在一些非常简单的场景,结算汇金类的业务也不会直接用</span> <code style="letter-spacing: 0px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(228, 105, 24);background-color: rgb(239, 239, 239);font-size: 0.875em;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal</span></code> <span style="letter-spacing: 0px;font-size: 14px;">来计算金额,原因有两点:<br></span> </section> <ol data-tool="mdnice编辑器" style="color: black;margin: 8px 0px;padding-left: 25px;list-style-type: decimal;line-height: 1.8 !important;" class="list-paddingleft-1"> <li style="line-height: 1.8 !important;"> <section style="text-align: left;color: rgb(1, 1, 1);margin-top: 0.3em;margin-bottom: 0.3em;font-weight: normal;line-height: 1.8 !important;"> <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;color: #1e6bb8;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal</span></code> <span style="font-size: 14px;">里面还是有很多隐蔽的坑的</span> </section></li> <li style="line-height: 1.8 !important;"> <section style="text-align: left;color: rgb(1, 1, 1);margin-top: 0.3em;margin-bottom: 0.3em;font-weight: normal;line-height: 1.8 !important;"> <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;color: #1e6bb8;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal</span></code> <span style="font-size: 14px;">没有提供金额的单位</span> </section></li> </ol> <h1 data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin: 0.3em 0px 15px;padding: 0px 0px 0.2em;font-weight: bold;font-size: 1.3em;border-bottom: 1px solid rgb(223, 226, 229);line-height: 1.8 !important;"><span style="font-size: 14px;line-height: 1.8 !important;">1. <code style="line-height: 1.8 !important;">BigDecimal</code>中的五个容易踩的坑</span></h1> <h2 data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin: 0em 0px 15px;padding: 0px 0px 0.2em;font-weight: bold;font-size: 1.2em;border-bottom: 1px solid rgb(223, 226, 229);line-height: 1.8 !important;"><span style="font-size: 14px;line-height: 1.8 !important;">1.1 <code style="line-height: 1.8 !important;">new BigDecimal()</code>还是<code style="line-height: 1.8 !important;">BigDecimal#valueOf()</code>?</span></h2> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">先看下面这段代码</span> </section> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">BigDecimal bd1 = <span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">new</span> BigDecimal(<span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">0.01</span>);<br style="line-height: 1.8 !important;">BigDecimal bd2 = <span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">BigDecimal.valueOf</span>(<span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">0.01</span>);<br style="line-height: 1.8 !important;">System.out.println(<span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"bd1 = "</span> + bd1);<br style="line-height: 1.8 !important;">System.out.println(<span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"bd2 = "</span> + bd2);<br style="line-height: 1.8 !important;"></span> </section></pre> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">输出到控制台的结果是:</span> </section> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">bd1 = <span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">0.01000000000000000020816681711721685132943093776702880859375</span><br style="line-height: 1.8 !important;">bd2 = <span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">0.01</span><br style="line-height: 1.8 !important;"></span> </section></pre> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">造成这种差异的原因是0.1这个数字计算机是无法精确表示的,送给</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal</span></code> <span style="font-size: 14px;">的时候就已经丢精度了,而</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal#valueOf</span></code> <span style="font-size: 14px;">的实现却完全不同</span> </section> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;"><span style="line-height: 1.8 !important;"><span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">public</span> <span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">static</span> BigDecimal <span style="color: rgb(153, 0, 0);font-weight: bold;line-height: 1.8 !important;">valueOf</span><span style="line-height: 1.8 !important;">(<span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">double</span> val)</span> </span>{<br style="line-height: 1.8 !important;"> <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// Reminder: a zero double returns '0.0', so we cannot fastpath</span><br style="line-height: 1.8 !important;"> <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// to use the constant ZERO. This might be important enough to</span><br style="line-height: 1.8 !important;"> <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// justify a factory approach, a cache, or a few private</span><br style="line-height: 1.8 !important;"> <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// constants, later.</span><br style="line-height: 1.8 !important;"> <span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">return</span> <span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">new</span> BigDecimal(Double.toString(val));<br style="line-height: 1.8 !important;">}<br style="line-height: 1.8 !important;"></span> </section></pre> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">它使用了浮点数相应的字符串来构造</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal</span></code> <span style="font-size: 14px;">对象,因此避免了精度问题。所以大家要尽量要使用字符串而不是浮点数去构造</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal</span></code> <span style="font-size: 14px;">对象,如果实在不行,就使用</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal#valueOf()</span></code> <span style="font-size: 14px;">方法吧。</span> </section> <h2 data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin: 0em 0px 15px;padding: 0px 0px 0.2em;font-weight: bold;font-size: 1.2em;border-bottom: 1px solid rgb(223, 226, 229);line-height: 1.8 !important;"><span style="font-size: 14px;line-height: 1.8 !important;">1.2 等值比较</span></h2> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">BigDecimal bd1 = <span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">new</span> BigDecimal(<span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"1.0"</span>);<br style="line-height: 1.8 !important;">BigDecimal bd2 = <span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">new</span> BigDecimal(<span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"1.00"</span>);<br style="line-height: 1.8 !important;">System.out.println(bd1.equals(bd2));<br style="line-height: 1.8 !important;">System.out.println(bd1.compareTo(bd2));<br style="line-height: 1.8 !important;"></span> </section></pre> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">控制台的输出将会是:</span> </section> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;"><span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">false</span><br style="line-height: 1.8 !important;"><span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">0</span><br style="line-height: 1.8 !important;"></span> </section></pre> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">究其原因是,</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal</span></code> <span style="font-size: 14px;">中</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">equals</span></code> <span style="font-size: 14px;">方法的实现会比较两个数字的精度,而</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">compareTo</span></code> <span style="font-size: 14px;">方法则只会比较数值的大小。</span> </section> <h2 data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin: 0em 0px 15px;padding: 0px 0px 0.2em;font-weight: bold;font-size: 1.2em;border-bottom: 1px solid rgb(223, 226, 229);line-height: 1.8 !important;"><span style="font-size: 14px;line-height: 1.8 !important;">1.3 <code style="line-height: 1.8 !important;">BigDecimal</code>并不代表无限精度</span></h2> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">先看这段代码</span> </section> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">BigDecimal a = <span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">new</span> BigDecimal(<span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"1.0"</span>);<br style="line-height: 1.8 !important;">BigDecimal b = <span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">new</span> BigDecimal(<span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"3.0"</span>);<br style="line-height: 1.8 !important;">a.divide(b) <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// results in the following exception.</span><br style="line-height: 1.8 !important;"></span> </section></pre> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">结果会抛出异常:</span> </section> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.<br style="line-height: 1.8 !important;"></span> </section></pre> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">关于这个异常,Oracle的官方文档有具体说明</span> </section> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">If the quotient has a nonterminating decimal expansion and the operation is specified to return an exact result, an ArithmeticException is thrown. Otherwise, the exact result of the division is returned, as done for other operations.</span> </section> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">大意是,如果除法的商的结果是一个无限小数但是我们期望返回精确的结果,那程序就会抛出异常。回到我们的这个例子,我们需要告诉</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">JVM</span></code> <span style="font-size: 14px;">我们不需要返回精确的结果就好了</span> </section> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">BigDecimal a = <span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">new</span> BigDecimal(<span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"1.0"</span>);<br style="line-height: 1.8 !important;">BigDecimal b = <span style="color: rgb(51, 51, 51);font-weight: bold;line-height: 1.8 !important;">new</span> BigDecimal(<span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"3.0"</span>);<br style="line-height: 1.8 !important;">a.divide(b, <span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">2</span>, RoundingMode.HALF_UP)<span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// 0.33</span><br style="line-height: 1.8 !important;"></span> </section></pre> <h2 data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin: 0em 0px 15px;padding: 0px 0px 0.2em;font-weight: bold;font-size: 1.2em;border-bottom: 1px solid rgb(223, 226, 229);line-height: 1.8 !important;"><span style="font-size: 14px;line-height: 1.8 !important;">1.4 <code style="line-height: 1.8 !important;">BigDecimal</code>转回<code style="line-height: 1.8 !important;">String</code>要小心</span></h2> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">BigDecimal d = BigDecimal.valueOf(<span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">12334535345456700.12345634534534578901</span>);<br style="line-height: 1.8 !important;">String out = d.toString(); <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// Or perform any formatting that needs to be done</span><br style="line-height: 1.8 !important;">System.out.println(out); <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// 1.23345353454567E+16</span><br style="line-height: 1.8 !important;"></span> </section></pre> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">可以看到结果已经被转换成了科学计数法,可能这个并不是预期的结果</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal</span></code> <span style="font-size: 14px;">有三个方法可以转为相应的字符串类型,切记不要用错:</span> </section> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;"><span style="line-height: 1.8 !important;">String <span style="color: rgb(153, 0, 0);font-weight: bold;line-height: 1.8 !important;">toString</span><span style="line-height: 1.8 !important;">()</span></span>; <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// 有必要时使用科学计数法</span><br style="line-height: 1.8 !important;"><span style="line-height: 1.8 !important;">String <span style="color: rgb(153, 0, 0);font-weight: bold;line-height: 1.8 !important;">toPlainString</span><span style="line-height: 1.8 !important;">()</span></span>; <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// 不使用科学计数法</span><br style="line-height: 1.8 !important;"><span style="line-height: 1.8 !important;">String <span style="color: rgb(153, 0, 0);font-weight: bold;line-height: 1.8 !important;">toEngineeringString</span><span style="line-height: 1.8 !important;">()</span></span>; <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// 工程计算中经常使用的记录数字的方法,与科学计数法类似,但要求10的幂必须是3的倍数</span><br style="line-height: 1.8 !important;"></span> </section></pre> <h2 data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin: 0em 0px 15px;padding: 0px 0px 0.2em;font-weight: bold;font-size: 1.2em;border-bottom: 1px solid rgb(223, 226, 229);line-height: 1.8 !important;"><span style="font-size: 14px;line-height: 1.8 !important;">1.5 执行顺序不能调换(乘法交换律失效)</span></h2> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">乘法满足交换律是一个常识,但是在计算机的世界里,会出现不满足乘法交换律的情况</span> </section> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">BigDecimal a = BigDecimal.valueOf(<span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">1.0</span>);<br style="line-height: 1.8 !important;">BigDecimal b = BigDecimal.valueOf(<span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">3.0</span>);<br style="line-height: 1.8 !important;">BigDecimal c = BigDecimal.valueOf(<span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">3.0</span>);<br style="line-height: 1.8 !important;">System.out.println(a.divide(b, <span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">2</span>, RoundingMode.HALF_UP).multiply(c)); <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// 0.990</span><br style="line-height: 1.8 !important;">System.out.println(a.multiply(c).divide(b, <span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">2</span>, RoundingMode.HALF_UP)); <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// 1.00</span><br style="line-height: 1.8 !important;"></span> </section></pre> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">别小看这这0.01的差别,在汇金领域,会产生非常大的金额差异。</span> </section> <h1 data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin: 0.3em 0px 15px;padding: 0px 0px 0.2em;font-weight: bold;font-size: 1.3em;border-bottom: 1px solid rgb(223, 226, 229);line-height: 1.8 !important;"><span style="font-size: 14px;line-height: 1.8 !important;">2. 最佳实践</span></h1> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">关于金额计算,很多业务团队会基于</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal</span></code> <span style="font-size: 14px;">再封装一个</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">Money</span></code> <span style="font-size: 14px;">类,其实我们直接可以用一个半官方的</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">Money</span></code> <span style="font-size: 14px;">类:JSR 354 ,虽然没能在</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">Java 9</span></code> <span style="font-size: 14px;">中成为</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">Java</span></code> <span style="font-size: 14px;">标准,很有可能集成到后续的</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">Java</span></code> <span style="font-size: 14px;">版本中成为官方库。</span> </section> <h2 data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin: 0em 0px 15px;padding: 0px 0px 0.2em;font-weight: bold;font-size: 1.2em;border-bottom: 1px solid rgb(223, 226, 229);line-height: 1.8 !important;"><span style="font-size: 14px;line-height: 1.8 !important;">2.1 <code style="line-height: 1.8 !important;">maven</code>坐标</span></h2> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;"><dependency><br style="line-height: 1.8 !important;"> <groupId>org.javamoney</groupId><br style="line-height: 1.8 !important;"> <artifactId>moneta</artifactId><br style="line-height: 1.8 !important;"> <version><span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">1.1</span></version><br style="line-height: 1.8 !important;"></dependency><br style="line-height: 1.8 !important;"></span> </section></pre> <h2 data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin: 0em 0px 15px;padding: 0px 0px 0.2em;font-weight: bold;font-size: 1.2em;border-bottom: 1px solid rgb(223, 226, 229);line-height: 1.8 !important;"><span style="font-size: 14px;line-height: 1.8 !important;">2.2 新建<code style="line-height: 1.8 !important;">Money</code>类</span></h2> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">CurrencyUnit cny = Monetary.getCurrency(<span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"CNY"</span>);<br style="line-height: 1.8 !important;">Money money = Money.of(<span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">1.0</span>, cny); <br style="line-height: 1.8 !important;"><span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// 或者 Money money = Money.of(1.0, "CNY");</span><br style="line-height: 1.8 !important;"><span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">//System.out.println(money);</span><br style="line-height: 1.8 !important;"></span> </section></pre> <h2 data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin: 0em 0px 15px;padding: 0px 0px 0.2em;font-weight: bold;font-size: 1.2em;border-bottom: 1px solid rgb(223, 226, 229);line-height: 1.8 !important;"><span style="font-size: 14px;line-height: 1.8 !important;">2.3 金额运算</span></h2> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">CurrencyUnit cny = Monetary.getCurrency(<span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"CNY"</span>);<br style="line-height: 1.8 !important;">Money oneYuan = Money.of(<span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">1.0</span>, cny);<br style="line-height: 1.8 !important;">Money threeYuan = oneYuan.add(Money.of(<span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">2.0</span>, <span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"CNY"</span>)); <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">//CNY 3</span><br style="line-height: 1.8 !important;">Money tenYuan = oneYuan.multiply(<span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">10</span>); <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// CNY 10</span><br style="line-height: 1.8 !important;">Money fiveFen = oneYuan.divide(<span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">2</span>); <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">//CNY 0.5</span><br style="line-height: 1.8 !important;"></span> </section></pre> <h2 data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin: 0em 0px 15px;padding: 0px 0px 0.2em;font-weight: bold;font-size: 1.2em;border-bottom: 1px solid rgb(223, 226, 229);line-height: 1.8 !important;"><span style="font-size: 14px;line-height: 1.8 !important;">2.4 比较相等</span></h2> <pre data-tool="mdnice编辑器" style="color: rgb(51, 51, 51);margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;line-height: 1.8 !important;"> <section style="overflow-x: auto;padding: 15px 16px 16px;color: rgb(51, 51, 51);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;font-size: 0.875em;background: rgb(248, 248, 248);border-radius: 5px;margin-left: 0px;margin-right: 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">Money fiveFen = Money.of(<span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">0.5</span>, <span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"CNY"</span>); <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">//CNY 0.5</span><br style="line-height: 1.8 !important;">Money anotherFiveFen = Money.of(<span style="color: rgb(0, 128, 128);line-height: 1.8 !important;">0.50</span>, <span style="color: rgb(221, 17, 68);line-height: 1.8 !important;">"CNY"</span>); <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// CNY 0.50</span><br style="line-height: 1.8 !important;">System.out.println(fiveFen.equals(anotherFiveFen)); <span style="color: rgb(153, 153, 136);font-style: italic;line-height: 1.8 !important;">// true</span><br style="line-height: 1.8 !important;"></span> </section></pre> <section style="color: rgb(51, 51, 51);padding-top: 8px;padding-bottom: 8px;font-size: inherit;margin: 0.1em 0px;line-height: 1.8 !important;"> <span style="font-size: 14px;">可以看到,这个类对金额做了显性的抽象,增加了金额的单位,也避免了直接使用</span> <code style="word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #e46918;background-color: #efefef;font-size: .875em;line-height: 1.8 !important;"><span style="font-size: 14px;">BigDecimal</span></code> <span style="font-size: 14px;">的一些坑。</span> </section> </section>