作者:微信小助手
<p style="letter-spacing: 0.544px;white-space: normal;font-size: 16px;background-color: rgb(255, 255, 255);color: rgb(0, 0, 0);font-family: PingFangSC-Light;text-align: center;" data-mpa-powered-by="yiban.io"><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;font-size: 12px;color: rgb(120, 114, 119);user-select: text !important;">点击上方 "</span><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;font-size: 12px;user-select: text !important;"><strong style="color: rgb(62, 62, 62);font-size: 14px;user-select: text !important;"><span style="letter-spacing: 0.544px;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;color: rgb(61, 170, 214);user-select: text !important;">zhisheng</span></strong></span><span style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;font-size: 12px;color: rgb(120, 114, 119);user-select: text !important;">"关注, <span style="letter-spacing: 0.544px;user-select: text !important;">星标或置顶一起成长</span></span><br></p> <p style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;color: rgb(43, 43, 43);font-size: 16px;background-color: rgb(255, 255, 255);text-align: center;"><a target="_blank" href="https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&album_id=1337172142412169216&__biz=MzIxMTE0ODU5NQ==#wechat_redirect" textvalue="Flink 从入门到精通" tab="innerlink" data-linktype="2" style="color: rgb(120, 172, 254);-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;letter-spacing: 0.544px;font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 13px;line-height: 1.75em;user-select: text !important;"><span style="letter-spacing: 0.544px;line-height: 1.75em;user-select: text !important;">Flink 从入门到精通</span></a><span style="letter-spacing: 0.544px;color: rgb(136, 136, 136);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 13px;line-height: 1.75em;user-select: text !important;"> 系列文章</span></p> <p style="font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;color: rgb(43, 43, 43);font-size: 16px;background-color: rgb(255, 255, 255);text-align: center;"><span style="letter-spacing: 0.544px;color: rgb(136, 136, 136);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;font-size: 13px;line-height: 1.75em;user-select: text !important;"><br></span></p> <section style="text-align: left;line-height: 1.75em;"> <span style="color: rgb(89, 89, 89);font-size: 15px;letter-spacing: 0.5px;">随着互联网飞速发展,企业业务种类会越来越多,业务数据量会越来越大,当发展到一定规模时,传统的数据存储结构逐渐无法满足企业需求,实时数据仓库就变成了一个必要的基础服务。以维表 Join 为例,数据在业务数据源中以范式表的形式存储,在分析时需要做大量的 Join 操作,降低性能。如果在数据清洗导入过程中就能流式的完成 Join,那么分析时就无需再次 Join,从而提升查询性能。</span> <br> </section> <section style="text-align: left;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 0.5px;"> </span> </section> <section style="text-align: left;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 0.5px;">利用实时数仓,企业可以实现实时 OLAP 分析、实时数据看板、实时业务监控、实时数据接口服务等用途。但想到实时数仓,很多人的第一印象就是架构复杂,难以操作与维护。而得益于新版 Flink 对 SQL 的支持,以及 TiDB HTAP 的特性,我们探索了一个高效、易用的 Flink+TiDB 实时数仓解决方案。</span> </section> <section style="text-align: left;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 0.5px;"> </span> </section> <section style="text-align: left;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 0.5px;">本文将首先介绍实时数仓的概念,然后介绍 Flink+TiDB 实时数仓的架构与优势,接着给出一些已经在使用中的用户场景,最后给出在 docker-compose 环境下的 Demo,用于读者进行尝试。</span> </section> <section style="text-align: left;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 0.5px;"><br></span> </section> <section style="box-sizing: border-box;font-size: 16px;"> <section style="margin: 20px 0% 10px;text-align: center;box-sizing: border-box;" powered-by="xiumi.us"> <section style="padding: 3px;display: inline-block;border-bottom: 5px solid rgb(71, 193, 168);color: rgb(40, 40, 38);font-size: 18px;box-sizing: border-box;"> <p style="box-sizing: border-box;"><strong style="box-sizing: border-box;">实时数仓的概念</strong></p> </section> </section> <section style="box-sizing: border-box;" powered-by="xiumi.us"> <p style="white-space: normal;box-sizing: border-box;"><br></p> </section> </section> <section style="text-align: left;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 0.5px;">数据仓库的概念在 90 年代由 Bill Inmon 提出,是指一个面向主题的、集成的、相对稳定的、反映历史变化的集合,用于支持管理决策。当时的数据仓库通过消息队列收集来自数据源的数据,通过每天或每周进行一次计算以供报表使用,也称为离线数仓。</span> </section> <section style="text-align: left;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 0.5px;"><br></span> </section> <p><br></p> <p style="text-align: center;"><img class="rich_pages" data-backh="193" data-backw="578" data-ratio="0.3338870431893688" data-s="300,640" data-type="png" data-w="602" src="/upload/d7c74c00c080965b197e084d9af980bb.png" style="width: 100%;height: auto;"></p> <p><br></p> <section style="text-align: center;line-height: 1.75em;"> <span style="co
作者:微信小助手
<section style="margin-top: 5px;text-align: justify;line-height: 1.75em;" data-mpa-powered-by="yiban.io"> <span style="font-size: 15px;letter-spacing: 1px;">今天和大家系统性聊聊TCP的负载均衡,高可用,与扩展性架构。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <strong style="font-size: 15px;letter-spacing: 1px;"><br></strong> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <strong style="font-size: 15px;letter-spacing: 1px;">web-server的负载均衡,高可用,扩展性架构是怎么实施的?</strong> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <img data-ratio="0.4267352185089974" data-s="300,640" src="/upload/500c3c7aa3bdcf741833ed660c922d48.png" data-type="png" data-w="389"> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">互联网架构中,<span style="font-size: 15px;color: rgb(255, 104, 39);">web-server接入一般使用nginx来做反向代理</span>,实施负载均衡。整个架构分三层:</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>(1)上游调用层</strong>,一般是browser或者APP;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>(2)中间反向代理层</strong>,nginx;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>(3)下游真实接入集群</strong>,web-server,常见web-server的有tomcat,apache;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <strong><span style="font-size: 15px;letter-spacing: 1px;">那整个访问过程是怎么样的?</span></strong> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)browser向daojia.com发起请求;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)DNS将daojia.com解析为<span style="font-size: 15px;color: rgb(255, 104, 39);">外网IP</span>(1.2.3.4);</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(3)browser通过外网IP(1.2.3.4)访问nginx;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(4)nginx实施负载均衡策略,常见策略有轮询,随机,IP-hash等;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(5)nginx将请求转发给<span style="font-size: 15px;color: rgb(255, 104, 39);">内网IP</span>(192.168.0.1)的web-server;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">由于<span style="font-size: 15px;color: rgb(255, 104, 39);">http短连接,以及web应用无状态</span>的特性,理论上<strong>任何一个http请求落在任意一台web-server都应该得到正常处理</strong>。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="color: rgb(0, 128, 255);"><em><span style="font-size: 15px;letter-spacing: 1px;">画外音:如果必须落在一台,说明架构可能不合理,难以水平扩展。</span></em></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">问题来了,<span style="font-size: 15px;color: rgb(255, 104, 39);">tcp是有状态的连接</span>,客户端和服务端一旦建立连接,<strong>一个client发起的请求必须落在同一台tcp-server上</strong>,此时如何做负载均衡,如何保证水平扩展呢?</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>方案一:单机法tcp-server</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <img data-ratio="0.3333333333333333" data-s="300,640" src="/upload/e83c84d1a1371bad40ae24426baea0ee.png" data-type="png" data-w="255"> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">单个tcp-server显然是可以保证请求一致性:</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)client向tcp.daojia.com发起tcp请求;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)DNS将</span> <span style="letter-spacing: 1px;font-size: 12px;">tcp.daojia.com</span> <span style="font-size: 15px;letter-spacing: 1px;">解析为外网IP(1.2.3.4);</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(3)client通过外网IP(1.2.3.4)向tcp-server发起请求;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>这个方案有什么缺点?</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="color: rgb(255, 104, 39);font-size: 15px;letter-spacing: 1px;">无法保证高可用。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>方案二:集群法tcp-server</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <img data-ratio="0.3095823095823096" data-s="300,640" src="/upload/dd559d2e8c275f6aee7e112df56b302e.png" data-type="png" data-w="407"> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><span style="font-size: 15px;color: rgb(255, 104, 39);">可以通过搭建tcp-server集群来保证高可用</span>,<strong>客户端来实现负载均衡</strong>:</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)client内配置有tcp1/tcp2/tcp3.daojia.com三个tcp-server的外网IP;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)客户端通过“随机”的方式选择tcp-server,假设选择到的是tcp1.daojia.com;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(3)通过DNS解析tcp1.daojia.com;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(4)通过外网IP连接真实的tcp-server;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>如何保证高可用呢?</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="color: rgb(255, 104, 39);font-size: 15px;letter-spacing: 1px;">如果client发现某个tcp-server连接不上,则选择另一个。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong><strong style="font-size: 15px;letter-spacing: 1px;white-space: normal;">这个方案有什么缺点?</strong></strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">每次连接前,需要多实施一次DNS访问:</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="color: rgb(255, 104, 39);font-size: 15px;letter-spacing: 1px;">(1)难以预防DNS劫持;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)多一次DNS访问意味着<span style="font-size: 15px;color: rgb(255, 104, 39);">更长的连接时间</span>,这个不足在手机端更为明显;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>如何解决DNS的问题?</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">直接将IP配置在客户端,可以解决上述两个问题,很多公司也就是这么做的,</span> <span style="color: rgb(0, 0, 0);font-size: 15px;letter-spacing: 1px;">俗称“</span> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(255, 76, 0);">IP直通车</span> <span style="color: rgb(0, 0, 0);font-size: 15px;letter-spacing: 1px;">”。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>“IP直通车”有什么新问题?</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">将IP写死在客户端,在客户端实施负载均衡,<span style="font-size: 15px;color: rgb(255, 104, 39);">扩展性很差</span>:</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><span style="font-size: 15px;color: rgb(255, 104, 39);">(1)如果原有IP发生变化</span>,客户端得不到实时通知;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><span style="font-size: 15px;color: rgb(255, 104, 39);">(2)如果新增IP</span>,即tcp-sever扩容,客户端也得不到实时通知;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><span style="font-size: 15px;color: rgb(255, 104, 39);">(3)如果负载均衡策略变化</span>,需要升级客户端;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>方案三:服务端实施负载均衡</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><span style="font-size: 15px;color: rgb(255, 104, 39);">只有将复杂的策略下沉到服务端,才能根本上解决扩展性的问题</span>。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <img data-ratio="0.2403846153846154" data-s="300,640" src="/upload/570a11853500112fc94f434eacc617c9.png" data-type="png" data-w="520"> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><span style="font-size: 15px;color: rgb(255, 104, 39);">增加一个http接口,将客户端的“IP配置”与“均衡策略”放到服务端</span>是一个不错的方案:</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)client每次访问tcp-server前,先调用一个<strong>新增的get-tcp-ip接口</strong>,对于client而言,<span style="font-size: 15px;color: rgb(255, 104, 39);">这个http接口只返回一个tcp-server的IP;</span></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)这个http接口,实现的是原client的IP均衡策略;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(3)拿到tcp-server的IP后,和原来一样向tcp-server发起TCP长连接;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">这样的话,扩展性问题就解决了:</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(1)如果原有IP发生变化,只需要修改get-tcp-ip接口的配置;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(2)如果新增IP,也是修改get-tcp-ip接口的配置;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">(3)如果负载均衡策略变化,不需要升级客户端;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">然而,<strong>新的问题</strong>又产生了,如果所有IP放在客户端,当有一个IP挂掉的时候,client可以再换一个IP连接,保证可用性,而get-tcp-ip接口只是维护静态的tcp-server集群IP,对于这些<span style="font-size: 15px;color: rgb(255, 104, 39);">IP对应的tcp-server是否可用</span>,是完全不知情的,怎么办呢?</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>方案四:tcp-server状态上报</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <img data-ratio="0.5238095238095238" data-s="300,640" src="/upload/4977a70ab1ea86e310a31c9cd072768e.png" data-type="png" data-w="357"> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">get-tcp-ip接口怎么知道tcp-server集群中各台服务器是否可用呢,<span style="font-size: 15px;color: rgb(255, 104, 39);">tcp-server主动上报是一个潜在方案</span>,如果某一个tcp-server挂了,则会终止上报,对于停止上报状态的tcp-server,get-tcp-ip接口,将不返回给client相应的tcp-server的外网IP。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>该设计的存在的问题?</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">诚然,状态上报解决了tcp-server高可用的问题,但这个设计犯了一个<span style="font-size: 15px;color: rgb(255, 104, 39);">“反向依赖”的耦合小错误</span>:使得tcp-server要依赖于一个与本身业务无关的web-server。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>方案五:tcp-server状态拉取</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <img data-ratio="0.5363128491620112" data-s="300,640" src="/upload/39d218f6772ac0754ec3923ec170535a.png" data-type="png" data-w="358"> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>更优的方案</strong>是:<span style="font-size: 15px;color: rgb(255, 104, 39);">web-server通过“拉”的方式获取各个tcp-server的状态</span>,而不是tcp-server通过“推”的方式上报自己的状态。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">这样的话,<span style="font-size: 15px;color: rgb(255, 104, 39);">每个tcp-server都独立与解耦</span>,只需专注于资深的tcp业务功能即可。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <br> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>高可用、负载均衡、扩展性</strong>等任务<span style="font-size: 15px;color: rgb(255, 104, 39);">由get-tcp-ip的web-server专注来执行</span>。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">多说一句,<span style="font-size: 15px;color: rgb(255, 104, 39);">将负载均衡实现在服务端</span>,还有一个<strong>好处</strong>,可以实现<span style="font-size: 15px;color: rgb(255, 104, 39);">异构tcp-server的负载均衡</span>,以及<span style="font-size: 15px;color: rgb(255, 104, 39);">过载保护</span>:</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>(1)静态实施</strong>:web-server下的多个tcp-server的IP可以配置负载权重,根据tcp-server的机器配置分配负载(nginx也有类似的功能);</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>(2)动态实施</strong>:web-server可以根据“拉”回来的tcp-server的状态,动态分配负载,并在tcp-server性能急剧下降时实施过载保护;</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>总结</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>web-server如何实施负载均衡?</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="color: rgb(255, 104, 39);font-size: 15px;letter-spacing: 1px;">利用nginx反向代理来轮询、随机、ip-hash。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>tcp-server怎么快速保证请求一致性?</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="color: rgb(255, 104, 39);font-size: 15px;letter-spacing: 1px;">单机。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>如何保证高可用?</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="color: rgb(255, 104, 39);font-size: 15px;letter-spacing: 1px;">客户配置多个tcp-server的域名。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>如何防止DNS劫持,以及加速?</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="color: rgb(255, 104, 39);font-size: 15px;letter-spacing: 1px;">IP直通车,客户端配置多个tcp-server的IP。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>如何保证扩展性?</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><span style="font-size: 15px;color: rgb(255, 104, 39);">服务端提供get-tcp-ip接口</span>,向client屏屏蔽负载均衡策略,并实施便捷扩容。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"> </span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><strong>如何保证高可用?</strong></span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><span style="font-size: 15px;color: rgb(255, 104, 39);">tcp-server“推”</span>状态给get-tcp-ip接口,</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;">or</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;"><span style="font-size: 15px;color: rgb(255, 104, 39);">get-tcp-ip接口“拉”</span>tcp-server状态。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <br> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(0, 0, 0);">细节重要,<strong>思路比细节更重要</strong>。</span> </section> <section style="margin-top: 5px;text-align: justify;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(0, 0, 0);">系统性了解架构,希望大家有收获,<strong>谢转</strong>。</span> </section>
作者:测试人生路
一.什么是token 1.客户端使用用户名跟密码请求登录 2.服务端收到请求,去验证用户名与密码 3.验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端 4.客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 LocalStorage 里,客户端每次向服务端请求资源的时候需要带着服务端签发的Token 5.服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据 6web/APP登录的时候发送加密的用户名和密码到服务器,服务器验证用户名和密码,如果成功,以某种方式比如随机生成32位的字符串作为token,存储到服务器中,并返回 token到web/APP,以后web/APP请求时凡是需要验证的地方都要带上该 token,然后服务器端验证 token,成功返回所需要的结果,失败返回错误信息,让他重新登录。其中服务器上 token 设置一个有效期,每次web/APP请求的时候都验证token 和有效期。 二、如何获取token的值,进行接口测试 接口测试的工具大部分都可以获取登录之后返回的token值,这里给大家讲解如何用apipost获取token值的方法。 先打开apipost,进行登录接口的编写,然后获取token的值。  接着我们来引用这个token的值,引用token的值需要我们先设置环境变量   环境选择为新建好的环境,在引用url地址。引用格式为{{变量名}}  在去设置后执行脚本获取token值,“token”是参数名称,response.json.token的意思是返回的json数据中的token值。  这些都设置好之后,就可以引用token了,token引用的方法和环境变量设置的url引用方法一样也是  三、接口流程测试 token值引用好之后,就可以进行接口流程化测试了。  选择接口点击添加到流程测试中   进行流程测试   这就是如何获取token值进行接口流程测试的步骤了。 工具下载地址:[https://www.apipost.cn/?dt=20201029](https://www.apipost.cn/?dt=20201029)
作者:微信小助手
<section class="xmteditor" style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn"></section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;" data-mpa-powered-by="yiban.io"> <img class="rich_pages" data-cropselx1="0" data-cropselx2="558" data-cropsely1="0" data-cropsely2="237" data-ratio="0.6" data-s="300,640" src="/upload/1f667b8862c167303be7cee78007b452.jpg" data-type="jpeg" data-w="800" style="float: left;width: 558px;height: 335px;"> </section> <p style="text-align: center;"><span style="color: rgb(120, 172, 254);font-size: 14px;"><strong>作者:</strong><strong style="font-size: 15px;font-family: Optima-Regular, PingFangTC-light;letter-spacing: 0.544px;white-space: pre-wrap;">王犇,</strong><span style="color: rgb(120, 172, 254);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: pre-wrap;">滴滴首席算法工程师</span></span><span style="font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: pre-wrap;color: rgb(120, 172, 254);font-size: 14px;"></span></p> <p style="text-align: center;"><span style="color: rgb(120, 172, 254);font-size: 14px;"><strong>来自:</strong><strong>滴滴技术</strong></span><span style="color: rgb(120, 172, 254);"><strong><span style="color: rgb(120, 172, 254);font-size: 15px;"></span></strong></span><br></p> <section style="font-size: 16px;"> <section style="margin: 10px 8px;"> <section style="display: inline-block;width: 100%;border-width: 1px;border-style: solid;border-color: transparent;padding: 10px;background-color: rgb(239, 239, 239);border-radius: 0px;"> <section style="transform: rotate(0deg);" powered-by="xiumi.us"> <section style="line-height: 1.75;font-size: 13px;"> <p>桔妹导读:说到滴滴的派单算法,大家可能感觉到既神秘又好奇,从出租车扬召到司机在滴滴平台抢单最后到平台派单,大家今天的出行体验已经发生了翻天覆地的变化,面对着每天数千万的呼叫,滴滴的派单算法一直在持续努力让更多人打到车,本篇文章会着重介绍我们是如何分析和建模这个问题,并且这其中面临了怎样的算法挑战,以及介绍一些我们常用的派单算法,这些算法能够让我们不断的提升用户的打车确定性。</p> </section> </section> </section> </section> </section> <p><br></p> <p><br></p> <h4 style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 32px;"><em><strong><span style="color: rgb(255, 125, 65);">1.</span></strong></em></span></h4> <h3 cid="n70" mdtype="heading" class="" style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 18px;"><strong><span style="color: rgb(64, 64, 64);">为什么我们需要更好的派单算法</span></strong><strong><span style="color: rgb(64, 64, 64);"></span></strong></span><span style="font-size: 14px;"></span></h3> <pre class="" ng-bind-html="message.MMActualContent" style="letter-spacing: 0.544px;background-color: rgb(255, 255, 255);"><p style="margin-left: 8px;margin-right: 8px;"><img class="rich_pages" data-copyright="0" data-ratio="0.0734375" data-s="300,640" src="/upload/8c0b0343939ede75396663634e9b61ae.png" data-type="png" data-w="1280" style="visibility: visible !important;width: 677px !important;"></p></pre> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">说到滴滴的派单算法,大家可能感觉到既神秘又好奇,从扬召到抢单到派单,我们又是如何演进到今天大家的打车体验的呢,我们首先来看一看,好的派单算法为什么是出行行业不可或缺的能力?</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">回想几年前,当我们还没有滴滴的时候,只能在寒风或者酷暑中等待可能有、可能没有的扬招出租车,到后来可以从滴滴上呼叫一辆出租车,乘客可以在室内相对舒适的等待车辆的到达,从线上到线下,乘客的确定性得到第一次的提升,然而这还不够,抢单的模式注定我们的应答率天花板不会太高,在15年,滴滴上线快车业务,我们从抢单演进到了派单模式,乘客的应答率有了20个点以上的提升,很多时候能够全天能够高达90+(高峰&局部供需紧张应答率会相对吃紧),乘客确定性再一次得到大幅的提升,由此可见,派单模式为滴滴创造了巨大用户价值。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">再看近年来不断兴起的O2O业务,从国内外的网约车公司,包括我们的友商Uber、Lyft都基于派单的产品形态进行司机和乘客之间的交易撮合,Uber上市的时候把派单引擎也作为核心技术能力放在了招股书中;再看我们的国内的外卖平台,核心派单系统的优劣也决定了整个平台的交易效率(单均配送成本)和用户体验(配送时长);最后,整个大物流行业近年来也不断在进行线上化的改造,如何撮合货物和司机,以及更好的拼单能力也是整个交易环节的关键和商业模式是否成立的前提。从运人到运物,派单引擎目前越来越多的被应用在现实的商业和生活中。</span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <h4 style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 32px;"><em><strong><span style="color: rgb(255, 125, 65);">2.</span></strong></em></span></h4> <h2 style="white-space: normal;line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><strong><span style="color: rgb(64, 64, 64);font-size: 18px;">派单问题初探</span></strong><span style="font-size: 14px;color: rgb(64, 64, 64);"></span></h2> <pre class="" ng-bind-html="message.MMActualContent" style="letter-spacing: 0.544px;background-color: rgb(255, 255, 255);"><p style="margin-left: 8px;margin-right: 8px;"><img class="rich_pages" data-copyright="0" data-ratio="0.0734375" data-s="300,640" src="/upload/8c0b0343939ede75396663634e9b61ae.png" data-type="png" data-w="1280" style="visibility: visible !important;width: 677px !important;"></p></pre> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">言归正传,这里我们也来看一下,滴滴网约车平台到底是怎么派单的。首先,我们来看下我们面对的是什么样的问题?</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"> </span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><em><strong><span style="font-size: 14px;color: rgb(64, 64, 64);">“订单分配 即是在派单系统中将 乘客发出的订单 分配给 在线司机 的过程”</span></strong></em><span style="font-size: 14px;color: rgb(64, 64, 64);"></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"> </span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">这是一个看似简单的,但实际上非常复杂的问题。说到这,可能有很多人就会问,能否就把 我的订单分配给离我最近的司机就好了?</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">的确啊,实际上目前滴滴的派单算法最大的原则就是 “就近分配” (70%~80%的订单就是分配给了最近的司机),据我所知,目前世界上其他的竞品公司(包括Uber),也均是基于这个原则分单的。 </span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">我们进一步来看这个问题,如果我们只按照就近分配,先到先得的贪心策略,是不是能最好的满足平台所有乘客和司机的诉求呢?答案是否定的,原因就在于,如果我们只基于当前时刻和当前局部的订单来进行决策,忽视了未来新的订单&司机的变化,还忽视了和你相邻的其他区域甚至整个城市的需求(注:在时序上来看,新的司机&订单的出现会导致,贪心策略反而违背了就近分配的目标)。这就是为什么这个问题依然是非常复杂的原因。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">这里稍微有点抽象了,不过没关系,我们再来一步一步的拆解一下订单分配的问题,让大家有个更好的理解:</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">简单看,在我们的平台上,每一个时刻,都有N个订单在被乘客创建,同时有M个司机可以被我们用来进行分配,我们强大的平台能够为派单算法给出司机的实时的地理位置坐标,以及所有订单的起终点位置,并且告诉我们每一个司机接到订单的实时导航距离。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"> </span></p> <h3 data-anchor-id="7yqg" style="margin-right: 8px;margin-left: 8px;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;"><strong><span style="color: rgb(64, 64, 64);">▍</span>如果是1个订单、1个司机</strong></h3> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <section style="text-align: center;margin-left: 8px;margin-right: 8px;"> <img class="rich_pages" data-ratio="0.5625" data-s="300,640" src="/upload/d3c5c0ad8f8b53145dae6945b061907.png" data-type="png" data-w="720" style=""> </section> <p style="line-height: 1.75em;text-align: center;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"> </span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">看上去似乎就非常简单了,我们直接把这个订单指派给这个司机就好了嘛。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">“那么为什么有时候附近有辆空车却不能指派给你呢?”</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">实际线上的系统会比这里稍微复杂一点,原因一方面有可能是司机正好网络出现故障,或者正在和客服沟通等等导致司机无法听单,另一方面的原因是并不是所有的车都能够符合服务你订单的要求,最基本的策略其实是人工设定的规则过滤。举几个最基础的例子:</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <ul style="list-style-type: disc;margin-left: 8px;margin-right: 8px;" class=" list-paddingleft-2"> <li> <section style="line-height: 1.75em;"> <span style="font-size: 14px;color: rgb(64, 64, 64);">规则A:快车司机不能接专车订单</span> </section></li> <li> <section style="line-height: 1.75em;"> <span style="font-size: 14px;color: rgb(64, 64, 64);">规则B:保证司机接单后不会通过限行限号区域</span> </section></li> <li> <section style="line-height: 1.75em;"> <span style="font-size: 14px;color: rgb(64, 64, 64);">规则C:为设定实时目的地的司机过滤不顺路区域</span> </section></li> <li> <section style="line-height: 1.75em;"> <span style="font-size: 14px;color: rgb(64, 64, 64);">规则D:为只听预约单司机过滤实时订单</span> </section></li> <li> <section style="line-height: 1.75em;"> <span style="font-size: 14px;color: rgb(64, 64, 64);">规则E:同一个订单只会发给一个司机一次</span> </section></li> <li> <section style="line-height: 1.75em;"> <span style="color: rgb(64, 64, 64);font-size: 14px;">......</span> </section></li> </ul> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"> </span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">必须澄清的一点是这里的规则并不会造成分单时不公平的效果,而完全是为了业务能正常运行而设立的,这些策略承担着保证业务正确性的重要职责。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"> </span></p> <h3 data-anchor-id="7yqg" style="margin-right: 8px;margin-left: 8px;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;"><strong><span style="color: rgb(64, 64, 64);">▍</span>如果是1个订单和2个司机</strong></h3> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">假设这两个司机都能够分配给这个订单,那么我们来看系统应该是如何分配的。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">首先第一种情况是,同一时刻下,这两个司机和订单的距离都完全一样的情况下,系统应该如何分配?</span></p> <section style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;"> <img class="rich_pages" data-ratio="0.5625" data-s="300,640" src="/upload/b732be64346cbbf7f8127333b154727f.png" data-type="png" data-w="720" style=""> </section> <section style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <br> </section> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">刚才也说到,我们平台订单分配最大的原则是就近分配,当距离完全一样的情况下,当前我们系统上会主要考虑司机的服务分的优劣,服务分较高的司机会获取到这个订单(注:服务分对分单的影响,简单的理解可以换算为多少分可以换成多少米距离的优势,这块不是今天的重点就不展开介绍),再说明一下,系统用到的是地图的导航距离,而非人直观看到的直线距离,有时候差一个路口就会因为需要掉头导致距离差异很大;并且如果司机的定位出现问题,也会出现分单过远的情况。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">那么我们来看第二种情况,如果A司机离的近,B司机离的远,系统怎么派?</span></p> <section style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;"> <img class="rich_pages" data-ratio="0.5625" data-s="300,640" src="/upload/564e87d84eca964bddc8814a4ca28455.png" data-type="png" data-w="720" style=""> </section> <section style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <br> </section> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">这就简单了,根据就近分配的原则,我们会把A司机分配给这个订单。嘿嘿~~,假设我们再把问题设置的更加实际一点,当订单发出时,B司机已经在线并空闲,但是A司机还没有出现(没有上线,或者还在送乘客),但再过1s,离得更近的A司机突然出现可被分单了,假设我们使用先到先得的贪心策略,那么B司机就会被分给这个订单,那就违背我们希望就近分单的目标了:(。所以看上去简单,但实际情况下,算法还需要变的更好一些,这个问题我们把它叫做派单中的时序问题,我们后面再来看怎么解决。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><strong><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></strong></p> <h3 data-anchor-id="7yqg" style="margin-right: 8px;margin-left: 8px;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;"><strong><span style="color: rgb(64, 64, 64);">▍</span>如果有N个乘客、M个司机</strong></h3> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">最后我们来考虑最复杂的多对多的情况,这也是线上系统每天高峰期都需要面对的挑战,我们一般把这种情况会形式化为一个二部图的匹配问题,在运筹领域也叫做matching的问题,如图所示:</span></p> <section style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;"> <img class="rich_pages" data-ratio="0.5625" data-s="300,640" src="/upload/272c58d495ad272f2d9759589e695d9c.png" data-type="png" data-w="720" style=""> </section> <section style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <br> </section> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">我们再把这个问题具象一点,假设这个时候我们有20个乘客,有20个司机,这些乘客都可以被这20个司机中的一个接驾,我们的系统需要把这20个乘客都分配出去,并且让大家的总体接驾的时长最短。听上去是不是有点复杂?我们套用下组合数学的知识,这其中可能的解法存在20的阶乘那么多,20的阶乘是什么概念呢?20*19*18*…*1= 2432902008176640000,这个数巨大无比,想要完全的暴力搜索是绝对不可能的。这里需要更聪明的办法。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"> </span></p> <h3 data-anchor-id="7yqg" style="margin-right: 8px;margin-left: 8px;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;"><strong><span style="color: rgb(64, 64, 64);">▍</span>如果有N个乘客、M个司机,一会再来几个乘客和司机?</strong></h3> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">这就是派单问题最大的挑战,我们不仅仅需要当前这个时刻的最优,我们要考虑未来一段时间整体的最优,新来的司机和乘客会在整个分配的网络中实时插入新的节点,如何更好的进行分配也就发生了新的变化,所以如何考虑时序对我们非常重要,这个问题在业内也被称为Dynamic VRP问题,这个Dynamic也就是随时间时序变化的意思,这也就是为什么,滴滴的派单问题远复杂于物流行业的相对静态的货物和路线的规划问题。假设我们知道了未来供需的完全真实的变化,仿真告诉我们,我们的系统有可能可以利用同样的运力完成1.2~1.5倍的需求量,这也是派单算法的同学持续为之努力的方向。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">想起前段时间的吐槽大会,大家提到文嵩曾说我们的派单问题比alpha go还要难,其实这两个问题还确实有点相似,都是在超大的搜索空间中找到一个近似最优的解,而alpha go则会在一个更加明确的游戏规则和环境中进行求解,它的难点在于博弈,而我们的派单问题难点在于未来供需不确定性&用户行为的不确定性。近年来在学界,已经有不少尝试在利用类似alpha go的技术进行VRP&TSP等方向的探索,强化学习结合运筹理论是未来运筹界非常前沿的方向之一(非技术同学可以跳过此处:))</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <h4 style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 32px;"><em><strong><span style="color: rgb(255, 125, 65);">3.</span></strong></em></span></h4> <h2 style="white-space: normal;line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><strong><span style="color: rgb(64, 64, 64);font-size: 18px;">派单算法简介</span></strong><span style="font-size: 14px;color: rgb(64, 64, 64);"></span></h2> <pre class="" ng-bind-html="message.MMActualContent" style="letter-spacing: 0.544px;background-color: rgb(255, 255, 255);"><p style="margin-left: 8px;margin-right: 8px;"><img class="rich_pages" data-copyright="0" data-ratio="0.0734375" data-s="300,640" src="/upload/8c0b0343939ede75396663634e9b61ae.png" data-type="png" data-w="1280" style="visibility: visible !important;width: 677px !important;"></p><p style="margin-left: 8px;margin-right: 8px;"><span style="letter-spacing: 0.544px;color: rgb(64, 64, 64);font-size: 14px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">上面我们已经描述了什么是订单分配问题,并且它所面临的各种挑战,</span><span style="color: rgb(64, 64, 64);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.544px;">那么在这里我们来</span></p><p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(64, 64, 64);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.544px;">聊一聊我们线上的派单策略是如何解决其中一部分问题的。</span></p></pre> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">在介绍具体策略之前,首先我们来说一下派单算法大的原则,目前派单策略主要的原则是:站在全局视角,尽量去满足尽可能多的出行需求,保证乘客的每一个叫车需求都可以更快更确定的被满足,并同时尽力去提升每一个司机的接单效率,让总的接驾距离和时间最短。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">如何理解这个原则呢?我们说策略会站在全局的角度去达成全局最优,这样对于每一个独立的需求来看,派单可能就不是“局部最优 ”,不过可以告诉大家的是,就算在这个策略下,仍然有70%~80%的需求也是符合当前距离最近的贪心派单结果的。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">接下来,这里会拿两个重要的派单策略的来进行介绍。(这里的内容主要是讲清楚策略的motivation为主哈,细节不再展开)</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <h3 data-anchor-id="7yqg" style="margin-right: 8px;margin-left: 8px;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;"><strong><span style="color: rgb(64, 64, 64);">▍</span>批量匹配(全局最优)</strong></h3> <p style="margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">派单策略中最为基础的部分,就是为了解决上一节所提到的时序问题。这个算法几乎是所有类似派单系统为了解决这个问题的最基础模型,在Uber叫做Batching Matching,我们内部也叫做“全局最优” 或者 “延迟集中分单”。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">这个Idea其实也非常直观,由于用户订单的产生和司机的出现往往并不在同一时间点,在时间维度上贪婪的分单方式(即每个订单出现时即选择附近最近的司机派单)并不能获得全局最优的效果。一个自然的想法就是先让乘客和司机稍等一会,待收集了一段时间的订单和司机信息后,再集中分配。这样,有了相对较多、较密集的订单、司机后,派单策略即可找到更近更合理的派单方式了。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">找寻司机和订单分配的全局最优是一个 二分图匹配问题 (bipartite graph matching) ,一边是乘客、一边是司机,可用运筹优化中各种解决Matching问题的方法进行求解。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">和再大家澄清一下,我们所采用的批量匹配的模式和大家所希望的,“把离我最近的司机派给我”的「就近派单模式」并不矛盾,我们也是寻求“乘客接驾时长最短”的最优解,大多数情况下也是指派离你最近的司机,但充分满足每一个乘客的“把离我最近的司机派给我”的个体需求, 有些时候反而会导致部分乘客的需求无法得到满足,比如说下面这种情况:</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">当编号1和2两个乘客同时叫车, 如果完全按照“就近派单”的模式, 虽然可以让1号乘客先被接单, 但是2号乘客会因为接驾距离较远, 导致等待时间变长, 甚至因为最近的司机超出平台派单距离, 导致2号乘客叫不到车。1、2号乘客总等待时长15分钟, 平均等待时长7.5分钟。</span></p> <section style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;"> <img class="rich_pages" data-ratio="0.5625" data-s="300,640" src="/upload/2f16a7f939623f37a2b1d534a0e7cbed.png" data-type="png" data-w="720" style=""> </section> <section style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <br> </section> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">我们采取的做法是, 把距离较远的2号车派给1号乘客。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">把1号车派给2号乘客, 这样一来, 1号乘客和2号乘客, 平均等待时长缩短为5分钟, 比就近派单,缩短了2.5分钟, 总等待时长缩短为10分钟, 比就近派单, 缩短了足足5分钟。</span></p> <section style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;"> <img class="rich_pages" data-ratio="0.5625" data-s="300,640" src="/upload/c81ce4b627f88c17c91d1db66292751c.png" data-type="png" data-w="720" style=""> </section> <section style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <br> </section> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">通过提升全局的效率,才能转化为让更多乘客的需求得到满足。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"> </span></p> <h3 data-anchor-id="7yqg" style="margin-right: 8px;margin-left: 8px;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;"><strong><span style="color: rgb(64, 64, 64);">▍</span>基于供需预测的分单</strong></h3> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(64, 64, 64);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(64, 64, 64);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;">“如果有先知告诉我们未来每一个订单的生成时间&地点,每一个司机的上线时间&地点,派单就会变成非常轻松的一件事”</span><br></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="color: rgb(64, 64, 64);font-size: 14px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="color: rgb(64, 64, 64);font-size: 14px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">刚才所说的批量匹配的方法,理论上能够保证那一个批次的匹配是最优的。但是这样就够了吗?</span><br></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">很遗憾,以上所述的延迟集中分单的策略只能解决部分的问题,仍不是一个完全的方案。其最大的问题,在于用户对系统派单的 响应时间 容忍度有限,很多情况下短短的几秒钟即会使用户对平台丧失信心,从而取消订单。故实际线上我们只累积了几秒钟的订单和司机信息进行集中分单,而这在大局上来说仍可近似看做时间维度上的贪婪策略。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">若想即时的获得最优派单结果,唯一的方法是利用对未来的预测,即进行基于供需预测的分单。这种想法说来玄妙,其实核心内容也很简单:如果我们预测出未来一个区域更有可能有更多的订单/司机,那么匹配的时候就让这个区域的司机/订单更多去等待匹配这同一个区域的订单/司机。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <h3 data-anchor-id="7yqg" style="margin-right: 8px;margin-left: 8px;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;"><span style="font-size: 16px;"><strong><span style="color: rgb(64, 64, 64);">▍</span>连环派单</strong></span></h3> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(64, 64, 64);font-size: 14px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(64, 64, 64);font-size: 14px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">基于供需预测的分单有很大意义,但由于预测的不确定性,其实际效果很难得到保证。为此,我们使用了一种更有确定性的预测方式来进行派单,即 连环派单。</span><br></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><em><strong><span style="font-size: 14px;color: rgb(64, 64, 64);">“连环派单,即将订单指派给 即将结束服务 的司机,条件为如果司机的终点与订单位置很相近”</span></strong></em><span style="font-size: 14px;color: rgb(64, 64, 64);"></span></p> <section style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <br> </section> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.5625" data-s="300,640" src="/upload/bf5dd2357a2b6f9e83c42eb7dfa833cd.png" data-type="png" data-w="720" style=""></p> <section style="margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <br> </section> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">与预测订单的分布相反,连环派单预测的是下一时刻空闲司机的所在位置。由于高峰期空闲司机多为司机完成订单后转换而来,预测司机的位置就变成了一个相对确定性的问题,即监测司机到目的地的距离和时间。当服务中的司机距终点很近,且终点离乘客新产生的订单也很近时,便会命中连环派单逻辑。司机在结束上一单服务后,会立刻进入新订单的接单过程中,有效地压缩了订单的应答时间、以及司机的接单距离。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <h3 data-anchor-id="7yqg" style="margin-right: 8px;margin-left: 8px;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;"><span style="font-size: 16px;"><strong><span style="color: rgb(64, 64, 64);">▍</span>如何做的更好</strong></span></h3> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(64, 64, 64);font-size: 14px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><br></span></p> <p style="margin-left: 8px;margin-right: 8px;"><span style="color: rgb(64, 64, 64);font-size: 14px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">整个派单算法核心克服的是未来供需的不确定性,动态的时空结构的建模,以及用户行为的不确定性,对于这些不确定性我们现在更多采用深度学习方法对我们的时空数据&用户行为进行建模预测。</span><br></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="color: rgb(64, 64, 64);font-size: 14px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="color: rgb(64, 64, 64);font-size: 14px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">另外,我们的问题相对于传统推荐搜索领域,多了一层匹配决策,我们到底积攒多久的订单进行分配,对于每一个分配来说我们都面临着分或者不分,现在分还是未来分配,并且分给谁的问题,这个问题天生就可以建模为强化学习问题,目前在我们的系统中也引入了强化学习方法来优化更长期的收益。</span><br></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">除了不断去优化之前说到的派单问题,整个派单系统还面临着大量其他的挑战,包括如何利用快车优享等多个品类的运力进行跨层的最优分配,如何同时对用户&司机&平台短期长期等多个目标进行优化,如何同时优化预约&实时订单,如何在具备网络效应的场景下对算法进行评估,如果建立一个较为精准的仿真系统等等,这里既是挑战,也是AI For Transportation中大量新的重新定义问题和创新算法的机会。</span></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <p style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);"><br></p> <h4 style="font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 32px;"><em><strong><span style="color: rgb(255, 125, 65);">4.</span></strong></em></span></h4> <p style="white-space: normal;line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><strong><span style="font-size: 18px;"><span style="color: rgb(64, 64, 64);"> </span><span style="color: rgb(64, 64, 64);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">总结</span></span></strong></p> <pre class="" ng-bind-html="message.MMActualContent" style="letter-spacing: 0.544px;background-color: rgb(255, 255, 255);"><p style="margin-left: 8px;margin-right: 8px;"><img class="rich_pages" data-copyright="0" data-ratio="0.0734375" data-s="300,640" src="/upload/8c0b0343939ede75396663634e9b61ae.png" data-type="png" data-w="1280" style="visibility: visible !important;width: 677px !important;"></p></pre> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">每天, 我们的派单系统要面对超过3000万用户的叫车需求, 高峰期每分钟接收超过6万乘车需求,平均每两秒就需要匹配几百到上千的乘客和司机 。我们当前的派单策略相对于最初的派单策略版本,每天能够多满足百万以上乘客的出行需求。为了让更多人能更快、更确定的打到车,我们的交易策略团队将在更好的公平感知的前提下,不断地优化和打磨我们的派单算法,为乘客&司机创造更多价值。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);">当然当前的派单策略还有很多不够完善和完备的地方,本身也是一个相当复杂的问题和系统,一方面借此机会让大家对派单有更好的理解和认识,另一方面,也更欢迎大家对我们提出更多的宝贵意见,帮助我们进一步成长。</span></p> <p style="line-height: 1.75em;margin-left: 8px;margin-right: 8px;"><span style="font-size: 14px;color: rgb(64, 64, 64);"><br></span></p> <p><br></p> <section class="" style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <p class="" style="max-width: 100%;min-height: 1em;color: rgb(62, 62, 62);font-size: 16px;letter-spacing: 0.544px;text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;">长按订阅更多精彩▼</p> <p style="max-width: 100%;min-height: 1em;color: rgb(62, 62, 62);font-size: 16px;letter-spacing: 0.544px;text-align: center;box-sizing: border-box !important;word-wrap: break-word !important;"><img class="rich_pages" data-ratio="1" data-s="300,640" src="/upload/cff6d4b6b063076da067005708088611.jpg" data-type="jpeg" data-w="258" style="box-shadow: rgb(170, 170, 170) 0px 0px 14px 0px;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;width: 205px !important;"></p> <p style="max-width: 100%;min-height: 1em;color: rgb(62, 62, 62);font-size: 16px;letter-spacing: 0.544px;text-align: right;line-height: 2em;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;">如有收获,点个在看,诚挚感谢</span><img class="" data-ratio="1" src="/upload/5f7454d24a334e968c32b8ce9ae53c5.png" data-type="png" data-w="19" style="display: inline-block;vertical-align: text-bottom;box-sizing: border-box !important;word-wrap: break-word !important;visibility: visible !important;width: 19px !important;"></p> </section>
作者:测试人生路
一、接口都有哪些类型? 接口一般分为两种:1.程序内部的接口 2.系统对外的接口 系统对外的接口:比如你要从别的网站或服务器上获取资源或信息,别人肯定不会把 数据库共享给你,他只能给你提供一个他们写好的方法来获取数据,你引用他提供的接口就能使用他写好的方法,从而达到数据共享的目的。 程序内部的接口:方法与方法之间,模块与模块之间的交互,程序内部抛出的接口,比如bbs系统,有登录模块、发帖模块等等,那你要发帖就必须先登录,那么这两个模块就得有交互,它就会抛出一个接口,供内部系统进行调用。 接口的分类:1.webservice接口 2.http api接口 webService接口是走soap协议通过http传输,请求报文和返回报文都是xml格式的,我们在测试的时候都用通过工具才能进行调用,测试。 http api接口是走http协议,通过路径来区分调用的方法,请求报文都是key-value形式的,返回报文一般都是json串,有get和post等方法,这也是最常用的两种请求方式。 json是一种通用的数据类型,所有的语言都认识它。(json的本质是字符串,他与其他语言无关,只是可以经过稍稍加工可以转换成其他语言的数据类型,比如可以转换成 Python中的字典,key-value的形式,可以转换成JavaScript中的原生对象,可以转换成 java中的类对象等。) 二、接口–前端、后端和测试 前端对接口进行使用:通过后端人员给的接口文档,来进行Ajax的设计,通过接口向服务发送请求,获取响应的数据,然后通过返回的数据进行下一步的页面跳转和显示。 后端生成接口:通过编写接口,为前端提供与服务器和数据请求交互的通道。编写对应的接口,需要传递的参数,参数类型等等。然后生成接口文档,分享给前端,让其按照接口文档编写对应的Ajax。 测试验证接口:可以通过接口文档,进行接口验证,查看后端开发的接口和前端所写的Ajax是否对应,有没有出错的接口,还可通过接口流程测试,知道整个系统之间的接口是否是相对应的,有没有接口是不对的,或者没有正常运行。 三、前端、后端和测试使用的接口工具 前端:一般使用postman、apipost或者jmeter进行接口验证和查看响应值 后端:多用swagger、apipost、postman等接口文档生成工具和测试工具 测试:jmeter、apipost和postman等可以对接口进行验证测试。 四、jmeter、postman、apipost、swagger工具介绍 jmeter可以进行接口测试和性能测试,但是对于做单纯的接口测试jmeter操作起来没有postman、apipost使用起来方便。jmeter重点在于压力测试,稳定性测试和负载测试。针对于接口和程序的稳定性设计的一块以软件性能为主接口测试为辅的接口测试工具。  postman是Google开发的一款接口测试的插件,也有客户端。国内禁用Google之后,postman的插件就不好下载和使用了。postman这款接口测试工具,是一款很轻便的接口验证工具,可以通过输入请求方法、url、参数直接进行接口请求访问,验证接口是否开通,还可以查看返回的响应值查看接口开发是否正常。不过因为是Google开发的所以只支持英文版。对于英文不好的人使用起来特别难受。  apipost这款接口测试工具,主要针对于接口验证和接口文档生成。apipost这款接口测试工具,是一款很轻便的接口验证工具,可以通过输入请求方法、url、参数直接进行接口请求访问,验证接口是否开通,还可以查看返回的响应值查看接口开发是否正常。根据这些接口验证信息,直接可以生成在线文档和离线版的word文档。是一款很棒的接口测试和接口文档生成工具,又不像swagger生成的接口文档都是英文的阅读起来也不太方便。apipost还具备协同工作,可以很有效的实现前端的接口联调。  swagger是一款通过针对与后端开发人员的一款接口文档生成工具。主要通过在代码中的注释生成接口文档的工具,不过生成的接口文档是英文的。  工具下载地址: jmeter:https://jmeter.apache.org/ apipost:[https://www.apipost.cn/?dt=20201030](https://www.apipost.cn/?dt=20201030) swagger:https://swagger.io/
作者:测试人生路
一、进行接口测试准备的东西 1.接口测试工具:apipost、jmeter等 2.接口文档,没有接口文档就用接口信息获取工具 3.接口信息收取工具:fiddler抓包工具、浏览器开发者工具(f12)等 二、web接口测试需要获取的东西 web接口测试需要知道的三个条件 1.请求方式:get、post、put、patch、delete等 2.url地址:分为http请求和https是请求,如:http://www.baidu.com和https://www.baidu.com 3.body传递的参数:一般是以json的格式传递参数如:"name":"xiaoming","pwd":"123"。一般以post传递参数请求为多。  三、fiddler抓包工具 fiddler是由 C# 开发的最强大好用的免费web调试工具之一,可记录所有客户端和服务见的 http 以及 https 请求,可监视设断点,甚至修改输入输出数据,它还包含了一个强大的基于事件脚本的子系统,并且能使用 .net 语言来拓展。 fiddler就是通过我们对网页的操作使用而进行的接口信息的抓取的。抓取之后按照上面需求的请求方法、url和body来查找需要的数据。  这是一个简单的登录请求被fiddler抓取到的信息,我们可以用到的分别是: 1.Protocol请求类型:http或https。这里是http 2.host+url两个加起来形成的接口访问地址。这里的请求地址为:127.0.0.1:8888/login 3.Request Haeders(请求头部信息)中的请求方法:get、post等。这里是post请求  之后再点击Inspectors中的WebForms查看body参数。 获取的参数为,参数名:参数值,name:xiaoming,pwd:111  四、接口测试 通过fiddler已经收取到这个接口的具体信息之后,我们通过接口测试工具apipost进行接口测试。 选择请求方法post、请求地址:http://127.0.0.1:8888/login、body参数:name:xiaoming,pwd:111  查看请求结果  这就是通过fiddler和apipost进行接口测试的步骤。 fiddler下载地址:https://www.telerik.com/fiddler apipost下载地址:[https://www.apipost.cn/?dt=20201031](https://www.apipost.cn/?dt=20201031)
作者:测试人生路
## 一、首先明白接口是什么 软件接口是指程序中具体负责在不同模块之间传输或接受数据的并做处理的类或者函数。(而不是指传输的数据!!) ## 二、什么是接口测试 接口测试就是通过向接口传递数据来测试这个接口是否正确。比如:一个QQ登录功能接口,就需要我们传递QQ号和密码去验证这个登录接口是否正确,能否使用。 ## 三、进行接口测试需要掌握哪些知识 1、了解系统及内部各个组件之间的业务逻辑交互; 2、了解接口的I/O(input/output:输入输出); 3、了解协议的基本内容,包括:通信原理、三次握手、常用的协议类型、报文构成、数据传输方式、常见的状态码、URL构成等; 4、常用的接口测试工具,比如:apipost、jmeter、loadrunner、soapUI等; 5、数据库基础操作命令(检查数据入库、提取测试数据等); 6、常见的字符类型,比如:char、varchar、text、int、float、datatime、string等; ## 四、如何学这些技能? 1、系统间业务交互逻辑:通过需求文档、流程图、思维导图、沟通等很多渠道和方式; 2、协议:推荐《图解http》这本书,内容生动,相对算是入门级的书籍,其他的还有《图解tcp、IP》等; 3、接口测试工具:百度这些工具,然后你会发现,好多的教学博客、相关问题解决方案、以及一些基于工具的书籍,当然,选择合适的书很重要; 4、数据库操作命令:学习网站(W3C、菜鸟教程)、教学博客,以及一些数据库相关书籍,入门级推荐:《mysql必知必会》、《oracle PL/SQL必知必会》等 5、知乎,百度和csdn等各大技术论坛都是你学习的好帮手 ## 五、接口测试分为两大类 1.手工通过工具进行测试 一般使用的工具有apipost、jmeter、postman等,博主使用的是国产的接口测试工具apipsot。  手工测试方法:通过抓包或其他方法(比如看文档)准备好输入数据包,然后用发包的工具把数据发给服务端的接口,之后校验其返回值。 2.自动化接口测试 以上,大家已经了解了手工做接口测试的流程。 那么下一步就是把一些接口测试编写成脚本,放在本地(自己电脑上),人工触发去批量得执行这些测试,并自动校验返回结果。 这里推荐一下使用的工具。 如果你是零基础的,推荐你用apipost或jmeter,两者都不需要你具备任何代码功底就能用。 我一般使用的是apipost的流程测试功能,把写好的接口,选择进行点击开始,就可以进行接口自动化测试了。  假如你有编程基础,可以考虑使用python+requests+pytest/robotframework来做接口测试。如果用了python+requests,那么性能测试方面可以用python+locust。也可以不用locust,只要你掌握了前面提过的数据驱动的思想,配合一些自动化框架开发基础,也一样可以把接口测试做到能在其他性能测试工具里重用,当然这个就复杂了,以后再说吧。 接口测试工具apipost下载地址:[https://www.apipost.cn/?dt=20201101](https://www.apipost.cn/?dt=20201101)
作者:微信小助手
<section style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn" data-mpa-powered-by="yiban.io"> <br> </section> <p style="line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"><span style="font-size: 14px;color: rgb(136, 136, 136);">作者丨漫话编程</span><br></p> <p style="line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"><span style="font-size: 14px;color: rgb(136, 136, 136);">来源丨漫话编程(mhcoding)</span><br></p> <section style="line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;"> <br> </section> <section style="line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;"> <section style="line-height: 1.75em;"> <section style="font-size: 16px;box-sizing: border-box;"> <section powered-by="xiumi.us" 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);letter-spacing: 1.5px;"> <span style="display: inline-block;width: 27.7031px;line-height: 0.8;font-weight: bolder;box-sizing: border-box;font-size: 16px;"> <section> “ </section></span> <section> <section style="padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="font-size: 15px;color: rgb(136, 136, 136);">对于业务开发来说,业务逻辑的复杂是必然的。</span> <span style="font-size: 15px;color: rgb(136, 136, 136);">随着业务发展,需求只会越来越复杂,为了考虑到各种各样的情况,代码中不可避免的会出现很多 if-else。</span> <span style="letter-spacing: 1px;font-size: 16px;"></span> </section> </section> </section> <section style="clear: both;box-sizing: border-box;"> <br> </section> </section> </section> </section> </section> <section style="line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <img class="rich_pages" data-ratio="0.6626936829558999" data-s="300,640" src="/upload/f0232a833aae9e02da5418543e32c25e.png" data-type="png" data-w="839" style=""> </section> <section style="text-align: center;line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;"><em>图片来自 Pexels</em></span> </section> <section style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">一旦代码中 if-else 过多,就会大大的影响其可读性和可维护性。</span> </section> <figure style="padding-left: 0.5em;padding-right: 0.5em;"> <img data-ratio="0.7097222222222223" src="/upload/79d4626fd2cbc197a38bfbc6c2ce1f66.jpg" data-type="jpeg" data-w="720" style="margin-right: auto;margin-left: auto;font-size: inherit;color: inherit;line-height: inherit;display: block;box-sizing: border-box !important;word-wrap: break-word !important;width: 677px !important;visibility: visible !important;" title=""> </figure> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-top: 5px;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">首先可读性,不言而喻,过多的 if-else 代码和嵌套,会使阅读代码的人很难理解到底是什么意思。尤其是那些没有注释的代码。</span> </section> <p style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">其次是可维护性,因为 if-else 特别多,想要新加一个分支的时候,就会很难添加,极其容易影响到其他的分支。</span> </section> <p style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">笔者曾经看到过一个支付的核心应用,这个应用支持了很多业务的线上支付功能,但是每个业务都有很多定制的需求,所以很多核心的代码中都有一大坨 if-else。</span> </section> <p style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">每个新业务需要定制的时候,都把自己的 if 放到整个方法的最前面,以保证自己的逻辑可以正常执行。这种做法,后果可想而知。</span> </section> <p style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">其实,if-else 是有办法可以消除掉的,其中比较典型的并且使用广泛的就是借助策略模式和工厂模式,准确的说是利用这两个设计模式的思想,彻底消灭代码中的 if-else。</span> </section> <p style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">本文就结合这两种设计模式,介绍如何消除 if-else,并且,还会介绍如何和 Spring 框架结合,这样读者看完本文之后就可以立即应用到自己的项目中。</span> </section> <p style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">本文涉及到一些代码,但是作者尽量用通俗的例子和伪代码等形式使内容不那么枯燥。</span> </section> <section style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"> <br> </section> <section mpa-from-tpl="t" style="padding-left: 0.5em;padding-right: 0.5em;"> <section mpa-from-tpl="t"> <section mpa-from-tpl="t"> <section style="box-sizing: border-box;font-size: 16px;"> <section style="border-bottom: 1px solid black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;letter-spacing: 1.5px;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="font-size: 16px;">恶心的 if-else</span></p> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="letter-spacing: 1px;color: rgb(71, 193, 168);font-size: 16px;">假设我们要做一个外卖平台,有这样的需求:</span> </section> <ul class="list-paddingleft-2" style="list-style-type: disc;"> <li> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">外卖平台上的某家店铺为了促销,设置了多种会员优惠,其中包含超级会员折扣 8 折、普通会员折扣 9 折和普通用户没有折扣三种。</span> </section></li> <li> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">希望用户在付款的时候,根据用户的会员等级,就可以知道用户符合哪种折扣策略,进而进行打折,计算出应付金额。</span> </section></li> <li> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">随着业务发展,新的需求要求专属会员要在店铺下单金额大于 30 元的时候才可以享受优惠。</span> </section></li> <li> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">接着,又有一个变态的需求,如果用户的超级会员已经到期了,并且到期时间在一周内,那么就对用户的单笔订单按照超级会员进行折扣,并在收银台进行强提醒,引导用户再次开通会员,而且折扣只进行一次。</span> </section></li> </ul> <p style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="letter-spacing: 1px;color: rgb(71, 193, 168);font-size: 16px;">那么,我们可以看到以下伪代码:</span> </section> <pre style="max-width: 100%;color: inherit;font-size: inherit;font-variant-ligatures: normal;orphans: 2;widows: 2;line-height: inherit;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;padding-left: 0.5em;padding-right: 0.5em;"> <section style="margin-right: 2px;margin-left: 2px;padding: 0.5em;max-width: 100%;line-height: 18px;font-size: 14px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);letter-spacing: 1.5px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> <span style="font-size: 16px;"><span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">public</span> BigDecimal calPrice(BigDecimal orderPrice, String buyerType) {</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (用户是专属会员) {</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (订单金额大于<span style="max-width: 100%;line-height: inherit;color: rgb(174, 135, 250);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">30</span>元) {</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> returen <span style="max-width: 100%;line-height: inherit;color: rgb(174, 135, 250);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">7</span>折价格;</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> }</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> }</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (用户是超级会员) {</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">return</span> <span style="max-width: 100%;line-height: inherit;color: rgb(174, 135, 250);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">8</span>折价格;</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> }</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (用户是普通会员) {</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">if</span>(该用户超级会员刚过期并且尚未使用过临时折扣){</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> 临时折扣使用次数更新();</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> returen <span style="max-width: 100%;line-height: inherit;color: rgb(174, 135, 250);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">8</span>折价格;</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> }</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">return</span> <span style="max-width: 100%;line-height: inherit;color: rgb(174, 135, 250);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">9</span>折价格;</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> }</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">return</span> 原价;</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;">}</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> </section></pre> <section style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">以上,就是对于这个需求的一段价格计算逻辑,使用伪代码都这么复杂,如果是真的写代码,那复杂度可想而知。</span> </section> <section style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">这样的代码中,有很多 if-else,并且还有很多的 if-else 的嵌套,无论是可读性还是可维护性都非常低。</span> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;line-height: 1.75em;font-size: 16px;">那么,如何改善呢?</span> </section> <section mpa-from-tpl="t" style="padding-left: 0.5em;padding-right: 0.5em;"> <section mpa-from-tpl="t"> <section mpa-from-tpl="t"> <p style="line-height: normal;"><br></p> <section style="box-sizing: border-box;font-size: 16px;"> <section style="transform: rotate(0deg);-webkit-transform: rotate(0deg);-moz-transform: rotate(0deg);-o-transform: rotate(0deg);box-sizing: border-box;" powered-by="xiumi.us"> <section style="border-bottom: 1px solid black;margin-top: 0.5em;margin-bottom: 0.5em;line-height: 1.2;box-sizing: border-box;letter-spacing: 1.5px;"> <section style="display: inline-block;border-bottom-width: 6px;border-bottom-style: solid;border-color: rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="box-sizing: border-box;"><span style="font-size: 16px;">策略模式</span></p> </section> </section> </section> </section> <section style="line-height: normal;"> <br> </section> </section> </section> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">接下来,我们尝试引入策略模式来提升代码的可维护性和可读性。</span> </section> <section style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="letter-spacing: 1px;color: rgb(71, 193, 168);font-size: 16px;">首先,定义一个接口:</span> </section> <pre style="max-width: 100%;color: inherit;font-size: inherit;font-variant-ligatures: normal;orphans: 2;widows: 2;line-height: inherit;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;padding-left: 0.5em;padding-right: 0.5em;"> <section style="margin-right: 2px;margin-left: 2px;padding: 0.5em;max-width: 100%;line-height: 18px;font-size: 14px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);letter-spacing: 1.5px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> <span style="max-width: 100%;line-height: inherit;color: rgb(128, 128, 128);font-size: 16px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">/**<br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> * @author mhcoding<br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> */</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"><span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">public</span> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">interface</span> <span style="max-width: 100%;line-height: inherit;color: rgb(165, 218, 45);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">UserPayService</span> {</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(128, 128, 128);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">/**<br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> * 计算应付价格<br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> */</span></span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="max-width: 100%;line-height: inherit;word-break: inherit !important;">public</span> BigDecimal <span style="max-width: 100%;line-height: inherit;color: rgb(165, 218, 45);word-break: inherit !important;">quote</span>(<span style="max-width: 100%;line-height: inherit;color: rgb(255, 152, 35);word-break: inherit !important;">BigDecimal orderPrice</span>)</span>;</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;">}</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> </section></pre> <section style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="letter-spacing: 1px;color: rgb(71, 193, 168);font-size: 16px;">接着定义几个策略类:</span> </section> <pre style="max-width: 100%;color: inherit;font-size: inherit;font-variant-ligatures: normal;orphans: 2;widows: 2;line-height: inherit;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;padding-left: 0.5em;padding-right: 0.5em;"> <section style="margin-right: 2px;margin-left: 2px;padding: 0.5em;max-width: 100%;line-height: 18px;font-size: 14px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);letter-spacing: 1.5px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> <span style="max-width: 100%;line-height: inherit;color: rgb(128, 128, 128);font-size: 16px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">/**<br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> * <span style="max-width: 100%;color: inherit;line-height: inherit;word-break: inherit !important;">@author</span> mhcoding<br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> */</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"><span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">public</span> <span style="max-width: 100%;color: inherit;line-height: inherit;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);word-break: inherit !important;">class</span> <span style="max-width: 100%;line-height: inherit;color: rgb(165, 218, 45);word-break: inherit !important;">ParticularlyVipPayService</span> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);word-break: inherit !important;">implements</span> <span style="max-width: 100%;line-height: inherit;color: rgb(165, 218, 45);word-break: inherit !important;">UserPayService</span> </span>{</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(91, 218, 237);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">@Override</span></span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="max-width: 100%;line-height: inherit;word-break: inherit !important;">public</span> BigDecimal <span style="max-width: 100%;line-height: inherit;color: rgb(165, 218, 45);word-break: inherit !important;">quote</span><span style="max-width: 100%;line-height: inherit;color: rgb(255, 152, 35);word-break: inherit !important;">(BigDecimal orderPrice)</span> </span>{</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">if</span> (消费金额大于<span style="max-width: 100%;line-height: inherit;color: rgb(174, 135, 250);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">30</span>元) {</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">return</span> <span style="max-width: 100%;line-height: inherit;color: rgb(174, 135, 250);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">7</span>折价格;</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> }</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> }</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;">}</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"><span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">public</span> <span style="max-width: 100%;color: inherit;line-height: inherit;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);word-break: inherit !important;">class</span> <span style="max-width: 100%;line-height: inherit;color: rgb(165, 218, 45);word-break: inherit !important;">SuperVipPayService</span> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);word-break: inherit !important;">implements</span> <span style="max-width: 100%;line-height: inherit;color: rgb(165, 218, 45);word-break: inherit !important;">UserPayService</span> </span>{</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(91, 218, 237);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">@Override</span></span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="max-width: 100%;line-height: inherit;word-break: inherit !important;">public</span> BigDecimal <span style="max-width: 100%;line-height: inherit;color: rgb(165, 218, 45);word-break: inherit !important;">quote</span><span style="max-width: 100%;line-height: inherit;color: rgb(255, 152, 35);word-break: inherit !important;">(BigDecimal orderPrice)</span> </span>{</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">return</span> <span style="max-width: 100%;line-height: inherit;color: rgb(174, 135, 250);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">8</span>折价格;</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> }</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;">}</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"><span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">public</span> <span style="max-width: 100%;color: inherit;line-height: inherit;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);word-break: inherit !important;">class</span> <span style="max-width: 100%;line-height: inherit;color: rgb(165, 218, 45);word-break: inherit !important;">VipPayService</span> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);word-break: inherit !important;">implements</span> <span style="max-width: 100%;line-height: inherit;color: rgb(165, 218, 45);word-break: inherit !important;">UserPayService</span> </span>{</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(91, 218, 237);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">@Override</span></span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;"><span style="max-width: 100%;line-height: inherit;word-break: inherit !important;">public</span> BigDecimal <span style="max-width: 100%;line-height: inherit;color: rgb(165, 218, 45);word-break: inherit !important;">quote</span><span style="max-width: 100%;line-height: inherit;color: rgb(255, 152, 35);word-break: inherit !important;">(BigDecimal orderPrice)</span> </span>{</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">if</span>(该用户超级会员刚过期并且尚未使用过临时折扣){</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> 临时折扣使用次数更新();</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> returen <span style="max-width: 100%;line-height: inherit;color: rgb(174, 135, 250);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">8</span>折价格;</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> }</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">return</span> <span style="max-width: 100%;line-height: inherit;color: rgb(174, 135, 250);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">9</span>折价格;</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"> }</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;">}</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> </section></pre> <section style="line-height: normal;padding-left: 0.5em;padding-right: 0.5em;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;margin-bottom: 5px;padding-left: 0.5em;padding-right: 0.5em;letter-spacing: 1.5px;"> <span style="color: rgb(89, 89, 89);letter-spacing: 1px;font-size: 16px;">引入了策略之后,我们可以按照如下方式进行价格计算:</span> </section> <pre style="max-width: 100%;color: inherit;font-size: inherit;font-variant-ligatures: normal;orphans: 2;widows: 2;line-height: inherit;background-color: rgb(255, 255, 255);box-sizing: border-box !important;word-wrap: break-word !important;padding-left: 0.5em;padding-right: 0.5em;"> <section style="margin-right: 2px;margin-left: 2px;padding: 0.5em;max-width: 100%;line-height: 18px;font-size: 14px;font-family: Consolas, Inconsolata, Courier, monospace;border-radius: 0px;color: rgb(169, 183, 198);background: rgb(40, 43, 46);letter-spacing: 1.5px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: normal !important;word-break: normal !important;overflow: auto !important;display: -webkit-box !important;"> <span style="max-width: 100%;line-height: inherit;color: rgb(128, 128, 128);font-size: 16px;box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">/**<br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> * @author mhcoding<br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> */</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <span style="font-size: 16px;"><span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">public</span> <span style="max-width: 100%;line-height: inherit;color: rgb(248, 35, 117);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">class</span> <span style="max-width: 100%;line-height: inherit;color: rgb(165, 218, 45);box-sizing: border-box !important;word-wrap: break-word !important;overflow-wrap: inherit !important;word-break: inherit !important;">Test</span> {</span> <br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"> <br style="max-width: 100%;box-sizing: border-box !important;
作者:cdhqyj
匿名内部类也就是没有名字的内部类 正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写 但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口 实例1:不使用匿名内部类来实现抽象方法 abstract class Person { public abstract void eat(); } class Child extends Person { public void eat() { System.out.println("eat something"); } } public class Demo { public static void main(String[] args) { Person p = new Child(); p.eat(); } } 运行结果:eat something 可以看到,我们用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用 但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦? 这个时候就引入了匿名内部类 实例2:匿名内部类的基本实现 abstract class Person { public abstract void eat(); } public class Demo { public static void main(String[] args) { Person p = new Person() { public void eat() { System.out.println("eat something"); } }; p.eat(); } } 运行结果:eat something 可以看到,我们直接将抽象类Person中的方法在大括号中实现了 这样便可以省略一个类的书写 并且,匿名内部类还能用于接口上 实例3:在接口上使用匿名内部类 interface Person { public void eat(); } public class Demo { public static void main(String[] args) { Person p = new Person() { public void eat() { System.out.println("eat something"); } }; p.eat(); } } 运行结果:eat something 由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现 常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口 实例4:Thread类的匿名内部类实现 public class Demo { public static void main(String[] args) { Thread t = new Thread() { public void run() { for (int i = 1; i <= 5; i++) { System.out.print(i + " "); } } }; t.start(); } } 运行结果:1 2 3 4 5 实例5:Runnable接口的匿名内部类实现 public class Demo { public static void main(String[] args) { Runnable r = new Runnable() { public void run() { for (int i = 1; i <= 5; i++) { System.out.print(i + " "); } } }; Thread t = new Thread(r); t.start(); } } 运行结果:1 2 3 4 5
作者:微信小助手
<section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="margin-top: 10px;margin-bottom: 10px;box-sizing: border-box;"> <section style="padding: 10px 10px 0px;background-color: rgb(239, 239, 239);box-sizing: border-box;"> <span style="display: inline-block;width: 5%;line-height: 0.8;font-weight: bolder;font-size: 48px;box-sizing: border-box;" title=""> <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="margin: 0px;padding: 0px;box-sizing: border-box;"><span style="letter-spacing: 1px;">因为工作岗位的原因,负责制定了关于后端组数据库的规约规范,作为所有产品线的规范,历经几版的修改,最终形成下边的文本。</span></p> </section> <section style="clear: both;box-sizing: border-box;"> <section> <svg viewbox="0 0 1 1" style="float:left;line-height:0;width:0;vertical-align:top;"></svg> </section> </section> </section> </section> </section> <section style="line-height: 1.75em;"> <br> </section> <section style="text-align: center;margin-left: 8px;margin-right: 8px;"> <img class="rich_pages" data-ratio="0.5611205432937182" data-s="300,640" src="/upload/4289fd5e91144abbc836d92f8e083149.png" data-type="png" data-w="1178" style=""> </section> <section style="text-align: center;line-height: 1.75em;"> <span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;"><em>图片来自 Pexels</em></span> <br> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">规范在整个后端执行也有大半年的时间,对于整个团队在开发阶段就减少不恰当的建表语句、错误 SQL、错误的索引有积极的意义,故分享出来给大家参考。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">下边分为建表规约、SQL 规约、索引规约三个部分,每部分的每一条都有强制、建议两个级别,大家在参考时,根据自己公司的情况来权衡。</span></p> <p style="line-height: normal;"><br></p> <section data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde"> <section> <section> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="border-bottom: 1px solid black;margin: 0.5em 0px;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-color: rgb(89, 89, 89);border-bottom: 6px solid rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">建表规约</p> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】</span>:</strong><span style="font-size: 15px;"><strong>①</strong></span><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">存储引擎必须使用 InnoDB</span></strong></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">InnoDB 支持事物、行级锁、并发性能更好,CPU 及内存缓存页优化使得资源利用率更高。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:②每张表必须设置一个主键 ID,且这个主键 ID 使用自增主键(在满足需要的情况下尽量短),除非在分库分表环境下</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">由于 InnoDB 组织数据的方式决定了需要有一个主键,而且若是这个主键 ID 是单调递增的可以有效提高插入的性能,避免过多的页分裂、减少表碎片提高空间的使用率。<br></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">而在分库分表环境下,则需要统一来分配各个表中的主键值,从而避免整个逻辑表中主键重复。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:③必须使用 utf8mb4 字符集</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在 MySQL 中的 UTF-8 并非“真正的 UTF-8”,而 utf8mb4”才是真正的“UTF-8”。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:④数据库表、表字段必须加入中文注释</span></strong></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">大家都别懒。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:⑤库名、表名、字段名均小写,下划线风格,不超过 32 个字符,必须见名知意,禁止拼音英文混用</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;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="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:⑥单表列数目必须小于 30,若超过则应该考虑将表拆分</span></strong></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">单表列数太多使得 MySQL 服务器处理 InnoDB 返回数据之间的映射成本太高。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:⑦禁止使用外键,如果有外键完整性约束,需要应用程序控制</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">外键会导致表与表之间耦合,UPDATE 与 DELETE 操作都会涉及相关联的表,十分影响 SQL 的性能,甚至会造成死锁。</span></p> <p style="line-height: normal;"><br></p> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:⑧必须把字段定义为 NOT NULL 并且提供默认值</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">解读:</span><br></p> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">NULL 的列使索引/索引统计/值比较都更加复杂,对 MySQL 来说更难优化。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">NULL 这种类型 MySQL 内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">NULL 值需要更多的存储空,无论是表还是索引中每行中的 NULL 的列都需要额外的空间来标识。</span></p></li> </ul> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><strong>【强制】:⑨禁用保留字,如 DESC、RANGE、MARCH 等</strong></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">请参考 MySQL 官方保留字。</span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:⑩如果存储的字符串长度几乎相等,使用 CHAR 定长字符串类型</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:能够减少空间碎片,节省存储空间。</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【建议】:</span></strong> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">⑪</span></strong> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">在一些场景下,考虑使用 TIMESTAMP 代替 DATETIME</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">解读:</span> <br> </section> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">这两种类型的都能表达"yyyy-MM-dd HH:mm:ss"格式的时间,TIMESTAMP 只需要占用 4 个字节的长度,可以存储的范围为(1970-2038)年,在各个时区,所展示的时间是不一样的。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">而 DATETIME 类型占用 8 个字节,对时区不敏感,可以存储的范围为(1001-9999)年。</span></p></li> </ul> <section data-role="list"> <p style="line-height: normal;"><br></p> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【建议】:⑫当心自动生成的 Schema,建议所有的 Schema 手动编写</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">对于一些数据库客户端不要太过信任。</span> </section> <section style="line-height: normal;"> <br> </section> <section data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde"> <section> <section> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="border-bottom: 1px solid black;margin: 0.5em 0px;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-color: rgb(89, 89, 89);border-bottom: 6px solid rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">SQL 规约</p> </section> </section> </section> <section style="line-height: normal;"> <br> </section> </section> </section> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: normal;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【建议】:①为了充分利用缓存,不允许使用自定义函数、存储函数、用户变量</span></strong></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、MySQL 库中的系统表,其查询结果都不会被缓存。<br></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">比如函数 NOW() 或者 CURRENT_DATE() 会因为不同的查询时间,返回不同的查询结果。</span></p> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:②在查询中指定所需的列,而不是直接使用“ *”返回所有的列</span></strong> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">解读:</span> <br> </section> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">读取不需要的列会增加 CPU、IO、NET 消耗。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">不能有效的利用覆盖索引。</span></p></li> </ul> <section data-role="list"> <p style="line-height: normal;"><br></p> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:③不允许使用属性隐式转换</span></strong> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">假设我们在手机号列上添加了索引,然后执行下面的 SQL 会发生什么?<br></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">explain SELECT user_name FROM parent WHERE phone=13812345678;很明显就是索引不生效,会全表扫描。</span></p> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【建议】:④在 WHERE 条件的属性上使用函数或者表达式</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">MySQL 无法自动解析这种表达式,无法使用到索引。</span> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:</span></strong> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">⑤</span><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">禁止使用外键与级联,一切外键概念必须在应用层解决</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"></span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。</span> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【建议】:⑥应尽量避免在 WHERE 子句中使用 or 作为连接条件</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">根据情况可以选择使用 UNION ALL 来代替 OR。</span> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:⑦不允许使用 % 开头的模糊查询</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">根据索引的最左前缀原理,%开头的模糊查询无法使用索引,可以使用 ES 来做检索。</span> </section> <p style="line-height: normal;"><br></p> <section data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde"> <section> <section> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="border-bottom: 1px solid black;margin: 0.5em 0px;line-height: 1.2;box-sizing: border-box;"> <section style="display: inline-block;border-color: rgb(89, 89, 89);border-bottom: 6px solid rgb(89, 89, 89);margin-bottom: -1px;font-size: 20px;color: rgb(89, 89, 89);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;">索引规约</p> </section> </section> </section> <p style="line-height: normal;"><br></p> </section> </section> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【建议】:①避免在更新比较频繁、区分度不高的列上单独建立索引</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">区分度不高的列单独创建索引的优化效果很小,但是较为频繁的更新则会让索引的维护成本更高。</span> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:②JOIN 的表不允许超过五个。需要 JOIN 的字段,数据类型必须绝对一致; 多表关联查询时,保证被关联的字段需要有索引</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">太多表的 JOIN 会让 MySQL 的优化器更难权衡出一个“最佳”的执行计划(可能性为表数量的阶乘),同时要注意关联字段的类型、长度、字符编码等等是否一致。</span> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:③在一个联合索引中,若第一列索引区分度等于 1,那么则不需要建立联合索引</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">索引通过第一列就能够完全定位的数据,所以联合索引的后边部分是不需要的。</span> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【强制】:④建立联合索引时,必须将区分度更高的字段放在左边</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">区分度更高的列放在左边,能够在一开始就有效的过滤掉无用数据。提高索引的效率,相应我们在 Mapper 中编写 SQL 的 WHERE 条件中有多个条件时,需要先看看当前表是否有现成的联合索引直接使用,注意各个条件的顺序尽量和索引的顺序一致。</span> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【建议】:⑤利用覆盖索引来进行查询操作,避免回表</span></strong> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">覆盖查询即是查询只需要通过索引即可拿到所需 DATA,而不再需要再次回表查询,所以效率相对很高。<br></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">我们在使用 EXPLAIN 的结果,extra 列会出现:"using index"。这里也要强调一下不要使用“SELECT * ”,否则几乎不可能使用到覆盖索引。</span></p> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【建议】:⑥在较长 VARCHAR 字段,例如 VARCHAR(100) 上建立索引时,应指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可</span></strong> </section> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">索引的长度与区分度是一对矛盾体,一般对字符串类型数据,若长度为 20 的索引,区分度会高达 90% 以上,则可以考虑创建长度例为 20 的索引,而非全字段索引。<br></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">例如可以使用 SELECT COUNT(DISTINCT LEFT(lesson_code, 20))/COUNT(*) FROM lesson;来确定 lesson_code 字段字符长度为 20 时文本区分度。</span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【建议】:⑦如果有 ORDER BY 的场景,请注意利用索引的有序性</span></strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;"><br></span></p> <section style="line-height: normal;"> <br> </section> <p style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">ORDER BY 最后的字段是联合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。</span></p> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 15px;letter-spacing: 1px;color: rgb(71, 193, 168);">解读:</span> <br> </section> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">假设有查询条件为 WHERE a=? and b=? ORDER BY c;存在索引:a_b_c,则此时可以利用索引排序。</span></p></li> <li><p><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">反例:在查询条件中包含了范围查询,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b;索引 a_b 无法排序。</span></p></li> </ul> <section data-role="list"> <p style="line-height: normal;"><br></p> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【建议】:⑧在 Where 中索引的列不能某个表达式的一部分,也不能是函数的参数</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">即是某列上已经添加了索引,但是若此列成为表达式的一部分、或者是函数的参数,MySQL 无法将此列单独解析出来,索引也不会生效。</span> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【建议】:⑨我们在 Where 条件中使用范围查询时,索引最多用于一个范围条件,超过一个则后边的不走索引</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">MySQL 能够使用多个范围条件里边的最左边的第一个范围查询,但是后边的范围查询则无法使用。</span> </section> <section style="line-height: normal;"> <br> </section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">【建议】:⑩在多个表进行外连接时,表之间的关联字段类型必须完全一致</span></strong> </section> <p style="line-height: normal;"><br></p> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <strong><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">解读:</span></strong> <span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">当两个表进行 Join 时,字段类型若没有完全一致,则加索引也不会生效,这里的完全一致包括但不限于字段类型、字段长度、字符集、Collection 等等。</span> </section> <p style="line-height: normal;"><br></p> <section data-tools="135编辑器" data-id="85988" data-color="#138bde" data-custom="#138bde"> <section data-width="100%"> <section> <section style="text-align: justify;margin-left: 8px;margin-right: 8px;line-height: 1.75em;"> <span style="font-size: 14px;color: rgb(71, 193, 168);"><em><span style="letter-spacing: 1px;">参考资料:</span></em></span> </section> </section> </section> </section> <ul style="list-style-type: disc;" class="list-paddingleft-2"> <li><p><span style="font-size: 14px;"><em><span style="color: rgb(89, 89, 89);letter-spacing: 1px;">《High.Performance.MySQL.3rd.Edition》</span></em></span></p></li> <li><p><span style="font-size: 14px;"><em><span style="color: rgb(89, 89, 89);letter-spacing: 1px;">《阿里巴巴java开发手册》</span></em></span></p></li> </ul> <section data-role="paragraph"> <p style="line-height: normal;"><br></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);line-height: 1.75em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(89, 89, 89);letter-spacing: 1px;box-sizing: border-box !important;word-wrap: break-word !important;"><em style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;">作者:</span></em><em style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;">浮雷</span></em><em style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;font-size: 14px;box-sizing: border-box !important;word-wrap: break-word !important;"><br></span></em></span></p> <p style="white-space: normal;max-width: 100%;min-height: 1em;font-family: -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);line-height: 1.75em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(89, 89, 89);letter-spacing: 1px;box-sizing: border-box !important;word-wrap: break-word !important;"><em style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="font-size: 14px;box-sizing: border-box !important;overflow-wrap: break-word !important;max-width: 100%;color: rgb(89, 89, 89);letter-spacing: 1px;"><em style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">编辑:陶家龙</span></em></span></em></span><br style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"></p> <p><em style="max-width: 100%;box-sizing: border-box !important;word-wrap: break-word !important;"><span style="max-width: 100%;line-height: 1.75em;font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;box-sizing: border-box !important;word-wrap: break-word !important;">出处:</span></em><em><span style="font-size: 15px;color: rgb(89, 89, 89);letter-spacing: 1px;">https://juejin.im/post/6871969929365553165</span></em></p> <p style="text-align: center;"><img class="rich_pages" data-ratio="0.39375" src="/upload/eccf90ae5f48405df149baa3056d4bf3.png" data-type="gif" data-w="640" style=""></p> <section style="box-sizing: border-box;font-style: normal;font-weight: 400;text-align: justify;font-size: 16px;"> <section style="margin: 0.5em 0px;box-sizing: border-box;"> <section style="font-size: 15px;border-style: solid;border-width: 0px 0px 1px;color: rgb(89, 89, 89);border-bottom: 1px solid rgba(215, 215, 215, 0.96);box-sizing: border-box;"> <p style="margin: 0px;padding: 0px;box-sizing: border-box;"><span style="letter-spacing: 1px;"><strong>精彩文章推荐:</strong></span></p> </section> </section> </section> <p style="line-height: 2em;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655837591&idx=2&sn=62612861416d958bb15a416bf1f9caf3&chksm=bd7490408a031956f2887db082c68058b75d082d565469a2ee550805bc1e1911abc6a7f0ff1e&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;" data-linktype="2"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;">明明加了索引,为什么查询还是慢?</span></a><br><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655837574&idx=2&sn=e89204058938fa90d8070b1f4feb4458&chksm=bd7490518a031947e1c91ee21b3baf9eb4763ce2b7feb2026176274eea1534cfa59c5b3e309c&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;" data-linktype="2"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;">CTO说了,delete后不加limit,直接滚蛋!</span></a><br><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655837426&idx=1&sn=956acc1972dea87c401e8f9e148bc013&chksm=bd7491258a031833183d11638b7039c37de9fde28896200d3dcc420f81d35e32c519754ce804&scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;" data-linktype="2"><span style="font-size: 14px;color: rgb(89, 89, 89);letter-spacing: 1px;">啥?我写的一条SQL让公司网站瘫痪了...</span></a><br></p> </section>