<?xml version="1.0" encoding="utf-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><title>小翔博客</title><link>https://www.liuyixiang.com/</link><description>用文字及图片记录美好生活</description><item><title>Storm参数配置及代码优化</title><link>https://www.liuyixiang.com/post/116203.html</link><description>&lt;h2 id=&quot;%E8%83%8C%E6%99%AF&quot; name=&quot;%E8%83%8C%E6%99%AF&quot; style=&quot;padding-right: 0px; border-bottom: 1px solid rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-size: 18px; font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;背景&lt;/h2&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;本人在维护一套由storm、kafka、zookeeper组成的分布式实时计算系统。当数据量很小的时候，系统处理起来其实是绰绰有余的，基本上按照系统默认配置来就可以了。然而当数据量增长到一定规模的时候，系统的各个配置都对整个系统的性能有着至关重要的影响。在不断的处理现网问题、研究的过程中，对系统的一些关键配置有一些心得。在这里分享出来，同大家一起学习交流。 今天我们在这里只介绍storm一些相关的比较重要的配置项和优化项。&lt;/p&gt;&lt;h2 id=&quot;%E5%8F%82%E6%95%B0%E9%85%8D%E7%BD%AE&quot; name=&quot;%E5%8F%82%E6%95%B0%E9%85%8D%E7%BD%AE&quot; style=&quot;padding-right: 0px; border-bottom: 1px solid rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-size: 18px; font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;参数配置&lt;/h2&gt;&lt;h3 id=&quot;%E5%B9%B6%E8%A1%8C%E5%BA%A6&quot; name=&quot;%E5%B9%B6%E8%A1%8C%E5%BA%A6&quot; style=&quot;margin: 20px 0px; padding-top: 0px; padding-bottom: 0px; padding-left: 0px; border-bottom-color: rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;并行度&lt;/h3&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;本人曾在上一篇文章中翻译过官方关于并行度的解释，但是实际在生产环境，这个并行度配置多少也是需要斟酌的。&lt;/p&gt;&lt;h4 id=&quot;spout%E5%B9%B6%E8%A1%8C%E5%BA%A6&quot; name=&quot;spout%E5%B9%B6%E8%A1%8C%E5%BA%A6&quot; style=&quot;margin: 20px 0px; padding: 0px; border-bottom: 1px solid rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;spout并行度&lt;/h4&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;spout的并行度主要和数据源有很大的关系。我们使用的是kafka消息发布订阅系统作为数据源，而kafka也是一套分布式系统。它的每一个topic，也是分布在不同的partition分区上。而这个&lt;strong&gt;partition数量便是spout并行度的上限&lt;/strong&gt;。spout会从指定topic的partition分区中取数据，这里有一个很重要的限制，就是&lt;strong&gt;每一个partition只能被一个线程消费&lt;/strong&gt;。也就是，如果我们spout的并行度比partition的数量要少，那么，一定会有部分spout线程去消费多个partition，这个是可以的。但是，如果spout的并行度比partition的数量多，那么问题来了，由于一个partition只能被一个线程消费，那么一定会有部分spout线程没有数据可以消费。&lt;/p&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;所以在配置spout并行度时需要注意，&lt;strong&gt;spout并行度&amp;lt;=topic的partition数量&lt;/strong&gt;。如果需要性能最大化，可以配置成&lt;strong&gt;spout并行度=topic的partition数量&lt;/strong&gt;。如果这个并行度还是不足以支撑现有的数据，那么你应该考虑去给kafka扩容或者增加分区了。&lt;/p&gt;&lt;h4 id=&quot;bolt%E5%B9%B6%E8%A1%8C%E5%BA%A6&quot; name=&quot;bolt%E5%B9%B6%E8%A1%8C%E5%BA%A6&quot; style=&quot;margin: 20px 0px; padding: 0px; border-bottom: 1px solid rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;bolt并行度&lt;/h4&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;bolt并行度有一个很简单的计算公式。&lt;/p&gt;&lt;div class=&quot;developer-code-block&quot; style=&quot;margin: 0px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;pre style=&quot;margin-top: 10px; margin-bottom: 0px; padding: 0.5em; overflow-x: auto; background-color: rgb(221, 221, 221); color: rgb(73, 73, 73); overflow-wrap: break-word; text-size-adjust: none; border: 1px solid rgb(30, 140, 197); border-radius: 5px; font-size: 14px; word-break: break-all;&quot;&gt;	bolt并行度&amp;gt;=每秒需要处理消息数(n/s)*消息处理时间(ms)/1000&lt;/pre&gt;&lt;/div&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;从这个计算公式可以很直观的看出原理。比如说一条消息在这个bolt中处理的时间是200ms，那么每一个bolt线程每秒钟可以处理5条数据。如果每秒中有1000个消息需要处理。那么我们至少需要200个线程去处理这些消息。&lt;/p&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;那么此bolt的并行度需要&amp;gt;=1000*2000/1000=200&lt;/p&gt;&lt;h3 id=&quot;MaxSpoutPending&quot; name=&quot;MaxSpoutPending&quot; style=&quot;margin: 20px 0px; padding-top: 0px; padding-bottom: 0px; padding-left: 0px; border-bottom-color: rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;MaxSpoutPending&lt;/h3&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;这个值的官方解释是&lt;/p&gt;&lt;div class=&quot;developer-code-block&quot; style=&quot;margin: 0px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;pre style=&quot;margin-top: 10px; margin-bottom: 0px; padding: 0.5em; overflow-x: auto; background-color: rgb(221, 221, 221); color: rgb(73, 73, 73); overflow-wrap: break-word; text-size-adjust: none; border: 1px solid rgb(30, 140, 197); border-radius: 5px; font-size: 14px; word-break: break-all;&quot;&gt;	The&amp;nbsp;maximum&amp;nbsp;number&amp;nbsp;of&amp;nbsp;tuples&amp;nbsp;that&amp;nbsp;can&amp;nbsp;be&amp;nbsp;pending&amp;nbsp;on&amp;nbsp;a&amp;nbsp;spout&amp;nbsp;task&amp;nbsp;at&amp;nbsp;any&amp;nbsp;given&amp;nbsp;time.
	This&amp;nbsp;config&amp;nbsp;applies&amp;nbsp;to&amp;nbsp;individual&amp;nbsp;tasks,&amp;nbsp;not&amp;nbsp;to&amp;nbsp;spouts&amp;nbsp;or&amp;nbsp;topologies&amp;nbsp;as&amp;nbsp;a&amp;nbsp;whole.
	A&amp;nbsp;pending&amp;nbsp;tuple&amp;nbsp;is&amp;nbsp;one&amp;nbsp;that&amp;nbsp;has&amp;nbsp;been&amp;nbsp;emitted&amp;nbsp;from&amp;nbsp;a&amp;nbsp;spout&amp;nbsp;but&amp;nbsp;has&amp;nbsp;not&amp;nbsp;been&amp;nbsp;acked&amp;nbsp;or&amp;nbsp;failed&amp;nbsp;yet.
	Note&amp;nbsp;that&amp;nbsp;this&amp;nbsp;config&amp;nbsp;parameter&amp;nbsp;has&amp;nbsp;no&amp;nbsp;effect&amp;nbsp;for&amp;nbsp;unreliable&amp;nbsp;spouts&amp;nbsp;that&amp;nbsp;don&amp;#39;t&amp;nbsp;tag&amp;nbsp;their&amp;nbsp;tuples&amp;nbsp;with&amp;nbsp;a&amp;nbsp;message&amp;nbsp;id.&lt;/pre&gt;&lt;/div&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;在启用Ack 的情况下，Spout中有个RotatingMap用来保存Spout已经发送出去，但还没有等到Ack 结果的消息。RotatingMap的最大个数是有限制的，为&lt;strong&gt;p*num-tasks&lt;/strong&gt;。其中p就是topology.max.spout.pending的值，也就是MaxSpoutPending（也可以由TopologyBuilder在setSpout 通过setMaxSpoutPending方法来设定），num-task是Spout的Task数。&lt;/p&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;所以这个值可以理解为，&lt;strong&gt;单个spout可以同时处理的消息数&lt;/strong&gt;。这个值如果设置的过大，会导致消息在这个队列里积攒的时间过长，如果超过了超时时间，就会导致消息failed，触发重发机制，恶性循环。这个值如果设置的过小，则会引起后面bolt的消息饥饿，而且消息不能及时的处理。没能有效的利用资源，task的处理能力未充分应用，不能达到最佳的吞吐量。&lt;/p&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;这个值的官方建议是：&lt;em&gt;开始时设置一个很小的 TOPOLOGY_MAX_SPOUT_PENDING（对于 trident 可以设置为 1，对于一般的 topology 可以设置为 executor 的数量），然后逐渐增大，直到数据流不再发生变化。这时你可能会发现结果大约等于 “2 × 吞吐率(每秒收到的消息数) × 端到端时延” （最小的额定容量的2倍）。&lt;/em&gt;&lt;/p&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;这里有个计算方法。&lt;/p&gt;&lt;div class=&quot;developer-code-block&quot; style=&quot;margin: 0px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;pre style=&quot;margin-top: 10px; margin-bottom: 0px; padding: 0.5em; overflow-x: auto; background-color: rgb(221, 221, 221); color: rgb(73, 73, 73); overflow-wrap: break-word; text-size-adjust: none; border: 1px solid rgb(30, 140, 197); border-radius: 5px; font-size: 14px; word-break: break-all;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1/Execute&amp;nbsp;latency*complete&amp;nbsp;latency&lt;/pre&gt;&lt;/div&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;即使用拓扑的complete latency除以Execute latency。&lt;/p&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;我们假设一条消息被完整的处理时间为200ms，spout的Execute latency（执行nextTuple的时间）为2ms，则在200ms内，大约有100条数据被发送。也就是使用topo的complete latency除以Execute latency即可。但实际上不应该考虑如此极端的情况，以避免过多的fail出现，所以可以设置为上述值除以1.5左右。&lt;/p&gt;&lt;h3 id=&quot;%E8%B6%85%E6%97%B6%E6%97%B6%E9%97%B4&quot; name=&quot;%E8%B6%85%E6%97%B6%E6%97%B6%E9%97%B4&quot; style=&quot;margin: 20px 0px; padding-top: 0px; padding-bottom: 0px; padding-left: 0px; border-bottom-color: rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;超时时间&lt;/h3&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;如果在storm ui中你看到整个topo或是spout有消息failed，但是单个的bolt并没有filed。那么一般情况是消息超时导致的。一条消息在进入等待队列中就开始计时，如果上面MaxSpoutPending设置的过大，或是机器负载过高等等，一条消息在有限的时间内没有完整的处理，那么这条消息就会failed，触发重发机制。如果是进行磁盘或者DB操作，那么就会引起数据重复。为了避免消息failed，一个方法就是设置合理的超时时间。系统默认的超时时间是30秒，你可以根据需要将它调的更大。&lt;/p&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;有几种方法设置这个时间。&lt;/p&gt;&lt;ul class=&quot;ul-level-0 list-paddingleft-2&quot; style=&quot;list-style-type: none;&quot;&gt;&lt;li&gt;&lt;p&gt;提交Topology 时设置适当的消息超时时间，&lt;/p&gt;&lt;/li&gt;&lt;ul class=&quot;ul-level-1 list-paddingleft-2&quot; style=&quot;list-style-type: circle;&quot;&gt;&lt;li&gt;&lt;p&gt;conf.setMessageTimeoutSecs(60);&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;config.put(Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS,60);&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;&lt;p&gt;也可以在storm.yaml中修改这个参数：&lt;/p&gt;&lt;/li&gt;&lt;ul class=&quot;ul-level-1 list-paddingleft-2&quot; style=&quot;list-style-type: circle;&quot;&gt;&lt;li&gt;&lt;p&gt;topology.message.timeout.secs: 30&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;h2 id=&quot;%E4%BB%A3%E7%A0%81%E4%BC%98%E5%8C%96&quot; name=&quot;%E4%BB%A3%E7%A0%81%E4%BC%98%E5%8C%96&quot; style=&quot;padding-right: 0px; border-bottom: 1px solid rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-size: 18px; font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;代码优化&lt;/h2&gt;&lt;h3 id=&quot;%E4%BD%BF%E7%94%A8%E7%BB%84%E4%BB%B6%E7%9A%84%E5%B9%B6%E8%A1%8C%E5%BA%A6%E4%BB%A3%E6%9B%BF%E7%BA%BF%E7%A8%8B%E6%B1%A0&quot; name=&quot;%E4%BD%BF%E7%94%A8%E7%BB%84%E4%BB%B6%E7%9A%84%E5%B9%B6%E8%A1%8C%E5%BA%A6%E4%BB%A3%E6%9B%BF%E7%BA%BF%E7%A8%8B%E6%B1%A0&quot; style=&quot;margin: 20px 0px; padding-top: 0px; padding-bottom: 0px; padding-left: 0px; border-bottom-color: rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;使用组件的并行度代替线程池&lt;/h3&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;在storm中，我们可以很方便的调整spout/bolt的并行度，即使启动拓扑时设置不合理，也可以使用rebanlance命令进行动态调整。&lt;/p&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;但有些人可能会在一个spout/bolt组件的task内部启动一个线程池，这些线程池所在的task会比其余task消耗更多的资源，因此这些task所在的worker会消耗较多的资源，有可能影响其它拓扑的正常执行。&lt;/p&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;因此，应该&lt;strong&gt;使用组件自身的并行度来代替线程池&lt;/strong&gt;，因为这些并行度会被合理分配到不同的worker中去。除此之外，还可以使用CGroup等技术进行资源的控制。&lt;/p&gt;&lt;h3 id=&quot;%E4%B8%8D%E8%A6%81%E5%9C%A8spout%E4%B8%AD%E5%A4%84%E7%90%86%E8%80%97%E6%97%B6%E7%9A%84%E6%93%8D%E4%BD%9C&quot; name=&quot;%E4%B8%8D%E8%A6%81%E5%9C%A8spout%E4%B8%AD%E5%A4%84%E7%90%86%E8%80%97%E6%97%B6%E7%9A%84%E6%93%8D%E4%BD%9C&quot; style=&quot;margin: 20px 0px; padding-top: 0px; padding-bottom: 0px; padding-left: 0px; border-bottom-color: rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;不要在spout中处理耗时的操作&lt;/h3&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;在storm中，spout是单线程的。如果nextTuple方法非常耗时，某个消息被成功执行完毕后，acker会给spout发送消息，spout若无法及时消费，则有可能导致 ack消息被丢弃，然后spout认为执行失败了。而在jstorm中将spout分成了3个线程，分别执行nextTuple, fail, ack方法。&lt;/p&gt;&lt;h3 id=&quot;fieldsGrouping%E7%9A%84%E6%95%B0%E6%8D%AE%E5%9D%87%E8%A1%A1%E6%80%A7&quot; name=&quot;fieldsGrouping%E7%9A%84%E6%95%B0%E6%8D%AE%E5%9D%87%E8%A1%A1%E6%80%A7&quot; style=&quot;margin: 20px 0px; padding-top: 0px; padding-bottom: 0px; padding-left: 0px; border-bottom-color: rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;fieldsGrouping的数据均衡性&lt;/h3&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;fieldsGrouping根据某个field的值进行分组。以userId为例，如果一个组件以userId的值作为分组，则具有相同userId的值会被发送到同一个task。如果某些userId的数据量特别大，会导致这接收这些数据的task负载特别高，从而导致数据均衡出现问题。我们在现网中曾经出现过这种数据倾斜的情况，就是因为单一用户刷单所导致单个task负载过高。&lt;/p&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;因此必须合理选择field的值，或者更换分组策略。&lt;/p&gt;&lt;h3 id=&quot;%E4%BC%98%E5%85%88%E4%BD%BF%E7%94%A8localOrShuffleGrouping%E4%BB%A3%E6%9B%BFshuffleGrouping&quot; name=&quot;%E4%BC%98%E5%85%88%E4%BD%BF%E7%94%A8localOrShuffleGrouping%E4%BB%A3%E6%9B%BFshuffleGrouping&quot; style=&quot;margin: 20px 0px; padding-top: 0px; padding-bottom: 0px; padding-left: 0px; border-bottom-color: rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;优先使用localOrShuffleGrouping代替shuffleGrouping&lt;/h3&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;localOrShuffleGrouping是指如果task发送消息给目标task时，发现同一个worker中有目标task，则优先发送到这个task；如果没有，则进行shuffle，随机选取一个目标task。&lt;/p&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;localOrShuffleGrouping其实是对shuffleGrouping的一个优化，因为消除了网络开销和序列化操作。&lt;/p&gt;&lt;h2 id=&quot;%E6%80%BB%E7%BB%93&quot; name=&quot;%E6%80%BB%E7%BB%93&quot; style=&quot;padding-right: 0px; border-bottom: 1px solid rgb(228, 232, 235); position: relative; color: rgb(30, 139, 195); font-size: 18px; font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;总结&lt;/h2&gt;&lt;p style=&quot;margin-top: 20px; margin-bottom: 20px; padding: 0px; color: rgb(51, 51, 51); font-family: &amp;quot;Microsoft Yahei&amp;quot;, 微软雅黑, arial, 宋体, sans-serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;实践出真知。在踩过这些坑之后，才会对这些参数或者优化项有更深刻的认识。希望前面的这些介绍和理解对大家有帮助。也欢迎研究storm或者其它系统的大家随时交流，共同提高。&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Tue, 23 Aug 2022 17:14:06 +0800</pubDate></item><item><title>Apache Doris在美团外卖数仓中的应用实践</title><link>https://www.liuyixiang.com/post/116201.html</link><description>&lt;h2 id=&quot;序言&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;序言&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;美团外卖数据仓库技术团队负责支撑日常业务运营及分析师的日常分析，由于外卖业务特点带来的数据生产成本较高和查询效率偏低的问题，他们通过引入Apache Doris引擎优化生产方案，实现了低成本生产与高效查询的平衡。并以此分析不同业务场景下，基于Kylin的MOLAP模式与基于Doris引擎的ROLAP模式的适用性问题。希望能对大家有所启发或者帮助。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;本文侧重于以Doris引擎为“发动机”的数仓生产架构的改进与思考。在开源的大环境下，各种数据引擎百花齐放，但由于业务的复杂性与多样性，目前并没有哪个引擎能够适配所有业务场景，因此希望通过我们的业务实践与思考为大家提供一些经验参考。美团外卖数仓技术团队致力于将数据应用效率最大化，同时兼顾研发、生产与运维成本的最小化，建设持续进步的数仓能力，也欢迎大家多给我们提出建议。&lt;/span&gt;&lt;/p&gt;&lt;h2 id=&quot;数仓交互层引擎的应用现状&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;数仓交互层引擎的应用现状&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;目前，互联网业务规模变得越来越大，不论是业务生产系统还是日志系统，基本上都是基于Hadoop/Spark分布式大数据技术生态来构建数据仓库，然后对数据进行适当的分层、加工、管理。而在数据应用交互层面，由于时效性的要求，数据最终的展现查询还是需要通过DBMS（MySQL）、MOLAP（Kylin）引擎来进行支撑。如下图所示：&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214436165884307624686.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;h2 id=&quot;汇总数据的交互&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;汇总数据的交互&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;业务团队日常经营分析最典型的场景就是各种维度下的自定义查询，面对如此灵活可变、所见即所得的应用场景，美团平台使用Kylin作为公司的主要MOLAP引擎。MOLAP是预计算生产，在增量业务，预设维度分析场景下表现良好，但在变化维的场景下生产成本巨大。例如，如果使用最新商家类型回溯商家近三个月的表现，需要重新计算三个月的Cube，需花费几个小时，来计算近TB的历史数据。另外，应对非预设维度分析，MOLAP模型需要重新进行适配计算，也需要一定的迭代工作。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;明细数据的交互&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;业务分析除了宏观数据之外，对明细数据查询也是一种刚需。通常大家会选择MySQL等关系型DB作为明细数据的快速检索查询，但当业务成长较快时，很快就会遇到性能瓶颈，并且运维成本也很高。例如，大数据量的同步、新增字段、历史数据更新等操作，它们的维护成本都非常高。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;外卖运营业务特点&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;美团的使命是“帮大家吃得更好，生活更好”。外卖业务为大家提供送餐服务，连接商家与用户，这是一个劳动密集型的业务，外卖业务有上万人的运营团队来服务全国几百万的商家，并以“商圈”为单元，服务于“商圈”内的商家。“商圈”是一个组织机构维度中的最小层级，源于外卖组织的特点，“商圈”及其上层组织机构是一个变化维度，当“商圈”边界发生变化时，就导致在往常日增量的业务生产方式中，历史数据的回溯失去了参考意义。在所有展现组织机构数据的业务场景中，组织机构的变化是一个绕不开的技术问题。此外，商家品类、类型等其它维度也存在变化维的问题。如下图所示：&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214436165884307631060.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;h2 id=&quot;数据生产面临的挑战&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;数据生产面临的挑战&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;数据爆炸，每日使用最新维度对历史数据进行回溯计算。在Kylin的MOLAP模式下存在如下问题：&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;box-sizing: border-box; margin-bottom: 1rem; padding: 0px 0px 0px 1.6rem; list-style-position: initial; list-style-image: initial; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-size: 10px; white-space: normal; background-color: rgb(253, 253, 253);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;历史数据每日刷新，失去了增量的意义。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;每日回溯历史数据量大，10亿+的历史数据回溯。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;数据计算耗时3小时+，存储1TB+，消耗大量计算存储资源，同时严重影响SLA的稳定性。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;预计算的大量历史数据实际使用率低下，实际工作中对历史的回溯80%集中在近1个月左右，但为了应对所有需求场景，业务要求计算近半年以上的历史。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;不支持明细数据的查询。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;解决方案-引入mpp引擎-数据现用现算&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;解决方案：引入MPP引擎，数据现用现算&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;既然变化维的历史数据预计算成本巨大，最好的办法就是现用现算，但现用现算需要强大的并行计算能力。OLAP的实现有MOLAP、ROLAP、HOLAP三种形式，MOLAP以Cube为表现形式，但计算与管理成本较高。ROLAP需要强大的关系型DB引擎支撑。长期以来，由于传统关系型DBMS的数据处理能力有限，所以ROLAP模式受到很大的局限性。随着分布式、并行化技术成熟应用，MPP引擎逐渐表现出强大的高吞吐、低时延计算能力，号称“亿级秒开”的引擎不在少数，ROLAP模式可以得到更好的延伸。单从业务实际应用考虑，性能在千万量级关联查询现场计算秒开的情况下，已经可以覆盖到很多应用场景，具备应用的可能性。例如：日数据量的ROLAP现场计算，周、月趋势的计算，以及明细数据的浏览都可以较好的应对。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;下图是MOLAP模式与ROLAP模式下应用方案的比较：&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214437165884307785016.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;MOLAP模式的劣势&lt;/span&gt;&lt;/p&gt;&lt;ol style=&quot;box-sizing: border-box; margin-bottom: 1rem; padding: 0px 0px 0px 1.6rem; list-style-position: initial; list-style-image: initial; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-size: 10px; white-space: normal; background-color: rgb(253, 253, 253);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;应用层模型复杂，根据业务需要以及Kylin生产需要，还要做较多模型预处理。这样在不同的业务场景中，模型的利用率也比较低。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Kylin配置过程繁琐，需要配置模型设计，并配合适当的“剪枝”策略，以实现计算成本与查询效率的平衡。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;由于MOLAP不支持明细数据的查询，在“汇总+明细”的应用场景中，明细数据需要同步到DBMS引擎来响应交互，增加了生产的运维成本。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;较多的预处理伴随着较高的生产成本。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;ROLAP模式的优势&lt;/span&gt;&lt;/p&gt;&lt;ol style=&quot;box-sizing: border-box; margin-bottom: 1rem; padding: 0px 0px 0px 1.6rem; list-style-position: initial; list-style-image: initial; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-size: 10px; white-space: normal; background-color: rgb(253, 253, 253);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;应用层模型设计简化，将数据固定在一个稳定的数据粒度即可。比如商家粒度的星形模型，同时复用率也比较高。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;App层的业务表达可以通过视图进行封装，减少了数据冗余，同时提高了应用的灵活性，降低了运维成本。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;同时支持“汇总+明细”。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;模型轻量标准化，极大的降低了生产成本。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;综上所述，在变化维、非预设维、细粒度统计的应用场景下，使用MPP引擎驱动的ROLAP模式，可以简化模型设计，减少预计算的代价，并通过强大的实时计算能力，可以支撑良好的实时交互体验。&lt;/span&gt;&lt;/p&gt;&lt;h2 id=&quot;双引擎下的应用场景适配问题&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;双引擎下的应用场景适配问题&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;架构上通过MOLAP+ROLAP双引擎模式来适配不同应用场景，如下图所示：&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214437165884307769482.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;技术权衡&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;MOLAP&lt;/span&gt;：通过预计算，提供稳定的切片数据，实现多次查询一次计算，减轻了查询时的计算压力，保证了查询的稳定性，是“空间换时间”的最佳路径。实现了基于Bitmap的去重算法，支持在不同维度下去重指标的实时统计，效率较高。&amp;nbsp;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;ROLAP&lt;/span&gt;：基于实时的大规模并行计算，对集群的要求较高。MPP引擎的核心是通过将数据分散，以实现CPU、IO、内存资源的分布，来提升并行计算能力。在当前数据存储以磁盘为主的情况下，数据Scan需要的较大的磁盘IO，以及并行导致的高CPU，仍然是资源的短板。因此，高频的大规模汇总统计，并发能力将面临较大挑战，这取决于集群硬件方面的并行计算能力。传统去重算法需要大量计算资源，实时的大规模去重指标对CPU、内存都是一个巨大挑战。目前Doris最新版本已经支持Bitmap算法，配合预计算可以很好地解决去重应用场景。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;业务模型适配&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214437165884307761485.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;MOLAP&lt;/span&gt;： 当业务分析维度相对固化，并在可以使用历史状态时，按照时间进行增量生产，加工成本呈线性增长状态，数据加工到更粗的粒度（如组织单元），减少结果数据量，提高交互效率。如上图所示，由A模型预计算到B模型，使用Kylin是一个不错的选择。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;ROLAP&lt;/span&gt;： 当业务分析维度灵活多变或者特定到最新的状态时（如上图A模型中，始终使用最新的商家组织归属查看历史），预计算回溯历史数据成本巨大。在这种场景下，将数据稳定在商家的粒度，通过现场计算进行历史数据的回溯分析，实现现用现算，可以节省掉预计算的巨大成本，并带来较大的应用灵活性。这种情况下适合MPP引擎支撑下的ROLAP生产模式。&lt;/span&gt;&lt;/p&gt;&lt;h2 id=&quot;mpp引擎的选型&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;MPP引擎的选型&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;目前开源的比较受关注的OLAP引擎很多，比如Greenplum、Apache Impala、Presto、Doris、ClickHouse、Druid、TiDB等等，但缺乏实践案例的介绍，所以我们也没有太多的经验可以借鉴。于是，我们就结合自身业务的需求，从引擎建设成本出发，并立足于公司技术生态融合、集成、易用性等维度进行综合考虑，作为选型依据，最终我们平台部门选择了2018年刚进入Apache社区的Doris。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214437165884307764898.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;h2 id=&quot;doris简介及特点&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Doris简介及特点&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Doris是基于MPP架构的OLAP引擎，主要整合了Google Mesa（数据模型）、Apache Impala（MPP Query Engine）和Apache ORCFile （存储格式，编码和压缩）的技术。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Doris的系统架构如下，主要分为FE和BE两个组件，FE主要负责查询的解析、编译、优化、调度和元数据管理；BE主要负责查询的执行和数据存储。关于Doris的更多技术细节，可参考其&lt;/span&gt;&lt;a href=&quot;http://doris.apache.org/documentation/cn/internal/metadata-design.html&quot; style=&quot;box-sizing: border-box; background-color: transparent; color: rgb(255, 196, 2); margin: 0px 4px; font-size: 14px; text-decoration: underline;&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;官方文档&lt;span class=&quot;fa fa-link&quot; aria-hidden=&quot;true&quot; style=&quot;box-sizing: border-box; -webkit-font-smoothing: antialiased; margin: 0px; padding-left: 2px; vertical-align: text-top; font-size: 14px; font-family: iconfont !important;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214438165884307850952.png&quot; alt=&quot;整体架构&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;div class=&quot;img-figure&quot; style=&quot;box-sizing: border-box; margin: 0.5rem 0px 1.5rem; text-align: center;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; padding: 0.4rem 1rem; color: #777777; font-family: serif; font-size: 14px;&quot;&gt;整体架构&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;Doris的特点：&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;box-sizing: border-box; margin-bottom: 1rem; padding: 0px 0px 0px 1.6rem; list-style-position: initial; list-style-image: initial; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-size: 10px; white-space: normal; background-color: rgb(253, 253, 253);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;同时支持高并发点查询和高吞吐的Ad-hoc查询。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;同时支持离线批量导入和实时数据导入。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;同时支持明细和聚合查询。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;兼容MySQL协议和标准SQL。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;支持Rollup Table和Rollup Table的智能查询路由。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;支持较好的多表Join策略和灵活的表达式查询。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;支持Schema在线变更。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;支持Range和Hash二级分区。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;doris在外卖数仓中的应用效率&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Doris在外卖数仓中的应用效率&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214438165884307870402.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;上图是我们在一个分析项目改造中的评估项目收益，整体在查询效率不变的情况下，生产耗能及存储成本都有较大收益。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;以20台BE+3FE的Doris环境，效率、性能表现情况如下：&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;box-sizing: border-box; margin-bottom: 1rem; padding: 0px 0px 0px 1.6rem; list-style-position: initial; list-style-image: initial; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-size: 10px; white-space: normal; background-color: rgb(253, 253, 253);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;支撑数据分析产品数十个以上，整体响应达到ms级。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;支持百万、千万级大表关联查询，同时进行维表关联的雪花模型，经过Colocate Join特性优化，可以实现秒级响应。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;日级别，基于商家明细现场计算，同时满足汇总及下钻明细查询，查询时效基本都可以控制在秒级。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;7日趋势分析，2~3秒。由于数据量较大，根据集群规模不同查询性能有所区别，但数据量较大时，调动的集群资源较多，因此MPP的并发性能受限于集群的性能。一般原则是并发较高的业务，需要严格控制查询时效（基本在毫秒级），对于并发不高的业务，允许进行较大的查询，但也要考虑集群的承受能力。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;通过一年来的应用以及Doris的不断改进升级，Doris的高可靠、高可用、高可扩展性也得到进一步验证，服务稳定可靠。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;准实时场景下的应用&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;准实时场景下的应用&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;离线业务分析大多基于T+1的离线数据，但在营销活动场景下，外卖团队往往需要当日的实时数据进行业务变化的监控与分析，通常情况下会采用实时流计算来实现。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;外卖实时业务监控有如下特点：&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;box-sizing: border-box; margin-bottom: 1rem; padding: 0px 0px 0px 1.6rem; list-style-position: initial; list-style-image: initial; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-size: 10px; white-space: normal; background-color: rgb(253, 253, 253);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;避免分钟级的生产波动影响，业务上10、15分钟准实时数据可以满足分析需要。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;实时数据需要与离线数据进行日环比与周同比的比对。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;订单业务需要事件时间，体验业务需要生产时间，业务对齐逻辑复杂。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;不同业务线需求差异大，指标需要良好扩展性。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;由于业务上的复杂性，实时流计算中，需要考虑诸多业务口径的对齐，业务ER模型在合流处理中开发成本较高，资源占用较大，通过设计基于Doris的准实时生产数仓，可以灵活地实现业务微批处理，且开发生产成本都比较低。以下为基于Doris的准实时数仓架构设计，是典型的实时Lambda生产架构：&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214438165884307818641.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;实现准实时计算方案，需要以下能力的支撑：&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;实时的写入能力&lt;/span&gt;：目前支持Kafka To Doris秒级延迟。在可靠性、稳定性建设方面仍需进一步提升。&amp;nbsp;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;引擎建设&lt;/span&gt;：短平快的计算+高效的存储性能。目前Doris引擎性能仍有进步空间，2020年将有较大改进提升，随着后续Page Cache，内存表等能力的上线，IO将不再拖后腿，并发能力将有较大提升。&amp;nbsp;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;可靠的调度能力&lt;/span&gt;：提供5、10、15、30分钟的调度保障能力。&amp;nbsp;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;Lambda架构简化&lt;/span&gt;：实时数据与离线数据更好的在Doris中进行融合，灵活支撑应用。&amp;nbsp;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;高效的OLAP交互&lt;/span&gt;：支撑业务的灵活查询访问，业务层通过视图进行逻辑封装直接复用汇总层多维模型，提高了开发效率，减少了运维成本。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;相比Storm、Flink中的窗口计算，准实时DB微批的优势：&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214438165884307817196.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;h2 id=&quot;doris引擎在美团的重要改进&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Doris引擎在美团的重要改进&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;Join 谓词下推的传递性优化&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214439165884307975706.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;如上图所示，对于下面的 SQL：&lt;/span&gt;&lt;/p&gt;&lt;pre style=&quot;box-sizing: border-box; overflow: auto; font-family: Courier, &amp;quot;Courier New&amp;quot;, monospace; font-size: 10px; padding: 0px; margin-top: 0px; margin-bottom: 1rem; line-height: 1.42857; word-break: break-all; overflow-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(238, 238, 238); border: 1px solid rgb(238, 238, 238); border-radius: 4px;&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;select&amp;nbsp;*&amp;nbsp;from&amp;nbsp;t1&amp;nbsp;join&amp;nbsp;t2&amp;nbsp;on&amp;nbsp;t1.id&amp;nbsp;=&amp;nbsp;t2.id&amp;nbsp;where&amp;nbsp;t1.id&amp;nbsp;=&amp;nbsp;1&lt;br/&gt;&lt;/span&gt;&lt;/pre&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Doris开源版本默认会对t2表进行全表Scan，这样会导致上面的查询超时，进而导致外卖业务在Doris上的第一批应用无法上线。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;于是我们在Doris中实现了第一个优化：Join谓词下推的传递性优化（MySQL和TiDB中称之为Constant Propagation）。Join谓词下推的传递性优化是指：基于谓词t1.id = t2.id和t1.id = 1, 我们可以推断出新的谓词t2.id = 1，并将谓词t2.id = 1下推到t2的Scan节点。 这样假如t2表有数百个分区的话，查询性能就会有数十倍甚至上百倍的提升，因为t2表参与Scan和Join的数据量会显著减少。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;查询执行多实例并发优化&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214439165884307988216.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;如上图所示，Doris默认在每个节点上为每个算子只会生成1个执行实例。这样的话，如果数据量很大，每个执行实例的算子就需要处理大量的数据，而且无法充分利用集群的CPU、IO、内存等资源。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;一个比较容易想到的优化手段是，我们可以在每个节点上为每个算子生成多个执行实例。这样每个算子只需要处理少量数据，而且多个执行实例可以并行执行。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;下图是并发度设置为5的优化效果，可以看到对于多种类型的查询，会有3到5倍的查询性能提升：&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214439165884307914415.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;Colocate Join&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Colocate Join（Local Join）是和Shuffle Join、Broadcast Join相对的概念，即将两表的数据提前按照Join Key Shard，这样在Join执行时就没有数据网络传输的开销，两表可以直接在本地进行Join。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;整个Colocate Join在Doris中实现的关键点如下：&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;box-sizing: border-box; margin-bottom: 1rem; padding: 0px 0px 0px 1.6rem; list-style-position: initial; list-style-image: initial; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-size: 10px; white-space: normal; background-color: rgb(253, 253, 253);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;数据导入时保证数据本地性。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;查询调度时保证数据本地性。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;数据Balance后保证数据本地性。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;查询Plan的修改。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Colocate Table元数据的持久化和一致性。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Hash Join的粒度从Server粒度变为Bucket粒度。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Colocate Join的条件判定。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;关于Doris Colocate Join的更多实现细节，可以参考《Apache Doris Colocate Join 原理与实践》。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;对于下面的SQL，Doris Colocate Join和Shuffle Join在不同数据量下的性能对比如下：&lt;/span&gt;&lt;/p&gt;&lt;pre style=&quot;box-sizing: border-box; overflow: auto; font-family: Courier, &amp;quot;Courier New&amp;quot;, monospace; font-size: 10px; padding: 0px; margin-top: 0px; margin-bottom: 1rem; line-height: 1.42857; word-break: break-all; overflow-wrap: break-word; color: rgb(51, 51, 51); background-color: rgb(238, 238, 238); border: 1px solid rgb(238, 238, 238); border-radius: 4px;&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;select&amp;nbsp;count(*)&amp;nbsp;FROM&amp;nbsp;A&amp;nbsp;t1&amp;nbsp;INNER&amp;nbsp;JOIN&amp;nbsp;[shuffle]&amp;nbsp;B&amp;nbsp;t5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ON&amp;nbsp;((t1.dt&amp;nbsp;=&amp;nbsp;t5.dt)&amp;nbsp;AND&amp;nbsp;(t1.id&amp;nbsp;=&amp;nbsp;t5.id))&amp;nbsp;INNER&amp;nbsp;JOIN&amp;nbsp;[shuffle]&amp;nbsp;C&amp;nbsp;t6&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ON&amp;nbsp;((t1.dt&amp;nbsp;=&amp;nbsp;t6.dt)&amp;nbsp;AND&amp;nbsp;(t1.id&amp;nbsp;=&amp;nbsp;t6.id))&amp;nbsp;where&amp;nbsp;t1.dt&amp;nbsp;in&amp;nbsp;(xxx&amp;nbsp;days);&lt;br/&gt;&lt;/span&gt;&lt;/pre&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214439165884307928260.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700; color: #000000; font-size: 14px;&quot;&gt;Bitmap 精确去重&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Doris之前实现精确去重的方式是现场计算的，实现方法和Spark、MapReduce类似：&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214440165884308093480.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;对于上图计算PV的SQL，Doris在计算时，会按照下图的方式进行计算，先根据page列和user_id列group by，最后再Count：&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214440165884308090850.png&quot; alt=&quot;图中是6行数据在2个BE节点上计算的示意图&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;div class=&quot;img-figure&quot; style=&quot;box-sizing: border-box; margin: 0.5rem 0px 1.5rem; text-align: center;&quot;&gt;&lt;span style=&quot;box-sizing: border-box; padding: 0.4rem 1rem; color: #777777; font-family: serif; font-size: 14px;&quot;&gt;图中是6行数据在2个BE节点上计算的示意图&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;显然，上面的计算方式，当数据量越来越大，到几十亿几百亿时，使用的IO资源、CPU资源、内存资源、网络资源会变得越来越多，查询也会变得越来越慢。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;于是我们在Doris中新增了一种Bitmap聚合指标，数据导入时，相同维度列的数据会使用Bitmap聚合。有了Bitmap后，Doris中计算精确去重的方式如下：&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2022/07/20220726214440165884308033740.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; display: block; margin: 2rem auto 0.2rem; max-width: 75%; cursor: pointer;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;可以看到，当使用Bitmap之后，之前的PV计算过程会大幅简化，现场查询时的 IO、CPU、内存，网络资源也会显著减少，并且不再会随着数据规模而线性增加。&lt;/span&gt;&lt;/p&gt;&lt;h2 id=&quot;总结与思考&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;总结与思考&lt;/span&gt;&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;在外卖运营分析的业务实践中，由于业务的复杂及应用场景的不同，没有哪一种数据生产方案能够解决所有业务问题。数据库引擎技术的发展，为我们提供更多手段提升数据建设方案。实践证明，以Doris引擎为驱动的ROLAP模式可以较好地处理汇总与明细、变化维的历史回溯、非预设维的灵活应用、准实时的批处理等场景。而以Kylin为基础的MOLAP模式在处理增量业务分析，固化维度场景，通过预计算以空间换时间方面依然重要。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;业务方面，通过外卖数仓Doris的成功实践以及跨BG的交流，美团已经有更多的团队了解并尝试使用Doris方案。而且在平台同学的共同努力下，引擎性能还有较大提升空间，相信以Doris引擎为驱动的ROLAP模式会为美团的业务团队带来更大的收益。从目前实践效果看，其完全有替代Kylin、Druid、ES等引擎的趋势。&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1rem; padding: 0px; font-size: 1.5rem; line-height: 1.75; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;目前，数据库技术进步飞速，近期柏睿数据发布全内存分布式数据库RapidsDB v4.0支持TB级毫秒响应（处理千亿数据可实现毫秒级响应）。可以预见，数据库技术的进步将大大改善数仓的分层管理与应用支撑效率，业务将变得“定义即可见”，也将极大地提升数据的价值。&lt;/span&gt;&lt;/p&gt;&lt;h2 id=&quot;参考资料&quot; style=&quot;box-sizing: border-box; font-family: &amp;quot;PingFang SC&amp;quot;, Verdana, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-weight: 500; line-height: 1.1; color: rgb(42, 41, 53); margin-top: 0.8em; margin-bottom: 1rem; font-size: 24px; padding-right: 0px; text-shadow: rgba(255, 255, 255, 0.75) 0px 1px 0px; white-space: normal; background-color: rgb(253, 253, 253);&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;参考资料&lt;/span&gt;&lt;/h2&gt;&lt;ul style=&quot;box-sizing: border-box; margin-bottom: 1rem; padding: 0px 0px 0px 1.6rem; list-style-position: initial; list-style-image: initial; color: rgb(51, 51, 51); font-family: &amp;quot;PingFang SC&amp;quot;, &amp;quot;Lantinghei SC&amp;quot;, &amp;quot;Microsoft Yahei&amp;quot;, &amp;quot;Hiragino Sans GB&amp;quot;, &amp;quot;Microsoft Sans Serif&amp;quot;, &amp;quot;WenQuanYi Micro Hei&amp;quot;, sans-serif; font-size: 10px; white-space: normal; background-color: rgb(253, 253, 253);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/apache/incubator-doris&quot; style=&quot;box-sizing: border-box; background-color: transparent; text-decoration: underline; color: rgb(255, 196, 2); font-size: 14px;&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Doris文档和源码&lt;span class=&quot;fa fa-link&quot; aria-hidden=&quot;true&quot; style=&quot;box-sizing: border-box; -webkit-font-smoothing: antialiased; margin: 0px; padding-left: 2px; vertical-align: text-top; font-size: 14px; font-family: iconfont !important;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://blog.bcmeng.com/post/apache-kylin-vs-baidu-palo.html&quot; style=&quot;box-sizing: border-box; background-color: transparent; text-decoration: underline; color: rgb(255, 196, 2); font-size: 14px;&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;Apache Kylin VS Apache Doris&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Tue, 26 Jul 2022 21:44:20 +0800</pubDate></item><item><title>kafka学习笔记之架构详解</title><link>https://www.liuyixiang.com/post/116197.html</link><description>&lt;h2 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.21739; color: rgb(51, 51, 51); margin-top: 2.43478em; margin-bottom: 1.21739em; font-size: 1.4375rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;基本概念&lt;/h2&gt;&lt;h3 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.10526; color: rgb(51, 51, 51); margin-top: 2.94737em; margin-bottom: 1.47368em; font-size: 1.1875rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Kafka 体系架构&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172248160741936861345.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Kafka 体系架构包括若干 Producer、若干 Broker、若干 Consumer，以及一个 ZooKeeper 集群。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;在 Kafka 中还有两个特别重要的概念—主题（Topic）与分区（Partition）。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Kafka 中的消息以主题为单位进行归类，生产者负责将消息发送到特定的主题（发送到 Kafka 集群中的每一条消息都要指定一个主题），而消费者负责订阅主题并进行消费。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;主题是一个逻辑上的概念，它还可以细分为多个分区，一个分区只属于单个主题，很多时候也会把分区称为主题分区（Topic-Partition）。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Kafka 为分区引入了多副本（Replica）机制，通过增加副本数量可以提升容灾能力。同一分区的不同副本中保存的是相同的消息（在同一时刻，副本之间并非完全一样），副本之间是“一主多从”的关系，其中 leader 副本负责处理读写请求，follower 副本只负责与 leader 副本的消息同步。当 leader 副本出现故障时，从 follower 副本中重新选举新的 leader 副本对外提供服务。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172249160741936959867.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;如上图所示，Kafka 集群中有4个 broker，某个主题中有3个分区，且副本因子（即副本个数）也为3，如此每个分区便有1个 leader 副本和2个 follower 副本。&lt;/p&gt;&lt;h3 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.10526; color: rgb(51, 51, 51); margin-top: 2.94737em; margin-bottom: 1.47368em; font-size: 1.1875rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;数据同步&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;分区中的所有副本统称为 AR（Assigned Replicas）。所有与 leader 副本保持一定程度同步的副本（包括 leader 副本在内）组成ISR（In-Sync Replicas），ISR 集合是 AR 集合中的一个子集。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;与 leader 副本同步滞后过多的副本（不包括 leader 副本）组成 OSR（Out-of-Sync Replicas），由此可见，AR=ISR+OSR。在正常情况下，所有的 follower 副本都应该与 leader 副本保持一定程度的同步，即 AR=ISR，OSR 集合为空。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Leader 副本负责维护和跟踪 ISR 集合中所有 follower 副本的滞后状态，当 follower 副本落后太多或失效时，leader 副本会把它从 ISR 集合中剔除。默认情况下，当 leader 副本发生故障时，只有在 ISR 集合中的副本才有资格被选举为新的 leader。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;HW 是 High Watermark 的缩写，俗称高水位，它标识了一个特定的消息偏移量（offset），消费者只能拉取到这个 offset 之前的消息。&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;LEO 是 Log End Offset 的缩写，它标识当前日志文件中下一条待写入消息的 offset。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172249160741936923053.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;如上图所示，第一条消息的 offset（LogStartOffset）为0，最后一条消息的 offset 为8，offset 为9的消息用虚线框表示，代表下一条待写入的消息。日志文件的 HW 为6，表示消费者只能拉取到 offset 在0至5之间的消息，而 offset 为6的消息对消费者而言是不可见的。&lt;/p&gt;&lt;h2 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.21739; color: rgb(51, 51, 51); margin-top: 2.43478em; margin-bottom: 1.21739em; font-size: 1.4375rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Kafka生产者客户端的整体结构&lt;/h2&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172250160741937099486.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;整个生产者客户端由两个线程协调运行，这两个线程分别为主线程和 Sender 线程（发送线程）。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;在主线程中由 KafkaProducer 创建消息，然后通过可能的拦截器、序列化器和分区器的作用之后缓存到消息累加器（RecordAccumulator，也称为消息收集器）中。Sender 线程负责从 RecordAccumulator 中获取消息并将其发送到 Kafka 中。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700;&quot;&gt;RecordAccumulator&lt;/span&gt;&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;RecordAccumulator 主要用来缓存消息以便 Sender 线程可以批量发送，进而减少网络传输的资源消耗以提升性能。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;主线程中发送过来的消息都会被追加到 RecordAccumulator 的某个双端队列（Deque）中，在 RecordAccumulator 的内部为每个分区都维护了一个双端队列。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;消息写入缓存时，追加到双端队列的尾部；Sender 读取消息时，从双端队列的头部读取。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Sender 从 RecordAccumulator 中获取缓存的消息之后，会进一步将原本&amp;lt;分区, Deque&amp;lt; ProducerBatch&amp;gt;&amp;gt; 的保存形式转变成 &amp;lt;Node, List&amp;lt; ProducerBatch&amp;gt; 的形式，其中 Node 表示 Kafka 集群的 broker 节点。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;KafkaProducer 要将此消息追加到指定主题的某个分区所对应的 leader 副本之前，首先需要知道主题的分区数量，然后经过计算得出（或者直接指定）目标分区，之后 KafkaProducer 需要知道目标分区的 leader 副本所在的 broker 节点的地址、端口等信息才能建立连接，最终才能将消息发送到 Kafka。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;所以这里需要一个转换，对于网络连接来说，生产者客户端是与具体的 broker 节点建立的连接，也就是向具体的 broker 节点发送消息，而并不关心消息属于哪一个分区。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700;&quot;&gt;InFlightRequests&lt;/span&gt;&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;请求在从 Sender 线程发往 Kafka 之前还会保存到 InFlightRequests 中，InFlightRequests 保存对象的具体形式为 Map&amp;lt;NodeId, Deque&amp;gt;，它的主要作用是缓存了已经发出去但还没有收到响应的请求（NodeId 是一个 String 类型，表示节点的 id 编号）。&lt;/p&gt;&lt;h3 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.10526; color: rgb(51, 51, 51); margin-top: 2.94737em; margin-bottom: 1.47368em; font-size: 1.1875rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;拦截器&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;生产者拦截器既可以用来在消息发送前做一些准备工作，比如按照某个规则过滤不符合要求的消息、修改消息的内容等，也可以用来在发送回调逻辑前做一些定制化的需求，比如统计类工作。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;生产者拦截器的使用也很方便，主要是自定义实现 org.apache.kafka.clients.producer. ProducerInterceptor 接口。ProducerInterceptor 接口中包含3个方法：&lt;/p&gt;&lt;pre class=&quot;line-numbers  language-java&quot; style=&quot;box-sizing: border-box; overflow: auto; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 13px; padding: 1em 1em 1em 3.8em; line-height: 1.5; color: rgb(101, 123, 131); word-break: normal; overflow-wrap: normal; background-color: rgb(253, 246, 227); border: 1px solid rgb(204, 204, 204); border-radius: 0.3em; tab-size: 4; hyphens: none; position: relative; counter-reset: linenumber 0;&quot;&gt;public&amp;nbsp;ProducerRecord&amp;lt;K,&amp;nbsp;V&amp;gt;&amp;nbsp;onSend(ProducerRecord&amp;lt;K,&amp;nbsp;V&amp;gt;&amp;nbsp;record);public&amp;nbsp;void&amp;nbsp;onAcknowledgement(RecordMetadata&amp;nbsp;metadata,&amp;nbsp;Exception&amp;nbsp;exception);public&amp;nbsp;void&amp;nbsp;close();&lt;/pre&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;KafkaProducer 在将消息序列化和计算分区之前会调用生产者拦截器的 onSend() 方法来对消息进行相应的定制化操作。一般来说最好不要修改消息 ProducerRecord 的 topic、key 和 partition 等信息。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;KafkaProducer 会在消息被应答（Acknowledgement）之前或消息发送失败时调用生产者拦截器的 onAcknowledgement() 方法，优先于用户设定的 Callback 之前执行。这个方法运行在 Producer 的I/O线程中，所以这个方法中实现的代码逻辑越简单越好，否则会影响消息的发送速度。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;close() 方法主要用于在关闭拦截器时执行一些资源的清理工作。&lt;/p&gt;&lt;h3 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.10526; color: rgb(51, 51, 51); margin-top: 2.94737em; margin-bottom: 1.47368em; font-size: 1.1875rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;序列化器&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;生产者需要用序列化器（Serializer）把对象转换成字节数组才能通过网络发送给 Kafka。而在对侧，消费者需要用反序列化器（Deserializer）把从 Kafka 中收到的字节数组转换成相应的对象。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;生产者使用的序列化器和消费者使用的反序列化器是需要一一对应的，如果生产者使用了某种序列化器，比如 StringSerializer，而消费者使用了另一种序列化器，比如 IntegerSerializer，那么是无法解析出想要的数据的。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;序列化器都需要实现org.apache.kafka.common.serialization.Serializer 接口，此接口有3个方法：&lt;/p&gt;&lt;pre class=&quot;line-numbers  language-java&quot; style=&quot;box-sizing: border-box; overflow: auto; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 13px; padding: 1em 1em 1em 3.8em; line-height: 1.5; color: rgb(101, 123, 131); word-break: normal; overflow-wrap: normal; background-color: rgb(253, 246, 227); border: 1px solid rgb(204, 204, 204); border-radius: 0.3em; tab-size: 4; hyphens: none; position: relative; counter-reset: linenumber 0;&quot;&gt;public&amp;nbsp;void&amp;nbsp;configure(Map&amp;lt;String,&amp;nbsp;?&amp;gt;&amp;nbsp;configs,&amp;nbsp;boolean&amp;nbsp;isKey)public&amp;nbsp;byte[]&amp;nbsp;serialize(String&amp;nbsp;topic,&amp;nbsp;T&amp;nbsp;data)public&amp;nbsp;void&amp;nbsp;close()&lt;/pre&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;configure() 方法用来配置当前类，serialize() 方法用来执行序列化操作。而 close() 方法用来关闭当前的序列化器。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;如下：&lt;/p&gt;&lt;pre class=&quot;line-numbers  language-java&quot; style=&quot;box-sizing: border-box; overflow: auto; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 13px; padding: 1em 1em 1em 3.8em; line-height: 1.5; color: rgb(101, 123, 131); word-break: normal; overflow-wrap: normal; background-color: rgb(253, 246, 227); border: 1px solid rgb(204, 204, 204); border-radius: 0.3em; tab-size: 4; hyphens: none; position: relative; counter-reset: linenumber 0;&quot;&gt;public&amp;nbsp;class&amp;nbsp;StringSerializer&amp;nbsp;implements&amp;nbsp;Serializer&amp;lt;String&amp;gt;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;String&amp;nbsp;encoding&amp;nbsp;=&amp;nbsp;&amp;quot;UTF8&amp;quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;configure(Map&amp;lt;String,&amp;nbsp;?&amp;gt;&amp;nbsp;configs,&amp;nbsp;boolean&amp;nbsp;isKey)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;propertyName&amp;nbsp;=&amp;nbsp;isKey&amp;nbsp;?&amp;nbsp;&amp;quot;key.serializer.encoding&amp;quot;&amp;nbsp;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;value.serializer.encoding&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Object&amp;nbsp;encodingValue&amp;nbsp;=&amp;nbsp;configs.get(propertyName);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(encodingValue&amp;nbsp;==&amp;nbsp;null)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;encodingValue&amp;nbsp;=&amp;nbsp;configs.get(&amp;quot;serializer.encoding&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(encodingValue&amp;nbsp;!=&amp;nbsp;null&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;encodingValue&amp;nbsp;instanceof&amp;nbsp;String)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;encoding&amp;nbsp;=&amp;nbsp;(String)&amp;nbsp;encodingValue;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;byte[]&amp;nbsp;serialize(String&amp;nbsp;topic,&amp;nbsp;String&amp;nbsp;data)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(data&amp;nbsp;==&amp;nbsp;null)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;data.getBytes(encoding);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;catch&amp;nbsp;(UnsupportedEncodingException&amp;nbsp;e)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;SerializationException(&amp;quot;Error&amp;nbsp;when&amp;nbsp;serializing&amp;nbsp;&amp;quot;&amp;nbsp;+
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;string&amp;nbsp;to&amp;nbsp;byte[]&amp;nbsp;due&amp;nbsp;to&amp;nbsp;unsupported&amp;nbsp;encoding&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;encoding);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;close()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;nothing&amp;nbsp;to&amp;nbsp;do
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}}&lt;/pre&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;configure() 方法，这个方法是在创建 KafkaProducer 实例的时候调用的，主要用来确定编码类型。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;serialize用来编解码，如果 Kafka 客户端提供的几种序列化器都无法满足应用需求，则可以选择使用如 Avro、JSON、Thrift、ProtoBuf 和 Protostuff 等通用的序列化工具来实现，或者使用自定义类型的序列化器来实现。&lt;/p&gt;&lt;h3 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.10526; color: rgb(51, 51, 51); margin-top: 2.94737em; margin-bottom: 1.47368em; font-size: 1.1875rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;分区器&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;消息经过序列化之后就需要确定它发往的分区，如果消息 ProducerRecord 中指定了 partition 字段，那么就不需要分区器的作用，因为 partition 代表的就是所要发往的分区号。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;如果消息 ProducerRecord 中没有指定 partition 字段，那么就需要依赖分区器，根据 key 这个字段来计算 partition 的值。分区器的作用就是为消息分配分区。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Kafka 中提供的默认分区器是 org.apache.kafka.clients.producer.internals.DefaultPartitioner，它实现了 org.apache.kafka.clients.producer.Partitioner 接口，这个接口中定义了2个方法，具体如下所示。&lt;/p&gt;&lt;pre class=&quot;line-numbers  language-java&quot; style=&quot;box-sizing: border-box; overflow: auto; font-family: Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 13px; padding: 1em 1em 1em 3.8em; line-height: 1.5; color: rgb(101, 123, 131); word-break: normal; overflow-wrap: normal; background-color: rgb(253, 246, 227); border: 1px solid rgb(204, 204, 204); border-radius: 0.3em; tab-size: 4; hyphens: none; position: relative; counter-reset: linenumber 0;&quot;&gt;public&amp;nbsp;int&amp;nbsp;partition(String&amp;nbsp;topic,&amp;nbsp;Object&amp;nbsp;key,&amp;nbsp;byte[]&amp;nbsp;keyBytes,&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Object&amp;nbsp;value,&amp;nbsp;byte[]&amp;nbsp;valueBytes,&amp;nbsp;Cluster&amp;nbsp;cluster);public&amp;nbsp;void&amp;nbsp;close();&lt;/pre&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;其中 partition() 方法用来计算分区号，返回值为 int 类型。partition() 方法中的参数分别表示主题、键、序列化后的键、值、序列化后的值，以及集群的元数据信息，通过这些信息可以实现功能丰富的分区器。close() 方法在关闭分区器的时候用来回收一些资源。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;在默认分区器 DefaultPartitioner 的实现中，close() 是空方法，而在 partition() 方法中定义了主要的分区分配逻辑。如果 key 不为 null，那么默认的分区器会对 key 进行哈希，最终根据得到的哈希值来计算分区号，拥有相同 key 的消息会被写入同一个分区。如果 key 为 null，那么消息将会以轮询的方式发往主题内的各个可用分区。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;自定义的分区器，只需同 DefaultPartitioner 一样实现 Partitioner 接口即可。由于每个分区下的消息处理都是有顺序的，我们可以利用自定义分区器实现在某一系列的key都发送到一个分区中，从而实现有序消费。&lt;/p&gt;&lt;h2 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.21739; color: rgb(51, 51, 51); margin-top: 2.43478em; margin-bottom: 1.21739em; font-size: 1.4375rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Broker&lt;/h2&gt;&lt;h3 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.10526; color: rgb(51, 51, 51); margin-top: 2.94737em; margin-bottom: 1.47368em; font-size: 1.1875rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Broker处理请求流程&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172250160741937084140.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;在Kafka的架构中，会有很多客户端向Broker端发送请求，Kafka 的 Broker 端有个 SocketServer 组件，用来和客户端建立连接，然后通过Acceptor线程来进行请求的分发，由于Acceptor不涉及具体的逻辑处理，非常得轻量级，因此有很高的吞吐量。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;接着Acceptor 线程采用轮询的方式将入站请求公平地发到所有网络线程中，网络线程池默认大小是 3个，表示每台 Broker 启动时会创建 3 个网络线程，专门处理客户端发送的请求，可以通过Broker 端参数 num.network.threads来进行修改。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;那么接下来处理网络线程处理流程如下：&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172250160741937055631.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;当网络线程拿到请求后，会将请求放入到一个共享请求队列中。Broker 端还有个 IO 线程池，负责从该队列中取出请求，执行真正的处理。如果是 PRODUCE 生产请求，则将消息写入到底层的磁盘日志中；如果是 FETCH 请求，则从磁盘或页缓存中读取消息。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;IO 线程池处中的线程是执行请求逻辑的线程，默认是8，表示每台 Broker 启动后自动创建 8 个 IO 线程处理请求，可以通过Broker 端参数 num.io.threads调整。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Purgatory组件是用来缓存延时请求（Delayed Request）的。比如设置了 acks=all 的 PRODUCE 请求，一旦设置了 acks=all，那么该请求就必须等待 ISR 中所有副本都接收了消息后才能返回，此时处理该请求的 IO 线程就必须等待其他 Broker 的写入结果。&lt;/p&gt;&lt;h3 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.10526; color: rgb(51, 51, 51); margin-top: 2.94737em; margin-bottom: 1.47368em; font-size: 1.1875rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;控制器&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;在 Kafka 集群中会有一个或多个 broker，其中有一个 broker 会被选举为控制器（Kafka Controller），它负责管理整个集群中所有分区和副本的状态。&lt;/p&gt;&lt;h4 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.3125; color: rgb(51, 51, 51); margin-top: 3.5em; margin-bottom: 1.75em; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;控制器是如何被选出来的？&lt;/h4&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Broker 在启动时，会尝试去 ZooKeeper 中创建 /controller 节点。Kafka 当前选举控制器的规则是：第一个成功创建 /controller 节点的 Broker 会被指定为控制器。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;在ZooKeeper中的 /controller_epoch 节点中存放的是一个整型的 controller_epoch 值。controller_epoch 用于记录控制器发生变更的次数，即记录当前的控制器是第几代控制器，我们也可以称之为“控制器的纪元”。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;controller_epoch 的初始值为1，即集群中第一个控制器的纪元为1，当控制器发生变更时，每选出一个新的控制器就将该字段值加1。Kafka 通过 controller_epoch 来保证控制器的唯一性，进而保证相关操作的一致性。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;每个和控制器交互的请求都会携带 controller_epoch 这个字段，如果请求的 controller_epoch 值小于内存中的 controller_epoch 值，则认为这个请求是向已经过期的控制器所发送的请求，那么这个请求会被认定为无效的请求。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;如果请求的 controller_epoch 值大于内存中的 controller_epoch 值，那么说明已经有新的控制器当选了。&lt;/p&gt;&lt;h4 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.3125; color: rgb(51, 51, 51); margin-top: 3.5em; margin-bottom: 1.75em; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;控制器是做什么的？&lt;/h4&gt;&lt;ul style=&quot;box-sizing: border-box; margin-bottom: 10px; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em;&quot;&gt;主题管理（创建、删除、增加分区）&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em;&quot;&gt;分区重分配&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em;&quot;&gt;Preferred 领导者选举&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;Preferred 领导者选举主要是 Kafka 为了避免部分 Broker 负载过重而提供的一种换 Leader 的方案。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em;&quot;&gt;集群成员管理（新增 Broker、Broker 主动关闭、Broker 宕机）&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;控制器组件会利用 Watch 机制检查 ZooKeeper 的 /brokers/ids 节点下的子节点数量变更。目前，当有新 Broker 启动后，它会在 /brokers 下创建专属的 znode 节点。一旦创建完毕，ZooKeeper 会通过 Watch 机制将消息通知推送给控制器，这样，控制器就能自动地感知到这个变化，进而开启后续的新增 Broker 作业。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em;&quot;&gt;数据服务&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;控制器上保存了最全的集群元数据信息。&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172253160741937378526.jpg&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h4 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.3125; color: rgb(51, 51, 51); margin-top: 3.5em; margin-bottom: 1.75em; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;控制器宕机了怎么办？&lt;/h4&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;当运行中的控制器突然宕机或意外终止时，Kafka 能够快速地感知到，并立即启用备用控制器来代替之前失败的控制器。这个过程就被称为 Failover，该过程是自动完成的，无需你手动干预。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172255160741937510682.jpg&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;h2 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.21739; color: rgb(51, 51, 51); margin-top: 2.43478em; margin-bottom: 1.21739em; font-size: 1.4375rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;消费者&lt;/h2&gt;&lt;h3 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.10526; color: rgb(51, 51, 51); margin-top: 2.94737em; margin-bottom: 1.47368em; font-size: 1.1875rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;消费组&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;在Kafka中，每个消费者都有一个对应的消费组。当消息发布到主题后，只会被投递给订阅它的每个消费组中的一个消费者。每个消费者只能消费所分配到的分区中的消息。而每一个分区只能被一个消费组中的一个消费者所消费。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172256160741937697167.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;入上图所示，我们可以设置两个消费者组来实现广播消息的作用，消费组A和组B都可以接受到生产者发送过来的消息。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;消费者与消费组这种模型可以让整体的消费能力具备横向伸缩性，我们可以增加（或减少）消费者的个数来提高（或降低）整体的消费能力。对于分区数固定的情况，一味地增加消费者并不会让消费能力一直得到提升，如果消费者过多，出现了&lt;span style=&quot;box-sizing: border-box; font-weight: 700;&quot;&gt;消费者的个数大于分区个数&lt;/span&gt;的情况，就会有消费者分配不到任何分区。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;如下：一共有8个消费者，7个分区，那么最后的消费者C7由于分配不到任何分区而无法消费任何消息。&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172256160741937636593.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;h3 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.10526; color: rgb(51, 51, 51); margin-top: 2.94737em; margin-bottom: 1.47368em; font-size: 1.1875rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;消费端分区分配策略&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Kafka 提供了消费者客户端参数 partition.assignment.strategy 来设置消费者与订阅主题之间的分区分配策略。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700;&quot;&gt;RangeAssignor分配策略&lt;/span&gt;&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;默认情况下，采用 RangeAssignor 分配策略。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;RangeAssignor 分配策略的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度，然后将分区按照跨度进行平均分配，以保证分区尽可能均匀地分配给所有的消费者。对于每一个主题，RangeAssignor 策略会将消费组内所有订阅这个主题的消费者按照名称的字典序排序，然后为每个消费者划分固定的分区范围，如果不够平均分配，那么字典序靠前的消费者会被多分配一个分区。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;假设消费组内有2个消费者 C0 和 C1，都订阅了主题 t0 和 t1，并且每个主题都有4个分区，那么订阅的所有分区可以标识为：t0p0、t0p1、t0p2、t0p3、t1p0、t1p1、t1p2、t1p3。最终的分配结果为：&lt;/p&gt;&lt;pre style=&quot;box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 13px; padding: 9.5px; margin-top: 0px; margin-bottom: 10px; line-height: 1.42857; color: rgb(51, 51, 51); word-break: break-all; overflow-wrap: break-word; background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 4px;&quot;&gt;消费者C0：t0p0、t0p1、t1p0、t1p1
消费者C1：t0p2、t0p3、t1p2、t1p3&lt;/pre&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;假设上面例子中2个主题都只有3个分区，那么订阅的所有分区可以标识为：t0p0、t0p1、t0p2、t1p0、t1p1、t1p2。最终的分配结果为：&lt;/p&gt;&lt;pre style=&quot;box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 13px; padding: 9.5px; margin-top: 0px; margin-bottom: 10px; line-height: 1.42857; color: rgb(51, 51, 51); word-break: break-all; overflow-wrap: break-word; background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 4px;&quot;&gt;消费者C0：t0p0、t0p1、t1p0、t1p1
消费者C1：t0p2、t1p2&lt;/pre&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;可以明显地看到这样的分配并不均匀。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700;&quot;&gt;RoundRobinAssignor分配策略&lt;/span&gt;&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;RoundRobinAssignor 分配策略的原理是将消费组内所有消费者及消费者订阅的所有主题的分区按照字典序排序，然后通过轮询方式逐个将分区依次分配给每个消费者。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;如果同一个消费组内所有的消费者的订阅信息都是相同的，那么 RoundRobinAssignor 分配策略的分区分配会是均匀的。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;如果同一个消费组内的消费者订阅的信息是不相同的，那么在执行分区分配的时候就不是完全的轮询分配，有可能导致分区分配得不均匀。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;假设消费组内有3个消费者（C0、C1 和 C2），t0、t0、t1、t2主题分别有1、2、3个分区，即整个消费组订阅了 t0p0、t1p0、t1p1、t2p0、t2p1、t2p2 这6个分区。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;具体而言，消费者 C0 订阅的是主题 t0，消费者 C1 订阅的是主题 t0 和 t1，消费者 C2 订阅的是主题 t0、t1 和 t2，那么最终的分配结果为：&lt;/p&gt;&lt;pre style=&quot;box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 13px; padding: 9.5px; margin-top: 0px; margin-bottom: 10px; line-height: 1.42857; color: rgb(51, 51, 51); word-break: break-all; overflow-wrap: break-word; background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 4px;&quot;&gt;消费者C0：t0p0
消费者C1：t1p0
消费者C2：t1p1、t2p0、t2p1、t2p2&lt;/pre&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;可以看 到 RoundRobinAssignor 策略也不是十分完美，这样分配其实并不是最优解，因为完全可以将分区 t1p1 分配给消费者 C1。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;span style=&quot;box-sizing: border-box; font-weight: 700;&quot;&gt;StickyAssignor分配策略&lt;/span&gt;&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;这种分配策略，它主要有两个目的：&lt;/p&gt;&lt;ol style=&quot;box-sizing: border-box; margin-bottom: 10px; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;分区的分配要尽可能均匀。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;分区的分配尽可能与上次分配的保持相同。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;假设消费组内有3个消费者（C0、C1 和 C2），它们都订阅了4个主题（t0、t1、t2、t3），并且每个主题有2个分区。也就是说，整个消费组订阅了 t0p0、t0p1、t1p0、t1p1、t2p0、t2p1、t3p0、t3p1 这8个分区。最终的分配结果如下：&lt;/p&gt;&lt;pre style=&quot;box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 13px; padding: 9.5px; margin-top: 0px; margin-bottom: 10px; line-height: 1.42857; color: rgb(51, 51, 51); word-break: break-all; overflow-wrap: break-word; background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 4px;&quot;&gt;消费者C0：t0p0、t1p1、t3p0
消费者C1：t0p1、t2p0、t3p1
消费者C2：t1p0、t2p1&lt;/pre&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;再假设此时消费者 C1 脱离了消费组，那么分配结果为：&lt;/p&gt;&lt;pre style=&quot;box-sizing: border-box; overflow: auto; font-family: Menlo, Monaco, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 13px; padding: 9.5px; margin-top: 0px; margin-bottom: 10px; line-height: 1.42857; color: rgb(51, 51, 51); word-break: break-all; overflow-wrap: break-word; background-color: rgb(245, 245, 245); border: 1px solid rgb(204, 204, 204); border-radius: 4px;&quot;&gt;消费者C0：t0p0、t1p1、t3p0、t2p0
消费者C2：t1p0、t2p1、t0p1、t3p1&lt;/pre&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;StickyAssignor 分配策略如同其名称中的“sticky”一样，让分配策略具备一定的“黏性”，尽可能地让前后两次分配相同，进而减少系统资源的损耗及其他异常情况的发生。&lt;/p&gt;&lt;h3 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.10526; color: rgb(51, 51, 51); margin-top: 2.94737em; margin-bottom: 1.47368em; font-size: 1.1875rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;再均衡（Rebalance）&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;再均衡是指分区的所属权从一个消费者转移到另一消费者的行为，它为消费组具备高可用性和伸缩性提供保障，使我们可以既方便又安全地删除消费组内的消费者或往消费组内添加消费者。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;弊端：&lt;/p&gt;&lt;ol style=&quot;box-sizing: border-box; margin-bottom: 10px; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;在再均衡发生期间，消费组内的消费者是无法读取消息的。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Rebalance 很慢。如果一个消费者组里面有几百个 Consumer 实例，Rebalance 一次要几个小时。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;在进行再均衡的时候消，费者当前的状态也会丢失。比如消费者消费完某个分区中的一部分消息时还没有来得及提交消费位移就发生了再均衡操作，之后这个分区又被分配给了消费组内的另一个消费者，原来被消费完的那部分消息又被重新消费一遍，也就是发生了重复消费。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Rebalance 发生的时机有三个：&lt;/p&gt;&lt;ol style=&quot;box-sizing: border-box; margin-bottom: 10px; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;组成员数量发生变化&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;订阅主题数量发生变化&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;订阅主题的分区数发生变化&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;后两类通常是业务的变动调整所导致的，我们一般不可控制，我们主要说说因为组成员数量变化而引发的 Rebalance 该如何避免。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;当 Consumer Group 完成 Rebalance 之后，每个 Consumer 实例都会定期地向 Coordinator 发送心跳请求，表明它还存活着。如果某个 Consumer 实例不能及时地发送这些心跳请求，Coordinator 就会认为该 Consumer 已经“死”了，从而将其从 Group 中移除，然后开启新一轮 Rebalance。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Consumer端可以设置&lt;span style=&quot;box-sizing: border-box; font-weight: 700;&quot;&gt;session.timeout.ms&lt;/span&gt;，默认是10s，表示如果 Coordinator 在 10 秒之内没有收到 Group 下某 Consumer 实例的心跳，它就会认为这个 Consumer 实例已经挂了。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Consumer端还可以设置&lt;span style=&quot;box-sizing: border-box; font-weight: 700;&quot;&gt;heartbeat.interval.ms&lt;/span&gt;，表示发送心跳请求的频率。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;以及&lt;span style=&quot;box-sizing: border-box; font-weight: 700;&quot;&gt;max.poll.interval.ms&lt;/span&gt;&amp;nbsp;参数，它限定了 Consumer 端应用程序两次调用 poll 方法的最大时间间隔。它的默认值是 5 分钟，表示你的 Consumer 程序如果在 5 分钟之内无法消费完 poll 方法返回的消息，那么 Consumer 会主动发起“离开组”的请求，Coordinator 也会开启新一轮 Rebalance。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;所以知道了上面几个参数后，我们就可以避免以下两个问题：&lt;/p&gt;&lt;ol style=&quot;box-sizing: border-box; margin-bottom: 10px; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;非必要 Rebalance 是因为未能及时发送心跳，导致 Consumer 被“踢出”Group 而引发的。&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;所以我们在生产环境中可以这么设置：&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em;&quot;&gt;&lt;br/&gt;&lt;/p&gt;&lt;/li&gt;&lt;ul style=&quot;box-sizing: border-box;&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;设置 session.timeout.ms = 6s。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;设置 heartbeat.interval.ms = 2s。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;&lt;p&gt;必要 Rebalance 是 Consumer 消费时间过长导致的。如何消费任务时间达到8分钟，而max.poll.interval.ms设置为5分钟，那么也会发生Rebalance，所以如果有比较重的任务的话，可以适当调整这个参数。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Consumer 端的频繁的 Full GC导致的长时间停顿，从而引发了 Rebalance。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h3 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.10526; color: rgb(51, 51, 51); margin-top: 2.94737em; margin-bottom: 1.47368em; font-size: 1.1875rem; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;消费者组再平衡全流程&lt;/h3&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;重平衡过程是靠消费者端的心跳线程（Heartbeat Thread），通知到其他消费者实例的。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;当协调者决定开启新一轮重平衡后，它会将“REBALANCE_IN_PROGRESS”封装进心跳请求的响应中，发还给消费者实例。当消费者实例发现心跳响应中包含了“REBALANCE_IN_PROGRESS”，就能立马知道重平衡又开始了，这就是重平衡的通知机制。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;所以，实际上heartbeat.interval.ms不止是设置了心跳的间隔时间，还可以控制重平衡通知的频率。&lt;/p&gt;&lt;h4 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.3125; color: rgb(51, 51, 51); margin-top: 3.5em; margin-bottom: 1.75em; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;消费者组状态机&lt;/h4&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;重平衡一旦开启，Broker 端的协调者组件就要完成整个重平衡流程，Kafka 设计了一套消费者组状态机（State Machine）来实现。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;Kafka 为消费者组定义了 5 种状态，它们分别是：Empty、Dead、PreparingRebalance、CompletingRebalance 和 Stable。&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172257160741937772187.jpg&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;状态机的各个状态流转：&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172257160741937772187.jpg&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;当有新成员加入或已有成员退出时，消费者组的状态从 Stable 直接跳到 PreparingRebalance 状态，此时，所有现存成员就必须重新申请加入组。当所有成员都退出组后，消费者组状态变更为 Empty。Kafka 定期自动删除过期位移的条件就是，组要处于 Empty 状态。因此，如果你的消费者组停掉了很长时间（超过 7 天），那么 Kafka 很可能就把该组的位移数据删除了。&lt;/p&gt;&lt;h4 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.3125; color: rgb(51, 51, 51); margin-top: 3.5em; margin-bottom: 1.75em; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;组协调器（GroupCoordinator）&lt;/h4&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;GroupCoordinator 是 Kafka 服务端中用于管理消费组的组件。协调器最重要的职责就是负责执行消费者再均衡的操作。&lt;/p&gt;&lt;h4 style=&quot;box-sizing: border-box; font-family: Poppins, &amp;quot;Helvetica Neue&amp;quot;, sans-serif; line-height: 1.3125; color: rgb(51, 51, 51); margin-top: 3.5em; margin-bottom: 1.75em; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;消费者端重平衡流程&lt;/h4&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;在消费者端，重平衡分为两个步骤：分别是加入组和等待领导者消费者（Leader Consumer）分配方案。即JoinGroup 请求和 SyncGroup 请求。&lt;/p&gt;&lt;ol style=&quot;box-sizing: border-box; margin-bottom: 10px; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em;&quot;&gt;加入组&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;当组内成员加入组时，它会向协调器发送 JoinGroup 请求。在该请求中，每个成员都要将自己订阅的主题上报，这样协调器就能收集到所有成员的订阅信息。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em;&quot;&gt;选择消费组领导者&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;一旦收集了全部成员的 JoinGroup 请求后，协调者会从这些成员中选择一个担任这个消费者组的领导者。&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;这里的领导者是具体的消费者实例，它既不是副本，也不是协调器。领导者消费者的任务是收集所有成员的订阅信息，然后根据这些信息，制定具体的分区消费分配方案。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em;&quot;&gt;选举分区分配策略&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;这个分区分配的选举是根据消费组内的各个消费者投票来决定的。&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;协调器会收集各个消费者支持的所有分配策略，组成候选集 candidates。每个消费者从候选集 candidates 中找出第一个自身支持的策略，为这个策略投上一票。计算候选集中各个策略的选票数，选票数最多的策略即为当前消费组的分配策略。&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;如果有消费者并不支持选出的分配策略，那么就会报出异常 IllegalArgumentException：Member does not support protocol。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172258160741937821783.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172258160741937837154.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;ol start=&quot;4&quot; style=&quot;box-sizing: border-box; margin-bottom: 10px; color: rgb(51, 51, 51); font-family: &amp;quot;PT Serif&amp;quot;, Georgia, serif; font-size: 16px; white-space: normal; background-color: rgb(255, 255, 255);&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em;&quot;&gt;发送 SyncGroup 请求&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;协调器会把消费者组订阅信息封装进 JoinGroup 请求的响应体中，然后发给领导者，由领导者统一做出分配方案，然后领导者发送 SyncGroup 请求给协调器。&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;&lt;img src=&quot;https://img.liuyixiang.com/upload/2020/12/20201208172258160741937859527.png&quot; alt=&quot;&quot; style=&quot;box-sizing: border-box; border: 0px; vertical-align: middle; max-width: 100%; height: auto;&quot;/&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p style=&quot;box-sizing: border-box; margin-top: 0px; margin-bottom: 1.75em;&quot;&gt;响应SyncGroup&lt;br style=&quot;box-sizing: border-box;&quot;/&gt;组内所有的消费者都会发送一个 SyncGroup 请求，只不过不是领导者的请求内容为空，然后就会接收到一个SyncGroup响应，接受订阅信息。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;</description><pubDate>Tue, 08 Dec 2020 17:20:58 +0800</pubDate></item><item><title>Python自学真的可以学好嘛？赠送Python编程快速上手电子书籍一本</title><link>https://www.liuyixiang.com/post/116193.html</link><description>&lt;p&gt;网盘资源链接：&lt;a href=&quot;https://pan.baidu.com/s/1-tUdZXO7p_oI379d0b8vQQ&quot; target=&quot;_blank&quot;&gt;https://pan.baidu.com/s/1-tUdZXO7p_oI379d0b8vQQ&lt;/a&gt;&lt;/p&gt; 
&lt;p&gt;提取码：25n9&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/na6B7j.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;首先，在当前诸多的计算机编程语言当中，Python语言确实算是比较简单易学的一种，即使没有任何编程基础的人，也完全可以通过自学来入门，但是要想能够把Python语言用得好，还需要有场景的支撑。&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/juMVBj.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;Python语言与Java、PHP等编程语言不同，Python语言在传统行业领域也有比较广泛的应用，随着诸多企业纷纷实现业务上云，未来Python语言的应用场景会得到进一步拓展，所以当前普通职场人学习Python是不错的选择。&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/b6nU7f.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;学习Python语言通常要经历三个阶段，其一是学习Python语言的基础语法，这个阶段还是相对比较容易地，由于Python语言语法结构比较清晰，规则也非常明确，所以学习起来并不会遇到太多的障碍。按照历史经验来看，这个学习阶段完全可以通过自学来完成。&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/uANnei.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;Python学习的第二个阶段是案例学习，案例通常有非常明确的场景，所以在学习案例的过程中也会学习一些应用场景的相关知识，早期可以重点学习一下Web开发，这个过程也会学习一些数据库知识和前端开发知识。虽然当前Web开发岗位的附加值并不算高，但是Web开发的知识体系比较成熟，初学者会有一个更好的学习体验。&lt;/p&gt; 
&lt;p&gt;从当前的技术发展趋势来看，学习Python语言的过程中，可以重点学习一下大数据知识，未来大数据领域会释放出大量的岗位需求。Python语言目前在大数据开发、大数据分析和大数据运维等领域都有比较广泛的应用，初学者可以根据自身的知识结构和能力特点来选择学习切入点。&lt;/p&gt; 
&lt;p&gt;学习Python的第三个阶段要结合具体的岗位开发任务，在掌握了Python的基本知识之后，下一步可以找一个实习岗位进行提升。对于普通职场人来说，可以结合自身的岗位任务来应用Python，比如通过Python来完成一些数据分析等等。&lt;/p&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt; 
&lt;p&gt;书版权归原文作者所有，如有侵权联系删除！&lt;/p&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  &amp;nbsp;</description><pubDate>Thu, 30 Jul 2020 21:23:00 +0800</pubDate></item><item><title>python：用python断你的网，没商量！领取python基础项目练习题</title><link>https://www.liuyixiang.com/post/116192.html</link><description>&lt;p&gt;网盘链接：&lt;a href=&quot;https://pan.baidu.com/s/1IoRy5Yky6wCq9B1g1mkMfQ&quot; target=&quot;_blank&quot;&gt;https://pan.baidu.com/s/1IoRy5Yky6wCq9B1g1mkMfQ&lt;/a&gt;&lt;/p&gt; 
&lt;p&gt;提取码：iww4&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/7ZrAfm.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;———文章仅供娱乐，请勿滥用———&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/mAvUVv.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;在断网攻击之前，我们先了解一下预备知识。&lt;/p&gt; 
&lt;p&gt;断网攻击分为很多种，比如DOS，SYN泛洪，ARP欺骗等等。我以最有趣的ARP欺骗为例，简单介绍一下。#ARP欺骗#&lt;/p&gt; 
&lt;p&gt;ARP欺骗——主机型欺骗&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/Brmuae.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;主机型欺骗&lt;/p&gt; 
&lt;p&gt;主机型欺骗：在同一个网关下，欺骗者不断地向一个主机（图中PC 1）发送“我是网关”的ARP数据包，导致PC1抛弃了之前真正的网关的MAC地址，转而相信欺骗者，将欺骗者发送的MAC设置为网关&lt;/p&gt; 
&lt;p&gt;地址。自此，PC1想要向外界发送数据请求时，将所有请求都发给了欺骗者，无法从网关连接到外界网络，断网成功。&lt;/p&gt; 
&lt;p&gt;ARP欺骗——网关型欺骗&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/yqaui2.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;网关型欺骗&lt;/p&gt; 
&lt;p&gt;网关型欺骗：在同一个网关下，欺骗者不断地向网关发送“我是PC1”的ARP数据包，过高的频率遮盖了真正的PC1 的ARP数据包，修改MAC缓存中PC 1的MAC地址为欺骗者的MAC地址，PC 1虽然可以发送给网关数据包，但是无法接收到回应，断网成功。&lt;/p&gt; 
&lt;p&gt;python第三方库：scapy&lt;/p&gt; 
&lt;p&gt;scapy是由python开发的网络监控工具，对于python有着天然的api，通过python接口编写一些代码，可以完成对手机的断网攻击。&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/IFnAFn.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;scapy&lt;/p&gt; 
&lt;p&gt;由于目前的路由器都具有较强的应对ARP攻击的防火墙，网关型欺骗基本无法实现，成功案例只有主机型欺骗。其实只要知道原理，代码仅需寥寥几行。&lt;/p&gt; 
&lt;p&gt;from scapy.all import *&lt;/p&gt; 
&lt;p&gt;def Arpsoof(ip):&lt;/p&gt; 
&lt;p&gt;srloop(ARP(psrc='网关IP,hwsrc=&quot;ff:ff:ff:ff:ff:ff&quot;,pdst=ip,op=2))&lt;/p&gt; 
&lt;p&gt;Arpsoof('PC1的IP')&lt;/p&gt; 
&lt;p&gt;#srloop：意为循环发送。&lt;/p&gt; 
&lt;p&gt;#ff:ff:ff:ff:ff:ff通常代表default，无。&lt;/p&gt; 
&lt;p&gt;示例：&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/iAZbUb.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;不断发送ARP数据包。&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/Afe2Mr.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;可以看到，手机虽然显示连接着wifi，但是却无法上网了。&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/q6Zv2e.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;也许奏效的防护方法：手机在wifi设置中将IP设置为静态IP，手机便会固定此时网关的MAC值，任你发送欺骗数据包，手机也不会更改MAC缓存(与不同型号品牌的手机有关)。&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/BzUNZf.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;书版权归原文作者所有，如有侵权联系删除&lt;/p&gt;  
&lt;p&gt;&lt;em&gt;文章权归原文作者所有，如有侵权联系删除&lt;/em&gt;&lt;/p&gt;</description><pubDate>Thu, 30 Jul 2020 21:20:00 +0800</pubDate></item><item><title>Python性能分析与优化PDF高清完整版免费下载|百度云盘</title><link>https://www.liuyixiang.com/post/116191.html</link><description>&lt;p&gt;&lt;a href=&quot;https://pan.baidu.com/s/1_8SokNLcfnUQBV2NTpgZqg&quot; target=&quot;_blank&quot;&gt;百度云盘|Python性能分析与优化PDF高清完整版免费下载&lt;/a&gt;&lt;/p&gt; 
&lt;p&gt;提取码：ubjt&lt;/p&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt; 
&lt;p&gt;&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/6b6BZb.png&quot; style=&quot;display: block; margin-left: auto; margin-right: auto&quot; class=&quot;aligncenter&quot;&gt;&lt;/p&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt; 
&lt;h2&gt;&lt;span&gt;内容简介&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;    
&lt;p&gt;全面掌握Python代码性能分析和优化方法，消除性能瓶颈，迅速改善程序性能！&lt;/p&gt; 
&lt;p&gt;对于Python程序员来说，仅仅知道如何写代码是不够的，还要能够充分利用关键代码的处理能力。本书将讨论如何对Python代码进行性能分析，找出性能瓶颈，并通过不同的性能优化技术消除瓶颈。&lt;/p&gt; 
&lt;p&gt;本书从基本的概念开始，循序渐进地介绍高级的优化主题。首先介绍了Python的主流性能分析器，以及用于帮助理解性能分析结果的可视化工具。然后介绍了通用的性能优化方法和专门针对Python的性能优化方法，带你浏览该语言的主要结构，让你只需做一点改变，即可迅速改善代码的性能。最后介绍了一些专门用于数据处理的程序库，教你如何正确地使用它们以获得最佳性能。&lt;/p&gt; 
&lt;p&gt;如果你是一名Python开发者，想优化Python代码的性能，或是想进一步提升编程能力，那么本书非常适合你阅读。&lt;/p&gt; 
&lt;p&gt;通过阅读本书，你将能够：&lt;/p&gt; 
&lt;p&gt;- 掌握逐步优化代码的方法，学会使用不同的性能分析工具&lt;/p&gt; 
&lt;p&gt;- 理解性能分析器的概念，学会如何观察输出结果&lt;/p&gt; 
&lt;p&gt;- 利用性能分析工具解释可视化的性能输出结果，改善脚本的性能&lt;/p&gt; 
&lt;p&gt;- 用Cython快速创建Python与C语言混合的应用程序&lt;/p&gt; 
&lt;p&gt;- 利用PyPy改善Python代码的性能&lt;/p&gt; 
&lt;p&gt;- 通过Numba、Parakeet和pandas优化数据处理代码&lt;/p&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt; 
&lt;h2&gt;&lt;span&gt;作者简介&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;    
&lt;p&gt;Fernando Doglio&lt;/p&gt; 
&lt;p&gt;Globant公司软件架构师。过去十年一直从事Web开发工作，期间使用了大多数最前沿的技术，如PHP、Ruby on Rails、MySQL、Python、Node.js、AngularJS、REST API等。Fernando喜欢钻研新事物，他的GitHub账户每个月也会因此获得回购。他还是开源拥护者，并通过网站lookingforpullrequests.com来获得人们的支持。Fernando另著有Pro REST API Development with Node.js。&lt;/p&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt; 
&lt;h2&gt;&lt;span&gt;目录&amp;nbsp;&lt;/span&gt;&lt;/h2&gt;  版权声明 阅读 
&lt;br&gt;译者序 阅读 
&lt;br&gt;前言 阅读 
&lt;br&gt;致谢 阅读 
&lt;br&gt;第 1 章　性能分析基础 阅读 
&lt;br&gt;第 2 章　性能分析器 
&lt;br&gt;第 3 章　可视化——利用GUI理解性能分析数据 
&lt;br&gt;第 4 章　优化每一个细节 
&lt;br&gt;第 5 章　多线程与多进程 
&lt;br&gt;第 6 章　常用的优化方法 
&lt;br&gt;第 7 章　用Numba、Parakeet和pandas实现极速数据处理 
&lt;br&gt;第 8 章　付诸实践</description><pubDate>Thu, 30 Jul 2020 21:09:00 +0800</pubDate></item><item><title>教你在 Linux 下时光穿梭</title><link>https://www.liuyixiang.com/post/116186.html</link><description>&lt;p&gt;时光穿梭？电影里的桥段吧？良许你又在唬人？&lt;/p&gt; 
&lt;p&gt;非也非也，良许在这里要给大家介绍 touch 命令，有了它你就可以改变时间戳，达到时光穿梭的目的。&lt;/p&gt; 
&lt;p&gt;touch 命令在我们的工作中使用也相当频繁，我们就由浅到深来详细讲解。&lt;/p&gt; 
&lt;h4&gt;touch 命令基本用法&lt;/h4&gt; 
&lt;p&gt;提起 touch 命令，大家想到的肯定是它的两个用法：&lt;/p&gt; 
&lt;ul&gt; 
 &lt;li&gt;改变时间戳&lt;/li&gt; 
 &lt;li&gt;创建新文件&lt;/li&gt; 
&lt;/ul&gt; 
&lt;p&gt;这两种用法大家在工作中早已用腻了，良许就不再赘述了。&lt;/p&gt; 
&lt;h4&gt;防止创建文件&lt;/h4&gt; 
&lt;p&gt;如果在 touch 后面直接跟上一个文件名，该文件如果不存在的话，将创建一个相应名字的文件。那么如果我们只想改变文件的时间戳，如果文件不存在时不进行文件创建该怎么做？这里需要加上 &lt;code&gt;-c&lt;/code&gt; 选项。&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;[alvin@VM_0_16_centos test]$ touch -c alvin
[alvin@VM_0_16_centos test]$ ll alvin
ls: cannot access alvin: No such file or directory
&lt;/code&gt;&lt;/pre&gt; 
&lt;h4&gt;仅改变文件访问时间&lt;/h4&gt; 
&lt;p&gt;我们知道，如果不带任何选项执行 touch 命令时，文件的访问时间及修改时间都是同时被改变成当前系统时间。如下所示：&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;[alvin@VM_0_16_centos test]$ stat file
  File: ‘file’
  Size: 10              Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d    Inode: 371115      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   alvin)   Gid: ( 1000/   alvin)
Access: 2019-02-20 14:20:21.154819675 +0800
Modify: 2019-02-20 14:20:21.154819675 +0800
Change: 2019-02-20 14:20:21.191819649 +0800
 Birth: -
[alvin@VM_0_16_centos test]$ touch file		# 在这里使用 touch 命令
[alvin@VM_0_16_centos test]$ stat file
  File: ‘file’
  Size: 10              Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d    Inode: 371115      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   alvin)   Gid: ( 1000/   alvin)
