<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Spark on My Learning Notes</title><link>https://eleanora-lyh.github.io/MyLearningNotes/categories/spark/</link><description>Recent content in Spark on My Learning Notes</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Tue, 20 Oct 2026 10:11:30 +0000</lastBuildDate><atom:link href="https://eleanora-lyh.github.io/MyLearningNotes/categories/spark/index.xml" rel="self" type="application/rss+xml"/><item><title>【二】Spark 下载与入门：环境配置、PySpark 交互与独立应用</title><link>https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/</link><pubDate>Wed, 14 Oct 2026 17:25:56 +0800</pubDate><guid>https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/</guid><description>&lt;p&gt;过程参考：Spark官网的&lt;a class="link" href="https://spark.apache.org/docs/latest/quick-start.html" target="_blank" rel="noopener"
 &gt;Quick Start - Spark 4.2.0 Documentation&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="1下载"&gt;1、下载
&lt;/h1&gt;&lt;p&gt;选择所需要的包类型 &lt;a class="link" href="http://spark.apache.org/" target="_blank" rel="noopener"
 &gt;http://spark.apache.org/&lt;/a&gt; ，得到spark-3.5.9-bin-hadoop3.tgz包并解压&lt;/p&gt;
&lt;p&gt;&lt;img alt="Spark 下载页：选择 Spark 3.5.9 与 Hadoop 3 预构建包" class="gallery-image" data-flex-basis="669px" data-flex-grow="278" height="453" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/assets/2026-10-14-17-28-53-image.png" srcset="https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/assets/2026-10-14-17-28-53-image_hu_ad085bb876adc657.png 800w, https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/assets/2026-10-14-17-28-53-image.png 1263w" width="1263"&gt;&lt;/p&gt;
&lt;h2 id="11-前置环境"&gt;1.1 前置环境
&lt;/h2&gt;&lt;h3 id="1已安装并配置-python"&gt;1、已安装并配置 Python
&lt;/h3&gt;&lt;p&gt;如果没有安装 Python，需要先安装 &lt;strong&gt;Python 3.8～3.11&lt;/strong&gt;并添加到环境变量中&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python --version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;输出&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\U&lt;/span&gt;sers&lt;span style="color:#ae81ff"&gt;\v&lt;/span&gt;-liuyuhang&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;ource&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;park3.5.9&amp;gt;python --version
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Python 3.12.10
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;用户变量中新增环境变量设置&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Driver 使用 Python 3.12&lt;/strong&gt;：&lt;code&gt;PYSPARK_DRIVER_PYTHON&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Worker 使用 Python 3.12&lt;/strong&gt;：&lt;code&gt;PYSPARK_PYTHON&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Windows 用户环境变量：配置 PYSPARK_DRIVER_PYTHON 与 PYSPARK_PYTHON" class="gallery-image" data-flex-basis="865px" data-flex-grow="360" height="302" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/assets/2026-10-14-17-57-55-image.png" srcset="https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/assets/2026-10-14-17-57-55-image_hu_baed563728770310.png 800w, https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/assets/2026-10-14-17-57-55-image.png 1089w" width="1089"&gt;&lt;/p&gt;
&lt;p&gt;图中两个变量均指向 Python 3.12 的解释器路径，仅记录本次配置；版本选择仍需以所用 Spark 版本的兼容要求为准。&lt;/p&gt;
&lt;p&gt;在系统环境变量中新增Spark安装路径 &lt;code&gt;C:\Users\v-liuyuhang\source\spark3.5.9\bin&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="2已安装-java并设置-java_home"&gt;2、已安装 Java并设置 &lt;code&gt;JAVA_HOME&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;64 位 JDK 8&lt;/strong&gt;，可以运行 &lt;strong&gt;Apache Spark 3.5.9&lt;/strong&gt;，满足最低要求&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;java -version
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;输出&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\U&lt;/span&gt;sers&lt;span style="color:#ae81ff"&gt;\v&lt;/span&gt;-liuyuhang&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;ource&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;park3.5.9&amp;gt;java -version
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;java version &lt;span style="color:#e6db74"&gt;&amp;#34;1.8.0_503&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Java&lt;span style="color:#f92672"&gt;(&lt;/span&gt;TM&lt;span style="color:#f92672"&gt;)&lt;/span&gt; SE Runtime Environment &lt;span style="color:#f92672"&gt;(&lt;/span&gt;build 1.8.0_503-b01&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Java HotSpot&lt;span style="color:#f92672"&gt;(&lt;/span&gt;TM&lt;span style="color:#f92672"&gt;)&lt;/span&gt; 64-Bit Server VM &lt;span style="color:#f92672"&gt;(&lt;/span&gt;build 25.503-b01, mixed mode&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;检查 JAVA_HOME（如果你使用了 JRE 而不是 JDK，Spark 运行时可能出问题，最好确认目录中存在&lt;code&gt;%JAVA_HOME%\bin\java.exe&lt;/code&gt;和&lt;code&gt;%JAVA_HOME%\bin\javac.exe&lt;/code&gt;）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;set JAVA_HOME
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;输出&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\U&lt;/span&gt;sers&lt;span style="color:#ae81ff"&gt;\v&lt;/span&gt;-liuyuhang&amp;gt;set JAVA_HOME
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;JAVA_HOME&lt;span style="color:#f92672"&gt;=&lt;/span&gt;C:&lt;span style="color:#ae81ff"&gt;\P&lt;/span&gt;rogram Files&lt;span style="color:#ae81ff"&gt;\J&lt;/span&gt;ava&lt;span style="color:#ae81ff"&gt;\j&lt;/span&gt;dk1.8.0_503&lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="3windows系统适配linux不需要"&gt;3、Windows系统适配(Linux不需要)
&lt;/h3&gt;&lt;p&gt;过程完全参考&lt;a class="link" href="https://github.com/Arjun650/Hadoop-3.3.4-Windows-Installation-Guide" target="_blank" rel="noopener"
 &gt;Arjun650/Hadoop-3.3.4-Windows-Installation-Guide&lt;/a&gt; 就可以，但是如果出现下载很慢的情况可以安装一个aria2工具，再用aria2多线程下载hadoop压缩包）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;winget install aria2.aria2
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;aria2c -x &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; -s &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; -k 1M https://repo.huaweicloud.com/apache/hadoop/common/hadoop-3.3.4/hadoop-3.3.4.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;下载的输出可以参考，这样操作之后几分钟就可以下载完成&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 8
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 9
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;10
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;11
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;12
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;13
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;14
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;15
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;16
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;17
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;18
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;19
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;20
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;21
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;22
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;23
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;24
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;25
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;26
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;27
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;28
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;29
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;30
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;31
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;32
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;33
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;34
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;35
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\U&lt;/span&gt;sers&lt;span style="color:#ae81ff"&gt;\v&lt;/span&gt;-liuyuhang&lt;span style="color:#ae81ff"&gt;\D&lt;/span&gt;ownloads&amp;gt;aria2c -x &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; -s &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt; -k 1M https://repo.huaweicloud.com/apache/hadoop/common/hadoop-3.3.4/hadoop-3.3.4.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;10/19 16:39:46 &lt;span style="color:#f92672"&gt;[&lt;/span&gt;NOTICE&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Downloading &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; item&lt;span style="color:#f92672"&gt;(&lt;/span&gt;s&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#75715e"&gt;#e1da51 0B/0B CN:1 DL:0B]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;10/19 16:39:54 &lt;span style="color:#f92672"&gt;[&lt;/span&gt;NOTICE&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Allocating disk space. Use --file-allocation&lt;span style="color:#f92672"&gt;=&lt;/span&gt;none to disable it. See --file-allocation option in man page &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; more details.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; *** Download Progress Summary as of Mon Oct &lt;span style="color:#ae81ff"&gt;19&lt;/span&gt; 16:40:46 &lt;span style="color:#ae81ff"&gt;2026&lt;/span&gt; ***
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;======================================================================================================================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#75715e"&gt;#e1da51 81MiB/663MiB(12%) CN:16 DL:3.2MiB ETA:2m56s]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FILE: C:/Users/v-liuyuhang/Downloads/hadoop-3.3.4.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;----------------------------------------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; *** Download Progress Summary as of Mon Oct &lt;span style="color:#ae81ff"&gt;19&lt;/span&gt; 16:41:47 &lt;span style="color:#ae81ff"&gt;2026&lt;/span&gt; ***
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;======================================================================================================================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#75715e"&gt;#e1da51 300MiB/663MiB(45%) CN:16 DL:3.6MiB ETA:1m40s]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FILE: C:/Users/v-liuyuhang/Downloads/hadoop-3.3.4.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;----------------------------------------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; *** Download Progress Summary as of Mon Oct &lt;span style="color:#ae81ff"&gt;19&lt;/span&gt; 16:42:47 &lt;span style="color:#ae81ff"&gt;2026&lt;/span&gt; ***
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;======================================================================================================================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#75715e"&gt;#e1da51 520MiB/663MiB(78%) CN:16 DL:3.4MiB ETA:41s]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FILE: C:/Users/v-liuyuhang/Downloads/hadoop-3.3.4.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;----------------------------------------------------------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#75715e"&gt;#e1da51 662MiB/663MiB(99%) CN:1 DL:28KiB ETA:13s]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;10/19 16:43:37 &lt;span style="color:#f92672"&gt;[&lt;/span&gt;NOTICE&lt;span style="color:#f92672"&gt;]&lt;/span&gt; Download complete: C:/Users/v-liuyuhang/Downloads/hadoop-3.3.4.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Download Results:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gid |stat|avg speed |path/URI
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;======&lt;/span&gt;+&lt;span style="color:#f92672"&gt;====&lt;/span&gt;+&lt;span style="color:#f92672"&gt;===========&lt;/span&gt;+&lt;span style="color:#f92672"&gt;=======================================================&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;e1da51|OK | 2.9MiB/s|C:/Users/v-liuyuhang/Downloads/hadoop-3.3.4.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Status Legend:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;OK&lt;span style="color:#f92672"&gt;)&lt;/span&gt;:download completed.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\U&lt;/span&gt;sers&lt;span style="color:#ae81ff"&gt;\v&lt;/span&gt;-liuyuhang&lt;span style="color:#ae81ff"&gt;\D&lt;/span&gt;ownloads&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;将下载的 &lt;code&gt; C:/Users/v-liuyuhang/Downloads/hadoop-3.3.4.tar.gz&lt;/code&gt;解压缩可以得到1.28G，后面跟着链接教程走就行。&lt;/p&gt;
&lt;p&gt;ps. 如果JAVA_HOME 路径里有的空格，如&lt;code&gt;JAVA_HOME=C:\Program Files\Java\jdk1.8.0_503\&lt;/code&gt;，需要在所有使用此路径时替换成&lt;code&gt;C:\PROGRA~1\Java\jdk1.8.0_503&lt;/code&gt;。因为 hdfs.cmd、hadoop-config.cmd 这类批处理脚本在拼接 &lt;code&gt;%JAVA_HOME%\bin\java&lt;/code&gt; 时没有加引号。&lt;/p&gt;
&lt;h1 id="2spark中的python-shell"&gt;2、Spark中的Python shell
&lt;/h1&gt;&lt;h2 id="21-启动shell"&gt;2.1 启动shell
&lt;/h2&gt;&lt;p&gt;进入一开始解压的目录 &lt;code&gt;C:\Users\v-liuyuhang\source\spark3.5.9\&lt;/code&gt; 打开cmd，执行&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bin&lt;span style="color:#ae81ff"&gt;\p&lt;/span&gt;yspark
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;可以看到&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 8
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 9
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;10
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;11
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;12
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;13
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;14
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;15
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;16
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;17
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;18
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\U&lt;/span&gt;sers&lt;span style="color:#ae81ff"&gt;\v&lt;/span&gt;-liuyuhang&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;ource&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;park3.5.9&amp;gt;bin&lt;span style="color:#ae81ff"&gt;\p&lt;/span&gt;yspark
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Python 3.12.10 &lt;span style="color:#f92672"&gt;(&lt;/span&gt;tags/v3.12.10:0cc8128, Apr &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt; 2025, 12:21:36&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;MSC v.1943 &lt;span style="color:#ae81ff"&gt;64&lt;/span&gt; bit &lt;span style="color:#f92672"&gt;(&lt;/span&gt;AMD64&lt;span style="color:#f92672"&gt;)]&lt;/span&gt; on win32
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Type &lt;span style="color:#e6db74"&gt;&amp;#34;help&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;copyright&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;credits&amp;#34;&lt;/span&gt; or &lt;span style="color:#e6db74"&gt;&amp;#34;license&amp;#34;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; more information.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;26/10/14 17:25:56 WARN Shell: Did not find winutils.exe: java.io.FileNotFoundException: java.io.FileNotFoundException: HADOOP_HOME and hadoop.home.dir are unset. -see https://wiki.apache.org/hadoop/WindowsProblems
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Setting default log level to &lt;span style="color:#e6db74"&gt;&amp;#34;WARN&amp;#34;&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;To adjust logging level use sc.setLogLevel&lt;span style="color:#f92672"&gt;(&lt;/span&gt;newLevel&lt;span style="color:#f92672"&gt;)&lt;/span&gt;. For SparkR, use setLogLevel&lt;span style="color:#f92672"&gt;(&lt;/span&gt;newLevel&lt;span style="color:#f92672"&gt;)&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;26/10/14 17:25:56 WARN NativeCodeLoader: Unable to load native-hadoop library &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; your platform... using builtin-java classes where applicable
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Welcome to
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ____ __
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; / __/__ ___ _____/ /__
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _&lt;span style="color:#ae81ff"&gt;\ \/&lt;/span&gt; _ &lt;span style="color:#ae81ff"&gt;\/&lt;/span&gt; _ &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;/ __/ &lt;span style="color:#e6db74"&gt;&amp;#39;_/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; /__ / .__/\_,_/_/ /_/\_\ version 3.5.9
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; /_/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;Using Python version 3.12.10 (tags/v3.12.10:0cc8128, Apr 8 2025 12:21:36)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;Spark context Web UI available at http://host.docker.internal:4040
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;Spark context available as &amp;#39;&lt;/span&gt;sc&lt;span style="color:#e6db74"&gt;&amp;#39; (master = local[*], app id = local-1791969960653).
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;SparkSession available as &amp;#39;&lt;/span&gt;spark&lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在 Spark 中，我们通过对分布式数据集的操作来表达我们的计算意图，这些计算会自动地在集群上并行进行。这样的数据集被称为弹性分布式数据集（resilient distributed dataset），简称 RDD。RDD 是 Spark 对分布式数据和计算的基本抽象。&lt;/p&gt;
&lt;h2 id="22-使用python-shell-即时统计"&gt;2.2 使用python shell 即时统计
&lt;/h2&gt;&lt;p&gt;先来使用 shell 从本地文本文件创建一个RDD 来作一些简单的即时统计。&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-pyspark" data-lang="pyspark"&gt;&amp;gt;&amp;gt;&amp;gt; lines = sc.textFile(&amp;#34;README.md&amp;#34;) # 创建一个名为lines的RDD,读取README.md文档
&amp;gt;&amp;gt;&amp;gt; lines.count() # 统计RDD中的元素个数
125
&amp;gt;&amp;gt;&amp;gt; lines.first() # 输出这个RDD中的第一个元素，也就是README.md的第一行
&amp;#39;# Apache Spark&amp;#39; 
&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;要退出任一 shell，按 Ctrl-D&lt;/p&gt;
&lt;h2 id="23-spark-ui"&gt;2.3 spark UI
&lt;/h2&gt;&lt;p&gt;日志的输出中可以由这个地址访问 Spark 用户界面，查看关于任务和集群的各种信息。&lt;/p&gt;
&lt;p&gt;&lt;img alt="PySpark 启动日志：Spark context Web UI 的访问地址" class="gallery-image" data-flex-basis="461px" data-flex-grow="192" height="780" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/assets/2026-10-14-18-05-34-image.png" srcset="https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/assets/2026-10-14-18-05-34-image_hu_64496763d0185e01.png 800w, https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/assets/2026-10-14-18-05-34-image.png 1499w" width="1499"&gt;&lt;/p&gt;
&lt;p&gt;点击日志中的 Web UI 地址，再进入 &lt;strong&gt;Stages&lt;/strong&gt; 页面，可以查看已执行的 Stage。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Spark Web UI 的 Stages 页面：两次操作对应的 Stage 0 与 Stage 1" class="gallery-image" data-flex-basis="434px" data-flex-grow="180" height="518" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/assets/2026-10-14-18-04-25-image.png" srcset="https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/assets/2026-10-14-18-04-25-image_hu_a52ddb2605637552.png 800w, https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/02spark-download-and-get-started/assets/2026-10-14-18-04-25-image.png 937w" width="937"&gt;&lt;/p&gt;
&lt;p&gt;图中展示了前面 &lt;code&gt;count()&lt;/code&gt; 和 &lt;code&gt;first()&lt;/code&gt; 两次操作产生的 Stage；仅凭 Stage 列表不能判断是否发生了宽依赖切分，具体依赖关系需要查看对应 Job 的 DAG。&lt;/p&gt;
&lt;h1 id="3spark核心概念简介"&gt;3、Spark核心概念简介
&lt;/h1&gt;&lt;p&gt;从上层来看，每个 Spark 应用都由一个驱动器程序（driver program）来发起集群上的各种并行操作。驱动器程序包含应用的 main 函数，并且定义了集群上的分布式数据集，还对这些分布式数据集应用了相关操作。在前面的例子里，实际的驱动器程序就是 Spark shell 本身。&lt;/p&gt;
&lt;p&gt;驱动器程序通过一个 SparkContext 对象来访问 Spark。这个对象代表对计算集群的一个连接。shell 启动时已经自动创建了一个 SparkContext 对象，是一个叫作 sc （SparkContext的缩写）的变量。我们可以通过例 2-3 中的方法尝试输出 sc 来查看它的类型。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; sc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;SparkContext master&lt;span style="color:#f92672"&gt;=&lt;/span&gt;local&lt;span style="color:#f92672"&gt;[&lt;/span&gt;*&lt;span style="color:#f92672"&gt;]&lt;/span&gt; appName&lt;span style="color:#f92672"&gt;=&lt;/span&gt;PySparkShell&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Spark 会自动将函数（比如&lt;code&gt;line.contains(&amp;quot;Python&amp;quot;)&lt;/code&gt;）发到各个执行器节点（executor）上。这样就可以在单一的驱动器程序中编程，并且让代码自动运行在多个节点上。&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;%%{init: {'themeVariables': {'fontSize': '22px', 'titleFontSize': '24px', 'nodeTextColor': '#1a1a1a'}, 'flowchart': {'nodeSpacing': 50, 'rankSpacing': 60}}}%%
flowchart LR

 subgraph Driver["Driver Program"]
 SC["SparkContext"]
 end

 subgraph Worker1["Worker Node 1"]
 E1["Executor"]
 T1["Task"]
 T2["Task"]
 end

 subgraph Worker2["Worker Node 2"]
 E2["Executor"]
 T3["Task"]
 T4["Task"]
 end

 SC --&gt; E1
 SC --&gt; E2

 E1 --&gt; T1
 E1 --&gt; T2
 E2 --&gt; T3
 E2 --&gt; T4

 Worker1 &lt;--&gt; Worker2&lt;/pre&gt;&lt;h1 id="4独立应用"&gt;4、独立应用
