作者:仲夏时节的梦想
可能写的不怎么好,给想实现和图里方法描述一样功能的人,在没有更好的方法时,可以参考此方法,方法只能从多到少进行判断,次数相同的情况下排序不一定。 
作者:微信小助手
<p style="font-variant-ligatures: normal;orphans: 2;white-space: normal;widows: 2;text-align: center;"><img class="" data-copyright="0" data-ratio="0.6481481481481481" data-s="300,640" src="/upload/d92d4e7b143c9acc809701b9700f90b4.jpg" data-type="jpeg" data-w="1080" style="orphans: 2;text-align: center;white-space: normal;widows: 2;"></p> <section class="" powered-by="xiumi.us" style="font-size: 17px;font-variant-ligatures: normal;orphans: 2;white-space: normal;widows: 2;max-width: 100%;color: rgb(51, 51, 51);font-family: -apple-system-font, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei UI', 'Microsoft YaHei', Arial, sans-serif;letter-spacing: 0.612px;text-align: justify;box-sizing: border-box !important;word-wrap: break-word !important;background-color: rgb(255, 255, 255);"> <section style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <section label="Copyright Reserved by PLAYHUDONG." donone="shifuMouseDownCard('shifu_c_008')" style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <section style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;font-size: 15px;box-sizing: border-box !important;word-wrap: break-word !important;"> <section class="" powered-by="xiumi.us" style="max-width: 100%;font-size: 16px;box-sizing: border-box !important;word-wrap: break-word !important;"> <section label="Copyright Reserved by PLAYHUDONG." donone="shifuMouseDownCard('shifu_c_008')" style="margin-right: 0em;margin-left: 0em;padding: 0.5em 1em;max-width: 100%;border-style: none;box-sizing: border-box !important;word-wrap: break-word !important;background-color: rgb(235, 235, 235);"> <p style="max-width: 100%;min-height: 1em;line-height: 1.75em;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="color: rgb(136, 136, 136);max-width: 100%;font-size: 15px;box-sizing: border-box !important;word-wrap: break-word !important;">阿里妹导读:<span style="font-size: 15px;orphans: 2;widows: 2;">搜索离线数据处理是一个典型的海量数据批次/实时计算结合的场景,阿里搜索中台团队立足内部技术结合开源大数据存储和计算系统,针对自身业务和技术特点构建了搜索离线平台,提供复杂业务场景下单日批次处理千亿级数据,秒级实时百万TPS吞吐的计算能力。</span></span></p> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> </section> <p style="font-variant-ligatures: normal;orphans: 2;white-space: normal;widows: 2;"><br></p> <p style="font-variant-ligatures: normal;orphans: 2;white-space: normal;widows: 2;"><strong style="font-size: 16px;"><span style="font-size: 15px;color: rgb(255, 129, 36);">背景</span></strong><br></p> <p style="font-variant-ligatures: normal;orphans: 2;white-space: normal;widows: 2;"><span style="font-size: 15px;color: rgb(62, 62, 62);"><br></span></p> <h2 data-type="h" style="font-variant-ligatures: normal;orphans: 2;white-space: normal;widows: 2;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(62, 62, 62);">什么是搜索离线?</span></strong></h2> <p style="font-variant-ligatures: normal;orphans: 2;white-space: normal;widows: 2;"><span style="color: rgb(62, 62, 62);font-size: 15px;line-height: 1.75em;"><br></span></p> <p style="font-variant-ligatures: normal;orphans: 2;white-space: normal;widows: 2;"><span style="color: rgb(62, 62, 62);font-size: 15px;line-height: 1.75em;">一个典型的商品搜索架构如下图所示,本文将要重点介绍的就是下图中的离线数据处理系统(Offline System)。</span><br></p> <p style="font-variant-ligatures: normal;orphans: 2;white-space: normal;widows: 2;text-a
作者:微信小助手
<p><span style="letter-spacing: 1px;font-size: 16px;"><strong><span style="letter-spacing: 1px;font-family: 宋体;">一,为什么要冗余数据</span></strong></span></p> <p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">互联网数据量很大的业务场景,往往数据库需要进行<strong>水平切分</strong>来降低单库数据量。</span></p> <p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">水平切分会有一个</span>patition key<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">,</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 14px;"><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">通过</span>patition key<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">的查询能够直接定位到库,但是非</span>patition key<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">上的查询可能就需要扫描多个库了。</span></span></span></p> <p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></p> <p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">此时常见的架构设计方案,是使用<strong>数据冗余</strong>这种<span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">反范式设计来满足分库后不同维度的查询需求</span>。</span></p> <p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></p> <p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">例如:订单业务,对用户和商家都有查询需求:</span></p> <p><span style="letter-spacing: 1px;font-size: 14px;">Order(oid, info_detail);</span></p> <p><span style="letter-spacing: 1px;font-size: 14px;">T(buyer_id, seller_id, oid);</span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">如果用</span>buyer_id<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">来分库,</span>seller_id<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">的查询就需要扫描多库。</span></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">如果用</span>seller_id<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">来分库,</span>buyer_id<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">的查询就需要扫描多库。</span></span></p> <p><br></p> <p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">此时可以使用</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据冗余</span><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">来分别满足buyer_id和seller_id上的查询需求:</span></p> <p><span style="letter-spacing: 1px;font-size: 14px;">T1(buyer_id, seller_id, oid)</span></p> <p><span style="letter-spacing: 1px;font-size: 14px;">T2(seller_id, buyer_id, oid)</span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">同一个数据,冗余两份,一份以</span>buyer_id<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">来分库,满足买家的查询需求;一份以</span>seller_id<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">来分库,满足卖家的查询需求。</span></span></p> <p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></p> <p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">如何实施数据的冗余,以及如何保证数据的一致性,是今天将要讨论的内容。</span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"> </span></p> <p><span style="letter-spacing: 1px;font-size: 16px;"><strong><span style="letter-spacing: 1px;font-family: 宋体;">二,如何进行数据冗余</span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">(1)服务同步双写</span></strong></span></p> <p><img data-ratio="0.8636363636363636" data-type="png" data-w="176" data-s="300,640" src="/upload/f832034643d4eafdeaa05e9b7259b078.png"><br><span style="letter-spacing: 1px;font-size: 14px;"><span style="color: rgb(51, 51, 51);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;background-color: white;">顾名思义,<span style="color: rgb(255, 41, 65);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;background-color: white;">由服务层同步写冗余数据</span>,如上图</span><span style="color: rgb(51, 51, 51);line-height: 1.6;letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;background-color: white;">1-4</span><span style="color: rgb(51, 51, 51);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;background-color: white;">流程:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">业务方调用服务,新增数据</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">服务先插入</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;">T1</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据</span></span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">服务再插入</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;">T2</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据</span></span></p></li> <li><p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">服务返回业务方新增数据成功</span></p></li> </ul> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">优点</span></strong><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">不复杂,服务层由单次写,变两次写</span></p></li> <li><p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据一致性相对较高(因为双写成功才返回)</span></p></li> </ul> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">缺点</span></strong><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">请求的处理时间增加(要插入两次,时间加倍)</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据仍可能不一致,例如第二步写入</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;">T1</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">完成后服务重启,则数据不会写入</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;">T2</span></span></p></li> </ul> <p><span style="letter-spacing: 1px;font-size: 14px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><span style="color: rgb(255, 41, 65);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">如果系统对处理时间比较敏感</span><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">,引出常用的第二种方案。</span></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;"></span></strong><strong><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">(2)服务异步双写</span></strong><strong><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;"></span></strong></span></p> <p><img data-ratio="0.4835820895522388" data-type="png" data-w="335" data-s="300,640" src="/upload/755b63b6654ab98aed71185f5d6fbd84.png"><br><span style="letter-spacing: 1px;font-size: 14px;"><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据的双写并不再由服务来完成,服务层异步发出一个消息,通过消息总线发送给一个专门的数据复制服务来写入冗余数据,如上图</span><span style="line-height: 1.6;letter-spacing: 1px;font-size: 14px;">1-6</span><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">流程:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">业务方调用服务,新增数据</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">服务先插入</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;">T1</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据</span></span></p></li> <li><p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">服务向消息总线发送一个异步消息(发出即可,不用等返回,通常很快就能完成)</span></p></li> <li><p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">服务返回业务方新增数据成功</span></p></li> <li><p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">消息总线将消息投递给数据同步中心</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据同步中心插入</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;">T2</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据</span></span></p></li> </ul> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">优点</span></strong><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">请求处理时间短(只插入</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;">1</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">次)</span></span></p></li> </ul> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">缺点</span></strong><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">系统的复杂性增加了,多引入了一个组件(消息总线)和一个服务(专用的数据复制服务)</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">因为返回业务线数据插入成功时,数据还不一定插入到</span>T2<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">中,因此数据有一个不一致时间窗口(这个窗口很短,最终是一致的)</span></span></p></li> <li><p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">在消息总线丢失消息时,冗余表数据会不一致</span></p></li> </ul> <p><span style="letter-spacing: 1px;font-size: 14px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><span style="color: rgb(0, 0, 0);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">不管是服务同步双写,还是服务异步双写,服务都需要关注“冗余数据”带来的复杂性。</span><span style="color: rgb(255, 41, 65);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">如果想解除“数据冗余”对系统的耦合</span><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">,引出常用的第三种方案。</span></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">(3)线下异步双写</span></strong></span></p> <p><img data-ratio="1.0242718446601941" data-type="png" data-w="206" data-s="300,640" src="/upload/91657329b3e735467873404934294284.png"><br><span style="letter-spacing: 1px;font-size: 14px;"><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">为了屏蔽<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">“冗余数据”对服务带来的复杂性<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">,</span></span>数据的双写不再由服务层来完成,而是由线下的一个服务或者任务来完成,如上图</span><span style="line-height: 1.6;letter-spacing: 1px;font-size: 14px;">1-6</span><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">流程:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">业务方调用服务,新增数据</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">服务先插入</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;">T1</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据</span></span></p></li> <li><p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">服务返回业务方新增数据成功</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据会被写入到数据库的</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;">log</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">中</span></span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">线下服务或者任务读取数据库的</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;">log</span></span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">线下服务或者任务插入</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;">T2</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据</span></span></p></li> </ul> <p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">优点</span></strong><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据双写与业务完全解耦</span></p></li> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">请求处理时间短(只插入</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: Arial,sans-serif;font-size: 14px;">1</span><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">次)</span></span></p></li> </ul> <p><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></p> <p><span style="letter-spacing: 1px;font-size: 14px;"><strong><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">缺点</span></strong><span style="background: white;color: rgb(51, 51, 51);letter-spacing: 1px;font-family: 宋体;font-size: 14px;">:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="letter-spacing: 1px;font-size: 14px;"><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">返回业务线数据插入成功时,数据还不一定插入到</span>T2<span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">中,因此数据有一个不一致时间窗口(这个窗口很短,最终是一致的)</span></span></p></li> <li><p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据的一致性依赖于线下服务或者任务的可靠性</span></p></li> </ul> <p><br></p> <p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">不管哪种方案,毕竟不是分布式事务,万一出现数据不一致,怎么办呢?</span></p> <p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">高并发的情况下,实时一致性很难,方法论是:<strong>最终一致性</strong>。</span></p> <p><span style="letter-spacing: 1px;font-family: 宋体;font-size: 14px;">实现方式是:<strong>异步检测,异步修复</strong>。</span><br></p> <p><br></p> <p><span style="letter-spacing: 1px;font-size: 16px;"><strong><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;"><span style="" sans="sans" helvetica="helvetica" yahei="yahei" gb="gb" neue="neue"></span><span style="" sans="sans" helvetica="helvetica" yahei="yahei" gb="gb" neue="neue"></span><span style="" sans="sans" helvetica="helvetica" yahei="yahei" gb="gb" neue="neue"></span>三<span style="letter-spacing: 1px;font-family: 宋体;">,</span>如何保证数据的一致性</span></strong></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">(1)线下扫描全量数据法</span></strong></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><img style="box-sizing: border-box;height: 186px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 677px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;visibility: visible;width: 181px;word-wrap: break-word;" data-ratio="1.0276243093922652" data-type="png" data-w="181" data-s="300,640" src="/upload/ca94446ce64f76eab8ff888f0dceb98.png"><br style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;line-height: 27.2px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">如上图所示,<span style="margin: 0px;padding: 0px;color: rgb(255, 0, 0);letter-spacing: 1px;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">线下启动一个离线的扫描工具,不停的比对正表</span></span><span style="margin: 0px;padding: 0px;color: rgb(255, 0, 0);line-height: 27.2px;letter-spacing: 1px;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">T1</span><span style="margin: 0px;padding: 0px;color: rgb(255, 0, 0);line-height: 27.2px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">和反表</span><span style="margin: 0px;padding: 0px;color: rgb(255, 0, 0);line-height: 27.2px;letter-spacing: 1px;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">T2</span><span style="margin: 0px;padding: 0px;color: rgb(255, 0, 0);line-height: 27.2px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">,如果发现数据不一致,就进行补偿修复</span><span style="margin: 0px;padding: 0px;line-height: 27.2px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">。</span></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><br></span></strong></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">优点</span></strong><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">比较简单,开发代价小</span></span></p></li> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">线上服务无需修改,修复工具与线上服务解耦</span></span></p></li> </ul> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><br></span></strong></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">缺点</span></strong><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">扫描效率低,会扫描大量的“已经能够保证一致”的数据</span></span></p></li> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">由于扫描的数据量大,扫描一轮的时间比较长,即数据如果不一致,不一致的时间窗口比较长</span></span></p></li> </ul> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"> </span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="margin: 0px;padding: 0px;color: rgb(255, 41, 65);letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">有没有只扫描“可能存在不一致可能性”的数据,而不是每次扫描全部数据,以提高效率的优化方法呢?</span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><br></span></strong></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">(2)线下扫描增量数据法</span></strong></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><img style="box-sizing: border-box;height: 160px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 677px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;visibility: visible;width: 345px;word-wrap: break-word;" data-ratio="0.463768115942029" data-type="png" data-w="345" data-s="300,640" src="/upload/772ea06302d38ada537c2cad4992de36.png"><br style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;line-height: 27.2px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">每次只扫描增量的日志数据,就能够极大提高效率,缩短数据不一致的时间窗口,如上图</span><span style="margin: 0px;padding: 0px;line-height: 27.2px;letter-spacing: 1px;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">1-4</span><span style="margin: 0px;padding: 0px;line-height: 27.2px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">流程所示:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">写入正表</span>T1</span></p></li> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">第一步成功后,写入日志</span>log1</span></p></li> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">写入反表</span>T2</span></p></li> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">第二步成功后,写入日志</span>log2</span></p></li> </ul> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">当然,我们<span style="margin: 0px;padding: 0px;color: rgb(255, 0, 0);letter-spacing: 1px;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">还是需要一个离线的扫描工具,不停的比对日志</span></span><span style="margin: 0px;padding: 0px;color: rgb(255, 0, 0);letter-spacing: 1px;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">log1</span><span style="margin: 0px;padding: 0px;color: rgb(255, 0, 0);letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">和日志</span><span style="margin: 0px;padding: 0px;color: rgb(255, 0, 0);letter-spacing: 1px;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">log2</span><span style="margin: 0px;padding: 0px;color: rgb(255, 0, 0);letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">,如果发现数据不一致,就进行补偿修复</span></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><br></span></strong></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">优点</span></strong><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">虽比方法一复杂,但仍然是比较简单的</span></span></p></li> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">数据扫描效率高,只扫描增量数据</span></span></p></li> </ul> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><br></span></strong></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">缺点</span></strong><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">线上服务略有修改(代价不高,多写了</span>2<span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">条日志)</span></span></p></li> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">虽然比方法一更实时,但时效性还是不高,不一致窗口取决于扫描的周期</span></span></p></li> </ul> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"> </span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">有没有<span style="margin: 0px;padding: 0px;color: rgb(255, 41, 65);letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">实时检测一致性并进行修复</span>的方法呢?</span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><br></span></strong></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">(3)线上<span style="display: inline !important;float: none;background-color: transparent;color: rgb(51, 51, 51);font-family: 宋体;font-size: 14px;font-style: normal;font-variant: normal;font-weight: 700;letter-spacing: 1px;orphans: 2;text-align: justify;text-decoration: none;text-indent: 0px;text-transform: none;-webkit-text-stroke-width: 0px;word-spacing: 0px;word-wrap: break-word;">实时检测</span>“消息对”法</span></strong></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><img style="box-sizing: border-box;height: 109px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 677px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;visibility: visible;width: 402px;word-wrap: break-word;" data-ratio="0.27114427860696516" data-type="png" data-w="402" data-s="300,640" src="/upload/f95d5c44e170ccc5390163aa0b600530.png"><br style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;line-height: 27.2px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">这次不是写日志了,而是向消息总线发送消息,如上图</span><span style="margin: 0px;padding: 0px;line-height: 27.2px;letter-spacing: 1px;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">1-4</span><span style="margin: 0px;padding: 0px;line-height: 27.2px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">流程所示:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">写入正表</span>T1</span></p></li> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">第一步成功后,发送消息</span>msg1</span></p></li> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">写入反表</span>T2</span></p></li> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">第二步成功后,发送消息</span>msg2</span></p></li> </ul> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">这次不是需要一个周期扫描的离线工具了,而是一个实时订阅消息的服务不停的收消息。</span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><br></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">假设正常情况下,</span>msg1<span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">和</span>msg2<span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">的接收时间应该在</span>3s<span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">以内,如果检测服务在收到</span>msg1<span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">后没有收到</span>msg2<span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">,就尝试检测数据的一致性,不一致时进行补偿修复。</span></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><br></span></strong></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">优点</span></strong><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">效率高</span></span></p></li> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">实时性高</span></span></p></li> </ul> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><br></span></strong></span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">缺点</span></strong><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">方案比较复杂,上线引入了消息总线这个组件</span></span></p></li> <li><p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">线下多了一个订阅总线的检测服务</span></span></p></li> </ul> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;"> </span></p> <p style="background-color: transparent;box-sizing: border-box;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;font-size: 14px;">however<span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-family: 宋体;font-size: 14px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">,技术方案本身就是一个投入产出比的折衷,可以根据业务对一致性的需求程度决定使用哪一种方法。我曾经做过IM系统,好友关系链上亿,好友数据正反表的数据冗余,使用的就是方法二。</span></span></p> <p><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;"><br></span></p> <p><span style="font-size: 16px;"><strong><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;">四,总结</span></strong></span></p> <p><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">互联网数据量大的业务场景,常常:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">使用<span style="color: rgb(255, 76, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">水平切分</span>来降低单库数据量</span></p></li> <li><p><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">使用<span style="color: rgb(255, 76, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">数据冗余</span>的反范式设计来满足不同维度的查询需求</span><br></p></li> <li><p><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">冗余数据三种方案:</span></p></li> </ul> <p><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">(1)<span style="color: rgb(255, 76, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">服务同步双写法</span>能够很容易的实现数据冗余</span></p> <p><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">(2)为了降低时延,可以优化为<span style="color: rgb(255, 76, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">服务异步双写法</span></span></p> <p><span style="line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">(3)为了屏蔽“冗余数据”对服务带来的复杂性,可以优化为<span style="color: rgb(255, 76, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">线下异步双写法</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="color: rgb(0, 0, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">保证数据一致性的方案:</span></p></li> </ul> <p><span style="color: rgb(0, 0, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">(<span style="display: inline !important;float: none;background-color: transparent;color: rgb(0, 0, 0);font-family: 宋体;font-size: 14px;font-style: normal;font-variant: normal;font-weight: 400;letter-spacing: 1px;line-height: 22.4px;orphans: 2;text-align: left;text-decoration: none;text-indent: 0px;text-transform: none;-webkit-text-stroke-width: 0px;word-spacing: 0px;">1</span>)最简单的方式,线下脚本</span><span style="color: rgb(255, 76, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">扫全量数据</span><span style="color: rgb(0, 0, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">比对</span></p> <p><span style="color: rgb(0, 0, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">(2)提高效率的方式,线下脚本</span><span style="color: rgb(255, 76, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">扫增量数据</span><span style="color: rgb(0, 0, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">比对</span></p> <p><span style="color: rgb(0, 0, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">(3)最实时的方式,</span><span style="color: rgb(255, 76, 0);line-height: 1.6;letter-spacing: 1px;font-family: 宋体;font-size: 14px;">线上检测“消息对”</span></p>
作者:仲夏时节的梦想
 integer类型的数据比较相等时,最好不用==判断,因为在大于127的时候,比较的结果不符合预期。建议比较integer类型变量时直接用.equals()方法比较。
作者:じ☆ve宝贝
# 前言 这份文档是Google Java编程风格规范的完整定义。当且仅当一个Java源文件符合此文档中的规则, 我们才认为它符合Google的Java编程风格。 与其它的编程风格指南一样,这里所讨论的不仅仅是编码格式美不美观的问题, 同时也讨论一些约定及编码标准。然而,这份文档主要侧重于我们所普遍遵循的规则, 对于那些不是明确强制要求的,我们尽量避免提供意见。 ## 1.1 术语说明 在本文档中,除非另有说明: 术语class可表示一个普通类,枚举类,接口或是annotation类型(@interface) 术语comment只用来指代实现的注释(implementation comments),我们不使用“documentation comments”一词,而是用Javadoc。 其他的术语说明会偶尔在后面的文档出现。 ## 1.2 指南说明 本文档中的示例代码并不作为规范。也就是说,虽然示例代码是遵循Google编程风格,但并不意味着这是展现这些代码的唯一方式。 示例中的格式选择不应该被强制定为规则。 源文件基础 ## 2.1 文件名 源文件以其最顶层的类名来命名,大小写敏感,文件扩展名为.java。 ## 2.2 文件编码:UTF-8 源文件编码格式为UTF-8。 ## 2.3 特殊字符 ### 2.3.1 空白字符 除了行结束符序列,ASCII水平空格字符(0×20,即空格)是源文件中唯一允许出现的空白字符,这意味着: 所有其它字符串中的空白字符都要进行转义。 制表符不用于缩进。 ### 2.3.2 特殊转义序列 对于具有特殊转义序列的任何字符(\b, \t, \n, \f, \r, “, ‘及\x29,我们使用它的转义序列,而不是相应的八进制(比如 )或Unicode(比如 )转义。 ### 2.3.3 非ASCII字符 对于剩余的非ASCII字符,是使用实际的Unicode字符(比如∞),还是使用等价的Unicode转义符(比如∞),取决于哪个能让代码更易于阅读和理解。 Tip: 在使用Unicode转义符或是一些实际的Unicode字符时,建议做些注释给出解释,这有助于别人阅读和理解。 例如: ``` String unitAbbrev ="μs"; | 赞,即使没有注释也非常清晰 String unitAbbrev ="\u03bcs";// "μs" | 允许,但没有理由要这样做 String unitAbbrev ="\u03bcs";// Greek letter mu, "s" | 允许,但这样做显得笨拙还容易出错 String unitAbbrev ="\u03bcs"; | 很糟,读者根本看不出这是什么 return'\ufeff'+ content;// byte order mark | Good,对于非打印字符,使用转义,并在必要时写上注释 ``` Tip: 永远不要由于害怕某些程序可能无法正确处理非ASCII字符而让你的代码可读性变差。当程序无法正确处理非ASCII字符时,它自然无法正确运行, 你就会去fix这些问题的了。(言下之意就是大胆去用非ASCII字符,如果真的有需要的话) 源文件结构 一个源文件包含(按顺序地): 许可证或版权信息(如有需要) package语句 import语句 一个顶级类(只有一个) 以上每个部分之间用一个空行隔开。 ## 3.1 许可证或版权信息 如果一个文件包含许可证或版权信息,那么它应当被放在文件最前面。 ## 3.2 package语句 package语句不换行,列限制(4.4节)并不适用于package语句。(即package语句写在一行里) ## 3.3 import语句 ### 3.3.1 import不要使用通配符 即,不要出现类似这样的import语句:import java.util.*; ### 3.3.2 不要换行 import语句不换行,列限制(4.4节)并不适用于import语句。(每个import语句独立成行) ### 3.3.3 顺序和间距 import语句可分为以下几组,按照这个顺序,每组由一个空行分隔: 所有的静态导入独立成组 com.googleimports(仅当这个源文件是在com.google包下) 第三方的包。每个顶级包为一组,字典序。例如:android, com, junit, org, sun javaimports javaximports 组内不空行,按字典序排列。 ## 3.4 类声明 ### 3.4.1 只有一个顶级类声明 每个顶级类都在一个与它同名的源文件中(当然,还包含.java后缀)。 例外:package-info.java,该文件中可没有package-info类。 ### 3.4.2 类成员顺序 类的成员顺序对易学性有很大的影响,但这也不存在唯一的通用法则。不同的类对成员的排序可能是不同的。 最重要的一点,每个类应该以某种逻辑去排序它的成员,维护者应该要能解释这种排序逻辑。比如, 新的方法不能总是习惯性地添加到类的结尾,因为这样就是按时间顺序而非某种逻辑来排序的。 #### 3.4.2.1 重载:永不分离 当一个类有多个构造函数,或是多个同名方法,这些函数/方法应该按顺序出现在一起,中间不要放进其它函数/方法。 格式 术语说明:块状结构(block-like construct)指的是一个类,方法或构造函数的主体。需要注意的是,数组初始化中的初始值可被选择性地视为块状结构(4.8.3.1节)。 ## 4.1 大括号 ### 4.1.1 使用大括号(即使是可选的) 大括号与if, else, for, do, while语句一起使用,即使只有一条语句(或是空),也应该把大括号写上。 ### 4.1.2 非空块:K & R 风格 对于非空块和块状结构,大括号遵循Kernighan和Ritchie风格 (Egyptian brackets): 左大括号前不换行 左大括号后换行 右大括号前换行 如果右大括号是一个语句、函数体或类的终止,则右大括号后换行; 否则不换行。例如,如果右大括号后面是else或逗号,则不换行。 示例: ### 4.8.1节给出了enum类的一些例外。 ### 4.1.3 空块:可以用简洁版本 一个空的块状结构里什么也不包含,大括号可以简洁地写成{},不需要换行。例外:如果它是一个多块语句的一部分(if/else 或 try/catch/finally) ,即使大括号内没内容,右大括号也要换行。 示例: ## 4.2 块缩进:2个空格 每当开始一个新的块,缩进增加2个空格,当块结束时,缩进返回先前的缩进级别。缩进级别适用于代码和注释。(见### ### 4.1.2节中的代码示例) ## 4.3 一行一个语句 每个语句后要换行。 ## 4.4 列限制:80或100 一个项目可以选择一行80个字符或100个字符的列限制,除了下述例外,任何一行如果超过这个字符数限制,必须自动换行。 例外: 不可能满足列限制的行(例如,Javadoc中的一个长URL,或是一个长的JSNI方法参考)。 package和import语句(见3.2节和3.3节)。 注释中那些可能被剪切并粘贴到shell中的命令行。 ## 4.5 自动换行 术语说明:一般情况下,一行长代码为了避免超出列限制(80或100个字符)而被分为多行,我们称之为自动换行(line-wrapping)。 我们并没有全面,确定性的准则来决定在每一种情况下如何自动换行。很多时候,对于同一段代码会有好几种有效的自动换行方式。 Tip: 提取方法或局部变量可以在不换行的情况下解决代码过长的问题(是合理缩短命名长度吧) ### 4.5.1 从哪里断开 自动换行的基本准则是:更倾向于在更高的语法级别处断开。 如果在非赋值运算符处断开,那么在该符号前断开(比如+,它将位于下一行)。注意:这一点与Google其它语言的编程风格不同(如C++和JavaScript)。 这条规则也适用于以下“类运算符”符号:点分隔符(.),类型界限中的&(<**T** extends Foo & Bar>),catch块中的管道符号(catch (FooException | BarException e) 如果在赋值运算符处断开,通常的做法是在该符号后断开(比如=,它与前面的内容留在同一行)。这条规则也适用于foreach语句中的分号。 方法名或构造函数名与左括号留在同一行。 逗号(,)与其前面的内容留在同一行。 ### 4.5.2 自动换行时缩进至少+4个空格 自动换行时,第一行后的每一行至少比第一行多缩进4个空格(注意:制表符不用于缩进。见2.3.1节)。 当存在连续自动换行时,缩进可能会多缩进不只4个空格(语法元素存在多级时)。一般而言,两个连续行使用相同的缩进当且仅当它们开始于同级语法元素。 第4.6.3水平对齐一节中指出,不鼓励使用可变数目的空格来对齐前面行的符号。 ## 4.6 空白 ### 4.6.1 垂直空白 以下情况需要使用一个空行: 类内连续的成员之间:字段,构造函数,方法,嵌套类,静态初始化块,实例初始化块。例外:两个连续字段之间的空行是可选的,用于字段的空行主要用来对字段进行逻辑分组。 在函数体内,语句的逻辑分组间使用空行。 类内的第一个成员前或最后一个成员后的空行是可选的(既不鼓励也不反对这样做,视个人喜好而定)。 要满足本文档中其他节的空行要求(比如3.3节:import语句) 多个连续的空行是允许的,但没有必要这样做(我们也不鼓励这样做)。 ### 4.6.2 水平空白 除了语言需求和其它规则,并且除了文字,注释和Javadoc用到单个空格,单个ASCII空格也出现在以下几个地方: 分隔任何保留字与紧随其后的左括号(()(如if, for catch等)。 分隔任何保留字与其前面的右大括号(})(如else, catch)。 在任何左大括号前({),两个例外:@SomeAnnotation({a, b})(不使用空格)。 String[][] x = foo;(大括号间没有空格,见下面的Note)。 在任何二元或三元运算符的两侧。这也适用于以下“类运算符”符号:类型界限中的&(<\T extends Foo & Bar>)。 catch块中的管道符号(catch (FooException | BarException e)。 foreach语句中的分号。 在, : ;及右括号())后 如果在一条语句后做注释,则双斜杠(//)两边都要空格。这里可以允许多个空格,但没有必要。 类型和变量之间:List list。 数组初始化中,大括号内的空格是可选的,即new int[] {5, 6}和new int[] { 5, 6 }都是可以的。 Note:这个规则并不要求或禁止一行的开关或结尾需要额外的空格,只对内部空格做要求。 ### 4.6.3 水平对齐:不做要求 术语说明:水平对齐指的是通过增加可变数量的空格来使某一行的字符与上一行的相应字符对齐。 这是允许的(而且在不少地方可以看到这样的代码),但Google编程风格对此不做要求。即使对于已经使用水平对齐的代码,我们也不需要去保持这种风格。 以下示例先展示未对齐的代码,然后是对齐的代码: Tip:对齐可增加代码可读性,但它为日后的维护带来问题。考虑未来某个时候,我们需要修改一堆对齐的代码中的一行。 这可能导致原本很漂亮的对齐代码变得错位。很可能它会提示你调整周围代码的空白来使这一堆代码重新水平对齐(比如程序员想保持这种水平对齐的风格), 这就会让你做许多的无用功,增加了reviewer的工作并且可能导致更多的合并冲突。 ## 4.7 用小括号来限定组:推荐 除非作者和reviewer都认为去掉小括号也不会使代码被误解,或是去掉小括号能让代码更易于阅读,否则我们不应该去掉小括号。 我们没有理由假设读者能记住整个Java运算符优先级表。 ## 4.8 具体结构 ### 4.8.1 枚举类 枚举常量间用逗号隔开,换行可选。 没有方法和文档的枚举类可写成数组初始化的格式: 由于枚举类也是一个类,因此所有适用于其它类的格式规则也适用于枚举类。 ### 4.8.2 变量声明 #### 4.8.2.1 每次只声明一个变量 不要使用组合声明,比如int a, b;。 #### 4.8.2.2 需要时才声明,并尽快进行初始化 不要在一个代码块的开头把局部变量一次性都声明了(这是c语言的做法),而是在第一次需要使用它时才声明。 局部变量在声明时最好就进行初始化,或者声明后尽快进行初始化。 ### 4.8.3 数组 #### 4.8.3.1 数组初始化:可写成块状结构 数组初始化可以写成块状结构,比如,下面的写法都是OK的: #### 4.8.3.2 非C风格的数组声明 中括号是类型的一部分:String[] args, 而非String args[]。 ### 4.8.4 switch语句 术语说明:switch块的大括号内是一个或多个语句组。每个语句组包含一个或多个switch标签(case FOO:或default:),后面跟着一条或多条语句。 #### 4.8.4.1 缩进 与其它块状结构一致,switch块中的内容缩进为2个空格。 每个switch标签后新起一行,再缩进2个空格,写下一条或多条语句。 #### 4.8.4.2 Fall-through:注释 在一个switch块内,每个语句组要么通过break, continue, return或抛出异常来终止,要么通过一条注释来说明程序将继续执行到下一个语句组, 任何能表达这个意思的注释都是OK的(典型的是用// fall through)。这个特殊的注释并不需要在最后一个语句组(一般是default)中出现。示例: #### 4.8.4.3 default的情况要写出来 每个switch语句都包含一个default语句组,即使它什么代码也不包含。 ### 4.8.5 注解(Annotations) 注解紧跟在文档块后面,应用于类、方法和构造函数,一个注解独占一行。这些换行不属于自动换行(第4.5节,自动换行),因此缩进级别不变。例如: 例外:单个的注解可以和签名的第一行出现在同一行。例如: 应用于字段的注解紧随文档块出现,应用于字段的多个注解允许与字段出现在同一行。例如: 参数和局部变量注解没有特定规则。 ### 4.8.6 注释 #### 4.8.6.1 块注释风格 块注释与其周围的代码在同一缩进级别。它们可以是/* ... */风格,也可以是// ...风格。对于多行的/* ... */注释,后续行必须从*开始, 并且与前一行的*对齐。以下示例注释都是OK的。 注释不要封闭在由星号或其它字符绘制的框架里。 Tip:在写多行注释时,如果你希望在必要时能重新换行(即注释像段落风格一样),那么使用/* ... */。 ### 4.8.7 Modifiers 类和成员的modifiers如果存在,则按Java语言规范中推荐的顺序出现。 命名约定 ## 5.1 对所有标识符都通用的规则 标识符只能使用ASCII字母和数字,因此每个有效的标识符名称都能匹配正则表达式w+。 在Google其它编程语言风格中使用的特殊前缀或后缀,如name_, mName, s_name和kName,在Java编程风格中都不再使用。 ## 5.2 标识符类型的规则 ### 5.2.1 包名 包名全部小写,连续的单词只是简单地连接起来,不使用下划线。 ### 5.2.2 类名 类名都以UpperCamelCase风格编写。 类名通常是名词或名词短语,接口名称有时可能是形容词或形容词短语。现在还没有特定的规则或行之有效的约定来命名注解类型。 测试类的命名以它要测试的类的名称开始,以Test结束。例如,HashTest或HashIntegrationTest。 ### 5.2.3 方法名 方法名都以lowerCamelCase风格编写。 方法名通常是动词或动词短语。 下划线可能出现在JUnit测试方法名称中用以分隔名称的逻辑组件。一个典型的模式是:test<\MethodUnderTest>_<\state>,例如testPop_emptyStack。 并不存在唯一正确的方式来命名测试方法。 ### 5.2.4 常量名 常量名命名模式为CONSTANT_CASE,全部字母大写,用下划线分隔单词。那,到底什么算是一个常量? 每个常量都是一个静态final字段,但不是所有静态final字段都是常量。在决定一个字段是否是一个常量时, 考虑它是否真的感觉像是一个常量。例如,如果任何一个该实例的观测状态是可变的,则它几乎肯定不会是一个常量。 只是永远不打算改变对象一般是不够的,它要真的一直不变才能将它示为常量。 这些名字通常是名词或名词短语。 ### 5.2.5 非常量字段名 非常量字段名以lowerCamelCase风格编写。 这些名字通常是名词或名词短语。 ### 5.2.6 参数名 参数名以lowerCamelCase风格编写。 参数应该避免用单个字符命名。 ### 5.2.7 局部变量名 局部变量名以lowerCamelCase风格编写,比起其它类型的名称,局部变量名可以有更为宽松的缩写。 虽然缩写更宽松,但还是要避免用单字符进行命名,除了临时变量和循环变量。 即使局部变量是final和不可改变的,也不应该把它示为常量,自然也不能用常量的规则去命名它。 ### 5.2.8 类型变量名 类型变量可用以下两种风格之一进行命名: 单个的大写字母,后面可以跟一个数字(如:E, T, X, T2)。 以类命名方式(5.2.2节),后面加个大写的T(如:RequestT, FooBarT)。 ## 5.3 驼峰式命名法(CamelCase) 驼峰式命名法分大驼峰式命名法(UpperCamelCase)和小驼峰式命名法(lowerCamelCase)。 有时,我们有不只一种合理的方式将一个英语词组转换成驼峰形式,如缩略语或不寻常的结构(例如”IPv6”或”iOS”)。Google指定了以下的转换方案。 名字从散文形式(prose form)开始: 把短语转换为纯ASCII码,并且移除任何单引号。例如:”Müller’s algorithm”将变成”Muellers algorithm”。 把这个结果切分成单词,在空格或其它标点符号(通常是连字符)处分割开。推荐:如果某个单词已经有了常用的驼峰表示形式,按它的组成将它分割开(如”AdWords”将分割成”ad words”)。 需要注意的是”iOS”并不是一个真正的驼峰表示形式,因此该推荐对它并不适用。 现在将所有字母都小写(包括缩写),然后将单词的第一个字母大写:每个单词的第一个字母都大写,来得到大驼峰式命名。 除了第一个单词,每个单词的第一个字母都大写,来得到小驼峰式命名。 最后将所有的单词连接起来得到一个标识符。 示例: ``` Prose form Correct Incorrect ------------------------------------------------------------------ "XML HTTP request"XmlHttpRequest XMLHTTPRequest "new customer ID"newCustomerId newCustomerID "inner stopwatch"innerStopwatch innerStopWatch "supports IPv6 on iOS?"supportsIpv6OnIos supportsIPv6OnIOS "YouTube importer"YouTubeImporter YoutubeImporter* ``` 加星号处表示可以,但不推荐。 Note:在英语中,某些带有连字符的单词形式不唯一。例如:”nonempty”和”non-empty”都是正确的,因此方法名checkNonempty和checkNonEmpty也都是正确的。 编程实践 ## 6.1 @Override:能用则用 只要是合法的,就把@Override注解给用上。 ## 6.2 捕获的异常:不能忽视 除了下面的例子,对捕获的异常不做响应是极少正确的。(典型的响应方式是打印日志,或者如果它被认为是不可能的,则把它当作一个AssertionError重新抛出。) 如果它确实是不需要在catch块中做任何响应,需要做注释加以说明(如下面的例子)。 例外:在测试中,如果一个捕获的异常被命名为expected,则它可以被不加注释地忽略。下面是一种非常常见的情形,用以确保所测试的方法会抛出一个期望中的异常, 因此在这里就没有必要加注释。 ## 6.3 静态成员:使用类进行调用 使用类名调用静态的类成员,而不是具体某个对象或表达式。 ## 6.4 Finalizers: 禁用 极少会去重写Object.finalize。 Tip:不要使用finalize。如果你非要使用它,请先仔细阅读和理解Effective Java第7条款:“Avoid Finalizers”,然后不要使用它。 Javadoc ## 7.1 格式 ### 7.1.1 一般形式 Javadoc块的基本格式如下所示: 或者是以下单行形式: 基本格式总是OK的。当整个Javadoc块能容纳于一行时(且没有Javadoc标记@XXX),可以使用单行形式。 ### 7.1.2 段落 空行(即,只包含最左侧星号的行)会出现在段落之间和Javadoc标记(@XXX)之前(如果有的话)。 除了第一个段落,每个段落第一个单词前都有标签<\p>,并且它和第一个单词间没有空格。 ### 7.1.3 Javadoc标记 标准的Javadoc标记按以下顺序出现:@param, @return, @throws, @deprecated, 前面这4种标记如果出现,描述都不能为空。 当描述无法在一行中容纳,连续行需要至少再缩进4个空格。 ## 7.2 摘要片段 每个类或成员的Javadoc以一个简短的摘要片段开始。这个片段是非常重要的,在某些情况下,它是唯一出现的文本,比如在类和方法索引中。 这只是一个小片段,可以是一个名词短语或动词短语,但不是一个完整的句子。它不会以A {@code Foo} is a...或This method returns...开头, 它也不会是一个完整的祈使句,如Save the record...。然而,由于开头大写及被加了标点,它看起来就像是个完整的句子。 Tip:一个常见的错误是把简单的Javadoc写成/** @return the customer ID */,这是不正确的。它应该写成/** Returns the customer ID. */。 ## 7.3 哪里需要使用Javadoc 至少在每个public类及它的每个public和protected成员处使用Javadoc,以下是一些例外: ### 7.3.1 例外:不言自明的方法 对于简单明显的方法如getFoo,Javadoc是可选的(即,是可以不写的)。这种情况下除了写“Returns the foo”,确实也没有什么值得写了。 单元测试类中的测试方法可能是不言自明的最常见例子了,我们通常可以从这些方法的描述性命名中知道它是干什么的,因此不需要额外的文档说明。 Tip:如果有一些相关信息是需要读者了解的,那么以上的例外不应作为忽视这些信息的理由。例如,对于方法名getCanonicalName, 就不应该忽视文档说明,因为读者很可能不知道词语canonical name指的是什么。 ### 7.3.2 例外:重写 如果一个方法重写了超类中的方法,那么Javadoc并非必需的。 ### 7.3.3 可选的Javadoc 对于包外不可见的类和方法,如有需要,也是要使用Javadoc的。如果一个注释是用来定义一个类,方法,字段的整体目的或行为, 那么这个注释应该写成Javadoc,这样更统一更友好。
作者:微信小助手
<section style="box-sizing: border-box;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="padding-top: 10px;padding-right: 10px;padding-left: 10px;box-sizing: border-box;background-color: rgb(239, 239, 239);"> <span style="display: inline-block;width: 5%;line-height: 0.8;font-weight: bolder;font-size: 48px;box-sizing: border-box;"> <section style="box-sizing: border-box;"> “ </section></span> <section style="display: inline-block;vertical-align: top;float: right;width: 90%;line-height: 1.5;font-size: 15px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;">上联:这个需求很简单,下联:怎么实现我不管,横批:今晚上线。</span></p> </section> <section style="clear: both;box-sizing: border-box;"></section> </section> </section> </section> </section> <p style="line-height: 1.75em;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;line-height: 27.2px;white-space: normal;text-align: center;"><img class="rich_pages" data-ratio="0.5187165775401069" data-s="300,640" src="/upload/f6a6e209018532913177651263fd700e.png" data-type="png" data-w="748"></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><span style="letter-spacing: 1px;color: rgb(89, 89, 89);font-size: 12px;"><em>经ThoughtWorks洞见(ID:TW-Insights)<em>公众号授权转载</em></em></span></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">暴力破解</p> </section> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">早上开完站会,小李领了张新卡,要对登录功能做升级改造,在原来只支持用户名密码登录模式的基础上,新增手机号和短信验证码登录。</span></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="rich_pages " src="/upload/2b989b633a6cc2614b250c7f0f4bfe06.jpg" data-w="633" data-type="jpeg" data-s="300,640" data-ratio="0.45813586097946285" data-cropsely2="348" data-cropsely1="0" data-cropselx2="618" data-cropselx1="0" data-copyright="0" style="max-width: 661px;height: 283.12px;box-sizing: border-box;visibility: visible;width: 618px;word-wrap: break-word;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">业务分析师薇薇早就准备好了故事卡,并且也考虑到这个功能的特殊性,除了平常的业务性验收标准外,还专门添加了一些和安全有关的条目。</span><span style="color: rgb(71, 193, 168);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">这张故事卡看上去是这样的:</span></p> <section data-tools="135编辑器" data-id="86005" style="line-height: 27.2px;white-space: normal;"> <section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin: 10px 0%;box-sizing: border-box;"> <section style="display: inline-block;width: 100%;vertical-align: top;border: 1px solid rgb(89, 89, 89);border-radius: 0px;box-sizing: border-box;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 5px;margin-right: 0%;margin-left: 0%;box-sizing: border-box;"> <section style="color: rgb(89, 89, 89);padding-right: 10px;padding-left: 10px;font-size: 15px;box-sizing: border-box;"> <p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;"><strong style="box-sizing: border-box;">故事卡-274:</strong></span></p> </section> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 8px;margin-right: 0%;margin-left: 0%;box-sizing: border-box;"> <section style="height: 1px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></section> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="box-sizing: border-box;"> <section style="display: inline-block;width: 100%;vertical-align: top;padding: 10px;box-sizing: border-box;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="box-sizing: border-box;"> <section style="font-size: 15px;color: rgb(89, 89, 89);line-height: 1.8;box-sizing: border-box;"> <p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">作为用户,我可以通过手机号和短信验证码登录,以便于我更方便的登录。<span style="line-height: 1.8;color: rgb(71, 193, 168);">安全验收标准:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">短信验证码有效期 5 分钟。</span></p></li> <li><p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">验证码为 4 位纯数字。</span></p></li> <li><p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">每个手机号 60 秒内只能发送一次短信验证码。</span></p></li> </ul> </section> </section> </section> </section> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-right: 0%;margin-bottom: 8px;margin-left: 0%;box-sizing: border-box;"> <section style="height: 1px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></section> </section> </section> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">小李看到故事卡中提到,验证码长度只有 4 位而且还是纯数字,隐约觉得强度有些不够,担心万一黑客来个多线程并发请求,或者拿一个集群来暴力登录,有可能会赶在有效期内破解出合法的验证码。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">小李把自己的担心讲给了业务分析师薇薇,并且建议把验证码长度增加到 6 位,或者在保持 4 位长度的情况下,改为数字和字母的组合,目的是增加验证码复杂性,提高暴力破解的门槛。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">薇薇听了这两种选择后直摇头,说道:“我理解你的担心,可是业务部门那边的需求很明确,就是为了优化用户登录体验,所以才决定做手机号和验证码登录,如果把验证码弄的这么复杂,那用户体验也好不到哪里去,不符合这个故事卡的初衷啊。”</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“对于用户而言,4 位数字验证码确实好记好填,可是对于黑客而言,就能很容易的完成暴力枚举,理论上最多 1 万次请求就能遍历完所有的验证码,更何况黑客没那么倒霉,要尝试到第 1 万次才猜对……”,小李说道。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">为了满足用户体验而在安全性上做出妥协,这种事情小李觉得自己无法说服自己,正准备掏出纸和笔跟薇薇做详细解释黑客攻击手段的时候,团队技术负责人老罗听见了他们俩的讨论。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">他慢慢脱下帽子,摸了摸正在朝着“地中海”模式演进的乌黑的秀发,说道:“那啥,服务器在验证登录请求的时候,不管验证码匹配还是不匹配,存在 Redis 里的验证码只要被取出来就立即作废,根本不给黑客暴力破解的机会。”</span></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="rich_pages " src="/upload/5686e3173e3c555042158cac788773cb.jpg" data-w="1280" data-type="jpeg" data-s="300,640" data-ratio="0.4859375" data-copyright="0" style="max-width: 661px;height: 321.31px;box-sizing: border-box;visibility: visible;width: 661px;word-wrap: break-word;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">小李的团队已经搭建好了 Redis,用来存储登录过程中发给用户的短信验证码,是一个手机号和验证码的键值对。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“对啊”,小李感觉眼前一亮,说道,“服务器在比对请求中的验证码和 Redis 中保存的这个用户手机号所对应的验证码的时候,如果发现不匹配,那依然还是直接把 Redis 中的这个验证码作废。</span><span style="line-height: 1.75em;font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这样黑客发第二次登录请求的时候,会因为 Redis 中找不到对应的记录而登录失败。这样既避免了暴力枚举攻击,同时也不再需要增加验证码的强度,导致用户体验的下降了。”</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">小李建议把刚才的讨论结果写到故事卡里,而薇薇提议能否不要立即作废:“万一用户输入验证码的时候手滑输错了,岂不是要等几十秒的时间再重新发第二个验证码?”</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“可以做到验证码 3 次输入错误后就作废吗?”薇薇问到。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“可以的,这个不难”小李坚定的回答到。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“好,那我们加一条安全验收标准吧”,薇薇边说边修改了故事卡,新增加了一条:</span><span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 1px;line-height: 1.75em;">保存于服务器端的验证码,至多可被使用 3 次(无论和请求中的验证码是否匹配),随后立即作废,以防止暴力攻击。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“对了小李”,老罗喝了口咖啡,最近连续的加班让老罗感觉很疲惫,只能靠喝咖啡强打精神。“60 秒内只能发 1 次短信那条,别忘了前后端都要做检查。”</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“知道知道,前端做不做都无所谓,关键是在后端要做限制。”小李连连点头。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“好,那就这么做,去忙吧”。老罗转身坐下,正准备继续刚才被打断的工作,此时一个念头快速在脑海里一闪而过。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">老罗在电脑上打开短信登录的这张故事卡,从头到尾又看了一次,最后目光停留在“短信验证码有效期 5 分钟”这条验收标准那里。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“短信每 60 秒发一次”,老罗心想:“但有效期是 5 分钟,那第 61 秒的时候假如又请求发送一次验证码,这时第一次发送的验证码还没过期,服务器端该怎么处理这个请求会比较稳妥呢?</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">显然,第二个验证码直接覆盖掉第一个会更加安全,也就是至始至终都只有一个处于有效状态的验证码,但这会不会给用户带来困惑?</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">毕竟偶尔还是有手机信号不好,等了 1 分多钟之后才收到第一个验证码的情况。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">如果不替换而是追加验证码呢?最极端的情况是会出现一个手机号有 5 个有效验证码的情况,会增加黑客暴力破解的成功概率。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">不过因为一个验证码最多只能被使用 3 次,之后就被作废了,所以实际上黑客暴力破解的难度依然很高。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">总的来说,直接覆盖的做法用户体验不佳但更安全,依然有效的做法用户体验更好但相对而言安全性略有降低。”</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">经过反复思考后,老罗最终选择保留验证码 5 分钟有效期的设置。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">防不胜防</p> </section> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">短信验证码登录的功能上线后,运行状态一直比较平稳,然而这种平静的氛围被一通电话打破了。</span></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="rich_pages " src="/upload/55f24771eba350fa9eef2dd101e445c6.jpg" data-w="1280" data-type="jpeg" data-s="300,640" data-ratio="0.45078125" data-cropy2="577.8640776699028" data-cropy1="0" data-cropx2="1280" data-cropx1="0" data-croporisrc="https://mmbiz.qpic.cn/mmbiz_jpg/aaVJqS7LaMLD1mLPRzOReH4vMIH6wVFXgl367ucueSvhftGUibNBOfEdBtMQ2RX6CY4aP1XkFhtHl7yyqmxBbEQ/?wx_fmt=jpeg" style="max-width: 677px;height: 278.67px;box-sizing: border-box;visibility: visible;width: 618px;word-wrap: break-word;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“喂,对,是我”,老罗桌上的电话响了,他忙着写代码,歪着脖子用肩膀和脸夹住话筒说道:“是客服部啊,有什么事我可以帮忙的?”</span><br style="box-sizing: border-box;max-width: 100%;word-wrap: break-word;"></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“是这样,我们今天突然收到很多顾客打来的电话,抱怨说收不到短信验证码,登录不了账户,他们基本都是新用户,只有用手机注册的账号,没有用户名密码,所以也不能用原先的用户名密码去登录账号。我们只好让顾客再等会儿试试,可能是信号不好,但后来他们反馈说还是收不到我们的短信,而且只是收不到我们的短信,所以,你们那边能帮忙看看是怎么回事吗?”电话那边一口气讲了一堆话。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“还有这种事,行,我知道了,我们马上调查分析一下。”老罗刚挂断电话,运维部的同事过来找到老罗,说短信配额今天消耗得很厉害,已经触发了 2 次告警了。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">运维同事做了一下简单的分析,发现早上 10 点和下午 2 点左右有两批次大量发送登录短信验证码的请求,但又没有观察到对应的后续登录请求,判断可能是被黑客攻击了,于是临时性的屏蔽了攻击来源 IP 地址的访问。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“来找你就是想和开发团队共同调查下这个问题,看接下来怎么处理会比较好。”运维部的同事说道。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">老罗觉得这个事和刚刚接到的客服部门说的是同一件事,便把刚才电话里听到的信息和运维同事讲了一遍。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“这更能证实是黑客攻击了,而且看来他们的目标应该不是暴力登录,而是故意消耗短信发送配额,一旦配额被用完,用户就无法正常登录,也算是某种程度上的拒绝式服务攻击了。” 运维部的同事说完看向老罗。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">老罗若有所思的说道:“没想到他们还能这么玩儿。我们目前只限制了一个手机号 60 秒内发一次验证码,却没有应对大量不同手机号的情况。”</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“那现在怎么处理比较好呢?虽然临时禁用了攻击者的 IP,但我们担心会误伤真实用户,而且黑客也可能会变换 IP 来继续进行攻击。”运维同事继续问道。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“有办法,在发短信验证码之前先要求输入图形验证码。”</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“嗯,有道理,你们什么时候能做好上线?”</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“我现在就加”,老罗还没说完就已经开始写代码了:“一会儿弄完紧急上线。”</span></p> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“行,我回去安排一下,咱们运维部全力配合。”</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“看来之前那张故事卡里的安全验收标准还差了一条”,老罗自然自语道:“如果加上一条图形验证码的要求恐怕就不会出这个事儿了。”</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <section data-tools="135编辑器" data-id="86005" style="line-height: 27.2px;white-space: normal;"> <section> <p style="margin-right: 8px;margin-left: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">发送短信验证码之前,先验证图形验证码是否正确。</span></p> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">权衡</p> </section> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“喂喂喂,这搞的什么鬼?”用户体验设计师 Jenny 抓住路过的老罗说:“我不过就是休了两天假,回来之后怎么发现登录这里多了个图形验证码出来?”</span></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="rich_pages " src="/upload/423897791094e3907465b95aac2a4fa9.jpg" data-w="1280" data-type="jpeg" data-s="300,640" data-ratio="0.478125" data-copyright="0" style="max-width: 661px;height: 315.81px;box-sizing: border-box;visibility: visible;width: 661px;word-wrap: break-word;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">老罗向 Jenny 解释了这个图形验证码的由来,是出于安全的考虑才增加的。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“我知道安全很重要,可是这图形验证码太伤害用户体验了,现在顾客登录过程中就要再多做一次输入,如果填错了还得重新再来一次,而且这图形验证码的风格和我们的 App 风格明显不匹配,另外,这图形验证码是不是也太扭曲了,我都看错好几回了……。”Jenny 显然并不认同这个方案。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“风格我们可以修改,这不是还有你嘛。”老罗为难的说到:“难度高是因为现在的图像识别技术突飞猛进,简单图片验证码很容易被破解。”</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“莫非就没有别的解决办法了吗?”Jenny 继续问道。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“其实也有,就看公司舍不舍得花这笔钱了。”老罗接着说:“登录界面可以动态决定是否要求输入图形验证码,对于正常用户可以让他们无需输入图形验证码,对于黑客或者疑似黑客的人,就要求他们输入。”</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“这听上去很好啊,另外,这和舍不舍得花钱有什么关系?”Jenny 不太明白。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“要动态决定是否要求输入图形验证码这件事儿,其实就是判断当前用我们 App 的人是真实的顾客还是黑客。我们自己没这个判断能力,不过有提供这种服务的第三方 API,只是他们都不是免费的,得花钱买。”老罗向 Jenny 解释到。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">阿某云和腾某云等等都提供这类服务,其主要原理是,服务器在处理登录请求的时候,先尽可能多的收集该请求的上下文信息。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">例如登录请求的来源 IP 地址,时间,手机号,User-Agent 等等数据,并且把这些数据传递给第三方 API,由他们进行一次分析判断,并把结果返回给服务器,告诉服务器当前请求者是可信用户还是可疑用户。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最终是否允许登录成功的决定权还是在服务器这边,只是借助了第三方 API 提供的分析结果来做判断而已。</span></p> <p style="margin-right: 8px;margin-bottom: 5px;margin-left: 8px;white-space: normal;text-align: center;line-height: 1.75em;"><img class="rich_pages " src="/upload/b5d00e6cdd6659dacc4c67814f72178f.jpg" data-w="1280" data-type="jpeg" data-s="300,640" data-ratio="0.315625" data-cropy2="461.96013289036546" data-cropy1="33.63787375415282" data-cropx2="1350" data-cropx1="0" data-croporisrc="https://mmbiz.qpic.cn/mmbiz_png/aaVJqS7LaMLD1mLPRzOReH4vMIH6wVFXuREY7w1WfChcTqXFgYxiaGhUA5DA0crEInibLcGbKMVm2Gnia14PNbvKw/?wx_fmt=png" style="max-width: 661px;height: 190.07px;box-sizing: border-box;visibility: visible;width: 602px;word-wrap: break-word;"></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“我不懂技术,不过好像也听懂了的样子。"Jenny 笑着说道。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“用第三方 API 做登录判断这事儿我拍不了板,得找领导批准,说不定还得走采购流程。”但老罗觉得这条路的方向是对的。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">“走,我们去问问领导的意见,我实在受不了现在这个图形验证码。”Jenny 拉着老罗径直朝着总经理办公室走去。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;border-bottom-width: 1px;border-bottom-style: solid;border-bottom-color: black;line-height: 1.2;box-sizing: border-box;"> <section style="margin-bottom: -1px;display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;">尾声</p> </section> </section> </section> </section> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">最终,老罗他们团队用上了某云的第三方 API 做登录防护,去掉了令 Jenny 抓狂的图形验证码。经过和业务部门的商量,验证码有效期最后缩短到了 2 分钟。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在这期间还出现了两个小插曲。运维部门的同事偶然间发现,应用程序日志文件里居然保存了所有用户的短信验证码,这是小李当初做调试的时候加上去的,后来忘记关掉了。好在并没有造成泄露,后来团队修复了这个问题。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">另一个小插曲是,团队做了微服务架构改造,把发送短信的功能拆分出来做成了一个独立微服务,但却没有给这个新的接口设置好访问控制权限。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">以至于任何人在无需登录的情况下,只要向这个接口发起请求就能成功发送一条短信给任意手机,短信内容还可以自定义。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这个问题是在安全团队做渗透测试的时候发现的,吓得老罗浑身冒冷汗。所幸发现及时,做了紧急修复,并没有造成安全事故。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;margin-bottom: 5px;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">薇薇后来把短信登录的故事卡作为案例保存了起来,把安全验收标准又重新做了一次梳理,</span><span style="font-size: 15px;letter-spacing: 1px;line-height: 1.75em;color: rgb(71, 193, 168);">所以最终的故事卡是这样的:</span></p> <section style="box-sizing: border-box;font-size: 16px;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin: 10px 0%;box-sizing: border-box;"> <section style="display: inline-block;width: 100%;vertical-align: top;border: 1px solid rgb(89, 89, 89);border-radius: 0px;box-sizing: border-box;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 5px;margin-right: 0%;margin-left: 0%;box-sizing: border-box;"> <section style="color: rgb(89, 89, 89);padding-right: 10px;padding-left: 10px;font-size: 15px;box-sizing: border-box;"> <p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;"><strong style="box-sizing: border-box;">故事卡-274:</strong></span></p> </section> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-top: 8px;margin-right: 0%;margin-left: 0%;box-sizing: border-box;"> <section style="height: 1px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></section> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="box-sizing: border-box;"> <section style="display: inline-block;width: 100%;vertical-align: top;padding: 10px;box-sizing: border-box;"> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="box-sizing: border-box;"> <section style="font-size: 15px;color: rgb(89, 89, 89);line-height: 1.8;box-sizing: border-box;"> <p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">作为用户,我可以通过手机号和短信验证码登录,以便于我更方便的登录。<span style="color: rgb(71, 193, 168);">安全验收标准:</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">短信验证码有效期 2 分钟。</span></p></li> <li><p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">验证码为 6 位纯数字。</span></p></li> <li><p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">每个手机号 60 秒内只能发送一次短信验证码,且这一规则的校验必须在服务器端执行。</span></p></li> <li><p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">同一个手机号在同一时间内可以有多个有效的短信验证码。</span></p></li> <li><p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">保存于服务器端的验证码,至多可被使用 3 次(无论和请求中的验证码是否匹配),随后立即作废,以防止暴力攻击。</span></p></li> <li><p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">短信验证码不可直接记录到日志文件。</span></p></li> <li><p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">发送短信验证码之前,先验证图形验证码是否正确(可选)。</span></p></li> <li><p style="white-space: normal;box-sizing: border-box;"><span style="letter-spacing: 1px;">集成第三方 API 做登录保护(可选)。</span></p></li> </ul> </section> </section> </section> </section> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <section style="margin-right: 0%;margin-bottom: 8px;margin-left: 0%;box-sizing: border-box;"> <section style="height: 1px;box-sizing: border-box;background-color: rgb(89, 89, 89);"></section> </section> </section> </section> </section> </section> </section> <p style="line-height: normal;"><br></p> <p style="margin-right: 8px;margin-left: 8px;white-space: normal;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">没成想,一个短信登录 API 背后,还能牵扯出这么多事儿来。</span></p> <p style="white-space: normal;line-height: normal;"><br></p> <p style="white-space: normal;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);letter-spacing: 1px;"><em><span style="font-size: 14px;">作者:马伟</span></em></span></p> <p style="white-space: normal;line-height: 1.75em;"><span style="color: rgb(89, 89, 89);letter-spacing: 1px;"><em><span style="font-size: 14px;">编辑:陶家龙、孙淑娟</span></em></span><br></p> <p style="white-space: normal;line-height: 1.75em;"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;"><em>出处:转载自公众号ThoughtWorks洞见(ID:TW-Insights)</em></span></p> <p style="line-height: 27.2px;white-space: normal;text-align: center;"><img class="rich_pages" data-copyright="0" data-ratio="0.3939393939393939" src="/upload/58a14061a632a0fe87d40beb73c1aa.gif" data-type="gif" data-w="660"></p> <section style="white-space: normal;box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="box-sizing: border-box;"> <section style="margin-top: 0.5em;margin-bottom: 0.5em;box-sizing: border-box;"> <section style="font-size: 15px;border-style: solid;border-width: 0px 0px 1px;color: rgb(89, 89, 89);border-bottom-color: rgba(215, 215, 215, 0.960784);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="letter-spacing: 1px;"><strong>精彩文章推荐:</strong></span></p> </section> </section> </section> </section> <p style="white-space: normal;line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655820851&idx=1&sn=935c442164ff41d09f4b1f49e98dd4f1&chksm=bd74d1e48a0358f22577ea6c13a249e8faa8a0bb16e20deb0d50422cb7ca7e9fd68e1c1a6b13&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2" style="color: rgb(89, 89, 89);font-size: 14px;letter-spacing: 1px;">如何设计一个亿级API网关?</a><br></p> <p style="white-space: normal;line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655824028&idx=1&sn=d58e7f613171fb144cf2587390114901&chksm=bd74e54b8a036c5dba7530e203aa4912717773418ce7719c7918e0af3a2d1d4d1c4abce46cdf&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2" style="color: rgb(89, 89, 89);font-size: 14px;letter-spacing: 1px;">震惊了,原来这才是Kafka的“真面目”!</a><br></p> <p style="white-space: normal;line-height: 2em;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655824052&idx=1&sn=5a81ac63dbefb2679cea6a2467c6b929&chksm=bd74e5638a036c75e8451cd3e66697cd714ed528c4e5dd17760464c63a57dc40c2834acabb8a&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2" style="color: rgb(89, 89, 89);font-size: 14px;letter-spacing: 1px;">程序员升职加薪套路都在4月的这十篇热门文章</a></p>
作者:微信小助手
<p><span style="letter-spacing: 1px;font-size: 15px;">一步一步,娓娓道来。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;">一般来说,并发量大,吞吐量大的互联网分层架构是怎么样的?</span></strong><strong><span style="letter-spacing: 1px;font-size: 15px;"></span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">数据库上层都有一个微服务,服务层记录“业务库”与“数据库实例配置”的映射关系,通过数据库连接池向数据库路由sql语句。</span></p> <p><img data-ratio="0.6568047337278107" data-type="png" data-s="300,640" data-w="169" src="/upload/35105ffb4dd33463edf3056dd09c845d.png"></p> <p><span style="letter-spacing: 1px;font-size: 15px;">如上图所示,服务层配置用户库user对应的数据库实例ip。</span></p> <p><span style="color: rgb(0, 82, 255);letter-spacing: 1px;"><em><span style="color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 15px;">画外音:其实是一个内网域名。</span></em></span></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><br style="margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;"></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><span style="letter-spacing: 1px;"><strong><span style="margin: 0px;padding: 0px;color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;display: inline !important;float: none;background-color: transparent;">该分层架构,如何应对数据库的</span></strong><span style="color: rgb(255, 76, 0);letter-spacing: 1px;"><strong><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;display: inline !important;float: none;background-color: transparent;">高可用</span></strong></span><strong><span style="margin: 0px;padding: 0px;color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;display: inline !important;float: none;background-color: transparent;">?</span></strong><strong><span style="margin: 0px;padding: 0px;color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;display: inline !important;float: none;background-color: transparent;"></span></strong></span></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;">数据库高可用,很常见的一种方式,使用</span><span style="margin: 0px;padding: 0px;color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">双主同步+keepalived+虚ip</span><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;">的方式进行。</span></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><img style="height: 110px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 556px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;" data-ratio="0.5188679245283019" data-type="png" data-s="300,640" data-w="212" src="/upload/964a0d09fe642d5484a452f081d2195c.png"></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;">如上图所示,两个相互同步的主库使用相同的虚ip。</span></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;"><br></span></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><img style="height: 108px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 556px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;" data-ratio="0.5094339622641509" data-type="png" data-s="300,640" data-w="212" src="/upload/2e0ab700e159d47ea225cb7fc396f748.png"></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;">当主库挂掉的时候,虚ip自动漂移到另一个主库,整个过程对调用方透明,通过这种方式保证数据库的高可用。</span></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><span style="color: rgb(0, 82, 255);letter-spacing: 1px;"><em><span style="margin: 0px;padding: 0px;color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 15px;">画外音:关于高可用,《</span></em><em><span style="margin: 0px;padding: 0px;color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 15px;"><a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651962050&idx=1&sn=f60b8bb833fe3425f5227da42e3b3adf&chksm=bd2d0f1e8a5a8608f81d42a16eea476d0bd4763f84f9a008ed616d1cfa050a4015780f898eb1&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2">互联网分层架构如何保证“高可用“?</a>》专题介绍过,本文不再展开。</span></em></span></p> <p style="background-color: transparent;clear: both;color: rgb(51, 51, 51);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;"><br></span></p> <p><span style="letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;">该分层架构,如何应对</span></strong><span style="color: rgb(255, 76, 0);letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;">数据量的暴增</span></strong></span><strong><span style="letter-spacing: 1px;font-size: 15px;">?</span></strong><strong><span style="letter-spacing: 1px;font-size: 15px;"></span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">随着数据量的增大,数据库要进行</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">水平切分</span><span style="letter-spacing: 1px;font-size: 15px;">,分库后将数据分布到不同的数据库实例(甚至物理机器)上,以达到降低数据量,增强性能的扩容目的。</span></p> <p><img data-ratio="0.36538461538461536" data-type="png" data-s="300,640" data-w="312" src="/upload/1224012d02f604439fc871155cb179e5.png"></p> <p><span style="letter-spacing: 1px;font-size: 15px;">如上图所示,用户库user分布在两个实例上,ip0和ip1,服务层通过用户标识uid取模的方式进行寻库路由,模2余0的访问ip0上的user库,模2余1的访问ip1上的user库。</span></p> <p><span style="color: rgb(0, 82, 255);letter-spacing: 1px;"><em><span style="color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 15px;">画外音:此时,水平切分集群的读写实例加倍,单个实例的数据量减半,性能增长可不止一倍。</span></em></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"> </span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">综上三点所述,大数据量,高可用的互联网微服务分层的架构如下:</span></p> <p><img data-ratio="0.2936708860759494" data-type="png" data-s="300,640" data-w="395" src="/upload/6cfbfaa694b6fc1ecd850faed576598a.png"></p> <p><span style="text-align: justify;color: rgb(51, 51, 51);text-transform: none;text-indent: 0px;letter-spacing: 1px;font-size: 15px;">既有水平切分,又保证高可用。</span></p> <p><br></p> <p><span style="letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;">如果数据量持续增大,2个库性能扛不住了,该怎么办呢?</span></strong><strong><span style="letter-spacing: 1px;font-size: 15px;"></span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">此时,需要继续水平拆分,拆成更多的库,降低单库数据量,增加库主库实例(机器)数量,提高性能。</span></p> <p><br></p> <p><span style="letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;"><span style="text-align: justify;color: rgb(51, 51, 51);text-transform: none;text-indent: 0px;letter-spacing: 1px;" important="important" transparent="transparent" inline="inline" neue="neue" sc="sc" gb="gb" ui="ui" normal="normal" none="none" px="px" helvetica="helvetica" sans="sans" yahei="yahei">新的问题来了,</span>分成n个库后,随着数据量的增加,要增加到2*n个库,数据库如何扩容,数据能否平滑迁移,能够持续对外提供服务,保证服务的可用性?</span></strong><strong><span style="letter-spacing: 1px;font-size: 15px;"></span></strong></span></p> <p><span style="color: rgb(0, 82, 255);letter-spacing: 1px;"><em><span style="color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 15px;">画外音:你遇到过类似的问题么?</span></em></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"> </span></p> <p><span style="letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;">停服扩容,是最容易想到的方案?</span></strong><strong><span style="letter-spacing: 1px;font-size: 15px;"></span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">在讨论秒级平滑扩容方案之前,先简要说明下停服务扩容的方案的步骤:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)站点挂一个公告“为了为广大用户提供更好的服务,本站点/游戏将在今晚00:00-2:00之间升级,届时将不能登录,用户周知”;</span></p> <p><span style="color: rgb(0, 82, 255);letter-spacing: 1px;"><em><span style="color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 15px;">画外音:见过这样的公告么,实际上在迁移数据。</span></em></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(2)</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">微服务停止服务</span><span style="letter-spacing: 1px;font-size: 15px;">,数据库不再有流量写入;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(3)</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">新建2*n个新库</span><span style="letter-spacing: 1px;font-size: 15px;">,并做好高可用;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(4)写一个小脚本进行</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">数据迁移</span><span style="letter-spacing: 1px;font-size: 15px;">,把数据从n个库里select出来,insert到2*n个库里;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(5)</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">修改微服务的数据库路由配置</span><span style="letter-spacing: 1px;font-size: 15px;">,模n变为模2*n;<br></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(6)</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">微服务<span style="text-align: justify;text-transform: none;text-indent: 0px;letter-spacing: 1px;" important="important" transparent="transparent" inline="inline" neue="neue" sc="sc" gb="gb" ui="ui" normal="normal" none="none" px="px" helvetica="helvetica" sans="sans" yahei="yahei">重启</span></span><span style="letter-spacing: 1px;font-size: 15px;">,连接新库重新对外提供服务;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">整个过程中,最耗时的是第四步数据迁移。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"> </span></p> <p><span style="letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;">如果出现问题,如何进行回滚?</span></strong><strong><span style="letter-spacing: 1px;font-size: 15px;"></span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">如果数据迁移失败,或者迁移后测试失败,则将</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">配置改回旧库,恢复服务</span><span style="letter-spacing: 1px;font-size: 15px;">即可。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"> </span></p> <p><span style="letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;">停服方案有什么优劣?</span></strong><strong><span style="letter-spacing: 1px;font-size: 15px;"></span></strong></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">优点:</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">简单</span><span style="letter-spacing: 1px;font-size: 15px;">。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"> </span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">缺点:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)需要停止服务,</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">方案不高可用</span><span style="letter-spacing: 1px;font-size: 15px;">;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(2)</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">技术同学压力大</span><span style="letter-spacing: 1px;font-size: 15px;">,所有工作要在规定时间内完成,根据经验,压力越大约容易出错;</span></p> <p><span style="color: rgb(0, 82, 255);letter-spacing: 1px;"><em><span style="color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 15px;">画外音:这一点很致命。</span></em></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(3)如果有问题第一时间没检查出来,启动了服务,运行一段时间后再发现有问题,则难以回滚,如果回档会</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">丢失一部分数据</span><span style="letter-spacing: 1px;font-size: 15px;">;</span></p> <p><br></p> <p><span style="letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;">有没有秒级实施、更平滑、更帅气的方案呢?</span></strong><strong><span style="letter-spacing: 1px;font-size: 15px;"></span></strong></span></p> <p><img data-ratio="0.2936708860759494" data-type="png" data-s="300,640" data-w="395" src="/upload/6cfbfaa694b6fc1ecd850faed576598a.png"></p> <p><span style="letter-spacing: 1px;font-size: 15px;">再次看一眼扩容前的架构,分两个库,假设每个库1亿数据量,<strong>如何</strong>平滑扩容,增加实例数,降低单库数据量呢?三个简单步骤搞定。</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"> </span></p> <p><span style="letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;">步骤一:修改配置。</span></strong><strong><span style="letter-spacing: 1px;font-size: 15px;"></span></strong></span></p> <p><img data-ratio="0.4496124031007752" data-type="png" data-s="300,640" data-w="387" src="/upload/a203ac24702576c56d46a2f21bc3887d.png"></p> <p><span style="letter-spacing: 1px;font-size: 15px;">主要修改两处:</span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="letter-spacing: 1px;font-size: 15px;">数据库实例所在的机器做<strong>双虚ip</strong>:</span></p></li> </ul> <p><span style="letter-spacing: 1px;"><span style="letter-spacing: 1px;font-size: 15px;">(1)原%2=0的库是虚ip0,现增加一个虚ip00;</span></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(2)原%2=1的库是虚ip1,<span style="color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;display: inline !important;float: none;background-color: transparent;" neue="neue" sc="sc" gb="gb" ui="ui" normal="normal" none="none" px="px" helvetica="helvetica" sans="sans" yahei="yahei" justify="justify">现增加一个虚ip11;</span></span></p> <ul class=" list-paddingleft-2" style="list-style-type: disc;"> <li><p><span style="letter-spacing: 1px;font-size: 15px;">修改服务的配置,将2个库的<strong>数据库配置</strong>,改为4个库的数据库配置,修改的时候要注意旧库与新库的映射关系:</span></p></li> </ul> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)%2=0的库,会变为%4=0与%4=2;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(2)%2=1的部分,会变为%4=1与%4=3;</span></p> <p><span style="color: rgb(0, 82, 255);letter-spacing: 1px;"><em><span style="color: rgb(0, 82, 255);font-size: 15px;">画外音:这样能够保证,依然路由到正确的数据。</span></em></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"> </span></p> <p><span style="letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;">步骤二:reload配置,实例扩容。</span></strong><strong><span style="letter-spacing: 1px;font-size: 15px;"></span></strong></span></p> <p><img data-ratio="0.4860759493670886" data-type="png" data-s="300,640" data-w="395" src="/upload/c9f2ef66d6c962c1008b28581d733f72.png"></p> <p><span style="letter-spacing: 1px;font-size: 15px;">服务层reload配置,reload可能是这么几种方式:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(a)比较原始的,重启服务,读新的配置文件;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(b)高级一点的,配置中心给服务发信号,重读配置文件,重新初始化数据库连接池;</span></p> <p><br></p> <p><span style="letter-spacing: 1px;font-size: 15px;">不管哪种方式,reload之后,数据库的实例扩容就完成了,原来是2个数据库实例提供服务,现在变为4个数据库实例提供服务,这个过程一般可以在秒级完成。</span></p> <p><br></p> <p><img style="background-color: transparent;color: rgb(51, 51, 51);" data-ratio="0.4860759493670886" data-type="png" data-s="300,640" data-w="395" src="/upload/c9f2ef66d6c962c1008b28581d733f72.png"></p> <p><span style="letter-spacing: 1px;font-size: 15px;">整个过程可以逐步重启,对服务的正确性和可用性完全没有影响:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(a)</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">即使%2寻库和%4寻库同时存在,也不影响数据的正确性</span><span style="letter-spacing: 1px;font-size: 15px;">,因为此时仍然是双主数据同步的;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(b)</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">即使%4=0与%4=2的寻库落到同一个数据库实例上,<span style="letter-spacing: 1px;font-size: 15px;display: inline !important;float: none;background-color: transparent;" neue="neue" sc="sc" gb="gb" ui="ui" normal="normal" none="none" px="px" helvetica="helvetica" sans="sans" yahei="yahei" justify="justify">也不影响数据的正确性</span></span><span style="color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;display: inline !important;float: none;background-color: transparent;">,<span style="color: rgb(51, 51, 51);letter-spacing: 1px;font-size: 15px;display: inline !important;float: none;background-color: transparent;" neue="neue" sc="sc" gb="gb" ui="ui" normal="normal" none="none" px="px" helvetica="helvetica" sans="sans" yahei="yahei" justify="justify">因为此时仍然是双主数据同步的;</span></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"> </span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">完成了实例的扩展,会发现每个数据库的数据量依然没有下降,所以第三个步骤还要做一些收尾工作。</span></p> <p><span style="color: rgb(0, 82, 255);letter-spacing: 1px;"><em><span style="color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 15px;">画外音:这一步,数据库实例个数加倍了。</span></em></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"> </span></p> <p><span style="letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;">步骤三:收尾工作,数据收缩。</span></strong><strong><span style="letter-spacing: 1px;font-size: 15px;"></span></strong></span></p> <p><img data-ratio="0.6212871287128713" data-type="png" data-s="300,640" data-w="404" src="/upload/9f7325770250c28d44617b6410dc32dd.png"></p> <p><span style="letter-spacing: 1px;font-size: 15px;">有这些一些收尾工作:</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(a)把双虚ip修改回单虚ip;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(b)解除旧的双主同步,让成对库的数据不再同步增加;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(c)增加新的双主同步,保证高可用;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(d)删除掉冗余数据,例如:ip0里%4=2的数据全部删除,只为%4=0的数据提供服务;</span></p> <p><span style="color: rgb(0, 82, 255);letter-spacing: 1px;"><em><span style="color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 15px;">画外音:这一步,<span style="text-align: justify;color: rgb(0, 82, 255);text-transform: none;text-indent: 0px;letter-spacing: 1px;" important="important" transparent="transparent" inline="inline" neue="neue" sc="sc" gb="gb" ui="ui" normal="normal" none="none" px="px" helvetica="helvetica" sans="sans" yahei="yahei" italic="italic">数据库单实例数据量减半了。</span></span></em></span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"> </span></p> <p><span style="letter-spacing: 1px;"><strong><span style="letter-spacing: 1px;font-size: 15px;">总结</span></strong><strong><span style="letter-spacing: 1px;font-size: 15px;"></span></strong></span></p> <p><img data-ratio="0.3071528751753156" data-type="png" data-s="300,640" data-w="713" src="/upload/a1006c12dd21e304f1989dae22cda75f.png"></p> <p><span style="letter-spacing: 1px;font-size: 15px;">互联网大数据量,高吞吐量,高可用微服务分层架构,数据库实现秒级平滑扩容的三个步骤为:</span><br></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(1)修改配置(双虚ip,微服务数据库路由);</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(2)reload配置,</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">实例增倍</span><span style="letter-spacing: 1px;font-size: 15px;">完成;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;">(3)删除冗余数据等收尾工作,</span><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">数据量减半</span><span style="letter-spacing: 1px;font-size: 15px;">完成;</span></p> <p><span style="letter-spacing: 1px;font-size: 15px;"> </span></p> <p><span style="letter-spacing: 1px;"><span style="color: rgb(255, 76, 0);letter-spacing: 1px;font-size: 15px;">思路</span><span style="letter-spacing: 1px;font-size: 15px;">比结论重要,希望大家有收获。</span><br></span></p> <p style="text-align: center;color: rgb(51, 51, 51);line-height: normal;clear: both;box-sizing: border-box;background-color: rgb(255, 255, 255);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><img width="auto" style="box-sizing: border-box;height: 130px;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 677px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;visibility: visible;width: 130px;word-wrap: break-word;" data-ratio="1" data-type="jpeg" data-s="300,640" data-w="250" src="/upload/7ddc9700032e2c5cee163f1f1a37b46c.jpg"></strong></span></p> <p style="text-align: center;color: rgb(51, 51, 51);line-height: normal;clear: both;box-sizing: border-box;background-color: rgb(255, 255, 255);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 12px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"><strong style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;">架构师之路</strong>-分享技术思路</strong><br style="box-sizing: border-box;margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;max-width: 100%;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;word-wrap: break-word;"></span></p> <p style="margin: 0px;padding: 0px;text-align: left;color: rgb(51, 51, 51);text-transform: none;text-indent: 0px;letter-spacing: 0.54px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;word-wrap: break-word;min-height: 17px;max-width: 677px;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">推荐文章:</span></p> <p style="margin: 0px;padding: 0px;text-align: left;color: rgb(51, 51, 51);text-transform: none;text-indent: 0px;letter-spacing: 0.54px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;word-wrap: break-word;min-height: 17px;max-width: 677px;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">《<a href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651962219&idx=1&sn=30545c7a9f46fa74a61cc09323a6a8c9&chksm=bd2d0eb78a5a87a1c16b1d10fbb688adb2848345b70fa2fbc161b3a566c7c3e02adaccd5981e&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2">1万属性100亿数据10万吞吐,架构设计?</a>》</span></p> <p style="margin: 0px;padding: 0px;text-align: left;color: rgb(51, 51, 51);text-transform: none;text-indent: 0px;letter-spacing: 0.54px;font-size: 17px;font-style: normal;font-variant: normal;font-weight: 400;text-decoration: none;word-spacing: 0px;white-space: normal;word-wrap: break-word;min-height: 17px;max-width: 677px;box-sizing: border-box;orphans: 2;-webkit-text-stroke-width: 0px;background-color: rgb(255, 255, 255);"><span style="margin: 0px;padding: 0px;letter-spacing: 1px;font-size: 15px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;">《<span style="margin: 0px;padding: 0px;color: rgb(0, 82, 255);letter-spacing: 1px;font-size: 15px;word-wrap: break-word;max-width: 100%;box-sizing: border-box;"><a style="color: rgb(87, 107, 149);margin-bottom: 0px;margin-left: 0px;margin-right: 0px;margin-top: 0px;padding-bottom: 0px;padding-left: 0px;padding-right: 0px;padding-top: 0px;text-decoration: none;" href="http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651962050&idx=1&sn=f60b8bb833fe3425f5227da42e3b3adf&chksm=bd2d0f1e8a5a8608f81d42a16eea476d0bd4763f84f9a008ed616d1cfa050a4015780f898eb1&scene=21#wechat_redirect" target="_blank" data-itemshowtype="0" data-linktype="2">互联网分层架构如何保证“高可用“?</a></span>》</span></p>
作者:じ☆ve宝贝
## 1、下载纯真ip地址数据库并解压为txt格式的文件 打开软件后选择解压 ## 2、替换多余空格为我们需要的格式() 我这里用一种土方法,在文本编辑器的“查找”栏中输入“ ”(9个空格,因为最大的间隔就是9个空格),“替换”栏输入分隔符“&”,然后全部替换。完成后把“查找”改成8个空格,“替换”不改,再全部替换。然后再7个、6个空格,直到全部空格都变成分隔符。 正数据: ``` 0.0.0.0&0.255.255.255||IANA&保留地址 1.0.0.0&1.0.0.255&澳大利亚&亚太互联网络信息中心 1.0.1.0&1.0.3.255&福建省&电信 ``` ## 3、利用java程序读取: JDBCManager ``` package cn.studyjava; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class JDBCManager { private String driver = "com.mysql.jdbc.Driver"; private String url = "jdbc:mysql://127.0.0.1:3306/bbs?useUnicode=true&characterEncoding=UTF-8"; private String user = "root"; private String password = "root"; /** * 加载驱动 * * @return */ public Connection getConnection() { Connection con = null; try { Class.forName(driver); con = DriverManager.getConnection(url, user, password); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return con; } public void closeConnection(Connection con, PreparedStatement ps, ResultSet rs) { if (null != con) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } if (null != ps) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (null != rs) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } } } ``` 处理类: ``` package cn.studyjava; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; public class ReadFile { public static void main(String[] args) throws SQLException, IOException { FileInputStream fis=new FileInputStream("D:\\test.txt"); InputStreamReader isr=new InputStreamReader(fis, "UTF-8"); BufferedReader br = new BufferedReader(isr); String line=""; String[] arrs=null; long currentTimeMillis = System.currentTimeMillis(); JDBCManager jm = new JDBCManager(); Connection connection = jm.getConnection(); connection.setAutoCommit(false); Statement stmt = connection.createStatement(); int i = 0; int j = 0; while ((line=br.readLine())!=null) { try { if(i%1000==0&& i!=0){ stmt.executeBatch(); //执行批处理 connection.commit(); System.out.println("第"+(++j)+"次"); } arrs=line.split("&"); String operator = (arrs[3] == "CZ88.NET" ? "局域网":arrs[3]); operator = operator.replaceAll("'", "\""); String sql = "insert into bbs_ip (ip_start,ip_end,addr,operator) values('"+arrs[0]+"','"+arrs[1]+"','"+arrs[2]+"','"+operator+"');"; stmt.addBatch(sql); i++; }catch(ArrayIndexOutOfBoundsException c){ String sql = "insert into bbs_ip (ip_start,ip_end,addr,operator) values('"+arrs[0]+"','"+arrs[1]+"','"+arrs[2]+"','');"; stmt.addBatch(sql); } catch (Exception e) { System.err.println(line); e.printStackTrace(); } } stmt.executeBatch(); //执行批处理 connection.commit(); jm.closeConnection(connection, stmt, null); br.close(); isr.close(); fis.close(); System.err.println("用时:"+(System.currentTimeMillis()-currentTimeMillis)); } } ``` ## 4、使用查询 ``` SELECT * FROM bbs_ip WHERE INET_ATON('125.34.19.135') BETWEEN INET_ATON(ip_start) AND INET_ATON(ip_end); ``` 当前很多应用都适用字符串char(15)来存储IP地址(占用16个字节),利用inet_aton()和inet_ntoa()函数,来存储IP地址效率很高,适用unsigned int 就可以满足需求,不需要使用bigint,只需要4个字节,节省存储空间,同时效率也高很多。 ### 我已经导出的数据文件 链接:http://pan.baidu.com/s/1o7k19GY 密码:3pvb 解压密码www.52pojie.cn/ ### 2020年5月最新纯真ip数据库导入mysql的SQL文件 https://studyjava.lanzous.com/icnkf7c
作者:俊熙
我要学Java呀,我要学Java
作者:じ☆ve宝贝
> 近日在hive上做数据库清洗,发现部分数据有串行,最终发现是因为爬虫抓取的内容中,携带了部分ASCII的控制字符导致的(例如:SOH标题开始……),因此写了如下程序过滤掉0到31、以及127不可见的ASCII值 ``` public static String filter(String content) { if (content != null && content.length() > 0) { char[] contentCharArr = content.toCharArray(); for (int i = 0; i < contentCharArr.length; i++) { if (contentCharArr[i] < 0x20 || contentCharArr[i] == 0x7F) { contentCharArr[i] = 0x20; } } return new String(contentCharArr); } return ""; } ``` ###### 参考资料 [ASCII码对照表](http://ascii.911cha.com/?year=soh)