Access: 2019-02-20 21:51:24.848774158 +0800		# 文件的访问时间/修改时间均已改成当前系统时间
Modify: 2019-02-20 21:51:24.848774158 +0800
Change: 2019-02-20 21:51:24.848774158 +0800
 Birth: -
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;这里使用到 stat 命令，可以查看文件更详细的信息。&lt;/p&gt; 
&lt;p&gt;如果我们只想改变文件的访问时间，只需加上 &lt;code&gt;-a&lt;/code&gt; 选项即可， a 即是单词 access 的缩写。&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;[alvin@VM_0_16_centos test]$ touch -a file
[alvin@VM_0_16_centos test]$ stat file
  File: ‘file’
  Size: 10              Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d    Inode: 371115      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   alvin)   Gid: ( 1000/   alvin)
Access: 2019-02-20 21:56:40.858021859 +0800		# 只有访问时间的时间戳被改变了
Modify: 2019-02-20 21:51:24.848774158 +0800		# 修改时间保持不变
Change: 2019-02-20 21:56:40.858021859 +0800
 Birth: -
&lt;/code&gt;&lt;/pre&gt; 
&lt;h4&gt;仅改变修改时间&lt;/h4&gt; 
&lt;p&gt;如果我们只想改变文件的修改时间，只需加上 &lt;code&gt;-m&lt;/code&gt; 选项即可， m 即是单词 modify 的缩写。&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;[alvin@VM_0_16_centos test]$ touch -m file
[alvin@VM_0_16_centos test]$ stat file
  File: ‘file’
  Size: 10              Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d    Inode: 371115      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   alvin)   Gid: ( 1000/   alvin)