&lt;/h1&gt;&lt;p&gt;除了上面shell那样的交互式运行之外，Spark 也可以在 Java、Scala 或 Python 的独立程序中被连接使用。&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;对比项&lt;/th&gt;
 &lt;th&gt;Spark shell&lt;/th&gt;
 &lt;th&gt;独立 Spark 程序&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;使用方式&lt;/td&gt;
 &lt;td&gt;在交互式窗口中逐条输入代码&lt;/td&gt;
 &lt;td&gt;编写完整程序，再启动运行&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;常见形式&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;spark-shell&lt;/code&gt;、&lt;code&gt;pyspark&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Scala / Java / Python 应用&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;初始化&lt;/td&gt;
 &lt;td&gt;shell 通常预先创建好 &lt;br/&gt;&lt;code&gt;sc&lt;/code&gt;（SparkContext）、&lt;code&gt;spark&lt;/code&gt;（SparkSession）&lt;/td&gt;
 &lt;td&gt;程序负责创建或获取上下文、会话&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;常见用途&lt;/td&gt;
 &lt;td&gt;探索数据、验证逻辑、调试&lt;/td&gt;
 &lt;td&gt;批处理任务、生产数据管道&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;shell和独立程序的主要区别在于你需要自行初始化SparkContext，它们不是两套执行引擎，而是同一套 Spark 执行能力的两种使用方式。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;例如，同样执行 &lt;code&gt;filter → groupBy → count&lt;/code&gt;，无论代码来自 shell 还是独立程序，后续都要经过执行计划、Stage 划分、Task 调度和 Executor 执行。&lt;/p&gt;
