Jekyll2024-01-03T06:46:49+00:00https://niocoder.com//feed.xml我的技术知识分享郑龙飞的个人博客郑龙飞什么?https://start.spring.io访问不了,本地搭建一个不就行了2021-01-06T00:00:00+00:002021-01-06T00:00:00+00:00https://niocoder.com//2021/01/06/%E6%90%AD%E5%BB%BA%E6%9C%AC%E5%9C%B0SpringInitializr<blockquote>
<p>无情不似多情苦,一寸还成千万缕。</p>
</blockquote>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/45.jpg" alt="" /></p>
<h3 id="前言">前言</h3>
<p><code class="language-plaintext highlighter-rouge">Spring Initializr</code>从本质上来说就是一个<code class="language-plaintext highlighter-rouge">Web</code>应用程序,它能为你生成<code class="language-plaintext highlighter-rouge">Spring Boot</code>项目结构。虽然不能生成应用程序代码,但它能为你提供一个基本的项目结构,以何种编程语言(<code class="language-plaintext highlighter-rouge">Java</code>,<code class="language-plaintext highlighter-rouge">Kotlin</code>,<code class="language-plaintext highlighter-rouge">Groovy</code>)构建的<code class="language-plaintext highlighter-rouge">Maven</code>或<code class="language-plaintext highlighter-rouge">Gradle</code>构建说明文件。你只需要写应用程序的代码就好了。</p>
<p><code class="language-plaintext highlighter-rouge">Spring Initializr</code> 有几种用法。</p>
<ol>
<li>通过<code class="language-plaintext highlighter-rouge">Web</code>界面使用。</li>
<li>通过<code class="language-plaintext highlighter-rouge">Spring Tool Suite</code>使用。</li>
<li>通过<code class="language-plaintext highlighter-rouge">IntelliJ IDEA</code>使用。</li>
<li>使用<code class="language-plaintext highlighter-rouge">Spring Boot CLI</code>使用。</li>
</ol>
<p>本例主要讲解<code class="language-plaintext highlighter-rouge">Web</code>界面和IntelliJ IDEA`的使用</p>
<h3 id="搭建本地startspringio">搭建本地<code class="language-plaintext highlighter-rouge">start.spring.io</code></h3>
<p>确保本地已安装<code class="language-plaintext highlighter-rouge">maven</code>环境变量且<code class="language-plaintext highlighter-rouge">settings.xml</code>已添加<code class="language-plaintext highlighter-rouge">aliyun</code>源</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><mirror></span>
<span class="nt"><id></span>nexus-aliyun<span class="nt"></id></span>
<span class="nt"><name></span>Nexus aliyun<span class="nt"></name></span>
<span class="nt"><url></span>http://maven.aliyun.com/nexus/content/groups/public/<span class="nt"></url></span>
<span class="nt"><mirrorOf></span>central<span class="nt"></mirrorOf></span>
<span class="nt"></mirror></span>
</code></pre></div></div>
<ol>
<li>下载源代码构建
<ol>
<li><code class="language-plaintext highlighter-rouge">git clone https://github.com/spring-io/start.spring.io.git</code></li>
<li><code class="language-plaintext highlighter-rouge">cd start.spring.io</code></li>
<li><code class="language-plaintext highlighter-rouge">mvn clean install -DskipTests</code> 时间略长,请耐心等待,会安装<code class="language-plaintext highlighter-rouge">node</code>和<code class="language-plaintext highlighter-rouge">yarn</code>依赖
<img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/46.png" alt="" /></li>
</ol>
</li>
<li>本地运行应用程序
<ol>
<li><code class="language-plaintext highlighter-rouge">cd start-site</code></li>
<li><code class="language-plaintext highlighter-rouge">mvn spring-boot:run</code></li>
<li>然后访问 <code class="language-plaintext highlighter-rouge">http://localhost:8080/</code>
<img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/48.png" alt="" /></li>
</ol>
</li>
</ol>
<h3 id="通过spring-initializr的web界面">通过<code class="language-plaintext highlighter-rouge">Spring Initializr</code>的<code class="language-plaintext highlighter-rouge">Web</code>界面</h3>
<p>要使用<code class="language-plaintext highlighter-rouge">Spring Initializr</code>,最直接的办法就是用浏览器打开<code class="language-plaintext highlighter-rouge">http://start.spring.io</code>,你应该能看到以下一个表单,由于上面我们已经在本地搭建好了<code class="language-plaintext highlighter-rouge">Spring Initializr</code>也可以直接访问<code class="language-plaintext highlighter-rouge">http://localhost:8080/</code> 查看以下表单
<img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/47.png" alt="" /></p>
<p>表单的左侧上方选项是,你想用<code class="language-plaintext highlighter-rouge">Maven</code>还是<code class="language-plaintext highlighter-rouge">Gradle</code>来构建项目,何种编程语言来编写代码,以及使用<code class="language-plaintext highlighter-rouge">Spring Boot</code>的哪个版本。程序默认生成<code class="language-plaintext highlighter-rouge">Maven</code>项目,并使用<code class="language-plaintext highlighter-rouge">Spring Boot</code>的最新版本(非里程碑和快照版本),但你也可以自由选择其他选项。</p>
<p>表单的左侧下方是,你指定项目的一些基本信息。最起码你要提供项目的<code class="language-plaintext highlighter-rouge">Group</code>、<code class="language-plaintext highlighter-rouge">Artifact</code>、项目名称、项目描述、报名、打包方式和依赖的<code class="language-plaintext highlighter-rouge">Java</code>版本。这些 信息是用来生成<code class="language-plaintext highlighter-rouge">Maven</code>的<code class="language-plaintext highlighter-rouge">pom.xml</code>文件(或者<code class="language-plaintext highlighter-rouge">Gradle</code>的<code class="language-plaintext highlighter-rouge">build.gradle</code>文件)的。</p>
<p>表单右侧要你指定项目依赖,最简单的方法就是在文本框里键入依赖的名称。随着你的输入会出现匹配依赖的列表,选中一个(或多个)依赖,选中的依赖就会加入项目。</p>
<p>填完表单,选好依赖,点击<code class="language-plaintext highlighter-rouge">Generate</code>按钮,<code class="language-plaintext highlighter-rouge">Spring Initializr</code>就会为你生成一个项目。 浏览器将会以<code class="language-plaintext highlighter-rouge">ZIP</code>文件的形式(文件名取决于<code class="language-plaintext highlighter-rouge">Artifact</code>字段的内容)把这个项目下载下来。根据你的选择,<code class="language-plaintext highlighter-rouge">ZIP</code>文件的内容也会略有不同。不管怎样,<code class="language-plaintext highlighter-rouge">ZIP</code>文件都会包含一个极其基础的项目,让你能着手使用<code class="language-plaintext highlighter-rouge">Spring Boot</code>开发应用程序。</p>
<p>解压项目目录如下:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>niocoder
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── niocoder
│ │ └── niocoder
│ │ └── NiocoderApplication.java
│ └── resources
│ ├── application.properties
│ ├── static
│ └── templates
└── test
└── java
└── com
└── niocoder
└── niocoder
└── NiocoderApplicationTests.java
</code></pre></div></div>
<p>如你所见,项目里基本没有代码,除了几个空目录外,还包含了如下几样东西。</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">pom.xml</code>: <code class="language-plaintext highlighter-rouge">Maven</code>构建文件说明</li>
<li><code class="language-plaintext highlighter-rouge">NiocoderApplication.java</code>: 一个带有<code class="language-plaintext highlighter-rouge">main()</code>方法的类,用于引导启动应用程序</li>
<li><code class="language-plaintext highlighter-rouge">NiocoderApplicationTests.java</code>: 一个空的<code class="language-plaintext highlighter-rouge">JUnit</code>测试类</li>
<li><code class="language-plaintext highlighter-rouge">application.properties</code>:一个空的<code class="language-plaintext highlighter-rouge">properties</code>文件,你可以根据需要添加配置属性</li>
</ul>
<p>在<code class="language-plaintext highlighter-rouge">Spring Boot</code>应用程序中,就连空目录都有自己的意义。<code class="language-plaintext highlighter-rouge">static</code>目录放置的是<code class="language-plaintext highlighter-rouge">Web</code>应用程序的 静态内容(<code class="language-plaintext highlighter-rouge">JavaScript</code>、样式表、图片,等等)。还有,稍后你将看到,用于呈现模型数据的模板 会放在<code class="language-plaintext highlighter-rouge">templates</code>目录里。</p>
<p>你很可能会把<code class="language-plaintext highlighter-rouge">Initializr</code>生成的项目导入<code class="language-plaintext highlighter-rouge">IDE</code>。</p>
<h3 id="在intellij-idea里创建spring-boot项目">在<code class="language-plaintext highlighter-rouge">IntelliJ IDEA</code>里创建<code class="language-plaintext highlighter-rouge">Spring Boot</code>项目</h3>
<p>要在<code class="language-plaintext highlighter-rouge">IntelliJ IDEA</code>里创建新的<code class="language-plaintext highlighter-rouge">Spring Boot</code>应用程序,在<code class="language-plaintext highlighter-rouge">File</code>菜单里选择<code class="language-plaintext highlighter-rouge">New</code> > <code class="language-plaintext highlighter-rouge">Project</code>。选择<code class="language-plaintext highlighter-rouge">customer</code> 输入<code class="language-plaintext highlighter-rouge">http://localhost:8080</code></p>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/49.png" alt="" /></p>
<p>点击<code class="language-plaintext highlighter-rouge">next</code></p>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/50.png" alt="" /></p>
<p><code class="language-plaintext highlighter-rouge">Spring Boo</code>t初始化向导的第二屏要求你提供项目的一些基本信息,比如项目名称、<code class="language-plaintext highlighter-rouge">Maven Group</code>和<code class="language-plaintext highlighter-rouge">Artifact</code>、<code class="language-plaintext highlighter-rouge">Java</code>版本,以及你是想用<code class="language-plaintext highlighter-rouge">Maven</code>还是<code class="language-plaintext highlighter-rouge">Gradle</code>来构建项目。描述好项目信息之后, 点击<code class="language-plaintext highlighter-rouge">Next</code>按钮就能看到第三屏了</p>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/51.png" alt="" /></p>
<p>第三屏就开始问你要往项目里添加什么依赖了。和之前一样,屏幕里的复选框和<code class="language-plaintext highlighter-rouge">Spring Boot</code>起步依赖是对应的。选完之后点击<code class="language-plaintext highlighter-rouge">Next</code>就到了向导的最后一屏,点击<code class="language-plaintext highlighter-rouge">finish</code>按钮,就能在<code class="language-plaintext highlighter-rouge">IDE</code>里得到一个空的<code class="language-plaintext highlighter-rouge">Spring Boot</code>项目了。</p>郑龙飞无情不似多情苦,一寸还成千万缕。关系型数据库设计三大范式到底是什么?2020-12-25T00:00:00+00:002020-12-25T00:00:00+00:00https://niocoder.com//2020/12/25/relation-db<blockquote>
<p>我欲穿花寻路,直入白云深处,浩气展虹霓。</p>
</blockquote>
<h2 id="范式定义">范式定义</h2>
<p>百度百科:设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。</p>
<p>人类语言: 范式可以理解为设计一张数据表的表结构,符合的标准级别、规范和要求。</p>
<p>而通常我们用的最多的就是第一范式(1NF)、第二范式(2NF)、第三范式(3NF),也就是本文要讲的“三大范式”。</p>
<p><strong>范式的优点</strong></p>
<p>采用范式可以降低数据的冗余性。</p>
<p>为什么要降低数据的冗余性?</p>
<ol>
<li>十几年前,磁盘很贵,为了减少磁盘存储。</li>
<li>以前没有分布式系统,都是单机,只能增加磁盘,磁盘个数也是有限的。</li>
<li>一次修改,需要修改多个表,很难保证数据一致性。</li>
</ol>
<p><strong>范式的缺点</strong></p>
<p>范式的缺点是获取数据时,需要通过Join拼接出最后的数据。</p>
<p><strong>目前范式的分类</strong></p>
<p>目前业界范式有:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)、第五范式(5NF)。</p>
<h2 id="什么是函数依赖">什么是函数依赖?</h2>
<p>百度百科:函数依赖简单点说就是:某个属性集决定另一个属性集时,称另一属性集依赖于该属性集。</p>
<p>人类语言:以下面表格为例,通俗易懂的解释,什么是函数依赖。</p>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>系名</th>
<th>系主任</th>
<th>科名</th>
<th>分数</th>
</tr>
</thead>
<tbody>
<tr>
<td>001</td>
<td>张三</td>
<td>计算机系</td>
<td>李雷</td>
<td>高等数学</td>
<td>87</td>
</tr>
<tr>
<td>001</td>
<td>张三</td>
<td>计算机系</td>
<td>李雷</td>
<td>大学英语</td>
<td>88</td>
</tr>
<tr>
<td>001</td>
<td>张三</td>
<td>计算机系</td>
<td>李雷</td>
<td>数据库设计</td>
<td>89</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>计算机系</td>
<td>李雷</td>
<td>高等数学</td>
<td>86</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>计算机系</td>
<td>李雷</td>
<td>java程序设计</td>
<td>90</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>计算机系</td>
<td>李雷</td>
<td>大学英语</td>
<td>98</td>
</tr>
<tr>
<td>003</td>
<td>王五</td>
<td>财务系</td>
<td>韩梅梅</td>
<td>高等数学</td>
<td>96</td>
</tr>
<tr>
<td>003</td>
<td>王五</td>
<td>财务系</td>
<td>韩梅梅</td>
<td>财务基础</td>
<td>95</td>
</tr>
</tbody>
</table>
<h3 id="完全函数依赖">完全函数依赖</h3>
<p>官方定义:设X,Y是关系R的两个属性集合,X’是X的真子集,存在X→Y,但对每一个X’都有X’!→Y,则称Y完全函数依赖于X。</p>
<p>人类语言:比如通过,(学号,课程) 推出分数 ,但是单独用学号推断不出来分数,那么就可以说:分数 完全依赖于(学号,课程) 。</p>
<p>总结:即:通过A B能得出C,但 是A B单独得不出C,那么说C完全依赖于AB。</p>
<h3 id="部分函数依赖">部分函数依赖</h3>
<p>官方定义:假如 Y函数依赖于 X,但同时 Y 并不完全函数依赖于 X,那么我们就称 Y 部分函数依赖于 X。</p>
<p>人类语言:比如通过,(学 号,课程) 推出姓名,因为其实直接可以通过,学号推出姓名,所以:姓名 部分依赖于 (学号,课程)。</p>
<p>总结:通过AB能得出C,通过A也能得出C,或者通过B也能得出C,那么说C部分依赖于AB。</p>
<h3 id="传递函数依赖">传递函数依赖</h3>
<p>官方定义:传递函数依赖:设X,Y,Z是关系R中互不相同的属性集合,存在X→Y(Y !→X),Y→Z,则称Z传递函数依赖于X。</p>
<p>人类语言:比如:学号 推出 系名 , 系名 推出 系主任, 但是,系主任推不出学号,系主任主要依赖于系名。这种情况可以说:系主任 传递依赖于 学号 。</p>
<p>总结:即:通 过A得 到B,通 过B得 到C,但 是C得不到A,那 么说C传递依赖于A。</p>
<h2 id="三范式的区别">三范式的区别</h2>
<h3 id="第一范式">第一范式</h3>
<p>第一范式1NF核心原则:属性不可切割。</p>
<p>举例说明:</p>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>系名</th>
<th>系主任</th>
<th>科名</th>
<th>分数</th>
<th>学籍信息</th>
</tr>
</thead>
<tbody>
<tr>
<td>001</td>
<td>张三</td>
<td>计算机系</td>
<td>李雷</td>
<td>高等数学</td>
<td>87</td>
<td>本科,大二</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>计算机系</td>
<td>李雷</td>
<td>大学英语</td>
<td>88</td>
<td>研究生,研三</td>
</tr>
</tbody>
</table>
<p>很明显上面表格设计是不符合第一范式的,学籍信息列中的数据不是原子数据项,是可以进行分割的,因此对表格进行修改,让表格符合第一范式的要求,修改结果如下图所示:</p>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>系名</th>
<th>系主任</th>
<th>科名</th>
<th>分数</th>
<th>学历</th>
<th>所在年级</th>
</tr>
</thead>
<tbody>
<tr>
<td>001</td>
<td>张三</td>
<td>计算机系</td>
<td>李雷</td>
<td>高等数学</td>
<td>87</td>
<td>本科</td>
<td>大二</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>计算机系</td>
<td>李雷</td>
<td>大学英语</td>
<td>88</td>
<td>研究生</td>
<td>研三</td>
</tr>
</tbody>
</table>
<p>实际上 ,1NF是所有关系型数据库的最基本要求 ,你在关系型数据库管理系统(RDBMS),例如SQL Server,Oracle,MySQL中创建数据表的时候,如果数据表的设计不符合这个最基本的要求,那么操作一定是不能成功的。也就是说,只要在RDBMS中已经存在
的数据表,一定是符合1NF的。</p>
<h3 id="第二范式">第二范式</h3>
<p>第二范式2NF核心原则:不能存在“部分函数依赖”。</p>
<p>举例说明:</p>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>系名</th>
<th>系主任</th>
<th>科名</th>
<th>分数</th>
</tr>
</thead>
<tbody>
<tr>
<td>001</td>
<td>张三</td>
<td>计算机系</td>
<td>李雷</td>
<td>高等数学</td>
<td>87</td>
</tr>
<tr>
<td>001</td>
<td>张三</td>
<td>计算机系</td>
<td>李雷</td>
<td>大学英语</td>
<td>88</td>
</tr>
<tr>
<td>001</td>
<td>张三</td>
<td>计算机系</td>
<td>李雷</td>
<td>数据库设计</td>
<td>89</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>计算机系</td>
<td>李雷</td>
<td>高等数学</td>
<td>86</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>计算机系</td>
<td>李雷</td>
<td>java程序设计</td>
<td>90</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>计算机系</td>
<td>李雷</td>
<td>大学英语</td>
<td>98</td>
</tr>
<tr>
<td>003</td>
<td>王五</td>
<td>财务系</td>
<td>韩梅梅</td>
<td>高等数学</td>
<td>96</td>
</tr>
<tr>
<td>003</td>
<td>王五</td>
<td>财务系</td>
<td>韩梅梅</td>
<td>财务基础</td>
<td>95</td>
</tr>
</tbody>
</table>
<p>以上表格明显存在,部分依赖。比 如,这张表的主键是 (学号,课名),分数确实完全依赖于(学号,课名),但是姓名并不完全依赖于(学号,课名),让表格符合第二范式的要求,修改结果如下图所示:</p>
<table>
<thead>
<tr>
<th>学号</th>
<th>科名</th>
<th>分数</th>
</tr>
</thead>
<tbody>
<tr>
<td>001</td>
<td>高等数学</td>
<td>87</td>
</tr>
<tr>
<td>001</td>
<td>大学英语</td>
<td>88</td>
</tr>
<tr>
<td>001</td>
<td>数据库设计</td>
<td>89</td>
</tr>
<tr>
<td>002</td>
<td>高等数学</td>
<td>86</td>
</tr>
<tr>
<td>002</td>
<td>java程序设计</td>
<td>90</td>
</tr>
<tr>
<td>002</td>
<td>大学英语</td>
<td>98</td>
</tr>
<tr>
<td>003</td>
<td>高等数学</td>
<td>96</td>
</tr>
<tr>
<td>003</td>
<td>财务基础</td>
<td>95</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>系名</th>
<th>系主任</th>
</tr>
</thead>
<tbody>
<tr>
<td>001</td>
<td>张三</td>
<td>计算机系</td>
<td>李雷</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>计算机系</td>
<td>李雷</td>
</tr>
<tr>
<td>003</td>
<td>王五</td>
<td>财务系</td>
<td>韩梅梅</td>
</tr>
</tbody>
</table>
<p>以上符合第二范式,去掉部分函数依赖依赖。</p>
<h3 id="第三范式">第三范式</h3>
<p>第三范式 3NF核心原则:不能存在传递函数依赖。</p>
<p>举例说明:</p>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>系名</th>
<th>系主任</th>
</tr>
</thead>
<tbody>
<tr>
<td>001</td>
<td>张三</td>
<td>计算机系</td>
<td>李雷</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>计算机系</td>
<td>李雷</td>
</tr>
<tr>
<td>003</td>
<td>王五</td>
<td>财务系</td>
<td>韩梅梅</td>
</tr>
</tbody>
</table>
<p>在上面这张表中,存 在传递函数依赖:学号->系 名->系主任,但是系主任推不出学号。</p>
<p>上面表需要再次拆解:</p>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>系名</th>
</tr>
</thead>
<tbody>
<tr>
<td>001</td>
<td>张三</td>
<td>计算机系</td>
</tr>
<tr>
<td>002</td>
<td>李四</td>
<td>计算机系</td>
</tr>
<tr>
<td>003</td>
<td>王五</td>
<td>财务系</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>系名</th>
<th>系主任</th>
</tr>
</thead>
<tbody>
<tr>
<td>计算机系</td>
<td>李雷</td>
</tr>
<tr>
<td>计算机系</td>
<td>李雷</td>
</tr>
<tr>
<td>财务系</td>
<td>韩梅梅</td>
</tr>
</tbody>
</table>
<h3 id="反三范式">反三范式</h3>
<p>没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是: 在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,减少了查询时的关联,提高查询效率,因为在数据库的操作中查询的比例要远远大于DML的比例。但是反范式化一定要适度,并且在原本已满足三范式的基础上再做调整的。</p>
<h2 id="总结">总结</h2>
<p>引用知乎大佬对范式的理解:</p>
<p>数据库设计应该也是分为三个境界的:</p>
<p>第一个境界,刚入门数据库设计,范式的重要性还未深刻理解。这时候出现的反范式设计,一般会出问题。</p>
<p>第二个境界,随着遇到问题解决问题,渐渐了解到范式的真正好处,从而能快速设计出低冗余、高效率的数据库。</p>
<p>第三个境界,再经过N年的锻炼,是一定会发觉范式的局限性的。此时再去打破范式,设计更合理的反范式部分。</p>郑龙飞我欲穿花寻路,直入白云深处,浩气展虹霓。 范式定义 百度百科:设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。 人类语言: 范式可以理解为设计一张数据表的表结构,符合的标准级别、规范和要求。 而通常我们用的最多的就是第一范式(1NF)、第二范式(2NF)、第三范式(3NF),也就是本文要讲的“三大范式”。 范式的优点 采用范式可以降低数据的冗余性。 为什么要降低数据的冗余性? 十几年前,磁盘很贵,为了减少磁盘存储。 以前没有分布式系统,都是单机,只能增加磁盘,磁盘个数也是有限的。 一次修改,需要修改多个表,很难保证数据一致性。 范式的缺点 范式的缺点是获取数据时,需要通过Join拼接出最后的数据。 目前范式的分类 目前业界范式有:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)、第五范式(5NF)。 什么是函数依赖? 百度百科:函数依赖简单点说就是:某个属性集决定另一个属性集时,称另一属性集依赖于该属性集。 人类语言:以下面表格为例,通俗易懂的解释,什么是函数依赖。 学号 姓名 系名 系主任 科名 分数 001 张三 计算机系 李雷 高等数学 87 001 张三 计算机系 李雷 大学英语 88 001 张三 计算机系 李雷 数据库设计 89 002 李四 计算机系 李雷 高等数学 86 002 李四 计算机系 李雷 java程序设计 90 002 李四 计算机系 李雷 大学英语 98 003 王五 财务系 韩梅梅 高等数学 96 003 王五 财务系 韩梅梅 财务基础 95 完全函数依赖 官方定义:设X,Y是关系R的两个属性集合,X’是X的真子集,存在X→Y,但对每一个X’都有X’!→Y,则称Y完全函数依赖于X。 人类语言:比如通过,(学号,课程) 推出分数 ,但是单独用学号推断不出来分数,那么就可以说:分数 完全依赖于(学号,课程) 。 总结:即:通过A B能得出C,但 是A B单独得不出C,那么说C完全依赖于AB。 部分函数依赖 官方定义:假如 Y函数依赖于 X,但同时 Y 并不完全函数依赖于 X,那么我们就称 Y 部分函数依赖于 X。 人类语言:比如通过,(学 号,课程) 推出姓名,因为其实直接可以通过,学号推出姓名,所以:姓名 部分依赖于 (学号,课程)。 总结:通过AB能得出C,通过A也能得出C,或者通过B也能得出C,那么说C部分依赖于AB。 传递函数依赖 官方定义:传递函数依赖:设X,Y,Z是关系R中互不相同的属性集合,存在X→Y(Y !→X),Y→Z,则称Z传递函数依赖于X。 人类语言:比如:学号 推出 系名 , 系名 推出 系主任, 但是,系主任推不出学号,系主任主要依赖于系名。这种情况可以说:系主任 传递依赖于 学号 。 总结:即:通 过A得 到B,通 过B得 到C,但 是C得不到A,那 么说C传递依赖于A。 三范式的区别 第一范式 第一范式1NF核心原则:属性不可切割。 举例说明: 学号 姓名 系名 系主任 科名 分数 学籍信息 001 张三 计算机系 李雷 高等数学 87 本科,大二 002 李四 计算机系 李雷 大学英语 88 研究生,研三 很明显上面表格设计是不符合第一范式的,学籍信息列中的数据不是原子数据项,是可以进行分割的,因此对表格进行修改,让表格符合第一范式的要求,修改结果如下图所示: 学号 姓名 系名 系主任 科名 分数 学历 所在年级 001 张三 计算机系 李雷 高等数学 87 本科 大二 002 李四 计算机系 李雷 大学英语 88 研究生 研三 实际上 ,1NF是所有关系型数据库的最基本要求 ,你在关系型数据库管理系统(RDBMS),例如SQL Server,Oracle,MySQL中创建数据表的时候,如果数据表的设计不符合这个最基本的要求,那么操作一定是不能成功的。也就是说,只要在RDBMS中已经存在 的数据表,一定是符合1NF的。 第二范式 第二范式2NF核心原则:不能存在“部分函数依赖”。 举例说明: 学号 姓名 系名 系主任 科名 分数 001 张三 计算机系 李雷 高等数学 87 001 张三 计算机系 李雷 大学英语 88 001 张三 计算机系 李雷 数据库设计 89 002 李四 计算机系 李雷 高等数学 86 002 李四 计算机系 李雷 java程序设计 90 002 李四 计算机系 李雷 大学英语 98 003 王五 财务系 韩梅梅 高等数学 96 003 王五 财务系 韩梅梅 财务基础 95 以上表格明显存在,部分依赖。比 如,这张表的主键是 (学号,课名),分数确实完全依赖于(学号,课名),但是姓名并不完全依赖于(学号,课名),让表格符合第二范式的要求,修改结果如下图所示: 学号 科名 分数 001 高等数学 87 001 大学英语 88 001 数据库设计 89 002 高等数学 86 002 java程序设计 90 002 大学英语 98 003 高等数学 96 003 财务基础 95 学号 姓名 系名 系主任 001 张三 计算机系 李雷 002 李四 计算机系 李雷 003 王五 财务系 韩梅梅 以上符合第二范式,去掉部分函数依赖依赖。 第三范式 第三范式 3NF核心原则:不能存在传递函数依赖。 举例说明: 学号 姓名 系名 系主任 001 张三 计算机系 李雷 002 李四 计算机系 李雷 003 王五 财务系 韩梅梅 在上面这张表中,存 在传递函数依赖:学号->系 名->系主任,但是系主任推不出学号。 上面表需要再次拆解: 学号 姓名 系名 001 张三 计算机系 002 李四 计算机系 003 王五 财务系 系名 系主任 计算机系 李雷 计算机系 李雷 财务系 韩梅梅 反三范式 没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是: 在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,减少了查询时的关联,提高查询效率,因为在数据库的操作中查询的比例要远远大于DML的比例。但是反范式化一定要适度,并且在原本已满足三范式的基础上再做调整的。 总结 引用知乎大佬对范式的理解: 数据库设计应该也是分为三个境界的: 第一个境界,刚入门数据库设计,范式的重要性还未深刻理解。这时候出现的反范式设计,一般会出问题。 第二个境界,随着遇到问题解决问题,渐渐了解到范式的真正好处,从而能快速设计出低冗余、高效率的数据库。 第三个境界,再经过N年的锻炼,是一定会发觉范式的局限性的。此时再去打破范式,设计更合理的反范式部分。ClickHouse集群搭建(二)2020-11-29T00:00:00+00:002020-11-29T00:00:00+00:00https://niocoder.com//2020/11/29/ClickHouse%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA2<blockquote>
<p>重叠泪痕缄锦字,人生只有情难死。</p>
</blockquote>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java43.jpg" alt="" /></p>
<h3 id="分布式集群安装">分布式集群安装</h3>
<p>在上一章我们已经完成<code class="language-plaintext highlighter-rouge">ClickHouse</code>分布式集群安装,也创建本地表和分布式表进行了测试,但是,假如停掉一个节点会发生神马情况?</p>
<h4 id="node03上kill掉clickhouse-server进程"><code class="language-plaintext highlighter-rouge">node03</code>上<code class="language-plaintext highlighter-rouge">kill</code>掉<code class="language-plaintext highlighter-rouge">clickhouse-server</code>进程</h4>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node03 ~]# ps <span class="nt">-ef</span> | <span class="nb">grep </span>clickhouse
clickho+ 2233 1 73 13:07 ? 00:00:02 clickhouse-server <span class="nt">--daemon</span> <span class="nt">--pid-file</span><span class="o">=</span>/var/run/clickhouse-server/clickhouse-server.pid <span class="nt">--config-file</span><span class="o">=</span>/etc/clickhouse-server/config.xml
root 2306 1751 0 13:07 pts/0 00:00:00 <span class="nb">grep</span> <span class="nt">--color</span><span class="o">=</span>auto clickhouse
<span class="o">[</span>root@node03 ~]# service clickhouse-server stop
Stop clickhouse-server service: DONE
<span class="o">[</span>root@node03 ~]# ps <span class="nt">-ef</span> | <span class="nb">grep </span>clickhouse
root 2337 1751 0 13:07 pts/0 00:00:00 <span class="nb">grep</span> <span class="nt">--color</span><span class="o">=</span>auto clickhouse
</code></pre></div></div>
<h4 id="node01上查询分布式表"><code class="language-plaintext highlighter-rouge">node01</code>上查询分布式表</h4>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>node01 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from cluster3s1r_all<span class="p">;</span> <span class="c"># node03没有被杀掉时</span>
SELECT <span class="k">*</span>
FROM cluster3s1r_all
┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐
│ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │
└────┴─────────────────────────┴────────────┴────────────┴──────┘
┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐
│ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │
└────┴───────────────────────┴──────────┴────────────┴──────┘
┌─id─┬─website──────────────┬─wechat─┬─FlightDate─┬─Year─┐
│ 3 │ http://www.xxxxx.cn/ │ xxxxx │ 2020-11-28 │ 2020 │
└────┴──────────────────────┴────────┴────────────┴──────┘
3 rows <span class="k">in </span>set. Elapsed: 0.037 sec.
node01 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from cluster3s1r_all<span class="p">;</span> <span class="c"># node03节点被杀掉时</span>
SELECT <span class="k">*</span>
FROM cluster3s1r_all
┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐
│ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │
└────┴─────────────────────────┴────────────┴────────────┴──────┘
↘ Progress: 1.00 rows, 59.00 B <span class="o">(</span>8.87 rows/s., 523.62 B/s.<span class="o">)</span> 0%
Received exception from server <span class="o">(</span>version 20.8.3<span class="o">)</span>:
Code: 279. DB::Exception: Received from localhost:9000. DB::Exception: All connection tries failed. Log:
Code: 32, e.displayText<span class="o">()</span> <span class="o">=</span> DB::Exception: Attempt to <span class="nb">read </span>after eof <span class="o">(</span>version 20.8.3.18<span class="o">)</span>
Code: 210, e.displayText<span class="o">()</span> <span class="o">=</span> DB::NetException: Connection refused <span class="o">(</span>node03:9000<span class="o">)</span> <span class="o">(</span>version 20.8.3.18<span class="o">)</span>
Code: 210, e.displayText<span class="o">()</span> <span class="o">=</span> DB::NetException: Connection refused <span class="o">(</span>node03:9000<span class="o">)</span> <span class="o">(</span>version 20.8.3.18<span class="o">)</span>
: While executing Remote.
1 rows <span class="k">in </span>set. Elapsed: 0.114 sec.
</code></pre></div></div>
<p>只返回了<code class="language-plaintext highlighter-rouge">node01</code>节点上的数据,<code class="language-plaintext highlighter-rouge">node03</code>节点上的两条数据丢失。</p>
<h3 id="数据备份">数据备份</h3>
<p>但在<code class="language-plaintext highlighter-rouge">ClickHouse</code>中,<code class="language-plaintext highlighter-rouge">replica</code>是挂在<code class="language-plaintext highlighter-rouge">shard</code>上的,因此要用多副本,必须先定义<code class="language-plaintext highlighter-rouge">shard</code>。</p>
<p>最简单的情况:1个分片多个副本。</p>
<h4 id="修改metrikaxml文件">修改<code class="language-plaintext highlighter-rouge">metrika.xml</code>文件</h4>
<p><code class="language-plaintext highlighter-rouge">node01</code> 上修改 <code class="language-plaintext highlighter-rouge">/etc/clickhouse-server/metrika.xml</code>集群配置文件</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><yandex></span>
<span class="c"><!-- 集群配置 --></span>
<span class="nt"><clickhouse_remote_servers></span>
<span class="c"><!-- 1分片2备份 --></span>
<span class="nt"><cluster_1shards_2replicas></span>
<span class="c"><!-- 数据分片1 --></span>
<span class="nt"><shard></span>
<span class="c"><!-- false代表一次性写入所有副本,true表示写入其中一个副本,配合zk来进行数据复制 --></span>
<span class="nt"><internal_replication></span>false<span class="nt"></internal_replication></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node01<span class="nt"></host></span>
<span class="nt"><port></span>9000<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node02<span class="nt"></host></span>
<span class="nt"><port></span>9000<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"></shard></span>
<span class="nt"></cluster_1shards_2replicas></span>
<span class="nt"></clickhouse_remote_servers></span>
<span class="nt"></yandex></span>
</code></pre></div></div>
<p>将修改后的配置分发到<code class="language-plaintext highlighter-rouge">node02</code>机器上</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika.xml node02:<span class="nv">$PWD</span>
metrika.xml 100% 674 618.9KB/s 00:00
</code></pre></div></div>
<p>如果配置文件没有问题,是不用重启<code class="language-plaintext highlighter-rouge">clickhouse-server</code>的,会自动加载配置文件,<code class="language-plaintext highlighter-rouge">node01</code>上查看集群信息</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 clickhouse-server]# clickhouse-client <span class="nt">-m</span>
ClickHouse client version 20.8.3.18.
Connecting to localhost:9000 as user default.
Connected to ClickHouse server version 20.8.3 revision 54438.
node01 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from system.clusters<span class="p">;</span>
SELECT <span class="k">*</span>
FROM system.clusters
┌─cluster───────────────────────────┬─shard_num─┬─shard_weight─┬─replica_num─┬─host_name─┬─host_address───┬─port─┬─is_local─┬─user────┬─default_database─┬─errors_count─┬─estimated_recovery_time─┐
│ cluster_1shards_2replicas │ 1 │ 1 │ 1 │ node01 │ 192.168.10.100 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ cluster_1shards_2replicas │ 1 │ 1 │ 2 │ node02 │ 192.168.10.110 │ 9000 │ 0 │ default │ │ 0 │ 0 │
│ test_cluster_two_shards │ 1 │ 1 │ 1 │ 127.0.0.1 │ 127.0.0.1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_cluster_two_shards │ 2 │ 1 │ 1 │ 127.0.0.2 │ 127.0.0.2 │ 9000 │ 0 │ default │ │ 0 │ 0 │
│ test_cluster_two_shards_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_cluster_two_shards_localhost │ 2 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_shard_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_shard_localhost_secure │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9440 │ 0 │ default │ │ 0 │ 0 │
│ test_unavailable_shard │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_unavailable_shard │ 2 │ 1 │ 1 │ localhost │ ::1 │ 1 │ 0 │ default │ │ 0 │ 0 │
└───────────────────────────────────┴───────────┴──────────────┴─────────────┴───────────┴────────────────┴──────┴──────────┴─────────┴──────────────────┴──────────────┴─────────────────────────┘
10 rows <span class="k">in </span>set. Elapsed: 0.018 sec.
</code></pre></div></div>
<h4 id="测试数据备份">测试数据备份</h4>
<p>在<code class="language-plaintext highlighter-rouge">node01</code>和<code class="language-plaintext highlighter-rouge">node02</code>上分别创建本地表<code class="language-plaintext highlighter-rouge">cluster1s2r_local</code></p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster1s2r_local</span>
<span class="p">(</span>
<span class="nv">`id`</span> <span class="n">Int32</span><span class="p">,</span>
<span class="nv">`website`</span> <span class="n">String</span><span class="p">,</span>
<span class="nv">`wechat`</span> <span class="n">String</span><span class="p">,</span>
<span class="nv">`FlightDate`</span> <span class="nb">Date</span><span class="p">,</span>
<span class="nb">Year</span> <span class="n">UInt16</span>
<span class="p">)</span>
<span class="n">ENGINE</span> <span class="o">=</span> <span class="n">MergeTree</span><span class="p">(</span><span class="n">FlightDate</span><span class="p">,</span> <span class="p">(</span><span class="nb">Year</span><span class="p">,</span> <span class="n">FlightDate</span><span class="p">),</span> <span class="mi">8192</span><span class="p">);</span>
</code></pre></div></div>
<p>在<code class="language-plaintext highlighter-rouge">node01</code>机器上创建分布式表,注意集群名称</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster1s2r_all</span> <span class="k">AS</span> <span class="n">cluster1s2r_local</span>
<span class="n">ENGINE</span> <span class="o">=</span> <span class="n">Distributed</span><span class="p">(</span><span class="n">cluster_1shards_2replicas</span><span class="p">,</span> <span class="k">default</span><span class="p">,</span> <span class="n">cluster1s2r_local</span><span class="p">,</span> <span class="n">rand</span><span class="p">());</span>
</code></pre></div></div>
<p>往分布式表<code class="language-plaintext highlighter-rouge">cluster1s2r_all</code>插入数据,<code class="language-plaintext highlighter-rouge">cluster1s2r_all</code> 会全部插入到<code class="language-plaintext highlighter-rouge">node01</code>和<code class="language-plaintext highlighter-rouge">node02</code>节点的<code class="language-plaintext highlighter-rouge">cluster1s2r_local</code>里</p>
<p>插入数据</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster1s2r_all</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="n">website</span><span class="p">,</span><span class="n">wechat</span><span class="p">,</span><span class="n">FlightDate</span><span class="p">,</span><span class="nb">Year</span><span class="p">)</span><span class="k">values</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="s1">'https://niocoder.com/'</span><span class="p">,</span><span class="s1">'java干货'</span><span class="p">,</span><span class="s1">'2020-11-28'</span><span class="p">,</span><span class="mi">2020</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster1s2r_all</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="n">website</span><span class="p">,</span><span class="n">wechat</span><span class="p">,</span><span class="n">FlightDate</span><span class="p">,</span><span class="nb">Year</span><span class="p">)</span><span class="k">values</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="s1">'http://www.merryyou.cn/'</span><span class="p">,</span><span class="s1">'javaganhuo'</span><span class="p">,</span><span class="s1">'2020-11-28'</span><span class="p">,</span><span class="mi">2020</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster1s2r_all</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="n">website</span><span class="p">,</span><span class="n">wechat</span><span class="p">,</span><span class="n">FlightDate</span><span class="p">,</span><span class="nb">Year</span><span class="p">)</span><span class="k">values</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="s1">'http://www.xxxxx.cn/'</span><span class="p">,</span><span class="s1">'xxxxx'</span><span class="p">,</span><span class="s1">'2020-11-28'</span><span class="p">,</span><span class="mi">2020</span><span class="p">);</span>
</code></pre></div></div>
<p>查询分布式表和本地表</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">node01</span> <span class="p">:)</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">cluster1s2r_all</span><span class="p">;</span> <span class="o">#</span> <span class="err">查询分布式表</span>
<span class="k">SELECT</span> <span class="o">*</span>
<span class="k">FROM</span> <span class="n">cluster1s2r_all</span>
<span class="err">┌─</span><span class="n">id</span><span class="err">─┬─</span><span class="n">website</span><span class="err">──────────────┬─</span><span class="n">wechat</span><span class="err">─┬─</span><span class="n">FlightDate</span><span class="err">─┬─</span><span class="nb">Year</span><span class="err">─┐</span>
<span class="err">│</span> <span class="mi">3</span> <span class="err">│</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">www</span><span class="p">.</span><span class="n">xxxxx</span><span class="p">.</span><span class="n">cn</span><span class="o">/</span> <span class="err">│</span> <span class="n">xxxxx</span> <span class="err">│</span> <span class="mi">2020</span><span class="o">-</span><span class="mi">11</span><span class="o">-</span><span class="mi">28</span> <span class="err">│</span> <span class="mi">2020</span> <span class="err">│</span>
<span class="err">└────┴──────────────────────┴────────┴────────────┴──────┘</span>
<span class="err">┌─</span><span class="n">id</span><span class="err">─┬─</span><span class="n">website</span><span class="err">─────────────────┬─</span><span class="n">wechat</span><span class="err">─────┬─</span><span class="n">FlightDate</span><span class="err">─┬─</span><span class="nb">Year</span><span class="err">─┐</span>
<span class="err">│</span> <span class="mi">2</span> <span class="err">│</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">www</span><span class="p">.</span><span class="n">merryyou</span><span class="p">.</span><span class="n">cn</span><span class="o">/</span> <span class="err">│</span> <span class="n">javaganhuo</span> <span class="err">│</span> <span class="mi">2020</span><span class="o">-</span><span class="mi">11</span><span class="o">-</span><span class="mi">28</span> <span class="err">│</span> <span class="mi">2020</span> <span class="err">│</span>
<span class="err">└────┴─────────────────────────┴────────────┴────────────┴──────┘</span>
<span class="err">┌─</span><span class="n">id</span><span class="err">─┬─</span><span class="n">website</span><span class="err">───────────────┬─</span><span class="n">wechat</span><span class="err">───┬─</span><span class="n">FlightDate</span><span class="err">─┬─</span><span class="nb">Year</span><span class="err">─┐</span>
<span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">niocoder</span><span class="p">.</span><span class="n">com</span><span class="o">/</span> <span class="err">│</span> <span class="n">java</span><span class="err">干货</span> <span class="err">│</span> <span class="mi">2020</span><span class="o">-</span><span class="mi">11</span><span class="o">-</span><span class="mi">28</span> <span class="err">│</span> <span class="mi">2020</span> <span class="err">│</span>
<span class="err">└────┴───────────────────────┴──────────┴────────────┴──────┘</span>
<span class="mi">3</span> <span class="k">rows</span> <span class="k">in</span> <span class="k">set</span><span class="p">.</span> <span class="n">Elapsed</span><span class="p">:</span> <span class="mi">0</span><span class="p">.</span><span class="mi">018</span> <span class="n">sec</span><span class="p">.</span>
<span class="n">node01</span> <span class="p">:)</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">cluster1s2r_local</span><span class="p">;</span> <span class="o">#</span> <span class="n">node01</span><span class="err">节点查询本地表</span>
<span class="k">SELECT</span> <span class="o">*</span>
<span class="k">FROM</span> <span class="n">cluster1s2r_local</span>
<span class="err">┌─</span><span class="n">id</span><span class="err">─┬─</span><span class="n">website</span><span class="err">─────────────────┬─</span><span class="n">wechat</span><span class="err">─────┬─</span><span class="n">FlightDate</span><span class="err">─┬─</span><span class="nb">Year</span><span class="err">─┐</span>
<span class="err">│</span> <span class="mi">2</span> <span class="err">│</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">www</span><span class="p">.</span><span class="n">merryyou</span><span class="p">.</span><span class="n">cn</span><span class="o">/</span> <span class="err">│</span> <span class="n">javaganhuo</span> <span class="err">│</span> <span class="mi">2020</span><span class="o">-</span><span class="mi">11</span><span class="o">-</span><span class="mi">28</span> <span class="err">│</span> <span class="mi">2020</span> <span class="err">│</span>
<span class="err">└────┴─────────────────────────┴────────────┴────────────┴──────┘</span>
<span class="err">┌─</span><span class="n">id</span><span class="err">─┬─</span><span class="n">website</span><span class="err">───────────────┬─</span><span class="n">wechat</span><span class="err">───┬─</span><span class="n">FlightDate</span><span class="err">─┬─</span><span class="nb">Year</span><span class="err">─┐</span>
<span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">niocoder</span><span class="p">.</span><span class="n">com</span><span class="o">/</span> <span class="err">│</span> <span class="n">java</span><span class="err">干货</span> <span class="err">│</span> <span class="mi">2020</span><span class="o">-</span><span class="mi">11</span><span class="o">-</span><span class="mi">28</span> <span class="err">│</span> <span class="mi">2020</span> <span class="err">│</span>
<span class="err">└────┴───────────────────────┴──────────┴────────────┴──────┘</span>
<span class="err">┌─</span><span class="n">id</span><span class="err">─┬─</span><span class="n">website</span><span class="err">──────────────┬─</span><span class="n">wechat</span><span class="err">─┬─</span><span class="n">FlightDate</span><span class="err">─┬─</span><span class="nb">Year</span><span class="err">─┐</span>
<span class="err">│</span> <span class="mi">3</span> <span class="err">│</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">www</span><span class="p">.</span><span class="n">xxxxx</span><span class="p">.</span><span class="n">cn</span><span class="o">/</span> <span class="err">│</span> <span class="n">xxxxx</span> <span class="err">│</span> <span class="mi">2020</span><span class="o">-</span><span class="mi">11</span><span class="o">-</span><span class="mi">28</span> <span class="err">│</span> <span class="mi">2020</span> <span class="err">│</span>
<span class="err">└────┴──────────────────────┴────────┴────────────┴──────┘</span>
<span class="mi">3</span> <span class="k">rows</span> <span class="k">in</span> <span class="k">set</span><span class="p">.</span> <span class="n">Elapsed</span><span class="p">:</span> <span class="mi">0</span><span class="p">.</span><span class="mi">015</span> <span class="n">sec</span><span class="p">.</span>
<span class="n">node02</span> <span class="p">:)</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">cluster1s2r_local</span><span class="p">;</span> <span class="o">#</span> <span class="n">node02</span><span class="err">节点查询本地表</span>
<span class="k">SELECT</span> <span class="o">*</span>
<span class="k">FROM</span> <span class="n">cluster1s2r_local</span>
<span class="err">┌─</span><span class="n">id</span><span class="err">─┬─</span><span class="n">website</span><span class="err">─────────────────┬─</span><span class="n">wechat</span><span class="err">─────┬─</span><span class="n">FlightDate</span><span class="err">─┬─</span><span class="nb">Year</span><span class="err">─┐</span>
<span class="err">│</span> <span class="mi">2</span> <span class="err">│</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">www</span><span class="p">.</span><span class="n">merryyou</span><span class="p">.</span><span class="n">cn</span><span class="o">/</span> <span class="err">│</span> <span class="n">javaganhuo</span> <span class="err">│</span> <span class="mi">2020</span><span class="o">-</span><span class="mi">11</span><span class="o">-</span><span class="mi">28</span> <span class="err">│</span> <span class="mi">2020</span> <span class="err">│</span>
<span class="err">└────┴─────────────────────────┴────────────┴────────────┴──────┘</span>
<span class="err">┌─</span><span class="n">id</span><span class="err">─┬─</span><span class="n">website</span><span class="err">───────────────┬─</span><span class="n">wechat</span><span class="err">───┬─</span><span class="n">FlightDate</span><span class="err">─┬─</span><span class="nb">Year</span><span class="err">─┐</span>
<span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">niocoder</span><span class="p">.</span><span class="n">com</span><span class="o">/</span> <span class="err">│</span> <span class="n">java</span><span class="err">干货</span> <span class="err">│</span> <span class="mi">2020</span><span class="o">-</span><span class="mi">11</span><span class="o">-</span><span class="mi">28</span> <span class="err">│</span> <span class="mi">2020</span> <span class="err">│</span>
<span class="err">└────┴───────────────────────┴──────────┴────────────┴──────┘</span>
<span class="err">┌─</span><span class="n">id</span><span class="err">─┬─</span><span class="n">website</span><span class="err">──────────────┬─</span><span class="n">wechat</span><span class="err">─┬─</span><span class="n">FlightDate</span><span class="err">─┬─</span><span class="nb">Year</span><span class="err">─┐</span>
<span class="err">│</span> <span class="mi">3</span> <span class="err">│</span> <span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">www</span><span class="p">.</span><span class="n">xxxxx</span><span class="p">.</span><span class="n">cn</span><span class="o">/</span> <span class="err">│</span> <span class="n">xxxxx</span> <span class="err">│</span> <span class="mi">2020</span><span class="o">-</span><span class="mi">11</span><span class="o">-</span><span class="mi">28</span> <span class="err">│</span> <span class="mi">2020</span> <span class="err">│</span>
<span class="err">└────┴──────────────────────┴────────┴────────────┴──────┘</span>
<span class="mi">3</span> <span class="k">rows</span> <span class="k">in</span> <span class="k">set</span><span class="p">.</span> <span class="n">Elapsed</span><span class="p">:</span> <span class="mi">0</span><span class="p">.</span><span class="mi">007</span> <span class="n">sec</span><span class="p">.</span>
</code></pre></div></div>
<p>查询<code class="language-plaintext highlighter-rouge">node01</code>和<code class="language-plaintext highlighter-rouge">node02</code>本地表<code class="language-plaintext highlighter-rouge">cluster1s2r_local</code>都是全量数据, 即使<code class="language-plaintext highlighter-rouge">sotp</code>到其中一个节点数据也不会丢失,数据副本已经生效。</p>
<h4 id="数据副本一致性问题">数据副本一致性问题</h4>
<p>既然有多副本,就有个一致性的问题:加入写入数据时,挂掉一台机器,会怎样?</p>
<p>模拟写入分布式表是某一个节点<code class="language-plaintext highlighter-rouge">down</code>机</p>
<ol>
<li>
<p>停掉<code class="language-plaintext highlighter-rouge">node02</code>节点服务
<code class="language-plaintext highlighter-rouge">service clickhouse-server stop</code></p>
</li>
<li>
<p>在<code class="language-plaintext highlighter-rouge">node01</code>节点上向分布式表<code class="language-plaintext highlighter-rouge">cluster1s2r_all</code>插入数据</p>
<p><code class="language-plaintext highlighter-rouge">sql INSERT INTO default.cluster1s2r_all (id,website,wechat,FlightDate,Year)values(4,'http://www.yyyyyy.cn/','yyyyy','2020-11-29',2020);</code></p>
</li>
<li>
<p>启动<code class="language-plaintext highlighter-rouge">node02</code>节点服务</p>
</li>
<li>
<p>查询验证是否同步
查看<code class="language-plaintext highlighter-rouge">node01</code>和<code class="language-plaintext highlighter-rouge">node02</code>机器的<code class="language-plaintext highlighter-rouge">cluster1s2r_local</code>、以及<code class="language-plaintext highlighter-rouge">cluster1s2r_all</code>,发现都是总数据量都增加了1条,说明这种情况下,集群节点之间能够自动同步</p>
</li>
</ol>
<p>上面是通过向分布式表<code class="language-plaintext highlighter-rouge">cluster1s2r_all</code>插入数据,如果通过本地表<code class="language-plaintext highlighter-rouge">cluster1s2r_local</code>,数据还能同步吗?</p>
<ol>
<li>在<code class="language-plaintext highlighter-rouge">node01</code>上往<code class="language-plaintext highlighter-rouge">cluster1s2r_local</code>插入1条数据;</li>
<li>查询<code class="language-plaintext highlighter-rouge">node02</code>,<code class="language-plaintext highlighter-rouge">cluster1s2r_local</code>数据没有同步</li>
</ol>
<p>综上所述,通过分布表写入数据,会自动同步数据;而通过本地表表写入数据,不会同步;一般i情况下是没什么大问题。</p>
<p><strong>但是生产情况总比理论复杂的多,以上配置可能会存在数据不一致的问题</strong></p>
<p><a href="https://clickhouse.tech/docs/zh/engines/table-engines/special/distributed/">官方文档</a>描述如下:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Each shard can have the internal_replication parameter defined <span class="k">in </span>the config file.
If this parameter is <span class="nb">set </span>to <span class="nb">true</span>, the write operation selects the first healthy replica and writes data to it. Use this alternative <span class="k">if </span>the Distributed table “looks at” replicated tables. In other words, <span class="k">if </span>the table where data will be written is going to replicate them itself.
If it is <span class="nb">set </span>to <span class="nb">false</span> <span class="o">(</span>the default<span class="o">)</span>, data is written to all replicas. In essence, this means that the Distributed table replicates data itself. This is worse than using replicated tables, because the consistency of replicas is not checked, and over <span class="nb">time </span>they will contain slightly different data.
</code></pre></div></div>
<p>翻译如下:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>分片可在配置文件中定义 ‘internal_replication’ 参数。
此参数设置为«true»时,写操作只选一个正常的副本写入数据。如果分布式表的子表是复制表<span class="o">(</span><span class="k">*</span>ReplicaMergeTree<span class="o">)</span>,请使用此方案。换句话说,这其实是把数据的复制工作交给实际需要写入数据的表本身而不是分布式表。
若此参数设置为«false»(默认值),写操作会将数据写入所有副本。实质上,这意味着要分布式表本身来复制数据。这种方式不如使用复制表的好,因为不会检查副本的一致性,并且随着时间的推移,副本数据可能会有些不一样。
</code></pre></div></div>
<p>简单理解如下:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>这个为true代表zk会挑选一个合适的节点写入,然后在后台进行多个节点之间数据的同步.
如果是false,则是一次性写入所有节点,以这种重复写入的方法实现节点之间数据的同步.
</code></pre></div></div>
<h4 id="自动数据备份">自动数据备份</h4>
<p>自动数据备份是表的行为,引擎为 <code class="language-plaintext highlighter-rouge">ReplicatedXXX</code>的表支持自动同步。</p>
<p><code class="language-plaintext highlighter-rouge">Replicated</code>前缀只用于<code class="language-plaintext highlighter-rouge">MergeTree</code>系列(<code class="language-plaintext highlighter-rouge">MergeTree</code>是最常用的引擎)。</p>
<p><strong>重点说明: <code class="language-plaintext highlighter-rouge">Replicated</code>表自动同步与之前的集群自动同步不同,是表的行为,与<code class="language-plaintext highlighter-rouge">metrika.xml</code>中的<code class="language-plaintext highlighter-rouge"><clickhouse_remote_servers></code>配置没有关系,只要有<code class="language-plaintext highlighter-rouge">zookeeper</code>配置就行了。</strong></p>
<p><code class="language-plaintext highlighter-rouge">node01</code>修改<code class="language-plaintext highlighter-rouge">metrika.xml</code>配置</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><yandex></span>
<span class="nt"><zookeeper-servers></span>
<span class="nt"><node</span> <span class="na">index=</span><span class="s">"1"</span><span class="nt">></span>
<span class="nt"><host></span>node01<span class="nt"></host></span>
<span class="nt"><port></span>2181<span class="nt"></port></span>
<span class="nt"></node></span>
<span class="nt"><node</span> <span class="na">index=</span><span class="s">"2"</span><span class="nt">></span>
<span class="nt"><host></span>node02<span class="nt"></host></span>
<span class="nt"><port></span>2181<span class="nt"></port></span>
<span class="nt"></node></span>
<span class="nt"><node</span> <span class="na">index=</span><span class="s">"3"</span><span class="nt">></span>
<span class="nt"><host></span>node03<span class="nt"></host></span>
<span class="nt"><port></span>2181<span class="nt"></port></span>
<span class="nt"></node></span>
<span class="nt"></zookeeper-servers></span>
<span class="nt"></yandex></span>
</code></pre></div></div>
<p>将修改后的配置分发到<code class="language-plaintext highlighter-rouge">node02</code>机器上</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika.xml node02:<span class="nv">$PWD</span>
metrika.xml
重启<span class="sb">`</span>clickhouse-server<span class="sb">`</span>,由于之前的表存在导致启动是失败。<span class="sb">`</span>error<span class="sb">`</span>日志
<span class="sb">```</span>shell
<span class="o">[</span>root@node01 clickhouse-server]# <span class="nb">tail</span> <span class="nt">-f</span> /var/log/clickhouse-server/clickhouse-server.err.log
7. DB::StorageDistributed::startup<span class="o">()</span> @ 0x10f1bd40 <span class="k">in</span> /usr/bin/clickhouse
8. ? @ 0x1151d922 <span class="k">in</span> /usr/bin/clickhouse
9. ThreadPoolImpl<ThreadFromGlobalPool>::worker<span class="o">(</span>std::__1::__list_iterator<ThreadFromGlobalPool, void<span class="k">*</span><span class="o">>)</span> @ 0xa43d6ad <span class="k">in</span> /usr/bin/clickhouse
10. ThreadFromGlobalPool::ThreadFromGlobalPool<void ThreadPoolImpl<ThreadFromGlobalPool>::scheduleImpl<void><span class="o">(</span>std::__1::function<void <span class="o">()></span>, int, std::__1::optional<unsigned long><span class="o">)</span>::<span class="s1">'lambda1'</span><span class="o">()>(</span>void&&, void ThreadPoolImpl<ThreadFromGlobalPool>::scheduleImpl<void><span class="o">(</span>std::__1::function<void <span class="o">()></span>, int, std::__1::optional<unsigned long><span class="o">)</span>::<span class="s1">'lambda1'</span><span class="o">()&&</span>...<span class="o">)</span>::<span class="s1">'lambda'</span><span class="o">()</span>::operator<span class="o">()()</span> const @ 0xa43dd93 <span class="k">in</span> /usr/bin/clickhouse
11. ThreadPoolImpl<std::__1::thread>::worker<span class="o">(</span>std::__1::__list_iterator<std::__1::thread, void<span class="k">*</span><span class="o">>)</span> @ 0xa43cc4d <span class="k">in</span> /usr/bin/clickhouse
12. ? @ 0xa43b3ff <span class="k">in</span> /usr/bin/clickhouse
13. start_thread @ 0x7ea5 <span class="k">in</span> /usr/lib64/libpthread-2.17.so
14. clone @ 0xfe8dd <span class="k">in</span> /usr/lib64/libc-2.17.so
<span class="o">(</span>version 20.8.3.18<span class="o">)</span>
2020.11.29 14:43:01.163530 <span class="o">[</span> 3643 <span class="o">]</span> <span class="o">{}</span> <Error> Application: DB::Exception: Requested cluster <span class="s1">'cluster_1shards_2replicas'</span> not found: <span class="k">while </span>loading database <span class="sb">`</span>default<span class="sb">`</span> from path /var/lib/clickhouse/metadata/default
</code></pre></div></div>
<p>删除之前的建表语句</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 default]# <span class="nb">rm</span> <span class="nt">-rf</span> /var/lib/clickhouse/metadata/default/<span class="k">*</span>.sql
</code></pre></div></div>
<p>启动<code class="language-plaintext highlighter-rouge">clickhouse-server</code></p>
<p>在<code class="language-plaintext highlighter-rouge">node01</code>和<code class="language-plaintext highlighter-rouge">node02</code>节点上创建数据库表</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- node01 节点</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="nv">`cluster_zk`</span>
<span class="p">(</span>
<span class="nv">`id`</span> <span class="n">Int32</span><span class="p">,</span>
<span class="nv">`website`</span> <span class="n">String</span><span class="p">,</span>
<span class="nv">`wechat`</span> <span class="n">String</span><span class="p">,</span>
<span class="nv">`FlightDate`</span> <span class="nb">Date</span><span class="p">,</span>
<span class="nb">Year</span> <span class="n">UInt16</span>
<span class="p">)</span>
<span class="n">ENGINE</span> <span class="o">=</span> <span class="n">ReplicatedMergeTree</span><span class="p">(</span><span class="s1">'/clickhouse/tables/cluster_zk'</span><span class="p">,</span> <span class="s1">'replica01'</span><span class="p">,</span> <span class="n">FlightDate</span><span class="p">,</span> <span class="p">(</span><span class="nb">Year</span><span class="p">,</span> <span class="n">FlightDate</span><span class="p">),</span> <span class="mi">8192</span><span class="p">);</span>
<span class="c1">-- node02节点</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="nv">`cluster_zk`</span>
<span class="p">(</span>
<span class="nv">`id`</span> <span class="n">Int32</span><span class="p">,</span>
<span class="nv">`website`</span> <span class="n">String</span><span class="p">,</span>
<span class="nv">`wechat`</span> <span class="n">String</span><span class="p">,</span>
<span class="nv">`FlightDate`</span> <span class="nb">Date</span><span class="p">,</span>
<span class="nb">Year</span> <span class="n">UInt16</span>
<span class="p">)</span>
<span class="n">ENGINE</span> <span class="o">=</span> <span class="n">ReplicatedMergeTree</span><span class="p">(</span><span class="s1">'/clickhouse/tables/cluster_zk'</span><span class="p">,</span> <span class="s1">'replica02'</span><span class="p">,</span> <span class="n">FlightDate</span><span class="p">,</span> <span class="p">(</span><span class="nb">Year</span><span class="p">,</span> <span class="n">FlightDate</span><span class="p">),</span> <span class="mi">8192</span><span class="p">);</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">node01</code>节点上插入数据</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster_zk</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="n">website</span><span class="p">,</span><span class="n">wechat</span><span class="p">,</span><span class="n">FlightDate</span><span class="p">,</span><span class="nb">Year</span><span class="p">)</span><span class="k">values</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="s1">'https://niocoder.com/'</span><span class="p">,</span><span class="s1">'java干货'</span><span class="p">,</span><span class="s1">'2020-11-28'</span><span class="p">,</span><span class="mi">2020</span><span class="p">);</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">node01</code>,<code class="language-plaintext highlighter-rouge">node02</code>节点上查询数据</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
node01 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from cluster_zk<span class="p">;</span> <span class="c"># node01节点</span>
SELECT <span class="k">*</span>
FROM cluster_zk
┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐
│ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │
└────┴───────────────────────┴──────────┴────────────┴──────┘
1 rows <span class="k">in </span>set. Elapsed: 0.004 sec.
node02 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from cluster_zk<span class="p">;</span> <span class="c"># node02节点</span>
SELECT <span class="k">*</span>
FROM cluster_zk
┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐
│ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │
└────┴───────────────────────┴──────────┴────────────┴──────┘
1 rows <span class="k">in </span>set. Elapsed: 0.004 sec.
</code></pre></div></div>
<p>查询<code class="language-plaintext highlighter-rouge">zk</code>信息</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>zk: localhost:2181<span class="o">(</span>CONNECTED<span class="o">)</span> 2] <span class="nb">ls</span> /clickhouse/tables/cluster_zk/replicas
<span class="o">[</span>replica02, replica01]
<span class="o">[</span>zk: localhost:2181<span class="o">(</span>CONNECTED<span class="o">)</span> 3]
</code></pre></div></div>
<h4 id="自动数据备份集群配置">自动数据备份集群配置</h4>
<p><code class="language-plaintext highlighter-rouge">node01</code>修改<code class="language-plaintext highlighter-rouge">metrika.xml</code>配置, 注意此处<code class="language-plaintext highlighter-rouge">internal_replication</code>为<code class="language-plaintext highlighter-rouge">true</code></p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><yandex></span>
<span class="nt"><clickhouse_remote_servers></span>
<span class="nt"><perftest_1shards_2replicas></span>
<span class="nt"><shard></span>
<span class="nt"><internal_replication></span>true<span class="nt"></internal_replication></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node01<span class="nt"></host></span>
<span class="nt"><port></span>9000<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node02<span class="nt"></host></span>
<span class="nt"><port></span>9000<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"></shard></span>
<span class="nt"></perftest_1shards_2replicas></span>
<span class="nt"></clickhouse_remote_servers></span>
<span class="nt"><zookeeper-servers></span>
<span class="nt"><node</span> <span class="na">index=</span><span class="s">"1"</span><span class="nt">></span>
<span class="nt"><host></span>node01<span class="nt"></host></span>
<span class="nt"><port></span>2181<span class="nt"></port></span>
<span class="nt"></node></span>
<span class="nt"><node</span> <span class="na">index=</span><span class="s">"2"</span><span class="nt">></span>
<span class="nt"><host></span>node02<span class="nt"></host></span>
<span class="nt"><port></span>2181<span class="nt"></port></span>
<span class="nt"></node></span>
<span class="nt"><node</span> <span class="na">index=</span><span class="s">"3"</span><span class="nt">></span>
<span class="nt"><host></span>node03<span class="nt"></host></span>
<span class="nt"><port></span>2181<span class="nt"></port></span>
<span class="nt"></node></span>
<span class="nt"></zookeeper-servers></span>
<span class="nt"></yandex></span>
</code></pre></div></div>
<p>将修改后的配置分发到<code class="language-plaintext highlighter-rouge">node02</code>机器上</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika.xml node02:<span class="nv">$PWD</span>
metrika.xml
</code></pre></div></div>
<p>查询集群信息</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">node01</span> <span class="p">:)</span> <span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="k">system</span><span class="p">.</span><span class="n">clusters</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="o">*</span>
<span class="k">FROM</span> <span class="k">system</span><span class="p">.</span><span class="n">clusters</span>
<span class="err">┌─</span><span class="k">cluster</span><span class="err">───────────────────────────┬─</span><span class="n">shard_num</span><span class="err">─┬─</span><span class="n">shard_weight</span><span class="err">─┬─</span><span class="n">replica_num</span><span class="err">─┬─</span><span class="n">host_name</span><span class="err">─┬─</span><span class="n">host_address</span><span class="err">───┬─</span><span class="n">port</span><span class="err">─┬─</span><span class="n">is_local</span><span class="err">─┬─</span><span class="k">user</span><span class="err">────┬─</span><span class="n">default_database</span><span class="err">─┬─</span><span class="n">errors_count</span><span class="err">─┬─</span><span class="n">estimated_recovery_time</span><span class="err">─┐</span>
<span class="err">│</span> <span class="n">perftest_1shards_2replicas</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="n">node01</span> <span class="err">│</span> <span class="mi">192</span><span class="p">.</span><span class="mi">168</span><span class="p">.</span><span class="mi">10</span><span class="p">.</span><span class="mi">100</span> <span class="err">│</span> <span class="mi">9000</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="k">default</span> <span class="err">│</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span>
<span class="err">│</span> <span class="n">perftest_1shards_2replicas</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">2</span> <span class="err">│</span> <span class="n">node02</span> <span class="err">│</span> <span class="mi">192</span><span class="p">.</span><span class="mi">168</span><span class="p">.</span><span class="mi">10</span><span class="p">.</span><span class="mi">110</span> <span class="err">│</span> <span class="mi">9000</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="k">default</span> <span class="err">│</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span>
<span class="err">│</span> <span class="n">test_cluster_two_shards</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">127</span><span class="p">.</span><span class="mi">0</span><span class="p">.</span><span class="mi">0</span><span class="p">.</span><span class="mi">1</span> <span class="err">│</span> <span class="mi">127</span><span class="p">.</span><span class="mi">0</span><span class="p">.</span><span class="mi">0</span><span class="p">.</span><span class="mi">1</span> <span class="err">│</span> <span class="mi">9000</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="k">default</span> <span class="err">│</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span>
<span class="err">│</span> <span class="n">test_cluster_two_shards</span> <span class="err">│</span> <span class="mi">2</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">127</span><span class="p">.</span><span class="mi">0</span><span class="p">.</span><span class="mi">0</span><span class="p">.</span><span class="mi">2</span> <span class="err">│</span> <span class="mi">127</span><span class="p">.</span><span class="mi">0</span><span class="p">.</span><span class="mi">0</span><span class="p">.</span><span class="mi">2</span> <span class="err">│</span> <span class="mi">9000</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="k">default</span> <span class="err">│</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span>
<span class="err">│</span> <span class="n">test_cluster_two_shards_localhost</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="n">localhost</span> <span class="err">│</span> <span class="p">::</span><span class="mi">1</span> <span class="err">│</span> <span class="mi">9000</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="k">default</span> <span class="err">│</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span>
<span class="err">│</span> <span class="n">test_cluster_two_shards_localhost</span> <span class="err">│</span> <span class="mi">2</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="n">localhost</span> <span class="err">│</span> <span class="p">::</span><span class="mi">1</span> <span class="err">│</span> <span class="mi">9000</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="k">default</span> <span class="err">│</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span>
<span class="err">│</span> <span class="n">test_shard_localhost</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="n">localhost</span> <span class="err">│</span> <span class="p">::</span><span class="mi">1</span> <span class="err">│</span> <span class="mi">9000</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="k">default</span> <span class="err">│</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span>
<span class="err">│</span> <span class="n">test_shard_localhost_secure</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="n">localhost</span> <span class="err">│</span> <span class="p">::</span><span class="mi">1</span> <span class="err">│</span> <span class="mi">9440</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="k">default</span> <span class="err">│</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span>
<span class="err">│</span> <span class="n">test_unavailable_shard</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="n">localhost</span> <span class="err">│</span> <span class="p">::</span><span class="mi">1</span> <span class="err">│</span> <span class="mi">9000</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="k">default</span> <span class="err">│</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span>
<span class="err">│</span> <span class="n">test_unavailable_shard</span> <span class="err">│</span> <span class="mi">2</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="n">localhost</span> <span class="err">│</span> <span class="p">::</span><span class="mi">1</span> <span class="err">│</span> <span class="mi">1</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="k">default</span> <span class="err">│</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span> <span class="mi">0</span> <span class="err">│</span>
<span class="err">└───────────────────────────────────┴───────────┴──────────────┴─────────────┴───────────┴────────────────┴──────┴──────────┴─────────┴──────────────────┴──────────────┴─────────────────────────┘</span>
<span class="mi">10</span> <span class="k">rows</span> <span class="k">in</span> <span class="k">set</span><span class="p">.</span> <span class="n">Elapsed</span><span class="p">:</span> <span class="mi">0</span><span class="p">.</span><span class="mi">018</span> <span class="n">sec</span><span class="p">.</span>
</code></pre></div></div>
<p>创建分布式表</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="k">default</span><span class="p">.</span><span class="n">clusterzk_all</span> <span class="k">AS</span> <span class="n">cluster_zk</span>
<span class="n">ENGINE</span> <span class="o">=</span> <span class="n">Distributed</span><span class="p">(</span><span class="n">perftest_1shards_2replicas</span><span class="p">,</span> <span class="k">default</span><span class="p">,</span> <span class="n">cluster_zk</span><span class="p">,</span> <span class="n">rand</span><span class="p">());</span>
</code></pre></div></div>
<p>分布式表查询数据</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ode01 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from clusterzk_all<span class="p">;</span>
SELECT <span class="k">*</span>
FROM clusterzk_all
┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐
│ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │
└────┴───────────────────────┴──────────┴────────────┴──────┘
1 rows <span class="k">in </span>set. Elapsed: 0.020 sec.
</code></pre></div></div>
<p>分布式表写入</p>
<p>上文已经提到,<code class="language-plaintext highlighter-rouge">internal_replication</code>为<code class="language-plaintext highlighter-rouge">true</code>,则通过分布表写入数据时,会自动找到“最健康”的副本写入,然后其他副本通过表自身的复制功能同步数据,最终达到数据一致。</p>
<h3 id="分片数据备份整合">分片+数据备份整合</h3>
<table>
<thead>
<tr>
<th>ip</th>
<th>主机</th>
<th><code class="language-plaintext highlighter-rouge">clickhouse</code></th>
<th>分片副本</th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="language-plaintext highlighter-rouge">192.168.10.100</code></td>
<td><code class="language-plaintext highlighter-rouge">node01</code></td>
<td><code class="language-plaintext highlighter-rouge">9000</code></td>
<td><code class="language-plaintext highlighter-rouge">01/01</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">192.168.10.100</code></td>
<td><code class="language-plaintext highlighter-rouge">node01</code></td>
<td><code class="language-plaintext highlighter-rouge">9001</code></td>
<td><code class="language-plaintext highlighter-rouge">03/02</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">192.168.10.100</code></td>
<td><code class="language-plaintext highlighter-rouge">node02</code></td>
<td><code class="language-plaintext highlighter-rouge">9000</code></td>
<td><code class="language-plaintext highlighter-rouge">02/01</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">192.168.10.100</code></td>
<td><code class="language-plaintext highlighter-rouge">node02</code></td>
<td><code class="language-plaintext highlighter-rouge">9001</code></td>
<td><code class="language-plaintext highlighter-rouge">01/02</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">192.168.10.100</code></td>
<td><code class="language-plaintext highlighter-rouge">node03</code></td>
<td><code class="language-plaintext highlighter-rouge">9000</code></td>
<td><code class="language-plaintext highlighter-rouge">03/01</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">192.168.10.100</code></td>
<td><code class="language-plaintext highlighter-rouge">node03</code></td>
<td><code class="language-plaintext highlighter-rouge">9001</code></td>
<td><code class="language-plaintext highlighter-rouge">02/02</code></td>
</tr>
</tbody>
</table>
<p>3分片2副本.</p>
<p>在<code class="language-plaintext highlighter-rouge">node01</code>,<code class="language-plaintext highlighter-rouge">node02</code>,<code class="language-plaintext highlighter-rouge">node03</code>的<code class="language-plaintext highlighter-rouge">9001</code>端口再启动一个<code class="language-plaintext highlighter-rouge">clickhouse-server</code>实例。</p>
<p>即<code class="language-plaintext highlighter-rouge">shard1</code>的两个副本放到<code class="language-plaintext highlighter-rouge">node01 9000</code>、<code class="language-plaintext highlighter-rouge">node02 9001</code>两个机器上,<code class="language-plaintext highlighter-rouge">shard2</code>的两个副本放到<code class="language-plaintext highlighter-rouge">node02 9000</code>、<code class="language-plaintext highlighter-rouge">node03 9001</code>上,<code class="language-plaintext highlighter-rouge">shard3</code>的两个副本放到<code class="language-plaintext highlighter-rouge">node03 9000</code>、<code class="language-plaintext highlighter-rouge">node01 9001</code>上.</p>
<h4 id="node01创建并修改config1xml"><code class="language-plaintext highlighter-rouge">node01</code>创建并修改<code class="language-plaintext highlighter-rouge">config1.xml</code></h4>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 clickhouse-server]# <span class="nb">cp</span> /etc/clickhouse-server/config.xml /etc/clickhouse-server/config1.xml
<span class="o">[</span>root@node01 clickhouse-server]# vim /etc/clickhouse-server/config1.xml
</code></pre></div></div>
<p>修改以下内容</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?xml version="1.0"?></span>
<span class="nt"><yandex></span>
<span class="c"><!--省略其他 --></span>
<span class="nt"><http_port></span>8124<span class="nt"></http_port></span>
<span class="nt"><tcp_port></span>9001<span class="nt"></tcp_port></span>
<span class="nt"><mysql_port></span>9005<span class="nt"></mysql_port></span>
<span class="nt"><interserver_http_port></span>9010<span class="nt"></interserver_http_port></span>
<span class="nt"><log></span>/var/log/clickhouse-server/clickhouse-server-1.log<span class="nt"></log></span>
<span class="nt"><errorlog></span>/var/log/clickhouse-server/clickhouse-server.err-1.log<span class="nt"></errorlog></span>
<span class="c"><!-- Path to data directory, with trailing slash. --></span>
<span class="nt"><path></span>/var/lib/clickhouse1/<span class="nt"></path></span>
<span class="c"><!-- Path to temporary data for processing hard queries. --></span>
<span class="nt"><tmp_path></span>/var/lib/clickhouse1/tmp/<span class="nt"></tmp_path></span>
<span class="nt"><user_files_path></span>/var/lib/clickhouse1/user_files/<span class="nt"></user_files_path></span>
<span class="nt"><format_schema_path></span>/var/lib/clickhouse1/format_schemas/<span class="nt"></format_schema_path></span>
<span class="nt"><include_from></span>/etc/clickhouse-server/metrika1.xml<span class="nt"></include_from></span>
<span class="c"><!--省略其他 --></span>
<span class="nt"></yandex></span>
</code></pre></div></div>
<h4 id="node01创建并修改metrikaxml"><code class="language-plaintext highlighter-rouge">node01</code>创建并修改<code class="language-plaintext highlighter-rouge">metrika.xml</code></h4>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><yandex></span>
<span class="c"><!--ck集群节点--></span>
<span class="nt"><clickhouse_remote_servers></span>
<span class="c"><!--ck集群名称--></span>
<span class="nt"><perftest_3shards_2replicas></span>
<span class="nt"><shard></span>
<span class="nt"><internal_replication></span>true<span class="nt"></internal_replication></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node01<span class="nt"></host></span>
<span class="nt"><port></span>9000<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node02<span class="nt"></host></span>
<span class="nt"><port></span>9001<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"></shard></span>
<span class="nt"><shard></span>
<span class="nt"><internal_replication></span>true<span class="nt"></internal_replication></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node02<span class="nt"></host></span>
<span class="nt"><port></span>9000<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node03<span class="nt"></host></span>
<span class="nt"><port></span>9001<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"></shard></span>
<span class="nt"><shard></span>
<span class="nt"><internal_replication></span>true<span class="nt"></internal_replication></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node03<span class="nt"></host></span>
<span class="nt"><port></span>9000<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node01<span class="nt"></host></span>
<span class="nt"><port></span>9001<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"></shard></span>
<span class="nt"></perftest_3shards_2replicas></span>
<span class="nt"></clickhouse_remote_servers></span>
<span class="c"><!--zookeeper相关配置--></span>
<span class="nt"><zookeeper-servers></span>
<span class="nt"><node</span> <span class="na">index=</span><span class="s">"1"</span><span class="nt">></span>
<span class="nt"><host></span>node01<span class="nt"></host></span>
<span class="nt"><port></span>2181<span class="nt"></port></span>
<span class="nt"></node></span>
<span class="nt"><node</span> <span class="na">index=</span><span class="s">"2"</span><span class="nt">></span>
<span class="nt"><host></span>node02<span class="nt"></host></span>
<span class="nt"><port></span>2181<span class="nt"></port></span>
<span class="nt"></node></span>
<span class="nt"><node</span> <span class="na">index=</span><span class="s">"3"</span><span class="nt">></span>
<span class="nt"><host></span>node03<span class="nt"></host></span>
<span class="nt"><port></span>2181<span class="nt"></port></span>
<span class="nt"></node></span>
<span class="nt"></zookeeper-servers></span>
<span class="nt"><macros></span>
<span class="nt"><shard></span>01<span class="nt"></shard></span> <span class="c"><!--分ID, 同一分片内的副本配置相同的分ID--></span>
<span class="nt"><replica></span>node01<span class="nt"></replica></span> <span class="c"><!--当前节点主机名--></span>
<span class="nt"></macros></span>
<span class="nt"><networks></span>
<span class="nt"><ip></span>::/0<span class="nt"></ip></span>
<span class="nt"></networks></span>
<span class="c"><!--压缩相关配置--></span>
<span class="nt"><clickhouse_compression></span>
<span class="nt"><case></span>
<span class="nt"><min_part_size></span>10000000000<span class="nt"></min_part_size></span>
<span class="nt"><min_part_size_ratio></span>0.01<span class="nt"></min_part_size_ratio></span>
<span class="nt"><method></span>lz4<span class="nt"></method></span> <span class="c"><!--压缩算法lz4压缩比zstd快, 更占磁盘--></span>
<span class="nt"></case></span>
<span class="nt"></clickhouse_compression></span>
<span class="nt"></yandex></span>
</code></pre></div></div>
<h4 id="复制metrikaxml文件为metrika1xml修改macros配置">复制<code class="language-plaintext highlighter-rouge">metrika.xml</code>文件为<code class="language-plaintext highlighter-rouge">metrika1.xml</code>,修改<code class="language-plaintext highlighter-rouge">macros</code>配置</h4>
<h5 id="node01metrikaxmlmacros配置"><code class="language-plaintext highlighter-rouge">node01</code><code class="language-plaintext highlighter-rouge">metrika.xml</code><code class="language-plaintext highlighter-rouge">macros</code>配置</h5>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><macros></span>
<span class="nt"><shard></span>01<span class="nt"></shard></span> <span class="c"><!--分ID, 同一分片内的副本配置相同的分ID--></span>
<span class="nt"><replica></span>node01<span class="nt"></replica></span> <span class="c"><!--当前节点主机名--></span>
<span class="nt"></macros></span>
</code></pre></div></div>
<h5 id="node01metrika1xmlmacros配置"><code class="language-plaintext highlighter-rouge">node01</code><code class="language-plaintext highlighter-rouge">metrika1.xml</code><code class="language-plaintext highlighter-rouge">macros</code>配置</h5>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><macros></span>
<span class="nt"><shard></span>03<span class="nt"></shard></span> <span class="c"><!--分ID, 同一分片内的副本配置相同的分ID--></span>
<span class="nt"><replica></span>node01<span class="nt"></replica></span> <span class="c"><!--当前节点主机名--></span>
<span class="nt"></macros></span>
</code></pre></div></div>
<h5 id="node02metrikaxmlmacros配置"><code class="language-plaintext highlighter-rouge">node02</code><code class="language-plaintext highlighter-rouge">metrika.xml</code><code class="language-plaintext highlighter-rouge">macros</code>配置</h5>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><macros></span>
<span class="nt"><shard></span>02<span class="nt"></shard></span> <span class="c"><!--分ID, 同一分片内的副本配置相同的分ID--></span>
<span class="nt"><replica></span>node02<span class="nt"></replica></span> <span class="c"><!--当前节点主机名--></span>
<span class="nt"></macros></span>
</code></pre></div></div>
<h5 id="node02metrika1xmlmacros配置"><code class="language-plaintext highlighter-rouge">node02</code><code class="language-plaintext highlighter-rouge">metrika1.xml</code><code class="language-plaintext highlighter-rouge">macros</code>配置</h5>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><macros></span>
<span class="nt"><shard></span>01<span class="nt"></shard></span> <span class="c"><!--分ID, 同一分片内的副本配置相同的分ID--></span>
<span class="nt"><replica></span>node02<span class="nt"></replica></span> <span class="c"><!--当前节点主机名--></span>
<span class="nt"></macros></span>
</code></pre></div></div>
<h5 id="node03metrikaxmlmacros配置"><code class="language-plaintext highlighter-rouge">node03</code><code class="language-plaintext highlighter-rouge">metrika.xml</code><code class="language-plaintext highlighter-rouge">macros</code>配置</h5>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><macros></span>
<span class="nt"><shard></span>03<span class="nt"></shard></span> <span class="c"><!--分ID, 同一分片内的副本配置相同的分ID--></span>
<span class="nt"><replica></span>node03<span class="nt"></replica></span> <span class="c"><!--当前节点主机名--></span>
<span class="nt"></macros></span>
</code></pre></div></div>
<h5 id="node03metrika1xmlmacros配置"><code class="language-plaintext highlighter-rouge">node03</code><code class="language-plaintext highlighter-rouge">metrika.1xml</code><code class="language-plaintext highlighter-rouge">macros</code>配置</h5>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><macros></span>
<span class="nt"><shard></span>02<span class="nt"></shard></span> <span class="c"><!--分ID, 同一分片内的副本配置相同的分ID--></span>
<span class="nt"><replica></span>node03<span class="nt"></replica></span> <span class="c"><!--当前节点主机名--></span>
<span class="nt"></macros></span>
</code></pre></div></div>
<h4 id="创建并修改clickhouse-server-1">创建并修改<code class="language-plaintext highlighter-rouge">clickhouse-server-1</code></h4>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 clickhouse-server]# <span class="nb">cp</span> /etc/rc.d/init.d/clickhouse-server /etc/rc.d/init.d/clickhouse-server-1
You have new mail <span class="k">in</span> /var/spool/mail/root
<span class="o">[</span>root@node01 clickhouse-server]# vim /etc/rc.d/init.d/clickhouse-server-1
</code></pre></div></div>
<p>修改以下内容</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">CLICKHOUSE_CONFIG</span><span class="o">=</span><span class="nv">$CLICKHOUSE_CONFDIR</span>/config1.xml
<span class="nv">CLICKHOUSE_PIDFILE</span><span class="o">=</span><span class="s2">"</span><span class="nv">$CLICKHOUSE_PIDDIR</span><span class="s2">/</span><span class="nv">$PROGRAM</span><span class="s2">-1.pid"</span>
</code></pre></div></div>
<h4 id="分发配置文件到node02和node03节点">分发配置文件到<code class="language-plaintext highlighter-rouge">node02</code>和<code class="language-plaintext highlighter-rouge">node03</code>节点</h4>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 clickhouse-server]# scp /etc/rc.d/init.d/clickhouse-server-1 node02:/etc/rc.d/init.d/
clickhouse-server-1 100% 11KB 4.0MB/s 00:00
You have new mail <span class="k">in</span> /var/spool/mail/root
<span class="o">[</span>root@node01 clickhouse-server]# scp /etc/rc.d/init.d/clickhouse-server-1 node03:/etc/rc.d/init.d/
clickhouse-server-1 100% 11KB 4.0MB/s 00:00
<span class="o">[</span>root@node01 clickhouse-server]# scp /etc/clickhouse-server/config1.xml node02:<span class="nv">$PWD</span>
config1.xml 100% 33KB 10.2MB/s 00:00
<span class="o">[</span>root@node01 clickhouse-server]# scp /etc/clickhouse-server/config1.xml node03:<span class="nv">$PWD</span>
config1.xml 100% 33KB 9.7MB/s 00:00
<span class="o">[</span>root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika.xml node02:<span class="nv">$PWD</span>
metrika.xml 100% 2008 1.0MB/s 00:00
You have new mail <span class="k">in</span> /var/spool/mail/root
<span class="o">[</span>root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika.xml node03:<span class="nv">$PWD</span>
metrika.xml 100% 2008 1.1MB/s 00:00
<span class="o">[</span>root@node01 clickhouse-server]#
<span class="o">[</span>root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika1.xml node02:<span class="nv">$PWD</span>
metrika1.xml 100% 2008 1.0MB/s 00:00
<span class="o">[</span>root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika1.xml node03:<span class="nv">$PWD</span>
metrika1.xml
</code></pre></div></div>
<p><strong>修改<code class="language-plaintext highlighter-rouge">node02</code>和<code class="language-plaintext highlighter-rouge">node03</code>的<code class="language-plaintext highlighter-rouge">macros</code>配置</strong></p>
<h4 id="启动clickhouse实例">启动<code class="language-plaintext highlighter-rouge">ClickHouse</code>实例</h4>
<p><code class="language-plaintext highlighter-rouge">node01</code>的<code class="language-plaintext highlighter-rouge">clickhouse-server-1</code>实例</p>
<p><code class="language-plaintext highlighter-rouge">node02</code>的<code class="language-plaintext highlighter-rouge">clickhouse-server-1</code>实例</p>
<p><code class="language-plaintext highlighter-rouge">node03</code>的<code class="language-plaintext highlighter-rouge">clickhouse-server</code>实例</p>
<p><code class="language-plaintext highlighter-rouge">node03</code>的<code class="language-plaintext highlighter-rouge">clickhouse-server-1</code>实例</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>service clickhouse-server restart
service clickhouse-server-1 restart
</code></pre></div></div>
<h4 id="查看集群信息">查看集群信息</h4>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>node01 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from system.clusters<span class="p">;</span>
SELECT <span class="k">*</span>
FROM system.clusters
┌─cluster───────────────────────────┬─shard_num─┬─shard_weight─┬─replica_num─┬─host_name─┬─host_address───┬─port─┬─is_local─┬─user────┬─default_database─┬─errors_count─┬─estimated_recovery_time─┐
│ perftest_3shards_2replicas │ 1 │ 1 │ 1 │ node01 │ 192.168.10.100 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ perftest_3shards_2replicas │ 1 │ 1 │ 2 │ node02 │ 192.168.10.110 │ 9001 │ 0 │ default │ │ 0 │ 0 │
│ perftest_3shards_2replicas │ 2 │ 1 │ 1 │ node02 │ 192.168.10.110 │ 9000 │ 0 │ default │ │ 0 │ 0 │
│ perftest_3shards_2replicas │ 2 │ 1 │ 2 │ node03 │ 192.168.10.120 │ 9001 │ 0 │ default │ │ 0 │ 0 │
│ perftest_3shards_2replicas │ 3 │ 1 │ 1 │ node03 │ 192.168.10.120 │ 9000 │ 0 │ default │ │ 0 │ 0 │
│ perftest_3shards_2replicas │ 3 │ 1 │ 2 │ node01 │ 192.168.10.100 │ 9001 │ 0 │ default │ │ 0 │ 0 │
│ test_cluster_two_shards │ 1 │ 1 │ 1 │ 127.0.0.1 │ 127.0.0.1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_cluster_two_shards │ 2 │ 1 │ 1 │ 127.0.0.2 │ 127.0.0.2 │ 9000 │ 0 │ default │ │ 0 │ 0 │
│ test_cluster_two_shards_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_cluster_two_shards_localhost │ 2 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_shard_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_shard_localhost_secure │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9440 │ 0 │ default │ │ 0 │ 0 │
│ test_unavailable_shard │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_unavailable_shard │ 2 │ 1 │ 1 │ localhost │ ::1 │ 1 │ 0 │ default │ │ 0 │ 0 │
└───────────────────────────────────┴───────────┴──────────────┴─────────────┴───────────┴────────────────┴──────┴──────────┴─────────┴──────────────────┴──────────────┴─────────────────────────┘
14 rows <span class="k">in </span>set. Elapsed: 0.019 sec.
</code></pre></div></div>
<h5 id="测试分片副本集群">测试分片+副本集群</h5>
<p>创建可复制表,<code class="language-plaintext highlighter-rouge">node01</code>节点执行即可,其他节点会自动创建。</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="nv">`cluster32r_local`</span> <span class="k">ON</span> <span class="k">cluster</span> <span class="n">perftest_3shards_2replicas</span>
<span class="p">(</span>
<span class="nv">`id`</span> <span class="n">Int32</span><span class="p">,</span>
<span class="nv">`website`</span> <span class="n">String</span><span class="p">,</span>
<span class="nv">`wechat`</span> <span class="n">String</span><span class="p">,</span>
<span class="nv">`FlightDate`</span> <span class="nb">Date</span><span class="p">,</span>
<span class="nb">Year</span> <span class="n">UInt16</span>
<span class="p">)</span>
<span class="n">ENGINE</span> <span class="o">=</span> <span class="n">ReplicatedMergeTree</span><span class="p">(</span><span class="s1">'/clickhouse/tables/{shard}/ontime'</span><span class="p">,</span><span class="s1">'{replica}'</span><span class="p">,</span> <span class="n">FlightDate</span><span class="p">,</span> <span class="p">(</span><span class="nb">Year</span><span class="p">,</span> <span class="n">FlightDate</span><span class="p">),</span> <span class="mi">8192</span><span class="p">);</span>
</code></pre></div></div>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>node01 :<span class="o">)</span> CREATE TABLE <span class="sb">`</span>cluster32r_local<span class="sb">`</span> ON cluster perftest_3shards_2replicas
:-] <span class="o">(</span>
:-] <span class="sb">`</span><span class="nb">id</span><span class="sb">`</span> Int32,
:-] <span class="sb">`</span>website<span class="sb">`</span> String,
:-] <span class="sb">`</span>wechat<span class="sb">`</span> String,
:-] <span class="sb">`</span>FlightDate<span class="sb">`</span> Date,
:-] Year UInt16
:-] <span class="o">)</span>
:-] ENGINE <span class="o">=</span> ReplicatedMergeTree<span class="o">(</span><span class="s1">'/clickhouse/tables/{shard}/ontime'</span>,<span class="s1">'{replica}'</span>, FlightDate, <span class="o">(</span>Year, FlightDate<span class="o">)</span>, 8192<span class="o">)</span><span class="p">;</span>
CREATE TABLE cluster32r_local ON CLUSTER perftest_3shards_2replicas
<span class="o">(</span>
<span class="sb">`</span><span class="nb">id</span><span class="sb">`</span> Int32,
<span class="sb">`</span>website<span class="sb">`</span> String,
<span class="sb">`</span>wechat<span class="sb">`</span> String,
<span class="sb">`</span>FlightDate<span class="sb">`</span> Date,
<span class="sb">`</span>Year<span class="sb">`</span> UInt16
<span class="o">)</span>
ENGINE <span class="o">=</span> ReplicatedMergeTree<span class="o">(</span><span class="s1">'/clickhouse/tables/{shard}/ontime'</span>, <span class="s1">'{replica}'</span>, FlightDate, <span class="o">(</span>Year, FlightDate<span class="o">)</span>, 8192<span class="o">)</span>
┌─host───┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
│ node03 │ 9001 │ 0 │ │ 5 │ 0 │
│ node03 │ 9000 │ 0 │ │ 4 │ 0 │
│ node01 │ 9001 │ 0 │ │ 3 │ 0 │
│ node01 │ 9000 │ 0 │ │ 2 │ 0 │
│ node02 │ 9000 │ 0 │ │ 1 │ 0 │
└────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘
┌─host───┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐
│ node02 │ 9001 │ 0 │ │ 0 │ 0 │
└────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘
6 rows <span class="k">in </span>set. Elapsed: 46.994 sec.
</code></pre></div></div>
<p>创建分布式表</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">cluster32r_all</span> <span class="k">AS</span> <span class="n">cluster32r_local</span> <span class="n">ENGINE</span> <span class="o">=</span> <span class="n">Distributed</span><span class="p">(</span><span class="n">perftest_3shards_2replicas</span><span class="p">,</span> <span class="k">default</span><span class="p">,</span> <span class="n">cluster32r_local</span><span class="p">,</span> <span class="n">rand</span><span class="p">());</span>
</code></pre></div></div>
<p>往第一个<code class="language-plaintext highlighter-rouge">shard</code>的副本插入数据(<code class="language-plaintext highlighter-rouge">node01 9000</code>),可以在第二个副本中查看数据(<code class="language-plaintext highlighter-rouge">node02 9001</code>)</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster32r_local</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="n">website</span><span class="p">,</span><span class="n">wechat</span><span class="p">,</span><span class="n">FlightDate</span><span class="p">,</span><span class="nb">Year</span><span class="p">)</span><span class="k">values</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="s1">'https://niocoder.com/'</span><span class="p">,</span><span class="s1">'java干货'</span><span class="p">,</span><span class="s1">'2020-11-28'</span><span class="p">,</span><span class="mi">2020</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster32r_local</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="n">website</span><span class="p">,</span><span class="n">wechat</span><span class="p">,</span><span class="n">FlightDate</span><span class="p">,</span><span class="nb">Year</span><span class="p">)</span><span class="k">values</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="s1">'http://www.merryyou.cn/'</span><span class="p">,</span><span class="s1">'javaganhuo'</span><span class="p">,</span><span class="s1">'2020-11-28'</span><span class="p">,</span><span class="mi">2020</span><span class="p">);</span>
</code></pre></div></div>
<p>使用客户端链接<code class="language-plaintext highlighter-rouge">node02 9001</code>实例查看</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node02 ~]# clickhouse-client <span class="nt">--port</span> 9001 <span class="nt">-m</span>
ClickHouse client version 20.8.3.18.
Connecting to localhost:9001 as user default.
Connected to ClickHouse server version 20.8.3 revision 54438.
node02 :<span class="o">)</span> show tables<span class="p">;</span>
SHOW TABLES
┌─name─────────────┐
│ cluster32r_local │
└──────────────────┘
1 rows <span class="k">in </span>set. Elapsed: 0.010 sec.
node02 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from cluster32r_local<span class="p">;</span>
SELECT <span class="k">*</span>
FROM cluster32r_local
┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐
│ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │
└────┴─────────────────────────┴────────────┴────────────┴──────┘
┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐
│ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │
└────┴───────────────────────┴──────────┴────────────┴──────┘
2 rows <span class="k">in </span>set. Elapsed: 0.018 sec.
</code></pre></div></div>
<p>分布式表查询</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>node01 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from cluster32r_all<span class="p">;</span>
SELECT <span class="k">*</span>
FROM cluster32r_all
┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐
│ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │
└────┴─────────────────────────┴────────────┴────────────┴──────┘
┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐
│ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │
└────┴───────────────────────┴──────────┴────────────┴──────┘
2 rows <span class="k">in </span>set. Elapsed: 0.030 sec.
</code></pre></div></div>
<p>所有副本节点均可本地表和分布式表均可读写数据</p>
<h3 id="下载">下载</h3>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/wechat/gzh.png" alt="" /></p>
<blockquote>
<p>关注微信公众号java干货回复 【clickhouse】</p>
</blockquote>郑龙飞重叠泪痕缄锦字,人生只有情难死。ClickHouse集群搭建(一)2020-11-28T00:00:00+00:002020-11-28T00:00:00+00:00https://niocoder.com//2020/11/28/ClickHouse%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA1<blockquote>
<p>满目山河空念远,落花风雨更伤春。</p>
</blockquote>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java42.jpg" alt="" /></p>
<h2 id="clickhouse概述"><code class="language-plaintext highlighter-rouge">ClickHouse</code>概述</h2>
<h3 id="什么是clickhouse">什么是<code class="language-plaintext highlighter-rouge">ClickHouse</code>?</h3>
<p><code class="language-plaintext highlighter-rouge">ClickHouse</code> 是俄罗斯的<code class="language-plaintext highlighter-rouge">Yandex</code>于2016年开源的列式存储数据库<code class="language-plaintext highlighter-rouge">(DBMS)</code>,主要用于在线分析处理查询<code class="language-plaintext highlighter-rouge">(OLAP)</code>,能够使用<code class="language-plaintext highlighter-rouge">SQL</code>查询实时生成分析数据报告。</p>
<h3 id="什么是列式存储-">什么是列式存储 ?</h3>
<p>以下面表为例</p>
<table>
<thead>
<tr>
<th><code class="language-plaintext highlighter-rouge">id</code></th>
<th><code class="language-plaintext highlighter-rouge">website</code></th>
<th><code class="language-plaintext highlighter-rouge">wechat</code></th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><code class="language-plaintext highlighter-rouge">https://niocoder.com/</code></td>
<td><code class="language-plaintext highlighter-rouge">java干货</code></td>
</tr>
<tr>
<td>2</td>
<td><code class="language-plaintext highlighter-rouge">http://www.merryyou.cn/</code></td>
<td><code class="language-plaintext highlighter-rouge">javaganhuo</code></td>
</tr>
</tbody>
</table>
<p>采用行式存储时,数据在磁盘上的组织结构为:</p>
<table>
<thead>
<tr>
<th><code class="language-plaintext highlighter-rouge">Row1</code></th>
<th> </th>
<th> </th>
<th><code class="language-plaintext highlighter-rouge">Row2</code></th>
<th> </th>
<th> </th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><code class="language-plaintext highlighter-rouge">https://niocoder.com/</code></td>
<td><code class="language-plaintext highlighter-rouge">java干货</code></td>
<td>2</td>
<td><code class="language-plaintext highlighter-rouge">http://www.merryyou.cn/</code></td>
<td><code class="language-plaintext highlighter-rouge">javaganhuo</code></td>
</tr>
</tbody>
</table>
<p>好处是想查某条记录所有的属性时,可以通过一次磁盘查找加顺序读取就可以。但是当想查所有记录<code class="language-plaintext highlighter-rouge">wechat</code>时,需要不停的查找,或者全表扫描才行,遍历的很多数据都是不需要的。</p>
<p>而采用列式存储时,数据在磁盘上的组织结构为:</p>
<table>
<thead>
<tr>
<th><code class="language-plaintext highlighter-rouge">col1</code></th>
<th> </th>
<th><code class="language-plaintext highlighter-rouge">col2</code></th>
<th> </th>
<th><code class="language-plaintext highlighter-rouge">col3</code></th>
<th> </th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
<td><code class="language-plaintext highlighter-rouge">https://niocoder.com/</code></td>
<td><code class="language-plaintext highlighter-rouge">http://www.merryyou.cn/</code></td>
<td><code class="language-plaintext highlighter-rouge">java干货</code></td>
<td><code class="language-plaintext highlighter-rouge">javaganhuo</code></td>
</tr>
</tbody>
</table>
<p>这时想查所有记录的<code class="language-plaintext highlighter-rouge">wechat</code>只需把<code class="language-plaintext highlighter-rouge">col3</code>那一列拿出来即可。</p>
<h2 id="集群环境搭建">集群环境搭建</h2>
<p>在 安装<code class="language-plaintext highlighter-rouge">ClickHouse</code> 具体开始前, 先来搭建一下环境,软件包下载见末尾。</p>
<h3 id="创建虚拟机">创建虚拟机</h3>
<h4 id="安装虚拟机-vmware">安装虚拟机 <code class="language-plaintext highlighter-rouge">VMWare</code></h4>
<p>安装<code class="language-plaintext highlighter-rouge">Vmware虚拟机</code></p>
<h4 id="导入-centos">导入 <code class="language-plaintext highlighter-rouge">CentOS</code></h4>
<p>将下载的<code class="language-plaintext highlighter-rouge">CentOS</code>系统中导入到 <code class="language-plaintext highlighter-rouge">VMWare</code>中</p>
<p><strong>注意事项</strong>:windows系统确认所有的关于VmWare的服务都已经启动,</p>
<p><strong>确认好<code class="language-plaintext highlighter-rouge">VmWare</code>生成的网关地址,另外确认VmNet8网卡已经配置好了IP地址。</strong></p>
<p>更多关于<code class="language-plaintext highlighter-rouge">VmWare</code>网络模式参考<a href="https://mp.weixin.qq.com/s/p9K7WGvRUGW79eE800yLEg">VMware虚拟机三种网络模式详解</a></p>
<h4 id="集群规划">集群规划</h4>
<table>
<thead>
<tr>
<th>IP</th>
<th>主机名</th>
<th>环境配置</th>
<th>安装</th>
<th><code class="language-plaintext highlighter-rouge">ClickHouse</code></th>
</tr>
</thead>
<tbody>
<tr>
<td><code class="language-plaintext highlighter-rouge">192.168.10.100</code></td>
<td><code class="language-plaintext highlighter-rouge">node01</code></td>
<td>关防火墙, <code class="language-plaintext highlighter-rouge">host</code>映射, 时钟同步</td>
<td><code class="language-plaintext highlighter-rouge">JDK</code>, <code class="language-plaintext highlighter-rouge">Zookeeper</code></td>
<td><code class="language-plaintext highlighter-rouge">clickhouse-server 9000</code> <code class="language-plaintext highlighter-rouge">clickhouse-server 9001</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">192.168.10.110</code></td>
<td><code class="language-plaintext highlighter-rouge">node02</code></td>
<td>关防火墙, <code class="language-plaintext highlighter-rouge">host</code>映射, 时钟同步</td>
<td><code class="language-plaintext highlighter-rouge">JDK</code>, <code class="language-plaintext highlighter-rouge">Zookeeper</code></td>
<td><code class="language-plaintext highlighter-rouge">clickhouse-server 9000</code> <code class="language-plaintext highlighter-rouge">clickhouse-server 9001</code></td>
</tr>
<tr>
<td><code class="language-plaintext highlighter-rouge">192.168.10.120</code></td>
<td><code class="language-plaintext highlighter-rouge">node03</code></td>
<td>关防火墙, <code class="language-plaintext highlighter-rouge">host</code>映射, 时钟同步</td>
<td><code class="language-plaintext highlighter-rouge">JDK</code>, <code class="language-plaintext highlighter-rouge">Zookeeper</code></td>
<td><code class="language-plaintext highlighter-rouge">clickhouse-server 9000</code> <code class="language-plaintext highlighter-rouge">clickhouse-server 9001</code></td>
</tr>
</tbody>
</table>
<h3 id="配置每台主机">配置每台主机</h3>
<p>更改<code class="language-plaintext highlighter-rouge">ip</code>地址和<code class="language-plaintext highlighter-rouge">HWADDR</code>地址</p>
<p><code class="language-plaintext highlighter-rouge">vim /etc/sysconfig/network-scripts/ifcfg-eth0</code></p>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/wechat/ch01.png" alt="" /></p>
<p><code class="language-plaintext highlighter-rouge">HWADDR</code>地址查看</p>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/wechat/ch04.png" alt="" /></p>
<p>修改主机名(重启后永久生效)</p>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/wechat/ch02.png" alt="" /></p>
<p><code class="language-plaintext highlighter-rouge">vim /etc/hostname</code></p>
<p>设置<code class="language-plaintext highlighter-rouge">hosts</code>域名映射</p>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/wechat/ch03.png" alt="" /></p>
<p><code class="language-plaintext highlighter-rouge">vim /etc/hosts</code></p>
<h4 id="关闭防火墙">关闭防火墙</h4>
<p>三台机器执行以下命令</p>
<p><code class="language-plaintext highlighter-rouge">systemctl status firewalld.service #查看防火墙状态</code>
<code class="language-plaintext highlighter-rouge">systemctl stop firewalld.service #关闭防火墙</code>
<code class="language-plaintext highlighter-rouge">systemctl disable firewalld.service #永久关闭防火墙</code></p>
<h4 id="免密登录">免密登录</h4>
<p>为了方便传输文件,三台机器之前配置免密登录.</p>
<ul>
<li><strong>免密 SSH 登录的原理</strong>
<ol>
<li>需要先在 B节点 配置 A节点 的公钥</li>
<li>A节点 请求 B节点 要求登录</li>
<li>B节点 使用 A节点 的公钥, 加密一段随机文本</li>
<li>A节点 使用私钥解密, 并发回给 B节点</li>
<li>B节点 验证文本是否正确</li>
</ol>
</li>
</ul>
<h5 id="三台机器生成公钥与私钥">三台机器生成公钥与私钥</h5>
<p>在三台机器执行以下命令,生成公钥与私钥</p>
<p><code class="language-plaintext highlighter-rouge">ssh-keygen -t rsa</code></p>
<p>执行该命令之后,按下三个回车即可</p>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/wechat/ch05.png" alt="" /></p>
<h5 id="拷贝公钥到node01机器">拷贝公钥到<code class="language-plaintext highlighter-rouge">node01</code>机器</h5>
<p>三台机器将拷贝公钥到<code class="language-plaintext highlighter-rouge">node01</code>机器</p>
<p>三台机器执行命令:</p>
<p><code class="language-plaintext highlighter-rouge">ssh-copy-id node01</code></p>
<h5 id="复制node01机器的认证到其他机器">复制<code class="language-plaintext highlighter-rouge">node01</code>机器的认证到其他机器</h5>
<p>将第一台机器的公钥拷贝到其他机器上</p>
<p>在<code class="language-plaintext highlighter-rouge">node01</code>机器上面执行以下命令</p>
<p><code class="language-plaintext highlighter-rouge">scp /root/.ssh/authorized_keys node02:/root/.ssh</code></p>
<p><code class="language-plaintext highlighter-rouge">scp /root/.ssh/authorized_keys node03:/root/.ssh</code></p>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/wechat/ch06.png" alt="" /></p>
<h4 id="设置时钟同步服务">设置时钟同步服务</h4>
<ul>
<li>为什么需要时间同步
<ul>
<li>因为很多分布式系统是有状态的, 比如说存储一个数据, A节点 记录的时间是 1, B节点 记录的时间是 2, 就会出问题</li>
</ul>
</li>
</ul>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">## 安装</span>
yum <span class="nb">install</span> <span class="nt">-y</span> ntp
<span class="c">## 启动定时任务</span>
crontab <span class="nt">-e</span>
</code></pre></div></div>
<p>随后在输入界面键入</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">*</span>/1 <span class="k">*</span> <span class="k">*</span> <span class="k">*</span> <span class="k">*</span> /usr/sbin/ntpdate ntp4.aliyun.com<span class="p">;</span>
</code></pre></div></div>
<h4 id="安装jdk">安装<code class="language-plaintext highlighter-rouge">JDK</code></h4>
<p>上传<code class="language-plaintext highlighter-rouge">jdk</code>并解压然后配置环境变量</p>
<p>所有软件的安装路径</p>
<p><code class="language-plaintext highlighter-rouge">mkdir -p /export/servers #node01,node02,node03机器上执行</code></p>
<p>所有软件压缩包的存放路径</p>
<p><code class="language-plaintext highlighter-rouge">mkdir -p /export/softwares # 创建文件夹</code></p>
<p>安装<code class="language-plaintext highlighter-rouge">rz</code>和<code class="language-plaintext highlighter-rouge">sz</code>命令</p>
<p><code class="language-plaintext highlighter-rouge">yum -y install lrzsz # 安装rz sz 命令</code></p>
<p>上传<code class="language-plaintext highlighter-rouge">jdk</code>安装包到<code class="language-plaintext highlighter-rouge">/export/softwares</code>路径下去,并解压</p>
<p><code class="language-plaintext highlighter-rouge">tar -zxvf jdk-8u141-linux-x64.tar.gz -C ../servers/</code></p>
<p>配置环境变量</p>
<p><code class="language-plaintext highlighter-rouge">vim /etc/profile</code></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">JAVA_HOME</span><span class="o">=</span>/export/servers/jdk1.8.0_141
<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span>:<span class="nv">$JAVA_HOME</span>/bin:<span class="nv">$PATH</span>
</code></pre></div></div>
<p>执行以下命令将<code class="language-plaintext highlighter-rouge">jdk</code>安装包分发到<code class="language-plaintext highlighter-rouge">node02</code>和<code class="language-plaintext highlighter-rouge">node03</code>节点上</p>
<p><code class="language-plaintext highlighter-rouge">scp -r /export/servers/jdk1.8.0_141/ node02:/export/servers/</code>
<code class="language-plaintext highlighter-rouge">scp -r /export/servers/jdk1.8.0_141/ node03:/export/servers/</code>
<code class="language-plaintext highlighter-rouge">scp /etc/profile node02:/etc/profile</code>
<code class="language-plaintext highlighter-rouge">scp /etc/profile node03:/etc/profile</code></p>
<p>刷新环境变量</p>
<p><code class="language-plaintext highlighter-rouge">source /etc/profile</code></p>
<h4 id="安装zookeeper">安装<code class="language-plaintext highlighter-rouge">Zookeeper</code></h4>
<table>
<thead>
<tr>
<th>服务器IP</th>
<th>主机名</th>
<th>myid的值</th>
</tr>
</thead>
<tbody>
<tr>
<td>192.168.10.100</td>
<td>node01</td>
<td>1</td>
</tr>
<tr>
<td>192.168.10.110</td>
<td>node02</td>
<td>2</td>
</tr>
<tr>
<td>192.168.10.120</td>
<td>node03</td>
<td>3</td>
</tr>
</tbody>
</table>
<p>上传<code class="language-plaintext highlighter-rouge">zookeeper</code>安装包到<code class="language-plaintext highlighter-rouge">/export/softwares</code>路径下去,并解压</p>
<p><code class="language-plaintext highlighter-rouge">tar -zxvf zookeeper-3.4.9.tar.gz -C ../servers/ </code></p>
<p><code class="language-plaintext highlighter-rouge">node01</code>修改配置文件如下</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd</span> /export/servers/zookeeper-3.4.9/conf/
<span class="nb">cp </span>zoo_sample.cfg zoo.cfg
<span class="c"># 创建数据存放节点</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> /export/servers/zookeeper-3.4.9/zkdatas/
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">vim zoo.cfg</code></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">dataDir</span><span class="o">=</span>/export/servers/zookeeper-3.4.9/zkdatas
<span class="c"># 保留多少个快照</span>
autopurge.snapRetainCount<span class="o">=</span>3
<span class="c"># 日志多少小时清理一次</span>
autopurge.purgeInterval<span class="o">=</span>1
<span class="c"># 集群中服务器地址</span>
server.1<span class="o">=</span>node01:2888:3888
server.2<span class="o">=</span>node02:2888:3888
server.3<span class="o">=</span>node03:2888:3888
</code></pre></div></div>
<p>在<code class="language-plaintext highlighter-rouge">node01</code>机器上</p>
<p><code class="language-plaintext highlighter-rouge">/export/servers/zookeeper-3.4.9/zkdatas/</code>这个路径下创建一个文件,文件名为<code class="language-plaintext highlighter-rouge">myid</code> ,文件内容为<code class="language-plaintext highlighter-rouge">1</code></p>
<p><code class="language-plaintext highlighter-rouge">echo 1 > /export/servers/zookeeper-3.4.9/zkdatas/myid</code></p>
<p>安装包分发到其他机器</p>
<p><code class="language-plaintext highlighter-rouge">node01</code>机器上面执行以下两个命令</p>
<p><code class="language-plaintext highlighter-rouge">scp -r /export/servers/zookeeper-3.4.9/ node02:/export/servers/</code></p>
<p><code class="language-plaintext highlighter-rouge">scp -r /export/servers/zookeeper-3.4.9/ node03:/export/servers/</code></p>
<p><code class="language-plaintext highlighter-rouge">node02</code>机器上修改<code class="language-plaintext highlighter-rouge">myid</code>的值为<code class="language-plaintext highlighter-rouge">2</code></p>
<p><code class="language-plaintext highlighter-rouge">echo 2 > /export/servers/zookeeper-3.4.9/zkdatas/myid</code></p>
<p><code class="language-plaintext highlighter-rouge">node03</code>机器上修改<code class="language-plaintext highlighter-rouge">myid</code>的值为<code class="language-plaintext highlighter-rouge">3</code></p>
<p><code class="language-plaintext highlighter-rouge">echo 3 > /export/servers/zookeeper-3.4.9/zkdatas/myid</code></p>
<p>启动<code class="language-plaintext highlighter-rouge">zookeeper</code>服务</p>
<p><code class="language-plaintext highlighter-rouge">node01</code>,<code class="language-plaintext highlighter-rouge">node02</code>,<code class="language-plaintext highlighter-rouge">node03</code>机器都要执行</p>
<p><code class="language-plaintext highlighter-rouge">/export/servers/zookeeper-3.4.9/bin/zkServer.sh start</code></p>
<p>查看启动状态</p>
<p><code class="language-plaintext highlighter-rouge">/export/servers/zookeeper-3.4.9/bin/zkServer.sh status</code></p>
<h3 id="安装前准备">安装前准备</h3>
<h4 id="cpu是否支持sse42">CPU是否支持SSE4.2</h4>
<p>查看CPU是否支持SSE4.2指令集</p>
<p><code class="language-plaintext highlighter-rouge">grep -q sse4_2 /proc/cpuinfo && echo "SSE 4.2 supported" || echo "SSE 4.2 not supported"
</code></p>
<h4 id="安装必要依赖">安装必要依赖</h4>
<p><code class="language-plaintext highlighter-rouge">yum install -y unixODBC libicudata </code></p>
<p><code class="language-plaintext highlighter-rouge">yum install -y libxml2-devel expat-devel libicu-devel</code></p>
<h3 id="安装clickhouse">安装<code class="language-plaintext highlighter-rouge">ClickHouse</code></h3>
<h4 id="单机模式">单机模式</h4>
<h5 id="上传4个文件到node01机器optsoftware">上传4个文件到<code class="language-plaintext highlighter-rouge">node01</code>机器<code class="language-plaintext highlighter-rouge">/opt/software/</code></h5>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 softwares]# ll
total 306776
<span class="nt">-rw-r--r--</span><span class="nb">.</span> 1 root root 6384 Nov 2 22:43 clickhouse-client-20.8.3.18-1.el7.x86_64.rpm
<span class="nt">-rw-r--r--</span><span class="nb">.</span> 1 root root 69093220 Nov 2 22:48 clickhouse-common-static-20.8.3.18-1.el7.x86_64.rpm
<span class="nt">-rw-r--r--</span><span class="nb">.</span> 1 root root 36772044 Nov 2 22:51 clickhouse-server-20.8.3.18-1.el7.x86_64.rpm
<span class="nt">-rw-r--r--</span><span class="nb">.</span> 1 root root 14472 Nov 2 22:43 clickhouse-server-common-20.8.3.18-1.el7.x86_64.rpm
</code></pre></div></div>
<h5 id="分别安装这4个rpm安装包">分别安装这4个<code class="language-plaintext highlighter-rouge">rpm</code>安装包</h5>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 softwares]# rpm <span class="nt">-ivh</span> clickhouse-common-static-20.8.3.18-1.el7.x86_64.rpm
Preparing... <span class="c">################################# [100%]</span>
Updating / installing...
1:clickhouse-common-static-20.8.3.1################################# <span class="o">[</span>100%]
<span class="o">[</span>root@node01 softwares]# rpm <span class="nt">-ivh</span> clickhouse-server-common-20.8.3.18-1.el7.x86_64.rpm
Preparing... <span class="c">################################# [100%]</span>
Updating / installing...
1:clickhouse-server-common-20.8.3.1################################# <span class="o">[</span>100%]
<span class="o">[</span>root@node01 softwares]# rpm <span class="nt">-ivh</span> clickhouse-server-20.8.3.18-1.el7.x86_64.rpm
Preparing... <span class="c">################################# [100%]</span>
Updating / installing...
1:clickhouse-server-20.8.3.18-1.el7################################# <span class="o">[</span>100%]
Create user clickhouse.clickhouse with datadir /var/lib/clickhouse
<span class="o">[</span>root@node01 softwares]# rpm <span class="nt">-ivh</span> clickhouse-client-20.8.3.18-1.el7.x86_64.rpm
Preparing... <span class="c">################################# [100%]</span>
Updating / installing...
1:clickhouse-client-20.8.3.18-1.el7################################# <span class="o">[</span>100%]
Create user clickhouse.clickhouse with datadir /var/lib/clickhouse
</code></pre></div></div>
<h5 id="rpm安装完毕后clickhouse-server和clickhouse-client配置目录如下"><code class="language-plaintext highlighter-rouge">rpm</code>安装完毕后,<code class="language-plaintext highlighter-rouge">clickhouse-server</code>和<code class="language-plaintext highlighter-rouge">clickhouse-client</code>配置目录如下</h5>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 softwares]# ll /etc/clickhouse-server/
total 44
<span class="nt">-rw-r--r--</span><span class="nb">.</span> 1 root root 33738 Oct 6 06:05 config.xml
<span class="nt">-rw-r--r--</span><span class="nb">.</span> 1 root root 5587 Oct 6 06:05 users.xml
<span class="o">[</span>root@node01 softwares]# ll /etc/clickhouse-client/
total 4
drwxr-xr-x. 2 clickhouse clickhouse 6 Nov 28 22:19 conf.d
<span class="nt">-rw-r--r--</span><span class="nb">.</span> 1 clickhouse clickhouse 1568 Oct 6 04:44 config.xml
</code></pre></div></div>
<h5 id="etcclickhouse-server的configxml为clickhouse核心配置文件主要内容如下"><code class="language-plaintext highlighter-rouge">/etc/clickhouse-server/</code>的<code class="language-plaintext highlighter-rouge">config.xml</code>为<code class="language-plaintext highlighter-rouge">ClickHouse</code>核心配置文件,主要内容如下</h5>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?xml version="1.0"?></span>
<span class="nt"><yandex></span>
<span class="c"><!-- 日志 --></span>
<span class="nt"><logger></span>
<span class="nt"><level></span>trace<span class="nt"></level></span>
<span class="nt"><log></span>/data1/clickhouse/log/server.log<span class="nt"></log></span>
<span class="nt"><errorlog></span>/data1/clickhouse/log/error.log<span class="nt"></errorlog></span>
<span class="nt"><size></span>1000M<span class="nt"></size></span>
<span class="nt"><count></span>10<span class="nt"></count></span>
<span class="nt"></logger></span>
<span class="c"><!-- 端口 --></span>
<span class="nt"><http_port></span>8123<span class="nt"></http_port></span>
<span class="nt"><tcp_port></span>9000<span class="nt"></tcp_port></span>
<span class="nt"><interserver_http_port></span>9009<span class="nt"></interserver_http_port></span>
<span class="c"><!-- 本机域名 --></span>
<span class="nt"><interserver_http_host></span>这里需要用域名,如果后续用到复制的话<span class="nt"></interserver_http_host></span>
<span class="c"><!-- 监听IP --></span>
<span class="nt"><listen_host></span>0.0.0.0<span class="nt"></listen_host></span>
<span class="c"><!-- 最大连接数 --></span>
<span class="nt"><max_connections></span>64<span class="nt"></max_connections></span>
<span class="c"><!-- 没搞懂的参数 --></span>
<span class="nt"><keep_alive_timeout></span>3<span class="nt"></keep_alive_timeout></span>
<span class="c"><!-- 最大并发查询数 --></span>
<span class="nt"><max_concurrent_queries></span>16<span class="nt"></max_concurrent_queries></span>
<span class="c"><!-- 单位是B --></span>
<span class="nt"><uncompressed_cache_size></span>8589934592<span class="nt"></uncompressed_cache_size></span>
<span class="nt"><mark_cache_size></span>10737418240<span class="nt"></mark_cache_size></span>
<span class="c"><!-- 存储路径 --></span>
<span class="nt"><path></span>/data1/clickhouse/<span class="nt"></path></span>
<span class="nt"><tmp_path></span>/data1/clickhouse/tmp/<span class="nt"></tmp_path></span>
<span class="c"><!-- user配置 --></span>
<span class="nt"><users_config></span>users.xml<span class="nt"></users_config></span>
<span class="nt"><default_profile></span>default<span class="nt"></default_profile></span>
<span class="nt"><log_queries></span>1<span class="nt"></log_queries></span>
<span class="nt"><default_database></span>default<span class="nt"></default_database></span>
<span class="nt"><remote_servers</span> <span class="na">incl=</span><span class="s">"clickhouse_remote_servers"</span> <span class="nt">/></span>
<span class="nt"><zookeeper</span> <span class="na">incl=</span><span class="s">"zookeeper-servers"</span> <span class="na">optional=</span><span class="s">"true"</span> <span class="nt">/></span>
<span class="nt"><macros</span> <span class="na">incl=</span><span class="s">"macros"</span> <span class="na">optional=</span><span class="s">"true"</span> <span class="nt">/></span>
<span class="c"><!-- 没搞懂的参数 --></span>
<span class="nt"><builtin_dictionaries_reload_interval></span>3600<span class="nt"></builtin_dictionaries_reload_interval></span>
<span class="c"><!-- 控制大表的删除 --></span>
<span class="nt"><max_table_size_to_drop></span>0<span class="nt"></max_table_size_to_drop></span>
<span class="nt"><include_from></span>/data1/clickhouse/metrika.xml<span class="nt"></include_from></span>
<span class="nt"></yandex></span>
</code></pre></div></div>
<h5 id="启动clickhouse">启动<code class="language-plaintext highlighter-rouge">ClickHouse</code></h5>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 softwares]# service clickhouse-server start
Start clickhouse-server service: Path to data directory <span class="k">in</span> /etc/clickhouse-server/config.xml: /var/lib/clickhouse/
DONE
<span class="o">[</span>root@node01 softwares]#
</code></pre></div></div>
<h4 id="使用client链接server">使用<code class="language-plaintext highlighter-rouge">client</code>链接<code class="language-plaintext highlighter-rouge">server</code></h4>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 softwares]# clickhouse-client <span class="nt">-m</span>
ClickHouse client version 20.8.3.18.
Connecting to localhost:9000 as user default.
Connected to ClickHouse server version 20.8.3 revision 54438.
node01 :<span class="o">)</span> show databases<span class="p">;</span>
SHOW DATABASES
┌─name───────────────────────────┐
│ _temporary_and_external_tables │
│ default │
│ system │
└────────────────────────────────┘
3 rows <span class="k">in </span>set. Elapsed: 0.007 sec.
node01 :<span class="o">)</span> <span class="k">select </span>1<span class="p">;</span>
SELECT 1
┌─1─┐
│ 1 │
└───┘
1 rows <span class="k">in </span>set. Elapsed: 0.005 sec.
node01 :<span class="o">)</span>
</code></pre></div></div>
<h4 id="分布式集群安装">分布式集群安装</h4>
<h5 id="在node02node03上面执行之前的所有的操作">在<code class="language-plaintext highlighter-rouge">node02</code>,<code class="language-plaintext highlighter-rouge">node03</code>上面执行之前的所有的操作</h5>
<h5 id="node01机器修改配置文件configxml"><code class="language-plaintext highlighter-rouge">node01</code>机器修改配置文件<code class="language-plaintext highlighter-rouge">config.xml</code></h5>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 softwares]# vim /etc/clickhouse-server/config.xml
<<span class="o">!</span><span class="nt">--</span> 打开这个 <span class="nt">--</span><span class="o">></span>
<listen_host>::</listen_host>
<<span class="o">!</span><span class="nt">--</span> Same <span class="k">for </span>hosts with disabled ipv6: <span class="nt">--</span><span class="o">></span>
<<span class="o">!</span><span class="nt">--</span> <listen_host>0.0.0.0</listen_host> <span class="nt">--</span><span class="o">></span>
<<span class="o">!</span><span class="nt">--</span> 新增外部配置文件metrika.xml <span class="nt">--</span><span class="o">></span>
<include_from>/etc/clickhouse-server/metrika.xml</include_from>
</code></pre></div></div>
<p>将修改后的配置分发到<code class="language-plaintext highlighter-rouge">node02</code>,<code class="language-plaintext highlighter-rouge">node03</code>机器上</p>
<p><code class="language-plaintext highlighter-rouge">scp config.xml node02:/etc/clickhouse-server/config.xml</code></p>
<p><code class="language-plaintext highlighter-rouge">scp config.xml node03:/etc/clickhouse-server/config.xml</code></p>
<h5 id="node01机器etcclickhouse-server目录下创建metrikaxml文件"><code class="language-plaintext highlighter-rouge">node01</code>机器<code class="language-plaintext highlighter-rouge">/etc/clickhouse-server/</code>目录下创建<code class="language-plaintext highlighter-rouge">metrika.xml</code>文件</h5>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><yandex></span>
<span class="c"><!-- 集群配置 --></span>
<span class="nt"><clickhouse_remote_servers></span>
<span class="c"><!-- 3分片1备份 --></span>
<span class="nt"><cluster_3shards_1replicas></span>
<span class="c"><!-- 数据分片1 --></span>
<span class="nt"><shard></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node01<span class="nt"></host></span>
<span class="nt"><port></span>9000<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"></shard></span>
<span class="c"><!-- 数据分片2 --></span>
<span class="nt"><shard></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node02<span class="nt"></host></span>
<span class="nt"><port></span> 9000<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"></shard></span>
<span class="c"><!-- 数据分片3 --></span>
<span class="nt"><shard></span>
<span class="nt"><replica></span>
<span class="nt"><host></span>node03<span class="nt"></host></span>
<span class="nt"><port></span>9000<span class="nt"></port></span>
<span class="nt"></replica></span>
<span class="nt"></shard></span>
<span class="nt"></cluster_3shards_1replicas></span>
<span class="nt"></clickhouse_remote_servers></span>
<span class="nt"></yandex></span>
</code></pre></div></div>
<p>配置说明</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">cluster_3shards_1replicas</code> 集群名称,可随意定义</li>
<li>共设置3个分片,每个分片只有1个副本;</li>
</ul>
<p>将<code class="language-plaintext highlighter-rouge">metrika.xml</code>配置文件分发到<code class="language-plaintext highlighter-rouge">node02</code>,<code class="language-plaintext highlighter-rouge">node03</code>机器上</p>
<p><code class="language-plaintext highlighter-rouge">scp metrika.xml node02:/etc/clickhouse-server/metrika.xml</code></p>
<p><code class="language-plaintext highlighter-rouge">scp metrika.xml node03:/etc/clickhouse-server/metrika.xml</code></p>
<h5 id="重启clickhouse-server--打开client查看集群">重启<code class="language-plaintext highlighter-rouge">ClickHouse-server</code> 打开<code class="language-plaintext highlighter-rouge">client</code>查看集群</h5>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@node01 clickhouse-server]# service clickhouse-server restart
Stop clickhouse-server service: DONE
Start clickhouse-server service: Path to data directory <span class="k">in</span> /etc/clickhouse-server/config.xml: /var/lib/clickhouse/
DONE
<span class="o">[</span>root@node01 clickhouse-server]# clickhouse-client <span class="nt">-m</span>
ClickHouse client version 20.8.3.18.
Connecting to localhost:9000 as user default.
Connected to ClickHouse server version 20.8.3 revision 54438.
node01 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from system.clusters<span class="p">;</span>
SELECT <span class="k">*</span>
FROM system.clusters
┌─cluster───────────────────────────┬─shard_num─┬─shard_weight─┬─replica_num─┬─host_name─┬─host_address───┬─port─┬─is_local─┬─user────┬─default_database─┬─errors_count─┬─estimated_recovery_time─┐
│ cluster_3shards_1replicas │ 1 │ 1 │ 1 │ node01 │ 192.168.10.100 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ cluster_3shards_1replicas │ 2 │ 1 │ 1 │ node02 │ 192.168.10.110 │ 9000 │ 0 │ default │ │ 0 │ 0 │
│ cluster_3shards_1replicas │ 3 │ 1 │ 1 │ node03 │ 192.168.10.120 │ 9000 │ 0 │ default │ │ 0 │ 0 │
│ test_cluster_two_shards │ 1 │ 1 │ 1 │ 127.0.0.1 │ 127.0.0.1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_cluster_two_shards │ 2 │ 1 │ 1 │ 127.0.0.2 │ 127.0.0.2 │ 9000 │ 0 │ default │ │ 0 │ 0 │
│ test_cluster_two_shards_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_cluster_two_shards_localhost │ 2 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_shard_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_shard_localhost_secure │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9440 │ 0 │ default │ │ 0 │ 0 │
│ test_unavailable_shard │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │
│ test_unavailable_shard │ 2 │ 1 │ 1 │ localhost │ ::1 │ 1 │ 0 │ default │ │ 0 │ 0 │
└───────────────────────────────────┴───────────┴──────────────┴─────────────┴───────────┴────────────────┴──────┴──────────┴─────────┴──────────────────┴──────────────┴─────────────────────────┘
11 rows <span class="k">in </span>set. Elapsed: 0.008 sec. │ 0 │
</code></pre></div></div>
<p>可以看到<code class="language-plaintext highlighter-rouge">cluster_3shards_1replicas</code>就是我们定义的集群名称,一共有三个分片,每个分片有一份数据。剩下的为配置文件默认自带的集群配置.</p>
<h5 id="测试分布式集群">测试分布式集群</h5>
<p>在<code class="language-plaintext highlighter-rouge">node01</code>,<code class="language-plaintext highlighter-rouge">node02</code>,<code class="language-plaintext highlighter-rouge">node03</code>上分别创建本地表<code class="language-plaintext highlighter-rouge">cluster3s1r_local</code></p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster3s1r_local</span>
<span class="p">(</span>
<span class="nv">`id`</span> <span class="n">Int32</span><span class="p">,</span>
<span class="nv">`website`</span> <span class="n">String</span><span class="p">,</span>
<span class="nv">`wechat`</span> <span class="n">String</span><span class="p">,</span>
<span class="nv">`FlightDate`</span> <span class="nb">Date</span><span class="p">,</span>
<span class="nb">Year</span> <span class="n">UInt16</span>
<span class="p">)</span>
<span class="n">ENGINE</span> <span class="o">=</span> <span class="n">MergeTree</span><span class="p">(</span><span class="n">FlightDate</span><span class="p">,</span> <span class="p">(</span><span class="nb">Year</span><span class="p">,</span> <span class="n">FlightDate</span><span class="p">),</span> <span class="mi">8192</span><span class="p">);</span>
</code></pre></div></div>
<p>在<code class="language-plaintext highlighter-rouge">node01</code>节点上创建分布式表</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster3s1r_all</span> <span class="k">AS</span> <span class="n">cluster3s1r_local</span>
<span class="n">ENGINE</span> <span class="o">=</span> <span class="n">Distributed</span><span class="p">(</span><span class="n">cluster_3shards_1replicas</span><span class="p">,</span> <span class="k">default</span><span class="p">,</span> <span class="n">cluster3s1r_local</span><span class="p">,</span> <span class="n">rand</span><span class="p">());</span>
</code></pre></div></div>
<p>往分布式表<code class="language-plaintext highlighter-rouge">cluster3s1r_all</code>插入数据,<code class="language-plaintext highlighter-rouge">cluster3s1r_all </code>会随机插入到三个节点的<code class="language-plaintext highlighter-rouge">cluster3s1r_local</code>里</p>
<p>插入数据</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster3s1r_all</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="n">website</span><span class="p">,</span><span class="n">wechat</span><span class="p">,</span><span class="n">FlightDate</span><span class="p">,</span><span class="nb">Year</span><span class="p">)</span><span class="k">values</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="s1">'https://niocoder.com/'</span><span class="p">,</span><span class="s1">'java干货'</span><span class="p">,</span><span class="s1">'2020-11-28'</span><span class="p">,</span><span class="mi">2020</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster3s1r_all</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="n">website</span><span class="p">,</span><span class="n">wechat</span><span class="p">,</span><span class="n">FlightDate</span><span class="p">,</span><span class="nb">Year</span><span class="p">)</span><span class="k">values</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="s1">'http://www.merryyou.cn/'</span><span class="p">,</span><span class="s1">'javaganhuo'</span><span class="p">,</span><span class="s1">'2020-11-28'</span><span class="p">,</span><span class="mi">2020</span><span class="p">);</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">default</span><span class="p">.</span><span class="n">cluster3s1r_all</span> <span class="p">(</span><span class="n">id</span><span class="p">,</span><span class="n">website</span><span class="p">,</span><span class="n">wechat</span><span class="p">,</span><span class="n">FlightDate</span><span class="p">,</span><span class="nb">Year</span><span class="p">)</span><span class="k">values</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="s1">'http://www.xxxxx.cn/'</span><span class="p">,</span><span class="s1">'xxxxx'</span><span class="p">,</span><span class="s1">'2020-11-28'</span><span class="p">,</span><span class="mi">2020</span><span class="p">);</span>
</code></pre></div></div>
<p>查询分布式表和本地表</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>node01 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from cluster3s1r_all<span class="p">;</span> <span class="c"># 查询总量查分布式表</span>
SELECT <span class="k">*</span>
FROM cluster3s1r_all
┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐
│ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │
└────┴─────────────────────────┴────────────┴────────────┴──────┘
┌─id─┬─website──────────────┬─wechat─┬─FlightDate─┬─Year─┐
│ 3 │ http://www.xxxxx.cn/ │ xxxxx │ 2020-11-28 │ 2020 │
└────┴──────────────────────┴────────┴────────────┴──────┘
┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐
│ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │
└────┴───────────────────────┴──────────┴────────────┴──────┘
3 rows <span class="k">in </span>set. Elapsed: 0.036 sec.
node01 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from cluster3s1r_local<span class="p">;</span> <span class="c"># node01本地表</span>
SELECT <span class="k">*</span>
FROM cluster3s1r_local
┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐
│ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │
└────┴─────────────────────────┴────────────┴────────────┴──────┘
1 rows <span class="k">in </span>set. Elapsed: 0.012 sec.
node02 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from cluster3s1r_local<span class="p">;</span> <span class="c"># node02本地表</span>
SELECT <span class="k">*</span>
FROM cluster3s1r_local
Ok.
0 rows <span class="k">in </span>set. Elapsed: 0.016 sec.
node03 :<span class="o">)</span> <span class="k">select</span> <span class="k">*</span> from cluster3s1r_local<span class="p">;</span> <span class="c">## node03 本地表</span>
SELECT <span class="k">*</span>
FROM cluster3s1r_local
┌─id─┬─website──────────────┬─wechat─┬─FlightDate─┬─Year─┐
│ 3 │ http://www.xxxxx.cn/ │ xxxxx │ 2020-11-28 │ 2020 │
└────┴──────────────────────┴────────┴────────────┴──────┘
┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐
│ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │
└────┴───────────────────────┴──────────┴────────────┴──────┘
2 rows <span class="k">in </span>set. Elapsed: 0.006 sec.
</code></pre></div></div>
<h2 id="下载">下载</h2>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/wechat/gzh.png" alt="" /></p>
<blockquote>
<p>关注微信公众号java干货回复 【clickhouse】</p>
</blockquote>郑龙飞满目山河空念远,落花风雨更伤春。Java-Collectors常用的20个方法2020-11-15T00:00:00+00:002020-11-15T00:00:00+00:00https://niocoder.com//2020/11/15/Java-Collectors%E5%B8%B8%E7%94%A8%E7%9A%8420%E4%B8%AA%E6%96%B9%E6%B3%95<blockquote>
<p>相思相见知何日?此时此夜难为情。</p>
</blockquote>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java41.jpg" alt="" /></p>
<h2 id="返回list集合-tolist"><code class="language-plaintext highlighter-rouge">返回List集合: toList()</code></h2>
<p>用于将元素累积到<code class="language-plaintext highlighter-rouge">List</code>集合中。它将创建一个新<code class="language-plaintext highlighter-rouge">List</code>集合(不会更改当前集合)。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">integers</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">,</span><span class="mi">4</span><span class="o">,</span><span class="mi">5</span><span class="o">,</span><span class="mi">6</span><span class="o">,</span><span class="mi">6</span><span class="o">);</span>
<span class="n">integers</span><span class="o">.</span><span class="na">stream</span><span class="o">().</span><span class="na">map</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span> <span class="n">x</span><span class="o">*</span><span class="n">x</span><span class="o">).</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">toList</span><span class="o">());</span>
<span class="c1">// output: [1,4,9,16,25,36,36]</span>
</code></pre></div></div>
<h2 id="返回set集合-toset"><code class="language-plaintext highlighter-rouge">返回Set集合: toSet()</code></h2>
<p>用于将元素累积到<code class="language-plaintext highlighter-rouge">Set</code>集合中。它会删除重复元素。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">integers</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">,</span><span class="mi">4</span><span class="o">,</span><span class="mi">5</span><span class="o">,</span><span class="mi">6</span><span class="o">,</span><span class="mi">6</span><span class="o">);</span>
<span class="n">integers</span><span class="o">.</span><span class="na">stream</span><span class="o">().</span><span class="na">map</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span> <span class="n">x</span><span class="o">*</span><span class="n">x</span><span class="o">).</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">toSet</span><span class="o">());</span>
<span class="c1">// output: [1,4,9,16,25,36]</span>
</code></pre></div></div>
<h2 id="返回指定的集合-tocollection"><code class="language-plaintext highlighter-rouge">返回指定的集合: toCollection()</code></h2>
<p>可以将元素雷击到指定的集合中。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">integers</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">,</span><span class="mi">4</span><span class="o">,</span><span class="mi">5</span><span class="o">,</span><span class="mi">6</span><span class="o">,</span><span class="mi">6</span><span class="o">);</span>
<span class="n">integers</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span> <span class="n">x</span> <span class="o">></span><span class="mi">2</span><span class="o">)</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">toCollection</span><span class="o">(</span><span class="nl">LinkedList:</span><span class="o">:</span><span class="k">new</span><span class="o">));</span>
<span class="c1">// output: [3,4,5,6,6]</span>
</code></pre></div></div>
<h2 id="计算元素数量-counting"><code class="language-plaintext highlighter-rouge">计算元素数量: Counting()</code></h2>
<p>用于返回计算集合中存在的元素个数。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">integers</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">,</span><span class="mi">4</span><span class="o">,</span><span class="mi">5</span><span class="o">,</span><span class="mi">6</span><span class="o">,</span><span class="mi">6</span><span class="o">);</span>
<span class="nc">Long</span> <span class="n">collect</span> <span class="o">=</span> <span class="n">integers</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">filter</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span> <span class="n">x</span> <span class="o"><</span><span class="mi">4</span><span class="o">)</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">counting</span><span class="o">());</span>
<span class="c1">// output: 3</span>
</code></pre></div></div>
<h2 id="求最小值-minby"><code class="language-plaintext highlighter-rouge">求最小值: minBy()</code></h2>
<p>用于返回列表中存在的最小值。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">integers</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">,</span><span class="mi">4</span><span class="o">,</span><span class="mi">5</span><span class="o">,</span><span class="mi">6</span><span class="o">,</span><span class="mi">6</span><span class="o">);</span>
<span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">strings</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"alpha"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">,</span><span class="s">"gamma"</span><span class="o">);</span>
<span class="n">integers</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">minBy</span><span class="o">(</span><span class="nc">Comparator</span><span class="o">.</span><span class="na">naturalOrder</span><span class="o">()))</span>
<span class="o">.</span><span class="na">get</span><span class="o">();</span>
<span class="c1">// output: 1</span>
<span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">minBy</span><span class="o">(</span><span class="nc">Comparator</span><span class="o">.</span><span class="na">naturalOrder</span><span class="o">()))</span>
<span class="o">.</span><span class="na">get</span><span class="o">();</span>
<span class="c1">// output: alpha</span>
</code></pre></div></div>
<p>按照整数排序返回1,按照字符串排序返回alpha</p>
<p>可以使用reverseOrder()方法反转顺序。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">integers</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">,</span><span class="mi">4</span><span class="o">,</span><span class="mi">5</span><span class="o">,</span><span class="mi">6</span><span class="o">,</span><span class="mi">6</span><span class="o">);</span>
<span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">strings</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"alpha"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">,</span><span class="s">"gamma"</span><span class="o">);</span>
<span class="n">integers</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">minBy</span><span class="o">(</span><span class="nc">Comparator</span><span class="o">.</span><span class="na">reverseOrder</span><span class="o">()))</span>
<span class="o">.</span><span class="na">get</span><span class="o">();</span>
<span class="c1">// output: 6</span>
<span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">minBy</span><span class="o">(</span><span class="nc">Comparator</span><span class="o">.</span><span class="na">reverseOrder</span><span class="o">()))</span>
<span class="o">.</span><span class="na">get</span><span class="o">();</span>
<span class="c1">// output: gamma</span>
</code></pre></div></div>
<p>同时可以自定义的对象定制比较器。</p>
<h2 id="求最大值-maxby"><code class="language-plaintext highlighter-rouge">求最大值: maxBy()</code></h2>
<p>和最小值方法类似,使用<code class="language-plaintext highlighter-rouge">maxBy()</code>方法来获得最大值。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">strings</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"alpha"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">,</span><span class="s">"gamma"</span><span class="o">);</span>
<span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">maxBy</span><span class="o">(</span><span class="nc">Comparator</span><span class="o">.</span><span class="na">naturalOrder</span><span class="o">()))</span>
<span class="o">.</span><span class="na">get</span><span class="o">();</span>
<span class="c1">// output: gamma</span>
</code></pre></div></div>
<h2 id="分区列表partitioningby"><code class="language-plaintext highlighter-rouge">分区列表:partitioningBy()</code></h2>
<p>用于将一个集合划分为2个集合并将其添加到映射中,1个满足给定条件,另一个不满足,例如从集合中分离奇数。因此它将在<code class="language-plaintext highlighter-rouge">map</code>中生成2条数据,1个以<code class="language-plaintext highlighter-rouge">true</code>为<code class="language-plaintext highlighter-rouge">key</code>,奇数为值,第2个以<code class="language-plaintext highlighter-rouge">false</code>为<code class="language-plaintext highlighter-rouge">key</code>,以偶数为值。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">strings</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"a"</span><span class="o">,</span><span class="s">"alpha"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">,</span><span class="s">"gamma"</span><span class="o">);</span>
<span class="nc">Map</span><span class="o"><</span><span class="nc">Boolean</span><span class="o">,</span> <span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">>></span> <span class="n">collect1</span> <span class="o">=</span> <span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">partitioningBy</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span> <span class="n">x</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">></span> <span class="mi">2</span><span class="o">));</span>
<span class="c1">// output: {false=[a], true=[alpha, beta, gamma]}</span>
</code></pre></div></div>
<p>这里我们将长度大于2的字符串与其余字符串分开。</p>
<h2 id="返回不可修改的list集合tounmodifiablelist"><code class="language-plaintext highlighter-rouge">返回不可修改的List集合:toUnmodifiableList()</code></h2>
<p>用于创建只读<code class="language-plaintext highlighter-rouge">List</code>集合。任何试图对此不可修改<code class="language-plaintext highlighter-rouge">List</code>集合进行更改的尝试都将导致<code class="language-plaintext highlighter-rouge">UnsupportedOperationException</code>。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">strings</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"alpha"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">,</span><span class="s">"gamma"</span><span class="o">);</span>
<span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">collect2</span> <span class="o">=</span> <span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">toUnmodifiableList</span><span class="o">());</span>
<span class="c1">// output: ["alpha","beta","gamma"]</span>
</code></pre></div></div>
<h2 id="返回不可修改的set集合tounmodifiableset"><code class="language-plaintext highlighter-rouge">返回不可修改的Set集合:toUnmodifiableSet()</code></h2>
<p>用于创建只读<code class="language-plaintext highlighter-rouge">Set</code>集合。任何试图对此不可修改<code class="language-plaintext highlighter-rouge">Set</code>集合进行更改的尝试都将导致<code class="language-plaintext highlighter-rouge">UnsupportedOperationException</code>。它会删除重复元素。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">strings</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"alpha"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">,</span><span class="s">"gamma"</span><span class="o">,</span><span class="s">"alpha"</span><span class="o">);</span>
<span class="nc">Set</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">readOnlySet</span> <span class="o">=</span> <span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">sorted</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">toUnmodifiableSet</span><span class="o">());</span>
<span class="c1">// output: ["alpha","beta","gamma"]</span>
</code></pre></div></div>
<h2 id="连接元素joining"><code class="language-plaintext highlighter-rouge">连接元素:Joining()</code></h2>
<p>用指定的字符串链接集合内的元素。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">strings</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"alpha"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">,</span><span class="s">"gamma"</span><span class="o">);</span>
<span class="nc">String</span> <span class="n">collect3</span> <span class="o">=</span> <span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">distinct</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">joining</span><span class="o">(</span><span class="s">","</span><span class="o">));</span>
<span class="c1">// output: alpha,beta,gamma</span>
<span class="nc">String</span> <span class="n">collect4</span> <span class="o">=</span> <span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="n">s</span> <span class="o">-></span> <span class="n">s</span><span class="o">.</span><span class="na">toString</span><span class="o">())</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">joining</span><span class="o">(</span><span class="s">","</span><span class="o">,</span><span class="s">"["</span><span class="o">,</span><span class="s">"]"</span><span class="o">));</span>
<span class="c1">// output: [alpha,beta,gamma]</span>
</code></pre></div></div>
<h2 id="long类型集合的平均值averaginglong"><code class="language-plaintext highlighter-rouge">Long类型集合的平均值:averagingLong()</code></h2>
<p>查找<code class="language-plaintext highlighter-rouge">Long</code>类型集合的平均值。</p>
<p>注意:返回的是<code class="language-plaintext highlighter-rouge">Double</code>类型而不是 <code class="language-plaintext highlighter-rouge">Long</code>类型</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Long</span><span class="o">></span> <span class="n">longValues</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mi">100</span><span class="n">l</span><span class="o">,</span><span class="mi">200</span><span class="n">l</span><span class="o">,</span><span class="mi">300</span><span class="n">l</span><span class="o">);</span>
<span class="nc">Double</span> <span class="n">d1</span> <span class="o">=</span> <span class="n">longValues</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">averagingLong</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span> <span class="n">x</span> <span class="o">*</span> <span class="mi">2</span><span class="o">));</span>
<span class="c1">// output: 400.0</span>
</code></pre></div></div>
<h2 id="integer类型集合的平均值averagingint"><code class="language-plaintext highlighter-rouge">Integer类型集合的平均值:averagingInt()</code></h2>
<p>查找<code class="language-plaintext highlighter-rouge">Integer</code>类型集合的平均值。</p>
<p>注意:返回的是<code class="language-plaintext highlighter-rouge">Double</code>类型而不是 <code class="language-plaintext highlighter-rouge">int</code>类型</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">integers</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">,</span><span class="mi">4</span><span class="o">,</span><span class="mi">5</span><span class="o">,</span><span class="mi">6</span><span class="o">,</span><span class="mi">6</span><span class="o">);</span>
<span class="nc">Double</span> <span class="n">d2</span> <span class="o">=</span> <span class="n">integers</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">averagingInt</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span> <span class="n">x</span><span class="o">*</span><span class="mi">2</span><span class="o">));</span>
<span class="c1">// output: 7.714285714285714</span>
</code></pre></div></div>
<h2 id="double类型集合的平均值averagingdouble"><code class="language-plaintext highlighter-rouge">Double类型集合的平均值:averagingDouble()</code></h2>
<p>查找<code class="language-plaintext highlighter-rouge">Double</code>类型集合的平均值。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Double</span><span class="o">></span> <span class="n">doubles</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mf">1.1</span><span class="o">,</span><span class="mf">2.0</span><span class="o">,</span><span class="mf">3.0</span><span class="o">,</span><span class="mf">4.0</span><span class="o">,</span><span class="mf">5.0</span><span class="o">,</span><span class="mf">5.0</span><span class="o">);</span>
<span class="nc">Double</span> <span class="n">d3</span> <span class="o">=</span> <span class="n">doubles</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">averagingDouble</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span> <span class="n">x</span><span class="o">));</span>
<span class="c1">// output: 3.35</span>
</code></pre></div></div>
<h2 id="创建maptomap"><code class="language-plaintext highlighter-rouge">创建Map:toMap()</code></h2>
<p>根据集合的值创建<code class="language-plaintext highlighter-rouge">Map</code>。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">strings</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"alpha"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">,</span><span class="s">"gamma"</span><span class="o">);</span>
<span class="nc">Map</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span><span class="nc">Integer</span><span class="o">></span> <span class="n">map</span> <span class="o">=</span> <span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span>
<span class="o">.</span><span class="na">toMap</span><span class="o">(</span><span class="nc">Function</span><span class="o">.</span><span class="na">identity</span><span class="o">(),</span><span class="nl">String:</span><span class="o">:</span><span class="n">length</span><span class="o">));</span>
<span class="c1">// output: {alpha=5, beta=4, gamma=5}</span>
</code></pre></div></div>
<p>创建了一个<code class="language-plaintext highlighter-rouge">Map</code>,其中集合值作为<code class="language-plaintext highlighter-rouge">key</code>,在集合中的出现次数作为值。</p>
<h2 id="在创建map时处理列表的重复项"><code class="language-plaintext highlighter-rouge">在创建</code>map<code class="language-plaintext highlighter-rouge">时处理列表的重复项</code></h2>
<p>集合中可以包含重复的值,因此,如果想从列表中创建一个<code class="language-plaintext highlighter-rouge">Map</code>,并希望使用集合值作为map的<code class="language-plaintext highlighter-rouge">key</code>,那么需要解析重复的<code class="language-plaintext highlighter-rouge">key</code>。由于map只包含唯一的<code class="language-plaintext highlighter-rouge">key</code>,可以使用比较器来实现这一点。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">strings</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"alpha"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">,</span><span class="s">"gamma"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">);</span>
<span class="nc">Map</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span><span class="nc">Integer</span><span class="o">></span> <span class="n">map</span> <span class="o">=</span> <span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span>
<span class="o">.</span><span class="na">toMap</span><span class="o">(</span><span class="nc">Function</span><span class="o">.</span><span class="na">identity</span><span class="o">(),</span><span class="nl">String:</span><span class="o">:</span><span class="n">length</span><span class="o">,(</span><span class="n">i1</span><span class="o">,</span><span class="n">i2</span><span class="o">)</span> <span class="o">-></span> <span class="n">i1</span><span class="o">));</span>
<span class="c1">// output: {alpha=5, gamma=5, beta=4}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Function.identity()</code>指向集合中的值,<code class="language-plaintext highlighter-rouge">i1</code>和<code class="language-plaintext highlighter-rouge">i2</code>是重复键的值。可以只保留一个值,这里选择<code class="language-plaintext highlighter-rouge">i1</code>,也可以用这两个值来计算任何东西,比如把它们相加,比较和选择较大的那个,等等。</p>
<h2 id="整数求和summingint-"><code class="language-plaintext highlighter-rouge">整数求和:summingInt ()</code></h2>
<p>查找集合中所有整数的和。它并不总是初始集合的和,就像我们在下面的例子中使用的我们使用的是字符串列表,首先我们把每个字符串转换成一个等于它的长度的整数,然后把所有的长度相加。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">strings</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"alpha"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">,</span><span class="s">"gamma"</span><span class="o">);</span>
<span class="nc">Integer</span> <span class="n">collect4</span> <span class="o">=</span> <span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">summingInt</span><span class="o">(</span><span class="nl">String:</span><span class="o">:</span><span class="n">length</span><span class="o">));</span>
<span class="c1">// output: 18</span>
</code></pre></div></div>
<p>或直接集合值和</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">integers</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">,</span><span class="mi">4</span><span class="o">,</span><span class="mi">5</span><span class="o">,</span><span class="mi">6</span><span class="o">,</span><span class="mi">6</span><span class="o">);</span>
<span class="nc">Integer</span> <span class="n">sum</span> <span class="o">=</span> <span class="n">integers</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">summingInt</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span> <span class="n">x</span><span class="o">));</span>
<span class="c1">// output: 27</span>
</code></pre></div></div>
<h2 id="double求和summingdouble-"><code class="language-plaintext highlighter-rouge">double求和:summingDouble ()</code></h2>
<p>类似于整数求和,只是它用于双精度值</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Double</span><span class="o">></span> <span class="n">doubleValues</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mf">1.1</span><span class="o">,</span><span class="mf">2.0</span><span class="o">,</span><span class="mf">3.0</span><span class="o">,</span><span class="mf">4.0</span><span class="o">,</span><span class="mf">5.0</span><span class="o">,</span><span class="mf">5.0</span><span class="o">);</span>
<span class="nc">Double</span> <span class="n">sum</span> <span class="o">=</span> <span class="n">doubleValues</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">summingDouble</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span><span class="n">x</span><span class="o">));</span>
<span class="c1">// output: 20.1</span>
</code></pre></div></div>
<h2 id="long求和summinglong-"><code class="language-plaintext highlighter-rouge">Long求和:summingLong ()</code></h2>
<p>与前两个相同,用于添加<code class="language-plaintext highlighter-rouge">long</code>值或<code class="language-plaintext highlighter-rouge">int</code>值。可以对<code class="language-plaintext highlighter-rouge">int</code>值使用<code class="language-plaintext highlighter-rouge">summinglong()</code>,但不能对<code class="language-plaintext highlighter-rouge">long</code>值使用<code class="language-plaintext highlighter-rouge">summingInt()</code>。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Long</span><span class="o">></span> <span class="n">longValues</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mi">100</span><span class="n">l</span><span class="o">,</span><span class="mi">200</span><span class="n">l</span><span class="o">,</span><span class="mi">300</span><span class="n">l</span><span class="o">);</span>
<span class="nc">Long</span> <span class="n">sum</span> <span class="o">=</span> <span class="n">longValues</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">summingLong</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span><span class="n">x</span><span class="o">));</span>
<span class="c1">// output: 600</span>
</code></pre></div></div>
<h2 id="long求和summinglong--1"><code class="language-plaintext highlighter-rouge">Long求和:summingLong ()</code></h2>
<p>与前两个相同,用于添加<code class="language-plaintext highlighter-rouge">long</code>值或<code class="language-plaintext highlighter-rouge">int</code>值。可以对<code class="language-plaintext highlighter-rouge">int</code>值使用<code class="language-plaintext highlighter-rouge">summinglong()</code>,但不能对<code class="language-plaintext highlighter-rouge">long</code>值使用<code class="language-plaintext highlighter-rouge">summingInt()</code>。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Long</span><span class="o">></span> <span class="n">longValues</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mi">100</span><span class="n">l</span><span class="o">,</span><span class="mi">200</span><span class="n">l</span><span class="o">,</span><span class="mi">300</span><span class="n">l</span><span class="o">);</span>
<span class="nc">Long</span> <span class="n">sum</span> <span class="o">=</span> <span class="n">longValues</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">summingLong</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span><span class="n">x</span><span class="o">));</span>
<span class="c1">// output: 600</span>
</code></pre></div></div>
<h2 id="汇总整数summarizingint-"><code class="language-plaintext highlighter-rouge">汇总整数:summarizingInt ()</code></h2>
<p>它给出集合中出现的值的所有主要算术运算值,如所有值的平均值、最小值、最大值、所有值的计数和总和。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">Integer</span><span class="o">></span> <span class="n">integers</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span><span class="mi">2</span><span class="o">,</span><span class="mi">3</span><span class="o">,</span><span class="mi">4</span><span class="o">,</span><span class="mi">5</span><span class="o">,</span><span class="mi">6</span><span class="o">,</span><span class="mi">6</span><span class="o">);</span>
<span class="nc">IntSummaryStatistics</span> <span class="n">stats</span> <span class="o">=</span> <span class="n">integers</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">summarizingInt</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span> <span class="n">x</span> <span class="o">));</span>
<span class="c1">//output: IntSummaryStatistics{count=7, sum=27, min=1, average=3.857143, max=6}</span>
</code></pre></div></div>
<p>可以使用<code class="language-plaintext highlighter-rouge">get</code>方法提取不同的值,如:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">stats</span><span class="o">.</span><span class="na">getAverage</span><span class="o">();</span> <span class="c1">// 3.857143</span>
<span class="n">stats</span><span class="o">.</span><span class="na">getMax</span><span class="o">();</span> <span class="c1">// 6</span>
<span class="n">stats</span><span class="o">.</span><span class="na">getMin</span><span class="o">();</span> <span class="c1">// 1</span>
<span class="n">stats</span><span class="o">.</span><span class="na">getCount</span><span class="o">();</span> <span class="c1">// 7</span>
<span class="n">stats</span><span class="o">.</span><span class="na">getSum</span><span class="o">();</span> <span class="c1">// 27</span>
</code></pre></div></div>
<h2 id="分组函数groupingby-"><code class="language-plaintext highlighter-rouge">分组函数:GroupingBy ()</code></h2>
<p><code class="language-plaintext highlighter-rouge">GroupingBy()</code>是一种高级方法,用于从任何其他集合创建<code class="language-plaintext highlighter-rouge">Map</code>。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">strings</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"alpha"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">,</span><span class="s">"gamma"</span><span class="o">);</span>
<span class="nc">Map</span><span class="o"><</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">>></span> <span class="n">collect</span> <span class="o">=</span> <span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">groupingBy</span><span class="o">(</span><span class="nl">String:</span><span class="o">:</span><span class="n">length</span><span class="o">));</span>
<span class="c1">// output: {4=[beta, beta], 5=[alpha, gamma]}</span>
</code></pre></div></div>
<p>它将字符串长度作为<code class="language-plaintext highlighter-rouge">key</code>,并将该长度的字符串列表作为<code class="language-plaintext highlighter-rouge">value</code>。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">List</span><span class="o"><</span><span class="nc">String</span><span class="o">></span> <span class="n">strings</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="s">"alpha"</span><span class="o">,</span><span class="s">"beta"</span><span class="o">,</span><span class="s">"gamma"</span><span class="o">);</span>
<span class="nc">Map</span><span class="o"><</span><span class="nc">Integer</span><span class="o">,</span> <span class="nc">LinkedList</span><span class="o"><</span><span class="nc">String</span><span class="o">>></span> <span class="n">collect1</span> <span class="o">=</span> <span class="n">strings</span>
<span class="o">.</span><span class="na">stream</span><span class="o">()</span>
<span class="o">.</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">groupingBy</span><span class="o">(</span><span class="nl">String:</span><span class="o">:</span><span class="n">length</span><span class="o">,</span>
<span class="nc">Collectors</span><span class="o">.</span><span class="na">toCollection</span><span class="o">(</span><span class="nl">LinkedList:</span><span class="o">:</span><span class="k">new</span><span class="o">)));</span>
<span class="c1">// output: {4=[beta, beta], 5=[alpha, gamma]}</span>
</code></pre></div></div>
<p>这里指定了<code class="language-plaintext highlighter-rouge">Map</code>中需要的列表类型(<code class="language-plaintext highlighter-rouge">Libkedlist</code>)。</p>
<hr />
<p><a href="https://user-gold-cdn.xitu.io/2019/9/18/16d42fc88345bad5?w=258&h=258&f=jpeg&s=26702" title="https://user-gold-cdn.xitu.io/2019/9/18/16d42fc88345bad5?w=258&h=258&f=jpeg&s=26702"><img src="//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6897912b2f634918a7e30022071e12d9~tplv-k3u1fbpfcp-zoom-1.image" alt="//p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6b3a50846a8d40c79b4db3de866b41d5~tplv-k3u1fbpfcp-zoom-1.image" title="https://user-gold-cdn.xitu.io/2019/9/18/16d42fc88345bad5?w=258&h=258&f=jpeg&s=26702" /></a></p>
<blockquote>
<p>🙂🙂🙂微信公众号<strong>java干货</strong></p>
</blockquote>郑龙飞相思相见知何日?此时此夜难为情。适合初学者入门Java程序2020-04-22T00:00:00+00:002020-04-22T00:00:00+00:00https://niocoder.com//2020/04/22/%E9%80%82%E5%90%88%E5%88%9D%E5%AD%A6%E8%80%85%E5%85%A5%E9%97%A8Java%E7%A8%8B%E5%BA%8F<blockquote>
<p>相思一夜梅花发,忽到窗前疑是君。</p>
</blockquote>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java40.jpg" alt="" /></p>
<h2 id="概述">概述</h2>
<p><code class="language-plaintext highlighter-rouge">Java</code>是在<code class="language-plaintext highlighter-rouge">IT</code>行业广泛使用的最流行的编程语言之一。它简单,健壮,可帮助我们重用代码。在本文中,让我们看一些了解<code class="language-plaintext highlighter-rouge">Java</code>基础的应用程序。</p>
<h3 id="入门的java程序">入门的Java程序</h3>
<h4 id="计算机程序">计算机程序</h4>
<p><strong>编写一个<code class="language-plaintext highlighter-rouge">Java</code>程序来执行基本的计算器操作。</strong></p>
<p>当你考虑使用计算器时,就会想到加,减,乘,除等运算。让我们借助以下程序来实现基本的计算器操作。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.niocoder</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Scanner</span><span class="o">;</span>
<span class="cm">/**
* Created by zhenglongfei on 2020/4/21
*
* @VERSION 1.0
*/</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">_01Calculator</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">Scanner</span> <span class="n">param</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Scanner</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">in</span><span class="o">);</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">"请输入第一个数字:"</span><span class="o">);</span>
<span class="kt">double</span> <span class="n">first</span> <span class="o">=</span> <span class="n">param</span><span class="o">.</span><span class="na">nextDouble</span><span class="o">();</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">"请输入第二个数字:"</span><span class="o">);</span>
<span class="kt">double</span> <span class="n">second</span> <span class="o">=</span> <span class="n">param</span><span class="o">.</span><span class="na">nextDouble</span><span class="o">();</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">"请输入运算符 (+, -, *, /): "</span><span class="o">);</span>
<span class="kt">char</span> <span class="n">operator</span> <span class="o">=</span> <span class="n">param</span><span class="o">.</span><span class="na">next</span><span class="o">().</span><span class="na">charAt</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
<span class="kt">double</span> <span class="n">result</span><span class="o">;</span>
<span class="c1">//switch case for each of the operations</span>
<span class="k">switch</span> <span class="o">(</span><span class="n">operator</span><span class="o">)</span> <span class="o">{</span>
<span class="k">case</span> <span class="sc">'+'</span><span class="o">:</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">first</span> <span class="o">+</span> <span class="n">second</span><span class="o">;</span>
<span class="k">break</span><span class="o">;</span>
<span class="k">case</span> <span class="sc">'-'</span><span class="o">:</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">first</span> <span class="o">-</span> <span class="n">second</span><span class="o">;</span>
<span class="k">break</span><span class="o">;</span>
<span class="k">case</span> <span class="sc">'*'</span><span class="o">:</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">first</span> <span class="o">*</span> <span class="n">second</span><span class="o">;</span>
<span class="k">break</span><span class="o">;</span>
<span class="k">case</span> <span class="sc">'/'</span><span class="o">:</span>
<span class="n">result</span> <span class="o">=</span> <span class="n">first</span> <span class="o">/</span> <span class="n">second</span><span class="o">;</span>
<span class="k">break</span><span class="o">;</span>
<span class="k">default</span><span class="o">:</span>
<span class="c1">// operator doesn't match any case constant (+, -, *, /)default:</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"Error! operator is not correct"</span><span class="o">);</span>
<span class="k">return</span><span class="o">;</span>
<span class="o">}</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">printf</span><span class="o">(</span><span class="s">"%.1f %c %.1f = %.1f"</span><span class="o">,</span> <span class="n">first</span><span class="o">,</span> <span class="n">operator</span><span class="o">,</span> <span class="n">second</span><span class="o">,</span> <span class="n">result</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>执行上述程序时,输出如下所示:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>请输入第一个数字:10
请输入第二个数字:10
请输入运算符 (+, -, *, /): +
10.0 + 10.0 = 20.0
</code></pre></div></div>
<h4 id="使用递归的阶乘程序">使用递归的阶乘程序</h4>
<p><strong>编写一个<code class="language-plaintext highlighter-rouge">Java</code>程序来计算一个数字的阶乘。</strong></p>
<p>数字的阶乘是所有小于或等于该数字的正数的乘积。<code class="language-plaintext highlighter-rouge">n</code>的阶乘由<code class="language-plaintext highlighter-rouge">n!</code>表示。现在,让我们编写一个程序,并使用递归查找数字的阶乘。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.niocoder</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Scanner</span><span class="o">;</span>
<span class="cm">/**
* Created by zhenglongfei on 2020/4/21
*
* @VERSION 1.0
*/</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">_02Factorial</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">Scanner</span> <span class="n">scanner</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Scanner</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">in</span><span class="o">);</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">"请输入一个数字:"</span><span class="o">);</span>
<span class="c1">//Stored the entered value in variable</span>
<span class="kt">int</span> <span class="n">num</span> <span class="o">=</span> <span class="n">scanner</span><span class="o">.</span><span class="na">nextInt</span><span class="o">();</span>
<span class="c1">//Called the user defined function fact</span>
<span class="kt">int</span> <span class="n">factorial</span> <span class="o">=</span> <span class="n">fact</span><span class="o">(</span><span class="n">num</span><span class="o">);</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"输入数字的阶乘是: "</span> <span class="o">+</span> <span class="n">factorial</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">static</span> <span class="kt">int</span> <span class="nf">fact</span><span class="o">(</span><span class="kt">int</span> <span class="n">number</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">number</span> <span class="o">==</span> <span class="mi">1</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">number</span> <span class="o">*</span> <span class="n">fact</span><span class="o">(</span><span class="n">number</span> <span class="o">-</span> <span class="mi">1</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>执行上面的程序时,您将获得一个数字的阶乘,如下所示:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>请输入一个数字:12
输入数字的阶乘是: 479001600
</code></pre></div></div>
<h4 id="斐波纳契数列的程序">斐波纳契数列的程序</h4>
<p><strong>编写一个<code class="language-plaintext highlighter-rouge">Java</code>程序来计算斐波那契数列直到<code class="language-plaintext highlighter-rouge">n</code>个数字。</strong></p>
<p>它是一个级数,其中下一项是前两项之和。例如:0 1 1 2 3 5 8 13……让我们编写一个<code class="language-plaintext highlighter-rouge">Java</code>程序来计算斐波那契数列。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.niocoder</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Scanner</span><span class="o">;</span>
<span class="cm">/**
* Created by zhenglongfei on 2020/4/21
*
* @VERSION 1.0
*/</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">_03Fibonacci</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">Scanner</span> <span class="n">scanner</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Scanner</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">in</span><span class="o">);</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">"请输入一个数字:"</span><span class="o">);</span>
<span class="c1">//Stored the entered value in variable</span>
<span class="kt">int</span> <span class="n">num</span> <span class="o">=</span> <span class="n">scanner</span><span class="o">.</span><span class="na">nextInt</span><span class="o">();</span>
<span class="kt">int</span> <span class="n">first</span> <span class="o">=</span> <span class="mi">0</span><span class="o">,</span> <span class="n">second</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="n">num</span> <span class="o">+</span> <span class="s">":"</span><span class="o">);</span>
<span class="k">while</span> <span class="o">(</span><span class="n">first</span> <span class="o"><</span> <span class="n">num</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="n">first</span> <span class="o">+</span> <span class="s">"+"</span><span class="o">);</span>
<span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="n">first</span> <span class="o">+</span> <span class="n">second</span><span class="o">;</span>
<span class="n">first</span> <span class="o">=</span> <span class="n">second</span><span class="o">;</span>
<span class="n">second</span> <span class="o">=</span> <span class="n">sum</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>执行上述代码后,输出如下所示:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>请输入一个数字:100
100:0+1+1+2+3+5+8+13+21+34+55+89+
</code></pre></div></div>
<h4 id="字符串的回文程序">字符串的回文程序</h4>
<p><strong>编写一个<code class="language-plaintext highlighter-rouge">Java</code>程序来找出给定的字符串是否是回文。</strong></p>
<p>回文是一个数字、字符串或序列,即使你颠倒了顺序,它们也是一样的。例如,<code class="language-plaintext highlighter-rouge">RACECAR</code>,如果向后拼写将与<code class="language-plaintext highlighter-rouge">RACECAR</code>相同。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="kn">package</span> <span class="nn">com.niocoder</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Scanner</span><span class="o">;</span>
<span class="cm">/**
* Created by zhenglongfei on 2020/4/21
*
* @VERSION 1.0
*/</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">_04Palindrome</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">Scanner</span> <span class="n">sc</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Scanner</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">in</span><span class="o">);</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">"请输入一个字符串: "</span><span class="o">);</span>
<span class="nc">String</span> <span class="n">str</span> <span class="o">=</span> <span class="n">sc</span><span class="o">.</span><span class="na">nextLine</span><span class="o">();</span>
<span class="n">checkPalindrome</span><span class="o">(</span><span class="n">str</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">checkPalindrome</span><span class="o">(</span><span class="nc">String</span> <span class="n">str</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">boolean</span> <span class="n">flag</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
<span class="kt">int</span> <span class="n">length</span> <span class="o">=</span> <span class="n">str</span><span class="o">.</span><span class="na">length</span><span class="o">();</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><=</span> <span class="n">length</span> <span class="o">/</span> <span class="mi">2</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">str</span><span class="o">.</span><span class="na">charAt</span><span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">!=</span> <span class="n">str</span><span class="o">.</span><span class="na">charAt</span><span class="o">(</span><span class="n">length</span> <span class="o">-</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="o">))</span> <span class="o">{</span>
<span class="n">flag</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
<span class="k">break</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">str</span> <span class="o">+</span> <span class="s">" 是否回文 = "</span> <span class="o">+</span> <span class="n">flag</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>运行代码时,它将检查给定的字符串是否是回文,如下所示:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>请输入一个字符串: abab
abab 是否回文 = false
请输入一个字符串: abba
abba 是否回文 = true
</code></pre></div></div>
<h4 id="图案程序">图案程序</h4>
<p><strong>用<code class="language-plaintext highlighter-rouge">Java</code>编写程序打印菱形图案。</strong></p>
<p>在这里,使用<code class="language-plaintext highlighter-rouge">for</code>循环来打印菱形图案。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.niocoder</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Scanner</span><span class="o">;</span>
<span class="cm">/**
* Created by zhenglongfei on 2020/4/21
*
* @VERSION 1.0
*/</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">_05DiamondPattern</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">n</span><span class="o">,</span> <span class="n">i</span><span class="o">,</span> <span class="n">j</span><span class="o">,</span> <span class="n">space</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">"请输入行数: "</span><span class="o">);</span>
<span class="nc">Scanner</span> <span class="n">s</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Scanner</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">in</span><span class="o">);</span>
<span class="n">n</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">nextInt</span><span class="o">();</span>
<span class="n">space</span> <span class="o">=</span> <span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span>
<span class="k">for</span> <span class="o">(</span><span class="n">j</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> <span class="n">j</span> <span class="o"><=</span> <span class="n">n</span><span class="o">;</span> <span class="n">j</span><span class="o">++)</span> <span class="o">{</span>
<span class="k">for</span> <span class="o">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> <span class="n">i</span> <span class="o"><=</span> <span class="n">space</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">" "</span><span class="o">);</span>
<span class="o">}</span>
<span class="n">space</span><span class="o">--;</span>
<span class="k">for</span> <span class="o">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> <span class="n">i</span> <span class="o"><=</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">j</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">"*"</span><span class="o">);</span>
<span class="o">}</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">""</span><span class="o">);</span>
<span class="o">}</span>
<span class="n">space</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>
<span class="k">for</span> <span class="o">(</span><span class="n">j</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> <span class="n">j</span> <span class="o"><=</span> <span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span> <span class="n">j</span><span class="o">++)</span> <span class="o">{</span>
<span class="k">for</span> <span class="o">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> <span class="n">i</span> <span class="o"><=</span> <span class="n">space</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">" "</span><span class="o">);</span>
<span class="o">}</span>
<span class="n">space</span><span class="o">++;</span>
<span class="k">for</span> <span class="o">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span> <span class="n">i</span> <span class="o"><=</span> <span class="mi">2</span> <span class="o">*</span> <span class="o">(</span><span class="n">n</span> <span class="o">-</span> <span class="n">j</span><span class="o">)</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">"*"</span><span class="o">);</span>
<span class="o">}</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">""</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>输出</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>请输入行数: 5
*
***
*****
*******
*********
*******
*****
***
*
</code></pre></div></div>
<h4 id="字符串反转程序">字符串反转程序</h4>
<p><strong>编写一个Java程序来反转给定字符串中的字母。</strong></p>
<p>这个<code class="language-plaintext highlighter-rouge">Java</code>程序会反转用户输入的字符串中存在的字母。例如,“ <code class="language-plaintext highlighter-rouge">Hello People</code>”将被称为“ <code class="language-plaintext highlighter-rouge">olleH elpoeP</code>”。让我们使用<code class="language-plaintext highlighter-rouge">Java</code>来实现相同的功能。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.niocoder</span><span class="o">;</span>
<span class="cm">/**
* Created by zhenglongfei on 2020/4/21
*
* @VERSION 1.0
*/</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">_06Stringreverse</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">String</span> <span class="n">str</span> <span class="o">=</span> <span class="s">"Welcome To niocoder"</span><span class="o">;</span>
<span class="nc">String</span><span class="o">[]</span> <span class="n">strArray</span> <span class="o">=</span> <span class="n">str</span><span class="o">.</span><span class="na">split</span><span class="o">(</span><span class="s">" "</span><span class="o">);</span>
<span class="k">for</span> <span class="o">(</span><span class="nc">String</span> <span class="n">temp</span> <span class="o">:</span> <span class="n">strArray</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">temp</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">3</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="kt">char</span><span class="o">[]</span> <span class="n">s1</span> <span class="o">=</span> <span class="n">strArray</span><span class="o">[</span><span class="n">i</span><span class="o">].</span><span class="na">toCharArray</span><span class="o">();</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">s1</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span> <span class="n">j</span> <span class="o">>=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">j</span><span class="o">--)</span> <span class="o">{</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="n">s1</span><span class="o">[</span><span class="n">j</span><span class="o">]);</span>
<span class="o">}</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="s">" "</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>上面程序的输出如下所示:</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Welcome
To
niocoder
emocleW oT redocoin
</code></pre></div></div>
<h4 id="镜像程序">镜像程序</h4>
<p><strong>编写一个Java程序来检查给定的数组是否为镜像数组。</strong></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.niocoder</span><span class="o">;</span>
<span class="cm">/**
* Created by zhenglongfei on 2020/4/21
*
* @VERSION 1.0
*/</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">_07MirrorInverse</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
<span class="kt">int</span> <span class="n">arr</span><span class="o">[]</span> <span class="o">=</span> <span class="o">{</span><span class="mi">3</span><span class="o">,</span><span class="mi">4</span><span class="o">,</span><span class="mi">2</span><span class="o">,</span><span class="mi">0</span><span class="o">,</span><span class="mi">1</span><span class="o">};</span>
<span class="k">if</span> <span class="o">(</span><span class="n">isMirrorInverse</span><span class="o">(</span><span class="n">arr</span><span class="o">))</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"Yes"</span><span class="o">);</span>
<span class="k">else</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"No"</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">static</span> <span class="kt">boolean</span> <span class="nf">isMirrorInverse</span><span class="o">(</span><span class="kt">int</span> <span class="n">arr</span><span class="o">[])</span> <span class="o">{</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">arr</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">arr</span><span class="o">[</span><span class="n">arr</span><span class="o">[</span><span class="n">i</span><span class="o">]]</span> <span class="o">!=</span> <span class="n">i</span><span class="o">)</span>
<span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
<span class="o">}</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>输出</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Yes
</code></pre></div></div>郑龙飞相思一夜梅花发,忽到窗前疑是君。ffmpeg 修改视频封面2020-04-07T00:00:00+00:002020-04-07T00:00:00+00:00https://niocoder.com//2020/04/07/ffmpeg%E4%BF%AE%E6%94%B9%E8%A7%86%E9%A2%91%E5%B0%81%E9%9D%A2<blockquote>
<p>千金纵买相如赋,脉脉此情谁诉。</p>
</blockquote>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java37.jpg" alt="" /></p>
<h2 id="概述">概述</h2>
<p>有时候我们希望使用某一张图片作为视频素材的封面 ,而不是素材中的某帧。今天使用<code class="language-plaintext highlighter-rouge">ffmpeg</code>批量替换视频素材封面。</p>
<h3 id="环境装备">环境装备</h3>
<p>从<a href="https://www.ffmpeg.org/">官网</a>下载安装 <code class="language-plaintext highlighter-rouge">ffmpeg</code></p>
<h3 id="准备素材">准备素材</h3>
<p>准备好视频素材和封面图片</p>
<h3 id="编写程序">编写程序</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">cn.merryyou.file</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.io.*</span><span class="o">;</span>
<span class="cm">/**
* 修改视频封面
* Created by i@merryyou.cn on 2020/3/24
*
* @VERSION 1.0
*/</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">ChangeVedioCover</span> <span class="o">{</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">FFMPEG_PATH</span> <span class="o">=</span> <span class="s">"D:/ffmpeg/bin/ffmpeg.exe"</span><span class="o">;</span> <span class="c1">// ffmpeg 程序迷路</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">FILE_PATH</span> <span class="o">=</span> <span class="s">"E:/BaiduNetdiskDownload/测试"</span><span class="o">;</span> <span class="c1">//需要替换封面的视频目录</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">IMAGE_PATH</span> <span class="o">=</span> <span class="s">"E:/BaiduNetdiskDownload/测试/1.png"</span><span class="o">;</span> <span class="c1">// 需要替换的封面照片</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">String</span> <span class="no">COMMAND</span> <span class="o">=</span> <span class="s">"%s -i %s -i %s -map 1 -map 0 -c copy -disposition:0 attached_pic -y %s"</span><span class="o">;</span> <span class="c1">// ffmpeg 替换封面的命令</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>
<span class="n">printPath</span><span class="o">(</span><span class="k">new</span> <span class="nc">File</span><span class="o">(</span><span class="no">FILE_PATH</span><span class="o">));</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">printPath</span><span class="o">(</span><span class="nc">File</span> <span class="n">file</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">IOException</span> <span class="o">{</span>
<span class="nc">File</span><span class="o">[]</span> <span class="n">files</span> <span class="o">=</span> <span class="n">file</span><span class="o">.</span><span class="na">listFiles</span><span class="o">();</span>
<span class="k">for</span> <span class="o">(</span><span class="nc">File</span> <span class="n">a</span> <span class="o">:</span> <span class="n">files</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">a</span><span class="o">.</span><span class="na">getAbsolutePath</span><span class="o">());</span>
<span class="k">if</span> <span class="o">(</span><span class="n">a</span><span class="o">.</span><span class="na">getAbsolutePath</span><span class="o">().</span><span class="na">endsWith</span><span class="o">(</span><span class="s">".mp4"</span><span class="o">))</span> <span class="o">{</span>
<span class="nc">String</span> <span class="n">newPath</span> <span class="o">=</span> <span class="n">a</span><span class="o">.</span><span class="na">getParent</span><span class="o">()</span> <span class="o">+</span> <span class="s">"/"</span> <span class="o">+</span> <span class="n">a</span><span class="o">.</span><span class="na">getName</span><span class="o">().</span><span class="na">substring</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">a</span><span class="o">.</span><span class="na">getName</span><span class="o">().</span><span class="na">lastIndexOf</span><span class="o">(</span><span class="s">"."</span><span class="o">))</span> <span class="o">+</span> <span class="s">"_.mp4"</span><span class="o">;</span> <span class="c1">// 新生成的文件名后面添加_ 下划线</span>
<span class="nc">String</span> <span class="n">cmd</span> <span class="o">=</span> <span class="nc">String</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="no">COMMAND</span><span class="o">,</span> <span class="no">FFMPEG_PATH</span><span class="o">,</span> <span class="n">a</span><span class="o">.</span><span class="na">getAbsolutePath</span><span class="o">(),</span> <span class="no">IMAGE_PATH</span><span class="o">,</span> <span class="n">newPath</span><span class="o">);</span>
<span class="n">execCmd</span><span class="o">(</span><span class="n">cmd</span><span class="o">);</span>
<span class="n">a</span><span class="o">.</span><span class="na">delete</span><span class="o">();</span><span class="c1">// 删除源文件</span>
<span class="o">}</span>
<span class="k">if</span> <span class="o">(</span><span class="n">a</span><span class="o">.</span><span class="na">isDirectory</span><span class="o">())</span> <span class="o">{</span>
<span class="n">printPath</span><span class="o">(</span><span class="n">a</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">execCmd</span><span class="o">(</span><span class="nc">String</span> <span class="n">cmd</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">ProcessBuilder</span> <span class="n">builder</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ProcessBuilder</span><span class="o">(</span><span class="s">"cmd.exe"</span><span class="o">,</span> <span class="s">"/C"</span><span class="o">,</span> <span class="n">cmd</span><span class="o">);</span>
<span class="nc">Process</span> <span class="n">process</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
<span class="k">try</span> <span class="o">{</span>
<span class="n">process</span> <span class="o">=</span> <span class="n">builder</span><span class="o">.</span><span class="na">redirectErrorStream</span><span class="o">(</span><span class="kc">true</span><span class="o">).</span><span class="na">start</span><span class="o">();</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
<span class="o">}</span>
<span class="nc">InputStream</span> <span class="n">in</span> <span class="o">=</span> <span class="n">process</span><span class="o">.</span><span class="na">getInputStream</span><span class="o">();</span>
<span class="n">outStream</span><span class="o">(</span><span class="n">in</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">outStream</span><span class="o">(</span><span class="nc">InputStream</span> <span class="n">in</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// 用一个读输出流类去读</span>
<span class="nc">BufferedReader</span> <span class="n">br</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">BufferedReader</span><span class="o">(</span><span class="k">new</span> <span class="nc">InputStreamReader</span><span class="o">(</span><span class="n">in</span><span class="o">));</span>
<span class="nc">String</span> <span class="n">line</span><span class="o">;</span>
<span class="c1">// 逐行读取输出到控制台</span>
<span class="k">try</span> <span class="o">{</span>
<span class="k">while</span> <span class="o">((</span><span class="n">line</span> <span class="o">=</span> <span class="n">br</span><span class="o">.</span><span class="na">readLine</span><span class="o">())</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// System.out.println(line);</span>
<span class="o">}</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<h3 id="效果如下">效果如下</h3>
<h4 id="修改封面前">修改封面前</h4>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java38.png" alt="" /></p>
<h4 id="修改封面后">修改封面后</h4>
<p><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java39.png" alt="" /></p>
<h2 id="总结">总结</h2>
<p>更多<code class="language-plaintext highlighter-rouge">ffmpeg</code> 命令参考<a href="https://ffmpeg.org/ffmpeg.html">链接</a></p>郑龙飞千金纵买相如赋,脉脉此情谁诉。你今天因为 YYYY-MM-dd 被提 BUG 了吗?2019-12-31T00:00:00+00:002019-12-31T00:00:00+00:00https://niocoder.com//2019/12/31/%E4%BD%A0%E4%BB%8A%E5%A4%A9%E5%9B%A0%E4%B8%BA%20YYYY-MM-dd%20%E8%A2%AB%E6%8F%90%20BUG%20%E4%BA%86%E5%90%97<blockquote>
<p>兽炉沈水烟,翠沼残花片,一行行写入相思传。</p>
</blockquote>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java36.jpg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java36.jpg"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java36.jpg" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java36.jpg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java36.jpg" /></a></p>
<h2 id="bug-表现">BUG 表现##</h2>
<p><img src="https://user-gold-cdn.xitu.io/2019/12/31/16f59fe071ac6020?w=828&h=1792&f=jpeg&s=121630" alt="https://user-gold-cdn.xitu.io/2019/12/31/16f59fe071ac6020?w=828&h=1792&f=jpeg&s=121630" /></p>
<h2 id="bug-原因">BUG 原因##</h2>
<p><code class="language-plaintext highlighter-rouge">YYYY</code> 是 <code class="language-plaintext highlighter-rouge">week-based-year</code>,今天就已经 <code class="language-plaintext highlighter-rouge">2020</code> 年了
<code class="language-plaintext highlighter-rouge">yyyy</code> 还是 <code class="language-plaintext highlighter-rouge">2019</code> 年</p>
<blockquote>
<p><code class="language-plaintext highlighter-rouge">YYYY</code> 是表示:当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,那么这周就算入下一年。</p>
</blockquote>
<p>原文链接:<a href="https://v2ex.com/t/633650#r_8407403">https://v2ex.com/t/633650#r_8407403</a></p>郑龙飞兽炉沈水烟,翠沼残花片,一行行写入相思传。使用Spring Boot 2.X构建RESTful服务2019-10-30T00:00:00+00:002019-10-30T00:00:00+00:00https://niocoder.com//2019/10/30/%E4%BD%BF%E7%94%A8Spring%20Boot%202.X%E6%9E%84%E5%BB%BARESTful%E6%9C%8D%E5%8A%A1<blockquote>
<p>明月松间照,清泉石上流。</p>
</blockquote>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot18.jpg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot18.jpg"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot18.jpg" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot18.jpg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot18.jpg" /></a></p>
<h2 id="概述">概述</h2>
<p><code class="language-plaintext highlighter-rouge">Spring Boot</code>是由<code class="language-plaintext highlighter-rouge">Pivotal</code>团队提供的全新框架,其设计目的是用来简化<code class="language-plaintext highlighter-rouge">Spring</code>应用的创建、运行、调试、部署等。它大大减少了基于<code class="language-plaintext highlighter-rouge">Spring</code>开发的生产级应用程序的工作量。因此,开发人员能够真正专注于以业务为中心的功能。</p>
<p>本章我们将通过几个步骤演示如何使用<code class="language-plaintext highlighter-rouge">Spring Boo</code>t构建<code class="language-plaintext highlighter-rouge">RESTful</code>服务。我们将创建一个简单的客户服务<code class="language-plaintext highlighter-rouge">CRUD</code>(也就是创建,读取,更新,删除)客户记录和每个客户拥有的银行帐户。</p>
<h3 id="spring-initializr"><code class="language-plaintext highlighter-rouge">Spring</code> <code class="language-plaintext highlighter-rouge">Initializr</code></h3>
<p><code class="language-plaintext highlighter-rouge">Spring Initializr</code>是展开<code class="language-plaintext highlighter-rouge">Spring Boot</code>的第一步。它用于创建<code class="language-plaintext highlighter-rouge">Spring Boot</code>应用程序的项目结构。在开始<code class="language-plaintext highlighter-rouge">Spring Boot</code>之前,我们需要弄清项目结构并确定将配置文件,属性文件和静态文件保留在何处。打开基于Web的界面开始。如下图所示,填写字段,然后单击“生成项目”按钮。</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">Group</code>: <code class="language-plaintext highlighter-rouge">com.howtodoinjava.rest</code></li>
<li><code class="language-plaintext highlighter-rouge">Artifact</code>: <code class="language-plaintext highlighter-rouge">customerservice</code></li>
<li><code class="language-plaintext highlighter-rouge">Name</code>: <code class="language-plaintext highlighter-rouge">customerservice</code></li>
<li><code class="language-plaintext highlighter-rouge">Package Name</code>: <code class="language-plaintext highlighter-rouge">com.howtodoinjava.rest.customerservice</code></li>
<li><code class="language-plaintext highlighter-rouge">Dependencies</code>: <code class="language-plaintext highlighter-rouge">Web</code>, <code class="language-plaintext highlighter-rouge">JPA</code>, <code class="language-plaintext highlighter-rouge">H2</code></li>
</ul>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot19.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot19.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot19.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot19.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot19.png" /></a></p>
<blockquote>
<p><code class="language-plaintext highlighter-rouge">Spring Initializr</code>创建一个项目</p>
</blockquote>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot20.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot20.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot20.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot20.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot20.png" /></a></p>
<blockquote>
<p>项目目录结构</p>
</blockquote>
<p>如下所示的<code class="language-plaintext highlighter-rouge">POM</code>文件表示启动项目的依赖关系。在<code class="language-plaintext highlighter-rouge">Spring Boot</code>中,不同的启动程序项目代表不同的<code class="language-plaintext highlighter-rouge">Spring</code>模块,例如<code class="language-plaintext highlighter-rouge">MVC</code>,<code class="language-plaintext highlighter-rouge">ORM</code>等。开发人员主要要做的是在依赖项中添加启动程序项目,<code class="language-plaintext highlighter-rouge">Spring Boot</code>将管理可传递的依赖项和版本。</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?xml version="1.0" encoding="UTF-8"?></span>
<span class="nt"><project</span> <span class="na">xmlns=</span><span class="s">"http://maven.apache.org/POM/4.0.0"</span> <span class="na">xmlns:xsi=</span><span class="s">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span class="na">xsi:schemaLocation=</span><span class="s">"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"</span><span class="nt">></span>
<span class="nt"><modelVersion></span>4.0.0<span class="nt"></modelVersion></span>
<span class="nt"><parent></span>
<span class="nt"><groupId></span>org.springframework.boot<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>spring-boot-starter-parent<span class="nt"></artifactId></span>
<span class="nt"><version></span>2.2.0.RELEASE<span class="nt"></version></span>
<span class="nt"><relativePath/></span> <span class="c"><!-- lookup parent from repository --></span>
<span class="nt"></parent></span>
<span class="nt"><groupId></span>com.howtodoinjava<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>customerservice<span class="nt"></artifactId></span>
<span class="nt"><version></span>0.0.1-SNAPSHOT<span class="nt"></version></span>
<span class="nt"><name></span>customerservice<span class="nt"></name></span>
<span class="nt"><description></span>Demo project for Spring Boot<span class="nt"></description></span>
<span class="nt"><properties></span>
<span class="nt"><java.version></span>1.8<span class="nt"></java.version></span>
<span class="nt"></properties></span>
<span class="nt"><dependencies></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.springframework.boot<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>spring-boot-starter-data-jpa<span class="nt"></artifactId></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.springframework.boot<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>spring-boot-starter-web<span class="nt"></artifactId></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>com.h2database<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>h2<span class="nt"></artifactId></span>
<span class="nt"><scope></span>runtime<span class="nt"></scope></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.springframework.boot<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>spring-boot-starter-test<span class="nt"></artifactId></span>
<span class="nt"><scope></span>test<span class="nt"></scope></span>
<span class="nt"><exclusions></span>
<span class="nt"><exclusion></span>
<span class="nt"><groupId></span>org.junit.vintage<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>junit-vintage-engine<span class="nt"></artifactId></span>
<span class="nt"></exclusion></span>
<span class="nt"></exclusions></span>
<span class="nt"></dependency></span>
<span class="nt"></dependencies></span>
<span class="nt"><build></span>
<span class="nt"><plugins></span>
<span class="nt"><plugin></span>
<span class="nt"><groupId></span>org.springframework.boot<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>spring-boot-maven-plugin<span class="nt"></artifactId></span>
<span class="nt"></plugin></span>
<span class="nt"></plugins></span>
<span class="nt"></build></span>
<span class="nt"></project></span>
</code></pre></div></div>
<p>如果我们运行<code class="language-plaintext highlighter-rouge">mvnw dependency:tree</code>命令,则底层依赖关系层次结构将如下所示</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< com.codespeaks.rest:customerservice >-----------------
[INFO] Building customerservice 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:3.0.2:tree (default-cli) @ customerservice ---
[INFO] com.codespeaks.rest:customerservice:jar:0.0.1-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter-data-jpa:jar:2.0.6.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:2.0.6.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:2.0.6.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.0.6.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:2.0.6.RELEASE:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO] | | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.10.0:compile
[INFO] | | | | \- org.apache.logging.log4j:log4j-api:jar:2.10.0:compile
[INFO] | | | \- org.slf4j:jul-to-slf4j:jar:1.7.25:compile
[INFO] | | +- javax.annotation:javax.annotation-api:jar:1.3.2:compile
[INFO] | | \- org.yaml:snakeyaml:jar:1.19:runtime
[INFO] | +- org.springframework.boot:spring-boot-starter-aop:jar:2.0.6.RELEASE:compile
[INFO] | | +- org.springframework:spring-aop:jar:5.0.10.RELEASE:compile
[INFO] | | \- org.aspectj:aspectjweaver:jar:1.8.13:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-jdbc:jar:2.0.6.RELEASE:compile
[INFO] | | +- com.zaxxer:HikariCP:jar:2.7.9:compile
[INFO] | | \- org.springframework:spring-jdbc:jar:5.0.10.RELEASE:compile
[INFO] | +- javax.transaction:javax.transaction-api:jar:1.2:compile
[INFO] | +- org.hibernate:hibernate-core:jar:5.2.17.Final:compile
[INFO] | | +- org.jboss.logging:jboss-logging:jar:3.3.2.Final:compile
[INFO] | | +- org.hibernate.javax.persistence:hibernate-jpa-2.1-api:jar:1.0.2.Final:compile
[INFO] | | +- org.javassist:javassist:jar:3.22.0-GA:compile
[INFO] | | +- antlr:antlr:jar:2.7.7:compile
[INFO] | | +- org.jboss:jandex:jar:2.0.3.Final:compile
[INFO] | | +- com.fasterxml:classmate:jar:1.3.4:compile
[INFO] | | +- dom4j:dom4j:jar:1.6.1:compile
[INFO] | | \- org.hibernate.common:hibernate-commons-annotations:jar:5.0.1.Final:compile
.........omitted for brevity.................
</code></pre></div></div>
<h3 id="application-properties"><code class="language-plaintext highlighter-rouge">Application</code> <code class="language-plaintext highlighter-rouge">Properties</code></h3>
<p>我们使用基于<code class="language-plaintext highlighter-rouge">YAML</code>(一种标记语言)的属性文件将配置属性定义为比<code class="language-plaintext highlighter-rouge">application.properties</code>更具可读性。</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">spring:application:name=customer-service</code> # 项目名称。</li>
<li><code class="language-plaintext highlighter-rouge">spring:h2:console:enabled=true</code> # 启用嵌入式h2控制台。使用内存数据库</li>
<li><code class="language-plaintext highlighter-rouge">spring:h2:console:path=/h2-console</code> # <code class="language-plaintext highlighter-rouge">h2-console</code>的访问路径</li>
<li><code class="language-plaintext highlighter-rouge">spring:jpa:show-sql=true</code> # 打印<code class="language-plaintext highlighter-rouge">sql</code></li>
<li><code class="language-plaintext highlighter-rouge">server:port=8088</code> # 服务的端口.</li>
<li><code class="language-plaintext highlighter-rouge">server:servlet:context-path=/restapi</code> # <code class="language-plaintext highlighter-rouge">base URL</code></li>
</ul>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">spring</span><span class="pi">:</span>
<span class="na">application</span><span class="pi">:</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">customer-service</span>
<span class="na">h2</span><span class="pi">:</span>
<span class="na">console</span><span class="pi">:</span>
<span class="na">enabled</span><span class="pi">:</span> <span class="no">true</span>
<span class="na">path</span><span class="pi">:</span> <span class="s">/h2-console</span>
<span class="na">jpa</span><span class="pi">:</span>
<span class="na">show-sql</span><span class="pi">:</span> <span class="no">true</span>
<span class="na">server</span><span class="pi">:</span>
<span class="na">port</span><span class="pi">:</span> <span class="m">8088</span>
<span class="na">servlet</span><span class="pi">:</span>
<span class="na">context-path</span><span class="pi">:</span> <span class="s">/restapi</span>
</code></pre></div></div>
<h3 id="domain-实体"><code class="language-plaintext highlighter-rouge">Domain</code> 实体</h3>
<p>在此示例中,我们定义<code class="language-plaintext highlighter-rouge">JPA</code>实体以展示以下<code class="language-plaintext highlighter-rouge">ER</code>图,其中<code class="language-plaintext highlighter-rouge">Customer</code>实体与<code class="language-plaintext highlighter-rouge">Account</code>实体具有一对多关系。<code class="language-plaintext highlighter-rouge">Account.CustomerId</code>是引用<code class="language-plaintext highlighter-rouge">Customer.CustomerId</code>的外键。</p>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot21.jpeg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot21.jpeg"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot21.jpeg" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot21.jpeg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot21.jpeg" /></a></p>
<p>使用以下注解将这些类表示为<code class="language-plaintext highlighter-rouge">JPA</code>实体</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">@Entity</code> 表示该类是一个实体类。</li>
<li><code class="language-plaintext highlighter-rouge">@Table</code> 表示此实体映射到的数据库表。</li>
<li><code class="language-plaintext highlighter-rouge">@Id</code> 表示实体的主键</li>
<li><code class="language-plaintext highlighter-rouge">@GeneratedValue</code> 表示生成主键的策略,默认策略是<code class="language-plaintext highlighter-rouge">AUTO</code>策略。</li>
<li><code class="language-plaintext highlighter-rouge">@Column</code> 表示实体属性的列映射。</li>
<li><code class="language-plaintext highlighter-rouge">@ManyToOne</code> 表示从帐户到客户的多对一个关系。此关系在本例中的实体<code class="language-plaintext highlighter-rouge">Account</code>上指定。</li>
<li><code class="language-plaintext highlighter-rouge">@JoinColumn</code> 表示外键列</li>
<li><code class="language-plaintext highlighter-rouge">@OnDelete</code> 在此示例中表示级联删除操作。删除客户实体后,其所有帐户将同时被删除。</li>
<li><code class="language-plaintext highlighter-rouge">@JsonIgnore</code> 表示在序列化结束反序列化期间JSON解析器将忽略的属性。</li>
</ul>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.howtodoinjava.customerservice.domin</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.persistence.*</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.io.Serializable</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.LocalDate</span><span class="o">;</span>
<span class="nd">@Table</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"CUSTOMER"</span><span class="o">)</span>
<span class="nd">@Entity</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Customer</span> <span class="kd">implements</span> <span class="nc">Serializable</span><span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">long</span> <span class="n">serialVersionUID</span> <span class="o">=</span> <span class="o">-</span><span class="mi">6759774343110776659L</span><span class="o">;</span>
<span class="nd">@Id</span>
<span class="nd">@GeneratedValue</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"CUSTOMERID"</span><span class="o">,</span><span class="n">updatable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">)</span>
<span class="kd">private</span> <span class="nc">Integer</span> <span class="n">customerId</span><span class="o">;</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"NAME"</span><span class="o">)</span>
<span class="kd">private</span> <span class="nc">String</span> <span class="n">customerName</span><span class="o">;</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"DATEOFBIRTH"</span> <span class="o">,</span><span class="n">nullable</span><span class="o">=</span><span class="kc">true</span><span class="o">)</span>
<span class="kd">private</span> <span class="nc">LocalDate</span> <span class="n">dateofBirth</span><span class="o">;</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"PHONENUMBER"</span><span class="o">)</span>
<span class="kd">private</span> <span class="nc">String</span> <span class="n">phoneNumber</span><span class="o">;</span>
<span class="kd">public</span> <span class="nc">Integer</span> <span class="nf">getCustomerId</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">customerId</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setCustomerId</span><span class="o">(</span><span class="nc">Integer</span> <span class="n">customerId</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">customerId</span> <span class="o">=</span> <span class="n">customerId</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">getCustomerName</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">customerName</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setCustomerName</span><span class="o">(</span><span class="nc">String</span> <span class="n">customerName</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">customerName</span> <span class="o">=</span> <span class="n">customerName</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">LocalDate</span> <span class="nf">getDateofBirth</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">dateofBirth</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setDateofBirth</span><span class="o">(</span><span class="nc">LocalDate</span> <span class="n">dateofBirth</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">dateofBirth</span> <span class="o">=</span> <span class="n">dateofBirth</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">getPhoneNumber</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">phoneNumber</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setPhoneNumber</span><span class="o">(</span><span class="nc">String</span> <span class="n">phoneNumber</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">phoneNumber</span> <span class="o">=</span> <span class="n">phoneNumber</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.howtodoinjava.customerservice.domin</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.fasterxml.jackson.annotation.JsonIgnore</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.hibernate.annotations.OnDelete</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.hibernate.annotations.OnDeleteAction</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.persistence.*</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.io.Serializable</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.math.BigDecimal</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.LocalDate</span><span class="o">;</span>
<span class="nd">@Table</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"ACCOUNT"</span><span class="o">)</span>
<span class="nd">@Entity</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">Account</span> <span class="kd">implements</span> <span class="nc">Serializable</span> <span class="o">{</span>
<span class="nd">@Id</span>
<span class="nd">@GeneratedValue</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"ACCOUNTNUMBER"</span><span class="o">,</span><span class="n">updatable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">)</span>
<span class="kd">private</span> <span class="nc">Integer</span> <span class="n">accountNumber</span><span class="o">;</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"ACCOUNTNAME"</span><span class="o">)</span>
<span class="kd">private</span> <span class="nc">String</span> <span class="n">accountName</span><span class="o">;</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"BALANCE"</span><span class="o">)</span>
<span class="kd">private</span> <span class="nc">BigDecimal</span> <span class="n">balance</span><span class="o">;</span>
<span class="nd">@Column</span><span class="o">(</span><span class="n">name</span><span class="o">=</span><span class="s">"OPENINGDATE"</span><span class="o">)</span>
<span class="kd">private</span> <span class="nc">LocalDate</span> <span class="n">openingDate</span><span class="o">;</span>
<span class="nd">@ManyToOne</span><span class="o">(</span><span class="n">fetch</span> <span class="o">=</span> <span class="nc">FetchType</span><span class="o">.</span><span class="na">LAZY</span><span class="o">,</span> <span class="n">optional</span> <span class="o">=</span> <span class="kc">false</span><span class="o">)</span>
<span class="nd">@JoinColumn</span><span class="o">(</span><span class="n">name</span> <span class="o">=</span> <span class="s">"CUSTOMERID"</span><span class="o">,</span> <span class="n">nullable</span> <span class="o">=</span> <span class="kc">false</span><span class="o">)</span>
<span class="nd">@OnDelete</span><span class="o">(</span><span class="n">action</span> <span class="o">=</span> <span class="nc">OnDeleteAction</span><span class="o">.</span><span class="na">CASCADE</span><span class="o">)</span>
<span class="nd">@JsonIgnore</span>
<span class="kd">private</span> <span class="nc">Customer</span> <span class="n">customer</span><span class="o">;</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="kt">long</span> <span class="n">serialVersionUID</span> <span class="o">=</span> <span class="o">-</span><span class="mi">6380749575516426900L</span><span class="o">;</span>
<span class="kd">public</span> <span class="nc">Integer</span> <span class="nf">getAccountNumber</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">accountNumber</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setAccountNumber</span><span class="o">(</span><span class="nc">Integer</span> <span class="n">accountNumber</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">accountNumber</span> <span class="o">=</span> <span class="n">accountNumber</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">getAccountName</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">accountName</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setAccountName</span><span class="o">(</span><span class="nc">String</span> <span class="n">accountName</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">accountName</span> <span class="o">=</span> <span class="n">accountName</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">BigDecimal</span> <span class="nf">getBalance</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">balance</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setBalance</span><span class="o">(</span><span class="nc">BigDecimal</span> <span class="n">balance</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">balance</span> <span class="o">=</span> <span class="n">balance</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">LocalDate</span> <span class="nf">getOpeningDate</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">openingDate</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setOpeningDate</span><span class="o">(</span><span class="nc">LocalDate</span> <span class="n">openingDate</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">openingDate</span> <span class="o">=</span> <span class="n">openingDate</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">Customer</span> <span class="nf">getCustomer</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">customer</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setCustomer</span><span class="o">(</span><span class="nc">Customer</span> <span class="n">customer</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">customer</span> <span class="o">=</span> <span class="n">customer</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<h3 id="repositories"><code class="language-plaintext highlighter-rouge">Repositories</code></h3>
<p><code class="language-plaintext highlighter-rouge">Spring Data JPA</code>在关系数据库之上抽象了持久层,并大大减少了<code class="language-plaintext highlighter-rouge">CRUD</code>操作和分页上的重复代码。通过扩展<code class="language-plaintext highlighter-rouge">JPA</code>实体及其主键类型的<code class="language-plaintext highlighter-rouge">JPARepository</code>接口,<code class="language-plaintext highlighter-rouge">Spring Data</code>将检测该接口并在运行时自动创建实现。可从继承中轻松获得的<code class="language-plaintext highlighter-rouge">CRUD</code>方法可以立即解决大多数数据访问用例。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.howtodoinjava.customerservice.repository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.howtodoinjava.customerservice.domin.Customer</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.jpa.repository.JpaRepository</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">CustomerRepository</span> <span class="kd">extends</span> <span class="nc">JpaRepository</span><span class="o"><</span><span class="nc">Customer</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">></span> <span class="o">{</span>
<span class="o">}</span>
</code></pre></div></div>
<p>使用J<code class="language-plaintext highlighter-rouge">PARepository</code>,我们还可以通过定义接口方法来创建自定义查询。<code class="language-plaintext highlighter-rouge">Spring Data JPA</code>从方法名称派生查询,并在运行时实现查询逻辑。<code class="language-plaintext highlighter-rouge">findByCustomerCustomerId</code>方法接受<code class="language-plaintext highlighter-rouge">Pageable</code>类型的参数<code class="language-plaintext highlighter-rouge">pageable</code>,并返<code class="language-plaintext highlighter-rouge">Account</code>类的的<code class="language-plaintext highlighter-rouge">Page</code>对象。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.howtodoinjava.customerservice.repository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.howtodoinjava.customerservice.domin.Account</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.domain.Page</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.domain.Pageable</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.jpa.repository.JpaRepository</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">AccountRepository</span> <span class="kd">extends</span> <span class="nc">JpaRepository</span><span class="o"><</span><span class="nc">Account</span><span class="o">,</span> <span class="nc">Integer</span><span class="o">></span> <span class="o">{</span>
<span class="nc">Page</span><span class="o"><</span><span class="nc">Account</span><span class="o">></span> <span class="nf">findByCustomerCustomerId</span><span class="o">(</span><span class="nc">Integer</span> <span class="n">customerId</span><span class="o">,</span> <span class="nc">Pageable</span> <span class="n">pageable</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<h3 id="restful-控制器"><code class="language-plaintext highlighter-rouge">RESTful</code> 控制器</h3>
<p>在<code class="language-plaintext highlighter-rouge">Spring MVC</code>(<code class="language-plaintext highlighter-rouge">Model</code>-<code class="language-plaintext highlighter-rouge">View</code>-<code class="language-plaintext highlighter-rouge">Controller</code>)中使用<code class="language-plaintext highlighter-rouge">@Controller</code>注解的控制器合并了业务逻辑和视图之间的数据流。在大多数情况下,控制器方法返回<code class="language-plaintext highlighter-rouge">ModelAndView</code>对象以呈现视图。但有时控制器方法返回的值会以<code class="language-plaintext highlighter-rouge">JSON/XML</code>格式显示给用户,而不是<code class="language-plaintext highlighter-rouge">HTML</code>页面。要实现这一点,可以使用注释<code class="language-plaintext highlighter-rouge">@ResponseBody</code>并自动将返回的值序列化为<code class="language-plaintext highlighter-rouge">JSON/XML</code>,然后将其保存到<code class="language-plaintext highlighter-rouge">HTTP</code>响应体中。<code class="language-plaintext highlighter-rouge">annotation</code> <code class="language-plaintext highlighter-rouge">@RestController</code>结合了前面的注释,为创建<code class="language-plaintext highlighter-rouge">RESTful</code>控制器提供了更多的便利。</p>
<p>注解<code class="language-plaintext highlighter-rouge">@GetMapping</code>,<code class="language-plaintext highlighter-rouge">@PostMapping</code>,<code class="language-plaintext highlighter-rouge">@PutMapping</code>和<code class="language-plaintext highlighter-rouge">@DeleteMapping</code>比其前身<code class="language-plaintext highlighter-rouge">@RequestMapping</code>更具<code class="language-plaintext highlighter-rouge">HTTP</code>请求特定性,前者<code class="language-plaintext highlighter-rouge">@RequestMapping</code>需要通过方法变量单独表示<code class="language-plaintext highlighter-rouge">HTTP</code>请求方法。</p>
<p>这分别是与客户和帐户相关的操作的两个控制器类。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.howtodoinjava.customerservice.controller</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.howtodoinjava.customerservice.domin.Customer</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.howtodoinjava.customerservice.repository.CustomerRepository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.beans.factory.annotation.Autowired</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.domain.Page</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.domain.Pageable</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.http.HttpStatus</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.http.ResponseEntity</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.*</span><span class="o">;</span>
<span class="nd">@RestController</span>
<span class="nd">@RequestMapping</span><span class="o">(</span><span class="s">"/customers"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">CustomerController</span> <span class="o">{</span>
<span class="nd">@Autowired</span>
<span class="kd">private</span> <span class="nc">CustomerRepository</span> <span class="n">customerRepository</span><span class="o">;</span>
<span class="nd">@PostMapping</span>
<span class="nd">@ResponseStatus</span><span class="o">(</span><span class="n">code</span> <span class="o">=</span> <span class="nc">HttpStatus</span><span class="o">.</span><span class="na">CREATED</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">Customer</span> <span class="nf">save</span><span class="o">(</span><span class="nd">@RequestBody</span> <span class="nc">Customer</span> <span class="n">customer</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">customerRepository</span><span class="o">.</span><span class="na">save</span><span class="o">(</span><span class="n">customer</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@GetMapping</span>
<span class="kd">public</span> <span class="nc">Page</span><span class="o"><</span><span class="nc">Customer</span><span class="o">></span> <span class="nf">all</span><span class="o">(</span><span class="nc">Pageable</span> <span class="n">pageable</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">customerRepository</span><span class="o">.</span><span class="na">findAll</span><span class="o">(</span><span class="n">pageable</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@GetMapping</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"/{customerId}"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">Customer</span> <span class="nf">findByCustomerId</span><span class="o">(</span><span class="nd">@PathVariable</span> <span class="nc">Integer</span> <span class="n">customerId</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">customerRepository</span><span class="o">.</span><span class="na">findById</span><span class="o">(</span><span class="n">customerId</span><span class="o">).</span><span class="na">orElseThrow</span><span class="o">(()</span> <span class="o">-></span> <span class="k">new</span> <span class="nc">RuntimeException</span><span class="o">(</span><span class="s">"Customer [customerId="</span> <span class="o">+</span> <span class="n">customerId</span> <span class="o">+</span> <span class="s">"] can't be found"</span><span class="o">));</span>
<span class="o">}</span>
<span class="nd">@DeleteMapping</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"/{customerId}"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">ResponseEntity</span><span class="o"><?></span> <span class="n">deleteCustomer</span><span class="o">(</span><span class="nd">@PathVariable</span> <span class="nc">Integer</span> <span class="n">customerId</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">customerRepository</span><span class="o">.</span><span class="na">findById</span><span class="o">(</span><span class="n">customerId</span><span class="o">).</span><span class="na">map</span><span class="o">(</span><span class="n">customer</span> <span class="o">-></span> <span class="o">{</span>
<span class="n">customerRepository</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="n">customer</span><span class="o">);</span>
<span class="k">return</span> <span class="nc">ResponseEntity</span><span class="o">.</span><span class="na">ok</span><span class="o">().</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">).</span><span class="na">orElseThrow</span><span class="o">(()</span> <span class="o">-></span> <span class="k">new</span> <span class="nc">RuntimeException</span><span class="o">(</span><span class="s">"Customer [customerId="</span> <span class="o">+</span> <span class="n">customerId</span> <span class="o">+</span> <span class="s">"] can't be found"</span><span class="o">));</span>
<span class="o">}</span>
<span class="nd">@PutMapping</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"/{customerId}"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">ResponseEntity</span><span class="o"><</span><span class="nc">Customer</span><span class="o">></span> <span class="nf">updateCustomer</span><span class="o">(</span><span class="nd">@PathVariable</span> <span class="nc">Integer</span> <span class="n">customerId</span><span class="o">,</span> <span class="nd">@RequestBody</span> <span class="nc">Customer</span> <span class="n">newCustomer</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">customerRepository</span><span class="o">.</span><span class="na">findById</span><span class="o">(</span><span class="n">customerId</span><span class="o">).</span><span class="na">map</span><span class="o">(</span><span class="n">customer</span> <span class="o">-></span> <span class="o">{</span>
<span class="n">customer</span><span class="o">.</span><span class="na">setCustomerName</span><span class="o">(</span><span class="n">newCustomer</span><span class="o">.</span><span class="na">getCustomerName</span><span class="o">());</span>
<span class="n">customer</span><span class="o">.</span><span class="na">setDateofBirth</span><span class="o">(</span><span class="n">newCustomer</span><span class="o">.</span><span class="na">getDateofBirth</span><span class="o">());</span>
<span class="n">customer</span><span class="o">.</span><span class="na">setPhoneNumber</span><span class="o">(</span><span class="n">newCustomer</span><span class="o">.</span><span class="na">getPhoneNumber</span><span class="o">());</span>
<span class="n">customerRepository</span><span class="o">.</span><span class="na">save</span><span class="o">(</span><span class="n">customer</span><span class="o">);</span>
<span class="k">return</span> <span class="nc">ResponseEntity</span><span class="o">.</span><span class="na">ok</span><span class="o">(</span><span class="n">customer</span><span class="o">);</span>
<span class="o">}).</span><span class="na">orElseThrow</span><span class="o">(()</span> <span class="o">-></span> <span class="k">new</span> <span class="nc">RuntimeException</span><span class="o">(</span><span class="s">"Customer [customerId="</span> <span class="o">+</span> <span class="n">customerId</span> <span class="o">+</span> <span class="s">"] can't be found"</span><span class="o">));</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">com.howtodoinjava.customerservice.controller</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.howtodoinjava.customerservice.domin.Account</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.howtodoinjava.customerservice.domin.Customer</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.howtodoinjava.customerservice.repository.AccountRepository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.howtodoinjava.customerservice.repository.CustomerRepository</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.beans.factory.annotation.Autowired</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.domain.Page</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.data.domain.Pageable</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.http.HttpStatus</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.http.ResponseEntity</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.springframework.web.bind.annotation.*</span><span class="o">;</span>
<span class="nd">@RestController</span>
<span class="nd">@RequestMapping</span><span class="o">(</span><span class="s">"/customers"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">AccountController</span> <span class="o">{</span>
<span class="nd">@Autowired</span>
<span class="kd">private</span> <span class="nc">CustomerRepository</span> <span class="n">customerRepository</span><span class="o">;</span>
<span class="nd">@Autowired</span>
<span class="kd">private</span> <span class="nc">AccountRepository</span> <span class="n">accountRepository</span><span class="o">;</span>
<span class="nd">@PostMapping</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"/{customerId}/accounts"</span><span class="o">)</span>
<span class="nd">@ResponseStatus</span><span class="o">(</span><span class="n">code</span> <span class="o">=</span> <span class="nc">HttpStatus</span><span class="o">.</span><span class="na">CREATED</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">Account</span> <span class="nf">save</span><span class="o">(</span><span class="nd">@PathVariable</span> <span class="nc">Integer</span> <span class="n">customerId</span><span class="o">,</span> <span class="nd">@RequestBody</span> <span class="nc">Account</span> <span class="n">account</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">customerRepository</span><span class="o">.</span><span class="na">findById</span><span class="o">(</span><span class="n">customerId</span><span class="o">).</span><span class="na">map</span><span class="o">(</span><span class="n">customer</span> <span class="o">-></span> <span class="o">{</span>
<span class="n">account</span><span class="o">.</span><span class="na">setCustomer</span><span class="o">(</span><span class="n">customer</span><span class="o">);</span>
<span class="k">return</span> <span class="n">accountRepository</span><span class="o">.</span><span class="na">save</span><span class="o">(</span><span class="n">account</span><span class="o">);</span>
<span class="o">}).</span><span class="na">orElseThrow</span><span class="o">(()</span> <span class="o">-></span> <span class="k">new</span> <span class="nc">RuntimeException</span><span class="o">(</span><span class="s">"Customer [customerId="</span> <span class="o">+</span> <span class="n">customerId</span> <span class="o">+</span> <span class="s">"] can't be found"</span><span class="o">));</span>
<span class="o">}</span>
<span class="nd">@GetMapping</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"/{customerId}/accounts"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">Page</span><span class="o"><</span><span class="nc">Account</span><span class="o">></span> <span class="nf">all</span><span class="o">(</span><span class="nd">@PathVariable</span> <span class="nc">Integer</span> <span class="n">customerId</span><span class="o">,</span> <span class="nc">Pageable</span> <span class="n">pageable</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">accountRepository</span><span class="o">.</span><span class="na">findByCustomerCustomerId</span><span class="o">(</span><span class="n">customerId</span><span class="o">,</span> <span class="n">pageable</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@DeleteMapping</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"/{customerId}/accounts/{accountId}"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">ResponseEntity</span><span class="o"><?></span> <span class="n">deleteAccount</span><span class="o">(</span><span class="nd">@PathVariable</span> <span class="nc">Integer</span> <span class="n">customerId</span><span class="o">,</span> <span class="nd">@PathVariable</span> <span class="nc">Integer</span> <span class="n">accountId</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(!</span><span class="n">customerRepository</span><span class="o">.</span><span class="na">existsById</span><span class="o">(</span><span class="n">customerId</span><span class="o">))</span> <span class="o">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">RuntimeException</span><span class="o">(</span><span class="s">"Customer [customerId="</span> <span class="o">+</span> <span class="n">customerId</span> <span class="o">+</span> <span class="s">"] can't be found"</span><span class="o">);</span>
<span class="o">}</span>
<span class="k">return</span> <span class="n">accountRepository</span><span class="o">.</span><span class="na">findById</span><span class="o">(</span><span class="n">accountId</span><span class="o">).</span><span class="na">map</span><span class="o">(</span><span class="n">account</span> <span class="o">-></span> <span class="o">{</span>
<span class="n">accountRepository</span><span class="o">.</span><span class="na">delete</span><span class="o">(</span><span class="n">account</span><span class="o">);</span>
<span class="k">return</span> <span class="nc">ResponseEntity</span><span class="o">.</span><span class="na">ok</span><span class="o">().</span><span class="na">build</span><span class="o">();</span>
<span class="o">}).</span><span class="na">orElseThrow</span><span class="o">(()</span> <span class="o">-></span> <span class="k">new</span> <span class="nc">RuntimeException</span><span class="o">(</span><span class="s">"Account [accountId="</span> <span class="o">+</span> <span class="n">accountId</span> <span class="o">+</span> <span class="s">"] can't be found"</span><span class="o">));</span>
<span class="o">}</span>
<span class="nd">@PutMapping</span><span class="o">(</span><span class="n">value</span> <span class="o">=</span> <span class="s">"/{customerId}/accounts/{accountId}"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">ResponseEntity</span><span class="o"><</span><span class="nc">Account</span><span class="o">></span> <span class="nf">updateAccount</span><span class="o">(</span><span class="nd">@PathVariable</span> <span class="nc">Integer</span> <span class="n">customerId</span><span class="o">,</span> <span class="nd">@PathVariable</span> <span class="nc">Integer</span> <span class="n">accountId</span><span class="o">,</span> <span class="nd">@RequestBody</span> <span class="nc">Account</span> <span class="n">newAccount</span><span class="o">)</span> <span class="o">{</span>
<span class="nc">Customer</span> <span class="n">customer</span> <span class="o">=</span> <span class="n">customerRepository</span><span class="o">.</span><span class="na">findById</span><span class="o">(</span><span class="n">customerId</span><span class="o">).</span><span class="na">orElseThrow</span><span class="o">(()</span> <span class="o">-></span> <span class="k">new</span> <span class="nc">RuntimeException</span><span class="o">(</span><span class="s">"Customer [customerId="</span> <span class="o">+</span> <span class="n">customerId</span> <span class="o">+</span> <span class="s">"] can't be found"</span><span class="o">));</span>
<span class="k">return</span> <span class="n">accountRepository</span><span class="o">.</span><span class="na">findById</span><span class="o">(</span><span class="n">accountId</span><span class="o">).</span><span class="na">map</span><span class="o">(</span><span class="n">account</span> <span class="o">-></span> <span class="o">{</span>
<span class="n">newAccount</span><span class="o">.</span><span class="na">setCustomer</span><span class="o">(</span><span class="n">customer</span><span class="o">);</span>
<span class="n">accountRepository</span><span class="o">.</span><span class="na">save</span><span class="o">(</span><span class="n">newAccount</span><span class="o">);</span>
<span class="k">return</span> <span class="nc">ResponseEntity</span><span class="o">.</span><span class="na">ok</span><span class="o">(</span><span class="n">newAccount</span><span class="o">);</span>
<span class="o">}).</span><span class="na">orElseThrow</span><span class="o">(()</span> <span class="o">-></span> <span class="k">new</span> <span class="nc">RuntimeException</span><span class="o">(</span><span class="s">"Account [accountId="</span> <span class="o">+</span> <span class="n">accountId</span> <span class="o">+</span> <span class="s">"] can't be found"</span><span class="o">));</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>在前面的控制器类中,我们如下定义了许多<code class="language-plaintext highlighter-rouge">RESTful URI</code></p>
<ul>
<li><code class="language-plaintext highlighter-rouge">/customers</code> <code class="language-plaintext highlighter-rouge">HTTP Get</code> # 获得所有客户</li>
<li><code class="language-plaintext highlighter-rouge">/customers</code> <code class="language-plaintext highlighter-rouge">HTTP Post</code> # 创建新客户</li>
<li><code class="language-plaintext highlighter-rouge">/customers/{customerId}</code> <code class="language-plaintext highlighter-rouge">HTTP Get</code> # 获得一个客户</li>
<li><code class="language-plaintext highlighter-rouge">/customers/{customerId}</code> <code class="language-plaintext highlighter-rouge">HTTP Delete</code> # 删除客户</li>
<li><code class="language-plaintext highlighter-rouge">/customers/{customerId}</code> <code class="language-plaintext highlighter-rouge">HTTP Put</code> # 更新客户</li>
<li><code class="language-plaintext highlighter-rouge">/customers/{customerId}/accounts</code> <code class="language-plaintext highlighter-rouge">HTTP Post</code> # 为客户创建一个帐户</li>
<li><code class="language-plaintext highlighter-rouge">/customers/{customerId}/accounts</code> <code class="language-plaintext highlighter-rouge">HTTP Get</code> # 根据客户获取帐户</li>
<li><code class="language-plaintext highlighter-rouge">/customers/{customerId}/accounts/{accountId}</code> <code class="language-plaintext highlighter-rouge">HTTP Delete</code> # 根据客户删除账户</li>
<li><code class="language-plaintext highlighter-rouge">/customers/{customerId}/accounts/{accountId}</code> <code class="language-plaintext highlighter-rouge">HTTP Put</code> # 根据客户更新帐户</li>
</ul>
<p>在关于<code class="language-plaintext highlighter-rouge">REST</code>风格的<code class="language-plaintext highlighter-rouge">API</code>设计指导原则,它超出了本文的范围。互联网上有一些不错的文章,大家可以自行查看。</p>
<h3 id="测试">测试</h3>
<p>可以在<code class="language-plaintext highlighter-rouge">Github</code>上找<code class="language-plaintext highlighter-rouge">到RESTful</code>服务示例。如果你对<code class="language-plaintext highlighter-rouge">Linux curl</code>命令不满意,我们可以通过简单地导入<code class="language-plaintext highlighter-rouge">Postman</code>集合文件来使用<code class="language-plaintext highlighter-rouge">Postman</code>调用<code class="language-plaintext highlighter-rouge">RESTful</code>服务。</p>
<p>检查数据库中的数据,通过<code class="language-plaintext highlighter-rouge">http://localhos:8088/restapi/h2-console/</code>访问<code class="language-plaintext highlighter-rouge">H2</code>控制台,并提供以下详细信息。</p>
<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Driver Class: org.h2.Driver
JDBC URL: jdbc:h2:mem:testdb
User Name: sa
Password: <blank>
</code></pre></div></div>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot22.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot22.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot22.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot22.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot22.png" /></a></p>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot23.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot23.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot23.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot23.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/springboot/springboot23.png" /></a></p>
<h2 id="总结">总结</h2>
<p><code class="language-plaintext highlighter-rouge">Spring Boot</code>并不与<code class="language-plaintext highlighter-rouge">Spring</code>框架存在竞争。恰恰相反,它使<code class="language-plaintext highlighter-rouge">Spring</code>更容易使用。在<code class="language-plaintext highlighter-rouge">starter</code>项目中,<code class="language-plaintext highlighter-rouge">Spring Boot</code>管理依赖项,使我们不必进行耗时且容易出错的依赖项管理,尤其是在应用程序复杂性增加的情况下。此外,<code class="language-plaintext highlighter-rouge">Spring Boot</code>通过检查类路径为我们执行自动配置。例如,如果<code class="language-plaintext highlighter-rouge">JPA</code>实现出现在类路径中,则<code class="language-plaintext highlighter-rouge">Spring Boot</code>将配置<code class="language-plaintext highlighter-rouge">DataSource</code>,<code class="language-plaintext highlighter-rouge">TransactionManager</code>和<code class="language-plaintext highlighter-rouge">EntityManagerFactory</code>等。
同时,覆盖<code class="language-plaintext highlighter-rouge">Spring Boot</code>为我们所做的配置非常简单。</p>
<p>上述代码都可以在<a href="https://github.com/longfeizheng/customerservice-RESTful">customerservice-RESTful</a>上找到</p>
<hr />
<p><a href="https://niocoder.com/assets/images/qrcode.jpg" title="https://niocoder.com/assets/images/qrcode.jpg"><img src="https://niocoder.com/assets/images/qrcode.jpg" alt="https://niocoder.com/assets/images/qrcode.jpg" title="https://niocoder.com/assets/images/qrcode.jpg" /></a></p>
<blockquote>
<p>🙂🙂🙂关注微信公众号<strong>java干货</strong>
不定期分享干货资料</p>
</blockquote>
<p>原文链接:<a href="https://medium.com/@codespeaks/build-restful-services-with-spring-boot-2-x-in-few-steps-95c895a7abf5">Build RESTful Services with Spring Boot 2.X in Few Steps</a></p>郑龙飞明月松间照,清泉石上流。java多线程(一)2019-10-29T00:00:00+00:002019-10-29T00:00:00+00:00https://niocoder.com//2019/10/29/java%E5%A4%9A%E7%BA%BF%E7%A8%8B%EF%BC%88%E4%B8%80%EF%BC%89<blockquote>
<p>何日请缨提锐旅,一鞭直渡清河洛。</p>
</blockquote>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java18.jpg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java18.jpg"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java18.jpg" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java18.jpg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java18.jpg" /></a></p>
<h2 id="概述">概述</h2>
<blockquote>
<p>进程 = 服务器、PC或Mac上同时运行的多个应用程序
线程 = 在一个进程中运行多个任务</p>
</blockquote>
<h3 id="进程">进程</h3>
<p>进程——当一个软件应用程序开始运行时,它会使用系统资源,如<code class="language-plaintext highlighter-rouge">I/O</code>设备、<code class="language-plaintext highlighter-rouge">CPU</code>、<code class="language-plaintext highlighter-rouge">RAM</code>、<code class="language-plaintext highlighter-rouge">HDD</code>,可能还有网络资源。类似地,其他软件应用程序也希望同时使用相同的系统资源。为了让多个软件应用程序共享系统资源,它们之间必须有明确的边界。否则,它们运行时会相互影响。通过<strong>进程</strong>来实现这种隔离。由于多个进程可以同时运行,一个操作系统通过进程执行多个任务!</p>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java19.jpeg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java19.jpeg"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java19.jpeg" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java19.jpeg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java19.jpeg" /></a></p>
<blockquote>
<p>使用<code class="language-plaintext highlighter-rouge">CPU</code>,内存,磁盘和网络在<code class="language-plaintext highlighter-rouge">OS</code>中运行的进程</p>
</blockquote>
<h3 id="线程">线程</h3>
<p>线程—软件应用程序(例如<code class="language-plaintext highlighter-rouge">Word</code>文档)可能不得不在处理用户事件和保存当前工作之间进行多任务处理。为了实现这一目标,每个任务都需要访问该进程可用的相同系统资源,但要在其自己的空间内。与<code class="language-plaintext highlighter-rouge">OS</code>相似,每个进程都可以通过<strong>线程</strong>为运行于其中的多个任务提供隔离。由于多个线程可以在进程中同时运行,因此一个进程通过线程执行多任务!</p>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java20.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java20.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java20.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java20.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java20.png" /></a></p>
<blockquote>
<p>在<code class="language-plaintext highlighter-rouge">Java</code>进程(<code class="language-plaintext highlighter-rouge">Java</code>虚拟机)中运行的线程</p>
</blockquote>
<h2 id="单线程与多线程应用程序">单线程与多线程应用程序</h2>
<h3 id="单线程应用">单线程应用</h3>
<p>打个比方:一家有8名成员的厨房部门的餐厅在任何给定时间都只能提供一张桌子,因为他们只有一名服务员。如果有更多客人,他们只能在大厅等候。</p>
<p>缺点</p>
<ul>
<li>在大厅等候的客人肯定很沮丧。</li>
<li>八个人在厨房,浪费资源。</li>
</ul>
<p>优点</p>
<ul>
<li>复杂性降低,一次指接待一批客人。</li>
</ul>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java21.jpeg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java21.jpeg"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java21.jpeg" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java21.jpeg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java21.jpeg" /></a></p>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java22.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java22.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java22.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java22.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java22.png" /></a></p>
<blockquote>
<p>单线程应用</p>
</blockquote>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java23.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java23.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java23.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java23.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java23.png" /></a></p>
<blockquote>
<p>执行时间</p>
</blockquote>
<h3 id="多线程应用">多线程应用</h3>
<p>一家有8名成员的厨房部门的餐厅在任何给定时间都只能提供16张桌子,因为他们有4个服务员。
优点</p>
<ul>
<li>客人的等待时间打打减少</li>
<li>充分合理的利用资源</li>
</ul>
<p>缺点</p>
<ul>
<li>资源共享到时业务复杂</li>
</ul>
<blockquote>
<p>由于餐厅现在引入了更多的服务员(线程),因此它正在有效地利用厨房(<code class="language-plaintext highlighter-rouge">CPU</code>)人员(核心)。结果,它在任何给定时间服务于更多的来宾(用户)</p>
</blockquote>
<p>这同样适用于软件应用程序。由于并发执行,本质上导致了响应能力的显着改善。</p>
<p>同样,可以拆分为多个独立子任务的长时间运行的任务可以在多个线程中并行运行,从而显着提高了应用程序的性能。</p>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java24.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java24.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java24.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java24.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java24.png" /></a></p>
<blockquote>
<p>多线程应用</p>
</blockquote>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java25.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java25.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java25.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java25.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java25.png" /></a></p>
<blockquote>
<p>执行时间</p>
</blockquote>
<p>并发带来的响应和并行引起的性能是多线程应用程序的动机。</p>
<h2 id="堆和栈">堆和栈</h2>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java26.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java26.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java26.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java26.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java26.png" /></a></p>
<p>上面我们编写了一个由线程执行的代码块。<code class="language-plaintext highlighter-rouge">Java</code>应用程序启动时,<code class="language-plaintext highlighter-rouge">Java</code>进程会生成主线程(<code class="language-plaintext highlighter-rouge">main</code>方法),这是应用程序的入口点。从现在开始,整个应用程序逻辑要么在主线程中执行,要么在我们从应用程序中派生的线程中执行,以实现并发执行</p>
<p>线程执行的应用程序逻辑在CPU中执行计算,计算结果则存储在<code class="language-plaintext highlighter-rouge">RAM</code>中。</p>
<p>每个线程将等待轮流使用一个<code class="language-plaintext highlighter-rouge">CPU</code>内核执行计算,并等待一个本地内存区域(每个方法调用的栈和栈中的多个栈帧)临时存储计算结果。线程完成代码执行后,通常会将结果刷新回<code class="language-plaintext highlighter-rouge">RAM</code>(堆)。</p>
<blockquote>
<p>堆是所有对象所在的线程之间的共享内存区域。
堆栈是分配给每个正在运行的线程的专用内存区域。
堆内存是垃圾收集器可直接回收的,通过删除应用程序中不再使用(引用)的对象来释放空间,而栈持有的内存空间在执行线程完成后被释放</p>
</blockquote>
<h3 id="堆">堆</h3>
<p>在<code class="language-plaintext highlighter-rouge">Java</code>应用程序中创建的所有对象都在堆的内存中分配了空间。只要从应用程序中的某处引用它,这些对象就存在。</p>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java27.jpeg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java27.jpeg"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java27.jpeg" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java27.jpeg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java27.jpeg" /></a></p>
<h3 id="栈">栈</h3>
<p>执行某个方法或调用一系列方法的线程将需要内存中的空间来存储局部变量和方法参数——这个内存区域称为栈。线程调用的每个方法都堆叠在前面的方法之上——调用称为<strong>栈帧</strong>。</p>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java28.jpeg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java28.jpeg"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java28.jpeg" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java28.jpeg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java28.jpeg" /></a></p>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java29.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java29.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java29.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java29.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java29.png" /></a></p>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java30.jpeg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java30.jpeg"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java30.jpeg" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java30.jpeg" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java30.jpeg" /></a></p>
<h2 id="锁">锁</h2>
<h3 id="monitor和锁"><code class="language-plaintext highlighter-rouge">monitor</code>和锁</h3>
<p>当一个对象及其状态被多个线程共享时,对该状态所做的任何修改(例如。网页计数器),必须以原子操作执行。否则,对象的状态将被并发修改破坏。原子性是通过使用锁来保护关键代码块来实现的,从而在相互竞争的线程之间强制互斥。</p>
<p>每个对象都有一个称为<code class="language-plaintext highlighter-rouge">monitor</code>的固有锁。由于这种语言规定,通过向方法签名中添加关键字<code class="language-plaintext highlighter-rouge">synchronized</code>,可以很容易地锁定关键代码块。在下面的程序中,安全对象的<code class="language-plaintext highlighter-rouge">open()</code>方法可以被线程访问,只有在获取了内部锁之后——注意方法签名中的<code class="language-plaintext highlighter-rouge">synchronized</code>关键字。</p>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java31.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java31.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java31.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java31.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java31.png" /></a></p>
<p><a href="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java32.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java32.png"><img src="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java32.png" alt="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java32.png" title="https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/java/java32.png" /></a></p>
<blockquote>
<p>通过获取内部锁依次访问安全对象的线程</p>
</blockquote>
<p>执行同步方法的线程被认为获得了对象的锁。在此线程完成方法的执行之前,没有其他线程可以执行此对象的这个或任何其他同步方法。</p>
<p>但是已经可以访问该对象锁的线程可以调用其他同步方法,而无需重新获取该锁。此机制称为重入锁定或重入同步。</p>
<p>锁或同步的另一个重要方面是建立<code class="language-plaintext highlighter-rouge">happens-before</code>关系。也就是说,在<code class="language-plaintext highlighter-rouge">synchronized</code>块中对对象的状态所做的任何修改都保证对其他线程可见,这些线程随后将通过获取相同的锁来访问相同的状态。</p>
<p>获取对象锁的方法有两种</p>
<ul>
<li>通过在方法上添加<code class="language-plaintext highlighter-rouge">synchronized</code>关键字</li>
<li>使用 <code class="language-plaintext highlighter-rouge">synchronized</code>同步代码块</li>
</ul>
<p>使用同步代码块是首选方法,因为它不会阻塞所有实例方法,而只会阻塞要防止并发访问的阻塞,从而提高了性能。</p>
<blockquote>
<p>实例方法的同步对象是<code class="language-plaintext highlighter-rouge">this</code>当前对象
静态方法的同步对象是当前对象的<code class="language-plaintext highlighter-rouge">class</code>类</p>
</blockquote>
<p>实际上,同步(或加锁)的作用是防止多线程应用程序中出现以下错误情况</p>
<ul>
<li>线程干扰——多个线程同时修改一个实例的状态,并在进程中破坏它,因为相互竞争的线程计划运行的时间不同,从而导致它们无法按顺序执行同一组操作。</li>
<li>内存不一致错误——即使写线程在读取之前完成了执行,读线程也会看到过时的数据。这是因为写线程会将更改后的值存储在<code class="language-plaintext highlighter-rouge">CPU</code>缓存中,而不是将其刷新到主内存中,其他所有线程都可以在主内存中看到更新后的状态。</li>
</ul>
<blockquote>
<p>当竞争线程修改/修改或修改/读取共享的可变状态时,如果没有适当的同步(涉及非原子操作),则由于交织而导致“竞争条件”和“内存不一致错误”</p>
</blockquote>
<h2 id="总结">总结</h2>
<ul>
<li>操作系统将<code class="language-plaintext highlighter-rouge">CPU</code>、<code class="language-plaintext highlighter-rouge">RAM</code>、<code class="language-plaintext highlighter-rouge">HDD</code>、网络设备等硬件资源提供给软件应用程序进行计算。</li>
<li>这些资源在操作系统中作为进程同时运行的多个应用程序之间共享。操作系统的强大之处在于其通过进程执行多任务的能力。</li>
<li>同样,作为进程运行的单个软件应用程序必须执行多任务,以便有效利用硬件资源。进程的能力在于它可以通过并发运行的线程来执行多任务。</li>
<li>通过并发性(例如。:一个<code class="language-plaintext highlighter-rouge">servlet</code>同时服务多个用户),通过并行的性能(例如。:并行调用多个<code class="language-plaintext highlighter-rouge">HTTP</code>端点来服务一个用户请求)是多线程应用程序的动机。</li>
<li>线程在<code class="language-plaintext highlighter-rouge">CPU</code>内核中运行。任何应用程序代码都在线程中运行。启动<code class="language-plaintext highlighter-rouge">Java</code>应用程序时,JVM从主线程内调用<code class="language-plaintext highlighter-rouge">main()</code>方法。</li>
<li>堆是分配给所有线程以存储对象的公共内存区域。任何引用堆中对象的线程都可以读取/修改该对象。</li>
<li>栈是分配给每个线程的私有内存区域(例如:两个线程同时调用一个通用实用程序方法将在其自己的栈中执行该方法,其中一个线程的局部变量和方法参数对另一个线程不可见)</li>
<li>获取公共锁后,访问堆中同一对象的多个线程必须同步执行此操作,以防止破坏对象的状态。</li>
</ul>
<hr />
<p><a href="https://niocoder.com/assets/images/qrcode.jpg" title="https://niocoder.com/assets/images/qrcode.jpg"><img src="https://niocoder.com/assets/images/qrcode.jpg" alt="https://niocoder.com/assets/images/qrcode.jpg" title="https://niocoder.com/assets/images/qrcode.jpg" /></a></p>
<blockquote>
<p>🙂🙂🙂关注微信公众号<strong>java干货</strong>
不定期分享干货资料</p>
</blockquote>
<p>原文链接:<a href="https://blog.usejournal.com/java-multithreading-part-1-ec0c42bbead6">Java: Multithreading — Part 1</a></p>郑龙飞何日请缨提锐旅,一鞭直渡清河洛。