Access: 2019-02-20 21:56:40.858021859 +0800
Modify: 2019-02-20 22:07:39.138701655 +0800
Change: 2019-02-20 22:07:39.138701655 +0800
 Birth: -
&lt;/code&gt;&lt;/pre&gt; 
&lt;h4&gt;更改为自定义时间戳&lt;/h4&gt; 
&lt;p&gt;不管是不带选项，还是带上 &lt;code&gt;-a&lt;/code&gt; 或 &lt;code&gt;-m&lt;/code&gt; 选项，都会将文件相应的时间改为当前系统时间戳。那如果我们想改为自定义的时间戳呢？要怎么处理？否则怎么算得上时光穿梭？&lt;/p&gt; 
&lt;p&gt;我们有两种方法来更改为自定义时间戳。&lt;/p&gt; 
&lt;p&gt;&lt;strong&gt;1. 加上 &lt;code&gt;-t&lt;/code&gt; 选项&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;比如我们将文件的时间戳改为一个将来时间：&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;[alvin@VM_0_16_centos test]$ touch -t 202001012020.20 file
[alvin@VM_0_16_centos test]$ stat file
  File: ‘file’
  Size: 10              Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d    Inode: 371115      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   alvin)   Gid: ( 1000/   alvin)
Access: 2020-01-01 20:20:20.000000000 +0800
Modify: 2020-01-01 20:20:20.000000000 +0800
Change: 2019-02-20 22:13:01.526965566 +0800
 Birth: -
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;在这里， -t 后面所带的时间戳的格式为：&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;[[CC]YY]MMDDhhmm [.SS]
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;具体来讲，是这样的：&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;CC - 年份的前两位 
YY - 年份的后两位 
MM - 月份 [01-12]
DD - 日期 [01-31]
hh - 时 [00-23]
mm - 分 [00-59]
SS - 秒 [00-61]
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;&lt;strong&gt;2. 加上 &lt;code&gt;-d&lt;/code&gt; 选项&lt;/strong&gt;&lt;/p&gt; 
&lt;p&gt;我们再用新方法将文件的时间戳改成一个过去的时间（2008年奥运会开幕式）：&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;[alvin@VM_0_16_centos test]$ touch -d '08-August-2008' file
[alvin@VM_0_16_centos test]$ stat file
  File: ‘file’
  Size: 10              Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d    Inode: 371115      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   alvin)   Gid: ( 1000/   alvin)