&lt;h2 id="41-spark和hive"&gt;4.1 Spark和Hive
&lt;/h2&gt;&lt;p&gt;Spark shell和独立 Spark 程序这种划分方式，很容易联想到Hive的Hive CLI和程序化客户端，也是交互式运行和程序化运行，这里简单区分一下&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;划分维度&lt;/th&gt;
 &lt;th&gt;Spark 示例&lt;/th&gt;
 &lt;th&gt;Hive 示例&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;交互式使用&lt;/td&gt;
 &lt;td&gt;Spark shell、PySpark shell&lt;/td&gt;
 &lt;td&gt;Hive CLI、Beeline&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;程序化使用&lt;/td&gt;
 &lt;td&gt;Scala / Java / Python Spark 应用&lt;/td&gt;
 &lt;td&gt;Java JDBC 程序等&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;客户端连接服务端&lt;/td&gt;
 &lt;td&gt;Spark Connect 等另行讨论&lt;/td&gt;
 &lt;td&gt;Beeline / JDBC → HiveServer2&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;但针对程序化使用来说，&lt;strong&gt;不能简单理解成&lt;/strong&gt;：&lt;del&gt;Spark 独立程序和 Hive JDBC 程序一样，都是连接远端查询服务器、发送查询&lt;/del&gt;。二者的区别在于是否包含了负责组织计算的 Driver。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Hive JDBC 程序通常是“查询请求的发起者”，不包含计算&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1. 建立与 HiveServer2 的连接。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2. 将 SQL 文本提交给 HiveServer2（HiveServer2 及其执行链路负责“理解查询并组织计算）
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3. 等待执行引擎的查询执行。
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4. 读取返回的结果。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;Java 程序本身不负责把 SQL 编译成分布式执行计划，也不负责把计算拆成任务并调度到集群。&lt;/p&gt;
&lt;p&gt;具体计算可能交给 Tez、MapReduce 等执行引擎，取决于 Hive 的版本和配置。&lt;/p&gt;
&lt;p&gt;MRAppMaster 向 YARN 申请 Container，在获得的 Container 中启动 Map/Reduce Task 的执行进程。详情参考&lt;a class="link" href="https://eleanora-lyh.github.io/MyLearningNotes/posts/hive/11hivesql%E5%BA%95%E5%B1%82%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B/" target="_blank" rel="noopener"
 &gt;【十一】HiveSQL底层执行过程：SQL 编译、任务生成与 YARN 调度流程&lt;/a&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;传统 Spark 独立应用则包含“计算执行的组织者”，也就是 Driver&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="42-统计单词数"&gt;4.2 统计单词数
&lt;/h2&gt;&lt;p&gt;现在写 Spark 业务， Scala、PySpark、SQL 三者更常见。大致这样理解：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;场景&lt;/th&gt;
 &lt;th&gt;常见选择&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;学习 Spark 原理&lt;br/&gt;（Spark 底层框架、性能敏感组件、强类型 Dataset、函数式转换）&lt;/td&gt;
 &lt;td&gt;优先 Scala&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Spark SQL、日常ETL、数据分析&lt;/td&gt;
 &lt;td&gt;SQL、PySpark&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;公司已有 Java 服务体系&lt;/td&gt;
 &lt;td&gt;Java&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;快速开发、算法与数据科学、Notebook 交互分析&lt;/td&gt;
 &lt;td&gt;PySpark&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;

 &lt;blockquote&gt;
 &lt;p&gt;Scala/Java 两者调用的最终都是 Spark 的执行引擎，&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Scala 的 lambda、集合操作、模式匹配与 Spark API 更自然&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Java 更容易和传统 Java 工程、Spring 生态及现有公共库集成&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

 &lt;/blockquote&gt;
&lt;h3 id="前置检查"&gt;前置检查
&lt;/h3&gt;&lt;p&gt;检查 Java，确保 JAVA_HOME 正确指向 JDK。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 8
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 9
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\U&lt;/span&gt;sers&lt;span style="color:#ae81ff"&gt;\v&lt;/span&gt;-liuyuhang&amp;gt;java -version
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;java version &lt;span style="color:#e6db74"&gt;&amp;#34;1.8.0_503&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Java&lt;span style="color:#f92672"&gt;(&lt;/span&gt;TM&lt;span style="color:#f92672"&gt;)&lt;/span&gt; SE Runtime Environment &lt;span style="color:#f92672"&gt;(&lt;/span&gt;build 1.8.0_503-b01&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Java HotSpot&lt;span style="color:#f92672"&gt;(&lt;/span&gt;TM&lt;span style="color:#f92672"&gt;)&lt;/span&gt; 64-Bit Server VM &lt;span style="color:#f92672"&gt;(&lt;/span&gt;build 25.503-b01, mixed mode&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\U&lt;/span&gt;sers&lt;span style="color:#ae81ff"&gt;\v&lt;/span&gt;-liuyuhang&amp;gt;javac -version
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;javac 1.8.0_503
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\U&lt;/span&gt;sers&lt;span style="color:#ae81ff"&gt;\v&lt;/span&gt;-liuyuhang&amp;gt;echo %JAVA_HOME%
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\P&lt;/span&gt;rogram Files&lt;span style="color:#ae81ff"&gt;\J&lt;/span&gt;ava&lt;span style="color:#ae81ff"&gt;\j&lt;/span&gt;dk1.8.0_503&lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;p&gt;检查 Spark 和 Scala 版本&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 8
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 9
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;10
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;11
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;12
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;13
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\U&lt;/span&gt;sers&lt;span style="color:#ae81ff"&gt;\v&lt;/span&gt;-liuyuhang&amp;gt;spark-submit --version
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Welcome to
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ____ __
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; / __/__ ___ _____/ /__
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _&lt;span style="color:#ae81ff"&gt;\ \/&lt;/span&gt; _ &lt;span style="color:#ae81ff"&gt;\/&lt;/span&gt; _ &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;/ __/ &lt;span style="color:#960050;background-color:#1e0010"&gt;&amp;#39;&lt;/span&gt;_/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /___/ .__/&lt;span style="color:#ae81ff"&gt;\_&lt;/span&gt;,_/_/ /_/&lt;span style="color:#ae81ff"&gt;\_\ &lt;/span&gt; version 3.5.9
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /_/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Using Scala version 2.12.18, Java HotSpot&lt;span style="color:#f92672"&gt;(&lt;/span&gt;TM&lt;span style="color:#f92672"&gt;)&lt;/span&gt; 64-Bit Server VM, 1.8.0_503
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Branch HEAD
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Compiled by user holden on 2026-07-12T18:40:50Z
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Revision 7c14a3c28b141cc97a330c4d0f5d2a6da7267f85
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Url https://github.com/apache/spark
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Type --help &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; more information.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在这一堆里输出里可以找到：Spark version 3.5.9、Scala version 2.12.18、Java version 1.8.0_503&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;检查 sbt，没有安装的话，以管理员身份打开cmd执行 &lt;code&gt;choco install sbt&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C:&lt;span style="color:#ae81ff"&gt;\U&lt;/span&gt;sers&lt;span style="color:#ae81ff"&gt;\v&lt;/span&gt;-liuyuhang&amp;gt;sbt --version
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sbt version in this project: 1.9.9
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sbt script version: 1.9.9
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;sbt&lt;/code&gt; 是 Scala 项目常用的构建工具，作用类似：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Java 的 Maven 或 Gradle&lt;/li&gt;
&lt;li&gt;.NET 的 dotnet CLI&lt;/li&gt;
&lt;li&gt;Python 的部分依赖管理和打包工具&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="创建项目目录"&gt;创建项目目录
&lt;/h3&gt;&lt;p&gt;如果是windows系统则中执行&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir spark-line-count&lt;span style="color:#ae81ff"&gt;\p&lt;/span&gt;roject
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir spark-line-count&lt;span style="color:#ae81ff"&gt;\d&lt;/span&gt;ata
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir spark-line-count&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;rc&lt;span style="color:#ae81ff"&gt;\m&lt;/span&gt;ain&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;cala&lt;span style="color:#ae81ff"&gt;\c&lt;/span&gt;om&lt;span style="color:#ae81ff"&gt;\e&lt;/span&gt;xample
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;type nul &amp;gt; spark-line-count&lt;span style="color:#ae81ff"&gt;\b&lt;/span&gt;uild.sbt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;type nul &amp;gt; spark-line-count&lt;span style="color:#ae81ff"&gt;\p&lt;/span&gt;roject&lt;span style="color:#ae81ff"&gt;\b&lt;/span&gt;uild.properties
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;type nul &amp;gt; spark-line-count&lt;span style="color:#ae81ff"&gt;\d&lt;/span&gt;ata&lt;span style="color:#ae81ff"&gt;\i&lt;/span&gt;nput.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;type nul &amp;gt; spark-line-count&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;rc&lt;span style="color:#ae81ff"&gt;\m&lt;/span&gt;ain&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;cala&lt;span style="color:#ae81ff"&gt;\c&lt;/span&gt;om&lt;span style="color:#ae81ff"&gt;\e&lt;/span&gt;xample&lt;span style="color:#ae81ff"&gt;\L&lt;/span&gt;ineCountApp.scala
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;如果是linux系统则执行&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir -p spark-line-count/project
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir -p spark-line-count/data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir -p spark-line-count/src/main/scala/com/example
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd spark-line-count
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;完成后在 &lt;code&gt;spark-line-count\data\input.txt&lt;/code&gt; 加几行文字作为测试的输入&lt;/p&gt;
&lt;h3 id="buildsbt"&gt;build.sbt
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;build.sbt&lt;/code&gt; 就是 Spark 项目的「配置文件 + 依赖清单」。编译、打包时 sbt 启动会读取它，生成一个&lt;strong&gt;键值对 Map&lt;/strong&gt;​ 来描述整个项目。决定：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;配置项&lt;/th&gt;
 &lt;th&gt;作用&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;项目名称&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;version&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;项目版本号&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;scalaVersion&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;使用哪个 Scala 版本&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;libraryDependencies&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;项目依赖哪些 jar（Spark、测试库等）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;assembly&lt;/code&gt; 相关&lt;/td&gt;
 &lt;td&gt;控制打包行为（合并 / 排除冲突）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;类比理解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Java 的 &lt;code&gt;pom.xml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Python 的 &lt;code&gt;requirements.txt + setup.py&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;针对我当前的版本&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Spark 3.5.9
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Scala 2.12.18
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Java 1.8.0_503
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sbt launcher 1.9.9
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;填入&lt;code&gt;build.sbt&lt;/code&gt;得到&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 8
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 9
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;10
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;11
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;12
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;13
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ThisBuild&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; organization &lt;span style="color:#66d9ef"&gt;:&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;com.example&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ThisBuild&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; scalaVersion &lt;span style="color:#66d9ef"&gt;:&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;2.12.18&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ThisBuild&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; version &lt;span style="color:#66d9ef"&gt;:&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;1.0.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;lazy&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; root &lt;span style="color:#66d9ef"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;project in file&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;settings&lt;span style="color:#f92672"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name &lt;span style="color:#66d9ef"&gt;:&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;spark-line-count&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ========== 依赖 ==========
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; libraryDependencies &lt;span style="color:#f92672"&gt;++=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Seq&lt;/span&gt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;org.apache.spark&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%%&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;spark-core&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;3.5.9&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;provided&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;org.apache.spark&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%%&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;spark-sql&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;3.5.9&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;%&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;provided&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;% &amp;quot;provided&amp;quot;&lt;/code&gt; （适合最后使用&lt;code&gt;spark-submit&lt;/code&gt;运行程序）表示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;编译项目时需要 Spark 依赖&lt;/li&gt;
&lt;li&gt;打包 JAR 时不把完整 Spark 包塞进应用 JAR&lt;/li&gt;
&lt;li&gt;运行时由本机 Spark 或集群提供 Spark 依赖&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;创建目录的时候已经创建了&lt;strong&gt;sbt 版本配置文件&lt;/strong&gt; &lt;code&gt;spark-line-count\project\build.properties&lt;/code&gt;，现在将其版本填入&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sbt.version=1.9.9
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="编写-scala-spark-程序"&gt;编写 Scala Spark 程序
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 8
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 9
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;10
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;11
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;12
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;13
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;14
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;15
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;16
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;17
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;18
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;19
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;20
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;21
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;22
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;23
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;24
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;25
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;26
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;27
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;28
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;29
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;30
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;31
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;32
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;33
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;34
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;35
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;36
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;37
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;38
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;39
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;40
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;package&lt;/span&gt; com.example
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; org.apache.spark.sql.SparkSession
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;object&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LineCountApp&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; main&lt;span style="color:#f92672"&gt;(&lt;/span&gt;args&lt;span style="color:#66d9ef"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Array&lt;/span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;String&lt;/span&gt;&lt;span style="color:#f92672"&gt;])&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;Unit&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 如果调用程序时传入了文件路径，就使用第一个参数；
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 否则默认读取项目下的 data/input.txt。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; inputPath &lt;span style="color:#66d9ef"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;args&lt;span style="color:#f92672"&gt;.&lt;/span&gt;nonEmpty&lt;span style="color:#f92672"&gt;)&lt;/span&gt; args&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;data/input.txt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 创建 SparkSession 对象
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; spark &lt;span style="color:#66d9ef"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;SparkSession&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;builder&lt;span style="color:#f92672"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;appName&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Scala Spark Line Count&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#75715e"&gt;//设置 Spark 应用名称
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;master&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;local[*]&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&lt;span style="color:#75715e"&gt;// 设置本地运行模式为local，使用所有可用的 CPU 核心
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;getOrCreate&lt;span style="color:#f92672"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 读取输入文件的所有行，返回Dataset[String]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; lines &lt;span style="color:#66d9ef"&gt;=&lt;/span&gt; spark&lt;span style="color:#f92672"&gt;.&lt;/span&gt;read&lt;span style="color:#f92672"&gt;.&lt;/span&gt;textFile&lt;span style="color:#f92672"&gt;(&lt;/span&gt;inputPath&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 触发 Spark Job：计算输入文件的总行数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; totalLineCount &lt;span style="color:#66d9ef"&gt;=&lt;/span&gt; lines&lt;span style="color:#f92672"&gt;.&lt;/span&gt;count&lt;span style="color:#f92672"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 触发 Spark Job：计算非空行的总行数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; nonEmptyLineCount &lt;span style="color:#66d9ef"&gt;=&lt;/span&gt; lines
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;filter&lt;span style="color:#f92672"&gt;(&lt;/span&gt;line &lt;span style="color:#66d9ef"&gt;=&amp;gt;&lt;/span&gt; line&lt;span style="color:#f92672"&gt;.&lt;/span&gt;trim&lt;span style="color:#f92672"&gt;.&lt;/span&gt;nonEmpty&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&lt;span style="color:#75715e"&gt;// 去掉行首和行尾空白。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;.&lt;/span&gt;count&lt;span style="color:#f92672"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; println&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;s&amp;#34;Input file: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;$inputPath&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; println&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;s&amp;#34;Total line count: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;$totalLineCount&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; println&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#e6db74"&gt;s&amp;#34;Non-empty line count: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;$nonEmptyLineCount&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;finally&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; spark&lt;span style="color:#f92672"&gt;.&lt;/span&gt;stop&lt;span style="color:#f92672"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="sbt-编译打包"&gt;sbt 编译+打包
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;先清理旧产物，再重新把 Scala 源代码编译成 JVM 可以执行的字节码(JAR)，但不会运行程序。&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sbt clean package
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;编译时会发生什么？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;读取构建配置&lt;/strong&gt;&lt;br&gt;
sbt 根据 &lt;code&gt;build.properties&lt;/code&gt; 选择 sbt 版本，再读取 &lt;code&gt;build.sbt&lt;/code&gt; 中的 Scala 版本、依赖等配置。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;准备编译器和依赖&lt;/strong&gt;&lt;br&gt;
首次编译时，可能需要下载 Scala 编译器和声明的依赖；已缓存的通常会复用。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;检查并编译源码&lt;/strong&gt;&lt;br&gt;
例如检查 &lt;code&gt;LineCountApp.scala&lt;/code&gt; 中的语法、类型，以及引用的类是否存在。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;生成 &lt;code&gt;.class&lt;/code&gt; 文件&lt;/strong&gt;&lt;br&gt;
编译成功后，通常输出到：&lt;code&gt;target\scala-&amp;lt;Scala二进制版本&amp;gt;\classes\&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;编译成功会输出Compilation completed如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[info] Compilation completed in 21.912s.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[success] Total time: 87 s (01:27), completed 2026-10-16 16:44:51
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;同时会生成文件&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;target\scala-2.12\spark-line-count_2.12-1.0.0.jar
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;后缀的&lt;code&gt;2.12-1.0.0.jar&lt;/code&gt; 分别表示scala-2.12、Scala 二进制版本1.0.0（来自&lt;code&gt;ThisBuild / version := &amp;quot;1.0.0&amp;quot;&lt;/code&gt;）&lt;/p&gt;
&lt;h3 id="spark-submit-运行"&gt;spark-submit 运行
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;启动已经打包好的 Spark 应用&lt;/strong&gt;（通常流程如下，所以必须先编译）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Scala 源码 → sbt 打包成 JAR → spark-submit 启动 → Spark 读取数据、执行计算
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;运行&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;spark-submit &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --class com.example.LineCountApp &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --master &lt;span style="color:#e6db74"&gt;&amp;#34;local[*]&amp;#34;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; target&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;cala-2.12&lt;span style="color:#ae81ff"&gt;\s&lt;/span&gt;park-line-count_2.12-1.0.0.jar &lt;span style="color:#e6db74"&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data&lt;span style="color:#ae81ff"&gt;\i&lt;/span&gt;nput.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;参数含义：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;参数&lt;/th&gt;
 &lt;th&gt;作用&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--class com.example.LineCountApp&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;指定包含 &lt;code&gt;main&lt;/code&gt; 方法的入口&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;--master &amp;quot;local[*]&amp;quot;&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;在本机运行，使用所有可用 CPU 核心&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;JAR 路径&lt;/td&gt;
 &lt;td&gt;要运行的应用包&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;JAR 后面的参数&lt;/td&gt;
 &lt;td&gt;传给应用的 &lt;code&gt;main(args)&lt;/code&gt;，这里是输入文件路径&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Spark 的选项放在 JAR 前面，应用自己的参数放在 JAR 后面。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;执行成功的话会看到有输出如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Input file: data/input.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Total line count: 6
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Non-empty line count: 5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="过程总结"&gt;过程总结
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 5
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 6
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 7
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 8
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt; 9
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;10
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;11
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;12
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;13
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;14
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;15
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;16
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;17
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;18
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sbt package
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;生成应用 JAR
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;spark-submit 启动 Driver
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Driver 执行 main()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;创建 SparkSession / SparkContext
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;textFile() 构建读取计划
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;count() 触发 Spark Job
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Task 读取文件分区并计数
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Driver 汇总并输出行数
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;spark.stop() 释放资源，程序退出
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description></item><item><title>【一】Spark 是什么：组件、应用场景与发展简史</title><link>https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/01spark%E6%98%AF%E4%BB%80%E4%B9%88/</link><pubDate>Tue, 13 Oct 2026 18:33:19 +0800</pubDate><guid>https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/01spark%E6%98%AF%E4%BB%80%E4%B9%88/</guid><description>&lt;p&gt;事先声明：内容大部分整理自书籍《Spark快速大数据分析 (图灵程序设计丛书) 》，部分总结和人话是原创，顺便提一嘴，书写的超级好理解，推荐大家读原文展开自己的思考！&lt;/p&gt;
&lt;h1 id="1spark的组成部分"&gt;1、Spark的组成部分
&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;定义&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Spark 是一个用来实现快速而通用的集群计算的平台。可以说它是一个大一统的软件栈，为各种不同应用场景专门设计了多个紧密集成的高级组件，如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Spark 组件与集群管理器" class="gallery-image" data-flex-basis="794px" data-flex-grow="331" height="346" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/01spark%E6%98%AF%E4%BB%80%E4%B9%88/assets/2026-10-14-10-38-12-image.png" srcset="https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/01spark%E6%98%AF%E4%BB%80%E4%B9%88/assets/2026-10-14-10-38-12-image_hu_13bab2a6886224fc.png 800w, https://eleanora-lyh.github.io/MyLearningNotes/posts/spark/01spark%E6%98%AF%E4%BB%80%E4%B9%88/assets/2026-10-14-10-38-12-image.png 1146w" width="1146"&gt;&lt;/p&gt;
&lt;p&gt;在速度方面，Spark 扩展了广泛使用的 MapReduce 计算模型，而且高效地支持更多计算模式，包括批处理、迭代算法、交互式查询、流处理。&lt;/p&gt;
&lt;h2 id="11-spark-core"&gt;1.1 Spark Core
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;park Core&lt;/strong&gt; 实现了 Spark 的基本功能，包含&lt;strong&gt;任务调度、内存管理、错误恢复、与存储系统交互&lt;/strong&gt;等模块。&lt;/p&gt;
&lt;p&gt;Spark Core 中还包含了对弹性分布式数据集（resilient distributed dataset，简称 RDD）的 API 定义&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;RDD 表示分布在多个计算节点上可以并行操作的元素集合，是 Spark 主要的编程抽象。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Spark Core 提供了创建和操作这些集合的多个 API。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="12-spark-sql"&gt;1.2 Spark SQL
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Spark SQL&lt;/strong&gt; 是 Spark 用来&lt;strong&gt;操作结构化数据&lt;/strong&gt;的程序包。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;通过 Spark SQL，我们可以使用 SQL 或者 Hive SQL（HQL）来查询数据。Spark SQL 支持多种数据源，比如 Hive 表、Parquet 以及 JSON 等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;除了为Spark 提供了一个 SQL 接口，Spark SQL 还支持开发者将 SQL 和传统的RDD 编程的数据操作方式相结合，不论是使用 Python、Java 还是 Scala，开发者都可以在单个的应用中同时使用 SQL 和复杂的数据分析。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

 &lt;blockquote&gt;
 &lt;p&gt;通过与Spark 所提供的丰富的计算环境进行如此紧密的结合，Spark SQL 得以从其他开源数据仓库工具中脱颖而出。&lt;/p&gt;
&lt;p&gt;Spark SQL 是在 Spark 1.0 中被引入的。&lt;/p&gt;
&lt;p&gt;在 Spark SQL 之前，加州大学伯克利分校曾经尝试修改 Apache Hive 以使其运行在 Spark 上，当时的项目叫作 Shark。现在，由于 Spark SQL 与 Spark 引擎和 API 的结合更紧密，Shark 已经被 Spark SQL 所取代&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="13-spark-streaming"&gt;1.3 Spark Streaming
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Spark Streaming&lt;/strong&gt; 是 Spark 提供的对&lt;strong&gt;实时数据进行流式计算&lt;/strong&gt;的组件。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;比如生产环境中的网页服务器日志，或是网络服务中用户提交的状态更新组成的消息队列，都是数据流。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Spark Streaming 提供了用来操作数据流的 API，并且与 Spark Core 中的 RDD API 高度对应。这样一来，程序员编写应用时的学习门槛就得以降低，不论是操作内存或硬盘中的数据，还是操作实时数据流，程序员都更能应对自如。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从底层设计来看，Spark Streaming 支持与Spark Core 同级别的容错性、吞吐量以及可伸缩性。&lt;/p&gt;
&lt;h2 id="14-mllib"&gt;1.4 MLlib
&lt;/h2&gt;&lt;p&gt;Spark 中还包含一个提供常见的机器学习（ML）功能的程序库，叫作MLlib。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;MLlib&lt;/strong&gt; 提供了很多种&lt;strong&gt;机器学习算法&lt;/strong&gt;，包括分类、回归、聚类、协同过滤等，还提供了模型评估、数据导入等额外的支持功能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MLlib 还提供了一些更底层的机器学习原语，包括一个通用的梯度下降优化算法。所有这些方法都被设计为可以在集群上轻松伸缩的架构。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="15-graphx"&gt;1.5 GraphX
&lt;/h2&gt;&lt;p&gt;GraphX 是用来操作图（比如社交网络的朋友关系图）的程序库，可以进行并行的图计算。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;与 Spark Streaming 和 Spark SQL 类似，GraphX 也扩展了Spark 的 RDD API，能用来创建一个顶点和边都包含任意属性的有向图。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;GraphX 还支持针对图的各种操作（比如进行图分割的 subgraph 和操作所有顶点的 mapVertices），以及一些常用图算法（比如 PageRank 和三角计数）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="16-集群管理器"&gt;1.6 集群管理器
&lt;/h2&gt;&lt;p&gt;就底层而言，Spark 设计为可以高效地在一个计算节点到数千个计算节点之间伸缩计算。&lt;/p&gt;
&lt;p&gt;为了实现这样的要求，同时获得最大灵活性，Spark 支持在各种集群管理器（cluster manager）上运行，包括 Hadoop YARN、Apache Mesos，以及 Spark 自带的一个简易调度器，叫作独立调度器。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如果要在没有预装任何集群管理器的机器上安装 Spark，那么 Spark 自带的独立调度器可以让你轻松入门；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;而如果已经有了一个装有 Hadoop YARN 或 Mesos 的集群，通过 Spark 对这些集群管理器的支持，你的应用也同样能运行在这些集群上。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="2spark-的用途"&gt;2、Spark 的用途
&lt;/h1&gt;&lt;p&gt;我概括一下，可以分成两类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;预处理机器学习/深度学习的数据集，执行计算任务 ——&amp;gt; AI 研发岗算法工程师&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;优化后端代码以及数据库的大量数据的计算 ——&amp;gt; 大数据开发工程师/数据仓库工程师&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="21-数据科学任务"&gt;2.1 数据科学任务
&lt;/h2&gt;&lt;p&gt;数据科学是过去几年里出现的新学科，关注的是数据分析领域。&lt;/p&gt;
&lt;p&gt;数据科学家（data scientist）就是主要负责分析数据并建模的人，他们具备 SQL、统计、预测建模（机器学习）等方面的经验，以及一定的使用 Python、Matlab 或 R 语言进行编程的能力。例如，在数据科学家完成初始的调研之后，我们可能最终会得到一个生产环境中的推荐系统，可以整合在网页应用中，为用户提供产品推荐。&lt;/p&gt;
&lt;p&gt;数据科学家在使用 Python 或 Pandas 等传统数据分析工具时所能处理的数据集/训练模型始终受限于单机（GPU只存在一台电脑上），而有了 Spark，再加上购买的云计算资源就能处理更大数据规模的问题。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;上面是搬运了英文书籍的原文，按我们中国话来说这个岗位对应的人，在大学读计算机视觉/自然语言的研究生毕业找到的AI研发岗工作啦
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="22-数据处理应用"&gt;2.2 数据处理应用
&lt;/h2&gt;&lt;p&gt;开发者一般有基本的软件工程概念，比如封装、接口设计以及面向对象的编程思想，他们通常有计算机专业的背景，并且能使用工程技术来设计和搭建软件系统，以实现业务用例。&lt;/p&gt;
&lt;p&gt;Spark 已经为工程师提供了足够的接口来快速实现常见的任务，以及对应用进行监视、审查和性能调优。其 API 模块化的特性（基于传递分布式的对象集）使得利用程序库进行开发以及本地测试大大简化。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;上面是搬运了英文书籍的原文，简单来说就是数据量太大啦，即使充分利用 关系型数据库全部性能 + 代码的多线程计算 还是很慢的任务（因为就算是把代码部署到网页服务器上，实际可利用的也是单机的CPU计算资源），此时就要考虑将传统数据库升级为数据仓库，同时将数据的处理逻辑从代码迁移到数仓任务中，利用分布式计算框架即Spark（通常可以利用&amp;gt;=3台计算机资源）来加速计算过程
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h1 id="3spark-简史"&gt;3、Spark 简史
&lt;/h1&gt;&lt;p&gt;Spark 是于 2009 年作为一个研究项目在加州大学伯克利分校 RAD 实验室（AMPLab 的前身）诞生。&lt;/p&gt;
&lt;p&gt;实验室中的一些研究人员曾经用过 Hadoop MapReduce。他们发现MapReduce 在迭代计算和交互计算的任务上表现得效率低下。因此，Spark从一开始就是为交互式查询和迭代算法设计的，同时还支持内存式存储和高效的容错机制。&lt;/p&gt;
&lt;p&gt;2011 年，AMPLab 开始基于 Spark 开发更高层的组件，比如 Shark（Spark上的 Hive）1 和 Spark Streaming。（Shark 已经被 Spark SQL 所取代）&lt;/p&gt;
&lt;p&gt;Spark 最早在 2010 年 3 月开源，并且在 2013 年 6 月交给了 Apache 基金会，现在已经成了 Apache 开源基金会的顶级项目。&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;阶段&lt;/th&gt;
 &lt;th&gt;年份&lt;/th&gt;
 &lt;th&gt;版本&lt;/th&gt;
 &lt;th&gt;一句话&lt;/th&gt;
 &lt;th&gt;解决了什么&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;① 起步&lt;/td&gt;
 &lt;td&gt;2009–2012&lt;/td&gt;
 &lt;td&gt;闭源&lt;/td&gt;
 &lt;td&gt;用&lt;strong&gt;内存 + DAG&lt;/strong&gt;​ 替代 MapReduce 的反复落盘&lt;/td&gt;
 &lt;td&gt;迭代计算（如机器学习）太慢&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;② 统一&lt;/td&gt;
 &lt;td&gt;2014&lt;/td&gt;
 &lt;td&gt;1.0&lt;/td&gt;
 &lt;td&gt;一套引擎跑批、流、SQL、ML、图&lt;/td&gt;
 &lt;td&gt;以前要学好几个框架&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;③ 声明式&lt;/td&gt;
 &lt;td&gt;2015–2016&lt;/td&gt;
 &lt;td&gt;1.x→2.0&lt;/td&gt;
 &lt;td&gt;RDD → DataFrame/SQL，优化器接管&lt;/td&gt;
 &lt;td&gt;用户手写代码，引擎没法优化&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;④ 榨干 CPU&lt;/td&gt;
 &lt;td&gt;2015–2018&lt;/td&gt;
 &lt;td&gt;2.x&lt;/td&gt;
 &lt;td&gt;Tungsten + 代码生成&lt;/td&gt;
 &lt;td&gt;JVM 对象、GC 成了新瓶颈&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;⑤ 自适应&lt;/td&gt;
 &lt;td&gt;2020&lt;/td&gt;
 &lt;td&gt;3.0&lt;/td&gt;
 &lt;td&gt;AQE、动态分区裁剪&lt;/td&gt;
 &lt;td&gt;静态计划猜不准真实数据&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;⑥ 服务化&lt;/td&gt;
 &lt;td&gt;2023–2025&lt;/td&gt;
 &lt;td&gt;3.4→4.0&lt;/td&gt;
 &lt;td&gt;Spark Connect、云原生、ANSI SQL&lt;/td&gt;
 &lt;td&gt;从&amp;quot;本地库&amp;quot;变成&amp;quot;远程数据服务&amp;quot;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;4、Spark&lt;/p&gt;
&lt;p&gt;分布式计算有个绕不开的现实问题：&lt;strong&gt;一个算子算完的结果，下游算子怎么拿到？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;两个算子可能跑在不同的机器上，上游某个 task 的输出，下游某个 task 要作为输入来读。解决办法是找个地方&amp;quot;寄存&amp;quot;一下，再网络传输。看一下在Hadoop MapReduce和Spark中都是怎么实现的&lt;/p&gt;
&lt;h1 id="4详解-spark-的出现契机"&gt;4、详解 Spark 的出现契机
&lt;/h1&gt;
 &lt;blockquote&gt;
 &lt;p&gt;实验室中的一些研究人员曾经用过 Hadoop MapReduce。他们发现MapReduce 在迭代计算和交互计算的任务上表现得效率低下。因此，Spark从一开始就是为交互式查询和迭代算法设计的，同时还支持内存式存储和高效的容错机制。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;这里我想展开说一下，众所周知新技术的出现一般是因为现有的技术无法解决或纯因为人懒，Spark的出现正是如此，因为随着时代发展数据量的增大，MapReduce处理数据的效率变低，达到人们的忍耐上限啦，所以必须重新设计一个计算模型来解决喽！&lt;/p&gt;
&lt;p&gt;先用一个简单的例子看看一样的数据处理逻辑交给MapReduce和Spark会发生什么？&lt;/p&gt;
&lt;p&gt;已知一段数据的处理执行逻辑如下&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR
 1.读取数据 --&gt; 2.初步过滤--&gt; 3.字段转换--&gt; 4.按Key聚合--&gt; 5.再过滤--&gt; 6.写出结果&lt;/pre&gt;&lt;h2 id="41hadoop-mapreduce"&gt;4.1、Hadoop MapReduce
&lt;/h2&gt;&lt;p&gt;Hadoop MapReduce采用的就是最朴素的做法：&lt;strong&gt;每一步的上游算完，都把结果写进 HDFS，下游再从 HDFS 读出来&lt;/strong&gt;，同时执行模型相对固定，阶段之间必须将中间结果落盘&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR
 A1[1.读取 HDFS]--&gt;B1[2.Map计算]--&gt;C1["3.写中间结果到磁盘(HDFS)"]--&gt;D1[4.Reduce通过网络拉取中间结果]--&gt;E1[5.Reduce计算]--&gt;F1["6.写结果到磁盘(HDFS)"]
 classDef default font-size:18px;&lt;/pre&gt;&lt;p&gt;复杂逻辑通常要拆成多个 MapReduce Job（每个 Job 之间通常以文件系统作为边界）&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;---
config:
 theme: default
 layout: elk
 elk:
 spacing: 
 nodeSubgraph: 5 # 节点与子图间距
---
flowchart LR
 subgraph Job1
 direction LR
 A1[1.读取 HDFS]--&gt;B1[2.Map计算]--&gt;C1["3.写中间结果到磁盘(HDFS)"]--&gt;D1[4.Reduce通过网络拉取中间结果]--&gt;E1[5.Reduce计算]--&gt;F1["6.写结果到磁盘(HDFS)"]
 end
 subgraph Job2
 direction LR
 A2[1.读取 HDFS]--&gt;B2[2.Map计算]--&gt;C2["3.写中间结果到磁盘(HDFS)"]--&gt;D2[4.Reduce通过网络拉取中间结果]--&gt;E2[5.Reduce计算]--&gt;F2["6.写结果到磁盘(HDFS)"]
 end
 subgraph Job3
 direction LR
 A3[1.读取 HDFS]--&gt;B3[2.Map计算]--&gt;C3["3.写中间结果到磁盘(HDFS)"]--&gt;D3[4.Reduce通过网络拉取中间结果]--&gt;E3[5.Reduce计算]--&gt;F3["6.写结果到磁盘(HDFS)"]
 end

 Task --&gt; Job1
 Task --&gt; Job2
 Task --&gt; Job3

 classDef default font-size:18px;&lt;/pre&gt;&lt;p&gt;落盘(存入HDFS)看着无害，其实开销巨大：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;磁盘 I/O&lt;/strong&gt;：数据从内存刷到磁盘&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;序列化&lt;/strong&gt;：对象要变成字节流才能写&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HDFS 多副本&lt;/strong&gt;：写一份数据默认要复制 3 份到不同机器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一次落盘就是一次&amp;quot;内存 → 磁盘 → 网络 → 磁盘 → 内存&amp;quot;的往返。如果你的 pipeline 有 10 个算子，每一步都落盘，那就是 10 次这样的往返。&lt;strong&gt;这是 MapReduce 慢的根本原因之一&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="42spark"&gt;4.2、Spark
&lt;/h2&gt;&lt;p&gt;Spark 通过&lt;strong&gt;窄依赖整合&lt;/strong&gt; Pipeline，再按 Shuffle 边界&lt;strong&gt;切分&lt;/strong&gt;为 Stage，最终&lt;strong&gt;组合&lt;/strong&gt;成全局 DAG。
——Spark 的优化逻辑分三层递进：&lt;strong&gt;先整合，再切分，最后编排&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="窄依赖pipeline-得以成立的前提"&gt;窄依赖：Pipeline 得以成立的前提
&lt;/h3&gt;&lt;p&gt;Spark 为什么能&amp;quot;不落盘&amp;quot;？——关键在&lt;strong&gt;窄依赖（Narrow Dependency）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;依赖说的是：&lt;strong&gt;下游的每一个分区，只由上游的少数几个分区计算而来&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;窄依赖&lt;/strong&gt;（如 &lt;code&gt;map&lt;/code&gt;、&lt;code&gt;filter&lt;/code&gt;、&lt;code&gt;select&lt;/code&gt;）：下游一个分区只依赖上游&lt;strong&gt;一个&lt;/strong&gt;分区。既然数据没重分布，上下游算子可以安排给&lt;strong&gt;同一台机器、同一个 task 串行执行&lt;/strong&gt;——中间结果不用存，直接拿对象在内存里传下去，这就是&lt;strong&gt;流水线（pipeline）&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;map → filter → map 全在一个 task 里一气呵成，中间不落盘
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;宽依赖&lt;/strong&gt;（如 &lt;code&gt;groupByKey&lt;/code&gt;、&lt;code&gt;join&lt;/code&gt;）：下游一个分区依赖上游&lt;strong&gt;很多&lt;/strong&gt;分区，必须&lt;strong&gt;Shuffle&lt;/strong&gt;——数据要按 key 重新分发到不同机器。这一步不可避免要落盘 + 网络传输。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以准确说法是：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;窄依赖之间不落盘，可以流水线；遇到宽依赖（Shuffle）才必须落盘。&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="pipeline--stage--dag-来优化执行过程"&gt;Pipeline / Stage / DAG 来优化执行过程
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;概念&lt;/th&gt;
 &lt;th&gt;是什么&lt;/th&gt;
 &lt;th&gt;关注的问题&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;算子&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;map&lt;/code&gt;、&lt;code&gt;filter&lt;/code&gt;、&lt;code&gt;reduceByKey&lt;/code&gt; 等数据处理操作&lt;/td&gt;
 &lt;td&gt;数据怎么变换？&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;DAG&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;数据集及其依赖构成的有向无环图&lt;/td&gt;
 &lt;td&gt;整体计算依赖是什么？&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Stage&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;由 Shuffle 依赖划分出的调度阶段&lt;/td&gt;
 &lt;td&gt;哪些计算可以在一个阶段完成？&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Pipeline&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;同一个 Task 内串联执行的操作链&lt;/td&gt;
 &lt;td&gt;一份数据如何连续经过多个操作？&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Task&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;一个 Stage 中针对一个分区的执行任务&lt;/td&gt;
 &lt;td&gt;谁来计算这一份数据？&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;Executor&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;工作节点上运行的 Spark 进程，提供执行线程、缓存等&lt;/td&gt;
 &lt;td&gt;Task 实际在哪里运行？&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;它们不是一条简单的上下级链条，而是“计算逻辑”和“执行调度”两个视角：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;计算逻辑&lt;/strong&gt;：算子组成 DAG；Shuffle 边界把 DAG 切成 Stage；Stage 内可流水线执行的操作组成 Pipeline。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行调度&lt;/strong&gt;：Stage 按分区生成 Task；Task 被分配给 Executor 执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用一个 RDD 按 Key 聚合的例子，把逻辑依赖、Shuffle 边界和 Task 在 Executor 上的执行过程串起来&lt;/p&gt;
&lt;h4 id="1形成逻辑dag"&gt;1、形成逻辑DAG
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;Spark 记录用户写下的一系列转换关系，形成逻辑上的 DAG，不立即计算：&lt;/strong&gt;&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;flowchart LR
 1.读取 --&gt; 2.filter--&gt; 3.map--&gt; A["4.groupByKey / reduceByKey"]--&gt; 5.filter--&gt; 6.write&lt;/pre&gt;&lt;p&gt;这张图里的 6 个方块，其实是 6 个 RDD 对象 + 它们之间的关系（依赖关系），串成图就是DAG。 filter、map、groupByKey 这些都是 transformation（变换），它们的执行结果是一个新的 RDD 引用。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;"&gt;&lt;tr&gt;&lt;td style="vertical-align:top;padding:0;margin:0;border:0;"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;1
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;2
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;3
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;4
&lt;/span&gt;&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%"&gt;
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rdd0 = sc.textFile(&amp;#34;...&amp;#34;) // RDD 0：读取
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rdd1 = rdd0.filter(...) // RDD 1：filter 变换
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rdd2 = rdd1.map(...) // RDD 2：map 变换
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rdd3 = rdd2.groupByKey(...) // RDD 3：shuffle 变换
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rdd4 = rdd3.filter(...) // RDD 4：filter 变换
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="2切分dag得到-stages"&gt;2、切分DAG得到 stages
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;collect() 触发 Job，DAG 按宽依赖（Shuffle 边界）切分得到 stages&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Driver 上的 DAGScheduler 沿 RDD 依赖分析计算：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-textile" data-lang="textile"&gt;[ Stage 0: map → filter → map ] ← 全是窄依赖，这一条就是一个pipeline可以串联执行
 ↓ Shuffle（落盘 + 网络）
[ Stage 1: groupByKey → map ] ← 把相同 Key 的数据重新组织到同一个分区，形成 Shuffle 依赖
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;注意：&lt;code&gt;reduceByKey&lt;/code&gt; 不是全部只在下游 Stage 执行。&lt;/strong&gt; 它的实现跨越边界：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;上游Stage 0：分区内预聚合、Shuffle Write。&lt;/li&gt;
&lt;li&gt;下游Stage 1：Shuffle Read、合并聚合。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="3pipeline-化--task-化-后执行stages的计算"&gt;3、Pipeline 化 + Task 化 后执行Stages的计算
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;切分之后，执行引擎再对每个 Stage 做两件事：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pipeline 化&lt;/strong&gt;：把 Stage 内的窄依赖算子合成一条管道，消除中间物化；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Task 化&lt;/strong&gt;：把这条管道切成 N 个并行 Task，分发到集群中的 Executor 上运行。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-textile" data-lang="textile"&gt;SQL / DataFrame
 │
 ▼
Catalyst 逻辑计划 ──(优化)──▶ 物理计划
 │
 ▼
按 Shuffle 边界切分为 Stage
 │
 ▼
每个 Stage 内部 Pipeline 化（窄依赖算子合成一条管道）
 │
 ▼
每条 Pipeline 切成 N 个 Task，分发到集群
 │
 ▼
同一台机器上的多个 Task 共享 Executor 的 JVM / 内存池
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;从上一步划分的 [ Stage 0: map → filter → map ] 可知分区内将执行预聚合、Shuffle Write。假设集群有 3 个输入分区，那么实际执行的 Pipeline（map → filter → map）的Task就有3 个。&lt;/p&gt;
&lt;p&gt;这个3个Task在通过Excutor获取计算资源后会按照上一步设定的pipeline并行处理各自分区的数据。&lt;/p&gt;
&lt;p&gt;例如 Task 0 在 Executor 1 上处理输入分区 0：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;读取一条订单。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;filter&lt;/code&gt; 判断是否有效。&lt;/li&gt;
&lt;li&gt;有效订单通过 &lt;code&gt;map&lt;/code&gt; 转成 &lt;code&gt;(userId, amount)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;在本分区内合并相同用户的金额。&lt;/li&gt;
&lt;li&gt;按目标分区写出 Shuffle 数据。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;由于Stage 0聚合数据后由于宽依赖必须将相同Key的数据合并到一起，所以下一步就是 Shuffle（落盘 + 网络）&lt;/p&gt;
&lt;p&gt;假设3个输入分区中都有用户 &lt;code&gt;&amp;quot;A&amp;quot;&lt;/code&gt; 的订单：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输入分区 0：&lt;code&gt;(&amp;quot;A&amp;quot;, 10)&lt;/code&gt;、&lt;code&gt;(&amp;quot;A&amp;quot;, 20)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;输入分区 1：&lt;code&gt;(&amp;quot;A&amp;quot;, 5)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;输入分区 2：&lt;code&gt;(&amp;quot;A&amp;quot;, 15)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上游预聚合后，分别得到 &lt;code&gt;&amp;quot;A&amp;quot;&lt;/code&gt; 的金额 &lt;code&gt;30&lt;/code&gt;、&lt;code&gt;5&lt;/code&gt;、&lt;code&gt;15&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;Partitioner 保证这些 &lt;code&gt;&amp;quot;A&amp;quot;&lt;/code&gt; 的输出属于&lt;strong&gt;同一个目标分区&lt;/strong&gt;。负责这个目标分区的下游 Task 会读取三个上游 Task 的相关输出。&lt;/p&gt;
&lt;p&gt;这里就看出 Shuffle 不是一个集中处理数据的单独进程，而是&lt;strong&gt;上游写出、下游拉取的分布式数据交换过程&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Stage 0 结束后才会执行 [ Stage 1: groupByKey → map ] Shuffle Read、合并聚合。假设集群有2 个输出分区，那么实际执行的 Pipeline（groupByKey → map）的Task就有2 个。&lt;/p&gt;
&lt;p&gt;负责 &lt;code&gt;&amp;quot;A&amp;quot;&lt;/code&gt; 所在分区的 Task：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;读取对应的 Shuffle 数据。&lt;/li&gt;
&lt;li&gt;合并金额，得到 &lt;code&gt;(&amp;quot;A&amp;quot;, 50)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;执行后面的 &lt;code&gt;map&lt;/code&gt;，生成 &lt;code&gt;&amp;quot;A,50&amp;quot;&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;将结果返回 Driver。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;Executor 提供资源，不决定计算边界，它可以：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同时执行多个 Task，具体取决于可用核数及 Task 资源需求。&lt;/li&gt;
&lt;li&gt;先执行 Stage 0 的 Task，再执行 Stage 1 的 Task。&lt;/li&gt;
&lt;li&gt;执行不同 Stage、不同 Job 的任务。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以，&lt;strong&gt;Stage 是计算的划分，Executor 是运行计算的资源，两者不是一一对应关系&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id="4步骤总结图"&gt;4、步骤总结图
&lt;/h4&gt;&lt;p&gt;把前面的所有步骤看明白后，就很好理解下面的完整执行图啦！&lt;/p&gt;
&lt;pre class="mermaid" style="visibility:hidden"&gt;%%{init: {'themeVariables': {'fontSize': '20px', 'titleFontSize': '22px', 'nodeTextColor': '#1a1a1a'}, 'flowchart': {'nodeSpacing': 50, 'rankSpacing': 60}}}%%

flowchart TB

 %% ============ Driver：触发与规划 ============
 subgraph DRIVER["Driver：Action 触发 Job，DAGScheduler 按 Shuffle 依赖划分 Stage"]
 ACTION["collect()：触发执行"]
 PLAN["RDD 依赖 DAG&lt;br/&gt;输入 → filter → map → reduceByKey → map"]
 ACTION --&gt; PLAN
 end

 %% ============ Job：两个 Stage ============
 subgraph JOB["Job：本例包含两个 Stage"]
 direction TB

 subgraph S0["Stage 0：ShuffleMapStage｜3 个输入分区 → 3 个 Task"]
 direction LR
 T00["Task 0：输入分区 0&lt;br/&gt;读取 → filter → map&lt;br/&gt;→ 分区内预聚合 → Shuffle Write"]
 T01["Task 1：输入分区 1&lt;br/&gt;读取 → filter → map&lt;br/&gt;→ 分区内预聚合 → Shuffle Write"]
 T02["Task 2：输入分区 2&lt;br/&gt;读取 → filter → map&lt;br/&gt;→ 分区内预聚合 → Shuffle Write"]
 end

 SH["Shuffle 边界：按 userId 重新分区&lt;br/&gt;每个上游 Task 的输出按目标分区分桶&lt;br/&gt;下游 Task 拉取属于自己的桶"]

 subgraph S1["Stage 1：ResultStage｜2 个结果分区 → 2 个 Task"]
 direction LR
 T10["Task 0：结果分区 0&lt;br/&gt;Shuffle Read → 合并聚合&lt;br/&gt;→ map → 返回结果"]
 T11["Task 1：结果分区 1&lt;br/&gt;Shuffle Read → 合并聚合&lt;br/&gt;→ map → 返回结果"]
 end

 T00 --&gt; SH
 T01 --&gt; SH
 T02 --&gt; SH
 SH --&gt; T10
 SH --&gt; T11
 end

 PLAN --&gt; S0

 %% ============ 运行资源 ============
 subgraph RUNTIME["运行资源：Executor 执行 Task，不固定属于某个 Stage"]
 direction LR
 E1["Executor 1&lt;br/&gt;线程池执行 Task&lt;br/&gt;管理缓存等"]
 E2["Executor 2&lt;br/&gt;线程池执行 Task&lt;br/&gt;管理缓存等"]
 end

 T00 -.-&gt;|"示例分配"| E1
 T01 -.-&gt;|"示例分配"| E2
 T02 -.-&gt;|"示例分配"| E1
 T10 -.-&gt;|"示例分配"| E2
 T11 -.-&gt;|"示例分配"| E1

 %% ============ 结果回收 ============
 RESULT["Driver 收到 collect 的结果"]
 T10 --&gt; RESULT
 T11 --&gt; RESULT

 %% ============ 样式：分区着色 ============
 classDef driver fill:#EDE7F6,stroke:#7E57C2,stroke-width:2px,color:#311B92;
 classDef stage0 fill:#E0F2F1,stroke:#00897B,stroke-width:2px,color:#004D40;
 classDef stage1 fill:#FFF3E0,stroke:#F57C00,stroke-width:2px,color:#E65100;
 classDef shuffle fill:#FFEBEE,stroke:#E53935,stroke-width:3px,color:#B71C1C;
 classDef executor fill:#ECEFF1,stroke:#607D8B,stroke-width:2px,color:#263238;
 classDef result fill:#E3F2FD,stroke:#1565C0,stroke-width:3px,color:#0D47A1;

 class DRIVER,ACTION,PLAN driver;
 class S0,T00,T01,T02 stage0;
 class S1,T10,T11 stage1;
 class SH shuffle;
 class RUNTIME,E1,E2 executor;
 class RESULT result;&lt;/pre&gt;&lt;p&gt;总结： Spark 的优势通常来自多方面：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;DAG 执行模型&lt;/strong&gt;，不局限于固定的 Map → Reduce 两阶段。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;窄依赖可以流水线执行&lt;/strong&gt;，不必在每个算子之间落盘。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据可以 Cache/Persist&lt;/strong&gt;，适合多次复用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lazy Evaluation&lt;/strong&gt;，可以先记录转换关系，遇到 Action 再执行。&lt;/li&gt;
&lt;li&gt;DataFrame/SQL 还能通过优化器优化执行计划。&lt;/li&gt;
&lt;/ol&gt;</description></item></channel></rss>