Access: 2008-08-08 00:00:00.000000000 +0800
Modify: 2008-08-08 00:00:00.000000000 +0800
Change: 2019-02-20 22:25:47.808490725 +0800
 Birth: -
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;在这里，时间的格式为：&lt;code&gt;日-月-年&lt;/code&gt; 。但是，这里的时间可以相当灵活，比如也支持 &lt;code&gt;yesterday&lt;/code&gt; 、 &lt;code&gt;1 year ago&lt;/code&gt; 等等模糊时间：&lt;/p&gt; 
&lt;pre&gt;&lt;code&gt;[alvin@VM_0_16_centos test]$ touch -d 'yesterday 08-August-2008' file
[alvin@VM_0_16_centos test]$ stat file
  File: ‘file’
  Size: 10              Blocks: 8          IO Block: 4096   regular file
Device: fd01h/64769d    Inode: 371115      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   alvin)   Gid: ( 1000/   alvin)
Access: 2008-08-07 00:00:00.000000000 +0800
Modify: 2008-08-07 00:00:00.000000000 +0800
Change: 2019-02-20 22:31:57.564725604 +0800
 Birth: -
&lt;/code&gt;&lt;/pre&gt; 
&lt;p&gt;除了更改时间，它还可以改时区。&lt;/p&gt; 
&lt;p&gt;更改时区，只需在 -d 后面跟上对应的时区就可以。&lt;/p&gt;  
&lt;p&gt;公众号：良许Linux&lt;/p&gt; 
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/Z3mYFr.jpg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;h3&gt;有收获？希望老铁们来个三连击，给更多的人看到这篇文章&lt;/h3&gt;</description><pubDate>Thu, 30 Jul 2020 20:07:00 +0800</pubDate></item><item><title>Python学习教程 不走弯路 提高效率！学霸都在用</title><link>https://www.liuyixiang.com/post/116190.html</link><description>&lt;p&gt;人生苦短，我用Python！Python学习教程 不走弯路 提高效率！学霸都在用&lt;/p&gt; 
&lt;p&gt;&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/QjaQRv&quot; class=&quot;aligncenter&quot;&gt;&lt;/p&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt; 
&lt;blockquote&gt; 
 &lt;p&gt;对于初学者想更轻松的学好Python开发技术，Python爬虫，Python大数据分析,人工智能等技术，这里给大家分享一套系统教学资源，加一下我建的Python技术的学习裙；九三七六六七五零九，一起学习。群里有相关开发工具，学习教程，每天还有专业的老司机在线直播分享知识与技术答疑解惑！&lt;/p&gt; 
&lt;/blockquote&gt; 
&lt;p&gt;零基础学习Python必须明确的几点：&lt;br&gt;1.明确你将来是做什么工作的，需要掌握哪些技能，很多人连这个就不知道就盲目的学，你首先清楚，现在公司需要什么人才，你应该奔着什么目标努力。Python的学习方向有很多，主要还是web。&lt;br&gt;2.系统的学习规划，规划好你每天学习的新知识和每天做的作业和练习，很多人想自学Python，两个就看完了，这样的能找到工作算是出奇了，现在学习Python想找到工作，没有4-6个月的根本不行，所以规划是一定要有的，我建议半年时间。&lt;br&gt;3.注意学习方法，很多人在学习Python的时候，开始学那一刻起就选择了错误的学习方法，所以最后注定会放弃，回到原来的岗位继续做着自己不喜欢的事情，学习Python需要一定的技巧，在开始学之前多跟别人问问，不要自己盲目的自学，浪费时间。&lt;/p&gt;</description><pubDate>Thu, 30 Jul 2020 19:46:00 +0800</pubDate></item><item><title>Python编程初学者指南PDF文档免费下载</title><link>https://www.liuyixiang.com/post/116189.html</link><description>&lt;p&gt;&lt;strong&gt;百度网盘&lt;/strong&gt;&amp;nbsp; &amp;nbsp;&lt;a href=&quot;https://pan.baidu.com/s/1LH-084FPfeO8ym9-DL6gVQ&quot; target=&quot;_blank&quot;&gt; Python编程初学者指南PDF文档免费下载&lt;/a&gt;&lt;/p&gt; 
&lt;p&gt;提取码：ivzg&lt;/p&gt; 
&lt;p&gt;如果你刚刚接触Python编程，而且正在寻找一本实用的教程，那么这本书为你量身打造。通过阅读本书，你不仅会学到很多实用的Python编程知识，还将懂得如何在实际工作中运用这些知识。&lt;/p&gt; 
&lt;p&gt;本书各个章节都配有大量能够帮助你理解相关知识的范例。每一个章节都会用一个完整的游戏来演示其中的关键知识点，最后都会对该章的知识点进行小结，还会给出一些小练习让你试试身手。学完这本书之后，你就能熟练地运用Python了。不仅如此，你还可以把本书中学到的基本编程知识运用到其他的编程语言上去。在学习Python编程的过程中，你将创建出许多简单的小游戏。&lt;/p&gt; 
&lt;p&gt;你还会学到：字符串的构建、切片和索引；定义函数；读写文本文件；创建并操作sprite；面向对象编程；创建GUI；处理声音和音乐，创建动画。本书的网站上都有些什么？Python 3.1.1的Windows安装包。pygame 1.9.1 for Python 3.1.x的Windows安装包。livewires游戏引擎。书中所有完整程序的源码。&lt;/p&gt; 
&lt;p&gt;&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/Uj6BB3.png&quot; class=&quot;aligncenter&quot;&gt;&lt;/p&gt;</description><pubDate>Thu, 30 Jul 2020 19:43:00 +0800</pubDate></item><item><title>用​python做一个简单逻辑的游戏——剪刀石头布</title><link>https://www.liuyixiang.com/post/116188.html</link><description>&lt;p&gt;前言&lt;/p&gt; 
&lt;p&gt;本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。&lt;/p&gt; 
&lt;p&gt;作者：萝卜的百科书&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/YRbQzq.jpeg&quot; class=&quot;aligncenter&quot;&gt; 
&lt;p&gt;&amp;nbsp;&lt;/p&gt;  
&lt;p&gt;我们的基础中的基础，在前几文中已经介绍完了，其他的知识用什么学什么就对了，接下来我们做款小游戏，纵观全文，先引入了一个函数，random()随机数，单用random（）这个函数，会产生一个随机的实数，范围在[0，1），若是要从自定的范围取出一个，那就要和randint()配合，这样能产生一个范围内的随机整数；&lt;/p&gt; 
&lt;p&gt;PS：如有需要Python学习资料的小伙伴可以加下方的群去找免费管理员领取&lt;/p&gt;  
&lt;img src=&quot;https://img.liuyixiang.com/image/2020/7/iAjyAv.gif&quot; class=&quot;aligncenter&quot;&gt;  
&lt;p&gt;之后，给范围内三个变量都赋予明确的意义；用到了多分支结构，给每个可能的取值都赋予了明确的意义！blist是python的一个第三方模块，简单说，就是列表，列表里有，剪刀；石头；布；如果你输入的内容不在列表里，执行输出错误的语句，重新输入，若输入的是列表里的内容，接下来，就该和电脑做判断了；仍然用到的是多分支结构.&lt;/p&gt; 
&lt;p&gt;是不是逻辑清晰，没有太多你不会的知识，不只是剪刀石头布，摇骰子，猜数字，等等方法都是一样的；还等什么，快去写下你自己设计的小游戏吧！&lt;/p&gt; 
&lt;p&gt;想要高端大气上档次的游戏，有界面有动画的怎么做呢！这里就需要python的一个库pygame，安装方法也很简单，若你用的是官方的IDLE编辑器，那么在win10，cmd黑窗口输入，pip install pygame；就可以了！若你用的是Anaconda，那么Anaconda prompt中输入上述代码就可以了，要保持jupyter该软件是运行状态，要不然会出现服务器未连接，安装错误的情况，该软件自带了一些常用库，用来做数据分析很不错。&lt;/p&gt;</description><pubDate>Thu, 30 Jul 2020 19:40:00 +0800</pubDate></item></channel></rss>