<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>SerMs</title>
  
  <subtitle>细节成就极致，逻辑成就完美✨</subtitle>
  <link href="https://blog.serms.top/atom.xml" rel="self"/>
  
  <link href="https://blog.serms.top/"/>
  <updated>2025-05-28T15:08:01.901Z</updated>
  <id>https://blog.serms.top/</id>
  
  <author>
    <name>SerMs</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>设计模式—前言</title>
    <link href="https://blog.serms.top/posts/92e28e2a.html"/>
    <id>https://blog.serms.top/posts/92e28e2a.html</id>
    <published>2024-06-18T01:31:37.000Z</published>
    <updated>2025-05-28T15:08:01.901Z</updated>
    
    <content type="html"><![CDATA[<h2 id="代码质量好坏如何评价"><a href="#代码质量好坏如何评价" class="headerlink" title="代码质量好坏如何评价?"></a>代码质量好坏如何评价?</h2><p>要想学习设计模式呢 我们就必须搞清楚设计模式到底在我们的编程过程中起到了怎样的作用,在编程世界中它处在一个什么样的位置,它到底是一种抽象的设计思想,还是一套具体的落地方案. </p><p>在学习设计模式之前呢 我们需要了解一下 代码质量的评价标准和编程的方法论</p><p>我们经常会听到有人对项目代码进行评价,一般就会说：“代码写得很烂”或者“代码写得很好”。<br>用“好”“烂”这样的词汇来描述代码质量，非常地笼统。</p><p>对于程序员来说，辨别代码写得“好”还是“烂”，是一个非常重要的能力。这也是我们写出好代码的前提。</p><p>代码质量的评价标准非常之多,我这里就给大家介绍一下最常用的几个标准</p><p><strong>评价代码的质量,有以下几个常用标准:</strong> </p><p><strong>1 ) 可维护性</strong></p><p>可维护性强的代码指的是:  在不去破坏原有的代码设计以及不引入新的BUG的前提下,能够快速的修改或者新增代码.</p><p>不易维护的代码指的是: 在添加或者修改一些功能逻辑的时候,存在极大的引入新的BUG的风险,并且需要花费的时间也很长.</p><blockquote><p>代码可维护性的评判标准比较模糊, 因为是否易维护是针对维护的人员来说的,不同水平的人对于同一份代码的维护能力是不同的. 所谓 ‘’难者不会 会者不难’’. 对于同样的系统,熟悉它的资深工程师会觉得代码可维护性还可以,而新人则会因为能力不足、了解不够深入等原因觉得代码的可维护性不是很好.</p></blockquote><p><strong>2 ) 可读性</strong></p><p>软件开发教父，Martin Fowler曾经说过一句话: “任何傻瓜都能够编写计算机能理解的代码，而优秀的程序员能够编写人类能理解的代码。” 这句话的意思非常容易理解,就是要求我们写出的代码是易读的、易理解的,因为代码的可读性会在很大程度上影响代码的可维护行性.</p><blockquote><p>code review ( 代码审查,一种测试代码可读性的手段 )</p><p>1.检查代码风格和编程规范: 代码是否符合编码规范、命名是否达意、注释是否详尽、模块划分是否清晰等</p><p>2.检查常规的 bad smell 和代码 bug: 是否存在重复代码、过长函数、过大类、过于亲密的两个 classes等</p></blockquote><p><strong>3 ) 可扩展性</strong></p><p>代码的可扩展性表示，我们在不修改或少量修改原有代码的情况下，通过扩展的方式添加新的功能代码。</p><blockquote><p>可扩展性的背后其实就是: “对修改关闭,对扩展开放” 这条设计原则,后面我们会详细的讲解</p></blockquote><p><strong>4 ) 灵活性</strong></p><p>“灵活” 是指在添加新代码的时候，已有代码能够不受影响，不产生冲突，不出现排斥，在保证自身不遭到破坏的前提下灵活地接纳新代码。</p><blockquote><p>下面的几个场景,可以体现代码的灵活性</p><p>1.添加新的功能代码时,原有代码已经预留了扩展点,我们不需要修改 直接在扩展点上新增代码即可.</p><p>2.当我们想实现一个功能模块时,发现原有代码中,已经抽象出了很多底层可以复用的模块、类等代码,我们可以直接拿来使用</p><p>3.当我们使用某组接口时,这组接口可以应对各种使用场景,满足不同需求,这个接口设计的十分的灵活易用.</p></blockquote><p><strong>5 ) 简洁性</strong></p><p>我们要遵从KISS ( Keep It Simple Stupid) 原则，代码要尽可能的简单；但是思从深而行从简，真正的高手能云淡风轻地用最简单的方法解决最复杂的问题。这也是一个编程老手跟编程新手的本质区别之一。</p><blockquote><p>代码的写法应当使别人理解它所需的时间最小化.  </p></blockquote><p><strong>6 ) 可复用性</strong> </p><p>代码的可复用性可以简单地理解为，尽量减少重复代码的编写，复用已有的代码.</p><blockquote><p>可复用性也是一个非常重要的代码评价标准，是很多设计原则、思想、模式等所 要达到的最终效果</p><p>可复用性与DRY(Don’t Repeat Yourself) 避免编写重复的代码逻辑. 原则关系紧密,后面我们会介绍有哪些编程方法可以提高代码复用性.</p></blockquote><p><strong>7 ) 可测试性</strong></p><p>单元测试在一个完整的软件开发流程中是必不可少的、非常重要的一个环节。通常写单元测试并不难，但有的时候，有的代码和功能难以测试，导致写起测试来困难重重。所以写出的代码具有可测试性，具有很重要的作用。</p><blockquote><p>代码可测试性的好坏，能从侧面上非常准确地反应代码质量的好坏</p></blockquote><h2 id="编程方法论"><a href="#编程方法论" class="headerlink" title="编程方法论"></a>编程方法论</h2><p>想要拥有编写高质量代码的能力,我们就要学习一些编程的方法论.其中就包含面向对象（我们可以把它看成一种设计思想）、设计原则、设计模式、编程规范、重构技巧等等</p><blockquote><p>接下来我就给大家介绍一下这几个概念和他们之间的联系,介绍这些呢 有助于大家对于整个课程中包含的知识点, 有一个全局的了解,将我们学到的知识有序的组织在头脑中,方便大家记忆.</p></blockquote><p><img src="https://bu.dusays.com/2024/06/18/6670ef45e7b58.jpg" alt="image-20220530160637842" style="zoom: 50%;" /> </p><p><strong>代码重构的工具就是面向对象设计思想、设计原则、设计模式、编码规范。</strong> </p><p><strong>1 ) 面向对象</strong></p><p>面向对象是一种编程思想,也是一种编程范式. 现在比较流行的编程语言大部分都是面向对象编程语言.而大部分项目也都是基于面向对象编程风格进行开发的.</p><blockquote><p>作为一名Java程序员,相信同学们对于面向对象的特点,以及面向对象思想在实际工作中的应用,都有一定深度的了解,这里边关于面向对象的一些相关知识点我们就不做赘述了.</p></blockquote><p><strong>2 ) 设计原则 (本次课程重点讲解)</strong></p><p>设计原则是指导我们代码设计的一些经验总结。在软件开发中，为了提高软件系统的可维护性和可复用性，增加软件的可扩展性和灵活性，程序员要尽量根据设计原则来开发程序，从而提高软件开发效率、节约软件开发成本和维护成本。</p><p>常用的设计原则</p><ul><li>单一职责原则</li><li>开闭原则</li><li>里氏代换原则</li><li>依赖倒转原则</li><li>接口隔离原则</li><li>迪米特法则</li></ul><blockquote><p>设计原则的特点就是比较的抽象,大家不需要死记硬背.每个人根据自己所积累的经验和所处的业务场景, 去灵活的运用这些原则即可</p></blockquote><p><strong>3 ) 设计模式 (本次课程重点讲解)</strong> </p><p>设计模式是针对软件开发中经常遇到的一些设计问题，总结出来的一套解决方案或者设计思 路。大部分设计模式要解决的都是代码的可扩展性问题。</p><blockquote><p>设计模式是遵循设计原则的, 设计模式相对于设计原则来说，没有那么抽象，而且大部分都不难理解，代码实现也并不复杂。这一块的学习难点是了解它们 都能解决哪些问题，掌握典型的应用场景，并且懂得不过度应用。</p></blockquote><p><strong>4 ) 编程规范</strong></p><p>编程规范主要解决的是代码的可读性问题。编码规范相对于设计原则、设计模式，更加具 体、更加偏重代码细节。</p><blockquote><p>对于编码规范，考虑到很多书籍已经讲得很好了（比如《编写可读性代码的艺术》《代码大全》《代码整洁之 道》等）。而且，每条编码规范都非常简单、非常明确，比较偏向于记忆，你只要照着来做 可以。</p></blockquote><p><strong>5 ) 重构</strong></p><p>维基百科:  在软件工程学里，<strong>重构</strong>代码一词通常是指在不改变代码的外部行为情况下而修改源代码，有时非正式地称为“清理干净”。在极限编程或其他敏捷方法学中，重构常常是软件开发循环的一部分：开发者轮流增加新的测试和功能，并重构代码来增进内部的清晰性和一致性。</p><blockquote><p>在软件开发中，只要软件在不停地迭代，就没有一劳永逸的设计。随着需求的变化，代码的 不停堆砌，原有的设计必定会存在这样那样的问题。针对这些问题，我们就需要进行代码重构。重构是软件开发中非常重要的一个环节。持续重构是保持代码质量不下降的有效手段， 能有效避免代码腐化到无可救药的地步。</p></blockquote><p><strong>6 ) 总结</strong></p><p>提高代码质量的方法论包含: </p><ul><li>面向对象思想 (基础)</li><li>设计原则 (指导方针) </li><li>设计模式 (设计原则的具体实现) </li><li>编程规范  (提高代码可读性) </li><li>重构 (面向对象设计思想、设计原则、设计模式、编码规范的融合贯通)</li></ul><blockquote><p>设计原则是高手的内功，设计模式是少林，武当，峨眉派的武术套路，规范是招式 出拳还 是横扫，重构是组合拳 融汇贯通各种组合，而想练好这些武功还要有扎实的基本功 面向对 象,在这里呢 也提醒同学们一句,不要一味的去追求最新的技术(并不是说这样做不好,但是千万不要本末倒置),而忘记了一个程序员最重要的能力 是写出高质量的代码.</p></blockquote><h2 id="设计模式概述"><a href="#设计模式概述" class="headerlink" title="设计模式概述"></a>设计模式概述</h2><ul><li><p><strong>设计模式（Design pattern）</strong>是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结</p><blockquote><p>在GOF编写的设计模式(可复用面向对象软件的基础)一书中说道:  本书涉及的设计模式<strong>并不描述新的或未经证实的设计</strong>，我们只<strong>收录那些在不同系统中多次使用过的成功设计</strong>。</p></blockquote></li><li><p>大部分设计模式要解决的都是代码的可重用性、可扩展性问题</p></li></ul><blockquote><p>如果说数据结构和算法是教你如何写出高效代码，那设计模式讲的是如何写出可扩展、可读、可维护的高质量代码，所以，它们跟平时的编码会有直接的关系，也会直接影响到你的开发能力。</p></blockquote><ul><li><p>学习设计模式的好处</p><ul><li><p>能够从容应对面试中的设计模式相关问题</p><blockquote><p>我们学习技术从最功利的角度讲,就是为了拿到心仪的offer,得到想要的薪资.所以在面试时特别是面试一些BAT大厂时,如果被问到设计模式的问题, 因为我们学了本次课程, 就不会再惧怕这些问题,设计模式反而会成为你面试中的亮点.</p></blockquote></li><li><p>不再编写 bullshit-code</p><blockquote><p>代码能力是一个程序员最基础的能力，是基本功，是展示一个程序员基础素养的最直接的衡量标准。你写的代码，实 际上就是你名片。我们通过学习设计模式,告别烂代码的编写,避免比如类设计不合理,代码结构混乱,分层不清晰 等等这样的代码问题. 让我们编写的代码成为团队中的标杆.</p></blockquote></li><li><p>提高复杂代码的设计和开发能力</p><blockquote><p>不再只是根据业务需求翻译代码,填充代码. 而是能够通过所学习的设计模式的相关知识写出高质量的代码.即使面对复杂代码功能或者系统的设计开发也能得心应手.</p></blockquote></li><li><p>有助于我们读懂源码,学习框架更加事半功倍</p><blockquote><p>对于一个有追求的程序员来说，对技术的积累，既要有广度，也要有深度。所以我们需要更多的去学习研究各种框架,中间件的底层原理,而这些优秀的框架 中间件的源码中会使用到很多设计思想 设计原则 设计模式 ,所以为了更好的独懂这些开源项目,我们必须好设计模式相关知识.</p></blockquote></li></ul></li></ul><h2 id="产生背景"><a href="#产生背景" class="headerlink" title="产生背景"></a>产生背景</h2><p><strong>“设计模式”</strong> 最初并不是出现在软件设计中，而是被用于建筑领域的设计中。</p><p>1977年美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任<code>克里斯托夫·亚历山大（Christopher Alexander）</code>在他的著作《建筑模式语言：城镇、建筑、构造》中描述了一些常见的建筑设计问题，并提出了 253 种关于对城镇、邻里、住宅、花园和房间等进行设计的基本模式。</p><p><img src="https://bu.dusays.com/2024/06/18/6670f58512c27.png" alt="image-20220113152017462" style="zoom:70%;" /></p><p><strong>四人帮（GOF）</strong></p><p>1990年软件工程界开始研讨设计模式的话题，后来召开了多次关于设计模式的研讨会。</p><blockquote><p>在之前设计模式的<strong>绝大部分以往并无文本记录</strong>，它们或是来源于面向对象设计者圈子里的非正式交流，或是来源于某些成功的面向对象系统的某些部分，但对<strong>设计新手</strong>来说，这些东西是很难学得到的。尽管这些设计<strong>不包括新的思路</strong>，但我们<strong>用一种新的、便于理解的方式将其展现给读者</strong>，即：具有统一格式的、已分类编目的若干组设计模式。</p></blockquote><p>1994 年，艾瑞克·伽马（ErichGamma）、理査德·海尔姆（Richard Helm）、拉尔夫·约翰森（Ralph Johnson）、约翰·威利斯迪斯（John Vlissides）等 4 位作者合作出版了《设计模式：可复用面向对象软件的基础》一书，在此书中收录了 23 个设计模式，这是设计模式领域里程碑的事件，导致了软件设计模式的突破。</p><p>备注：由于书名太长， 人们将其简称为 “四人组 （Gang of Four， GoF） 的书”， 并且很快进一步简化为 “GoF 的书</p><h2 id="设计模式分类"><a href="#设计模式分类" class="headerlink" title="设计模式分类"></a>设计模式分类</h2><blockquote><p>按照不同的业务领域和场景的复杂程度，以及选择不同的设计模式，在整个系统建设落地中都会有不同的呈现形式。</p><p>就像出行可以选择不同的交通工具一样，如近距离骑自行车、中短程驾车、远程乘坐高铁或飞机等</p></blockquote><p>GoF设计模式只有23个，但是它们各具特色 ，每个模式都为某一个可重复的设计问题提供了一套解决方案。</p><p>根据它们的用途，设计模式可分为 <strong>创建型(Creational) </strong>，<strong>结构型(Structural)  </strong>和<strong>行为型(Behavioral)</strong></p><ul><li><p><strong>创建型模式(5种)：提供创建对象的机制，提升已有代码的灵活性和可复用性</strong></p><p>常用的有：单例模式、工厂模式（工厂方法和抽象工厂）、建造者模式。 </p><p>不常用的有：原型模式。</p><p><img src="https://bu.dusays.com/2024/06/18/6670f590b258a.jpg" alt="image-20220530160637842"  />   </p></li></ul><ul><li><p><strong>结构型模式(7种)：介绍如何将对象和类组装成较大的结构，并同时保持结构的灵活和高效</strong></p><p>常用的有：代理模式、桥接模式、装饰者模式、适配器模式。 </p><p>不常用的有：门面模式、组合模式、享元模式。</p><p><img src="https://bu.dusays.com/2024/06/18/6670f5925c81f.jpg" alt="image-20220530160637842"  /> </p></li></ul><ul><li><p><strong>行为模式(11种)：负责对象间的高效沟通和职责传递委派</strong></p><p>常用的有：观察者模式、模板模式、策略模式、职责链模式、迭代器模式、状态模式。<br>不常用的有：访问者模式、备忘录模式、命令模式、解释器模式、中介模式。</p></li></ul><p><img src="https://bu.dusays.com/2024/06/18/6670f5991bd50.jpg" alt="image-20220530160637842"  /> </p><h1 id="UML图"><a href="#UML图" class="headerlink" title="UML图"></a>UML图</h1><p>统一建模语言（Unified  Modeling Language，UML）是用来设计软件的可视化建模语言。它的特点是简单、统一、图形化、能表达软件设计中的动态与静态信息。</p><p>UML 从目标系统的不同角度出发，定义了用例图、类图、对象图、状态图、活动图、时序图、协作图、构件图、部署图等 9 种图。  </p><p>这里我们只介绍类图.</p><blockquote><p>我们要去研究一个设计模式的时候,是需要借助UML类图更加准确的描述所使用的设计模式,和设计模式下类与类之间的关系</p></blockquote><h2 id="类图概述"><a href="#类图概述" class="headerlink" title="类图概述"></a>类图概述</h2><p>类图(Class diagram)是显示了模型的静态结构，特别是模型中存在的类、类的内部结构以及它们与其他类的关系等。类图不显示暂时性的信息。类图是面向对象建模的主要组成部分。</p><h2 id="类图的作用"><a href="#类图的作用" class="headerlink" title="类图的作用"></a>类图的作用</h2><ul><li>在软件工程中，类图是一种静态的结构图，描述了系统的类的集合，类的属性和类之间的关系，可以简化了人们对系统的理解；</li><li>类图是系统分析和设计阶段的重要产物，是系统编码和测试的重要模型。</li></ul><h2 id="类图表示法"><a href="#类图表示法" class="headerlink" title="类图表示法"></a>类图表示法</h2><p>UML类图中具体类、抽象类、接口和包有不同的表示方法。</p><h3 id="在UML类图中表示具体类"><a href="#在UML类图中表示具体类" class="headerlink" title="在UML类图中表示具体类"></a>在UML类图中表示具体类</h3><p>具体类在类图中用矩形框表示，矩形框分为三层：第一层是类名字。第二层是类的成员变量；第三层是类的方法。成员变量以及方法前的访问修饰符用符号来表示：</p><ul><li>“+” 表示 <code>public</code>；</li><li>“-” 表示 <code>private</code>；</li><li>“#” 表示 <code>protected</code>；</li><li>不带符号表示 <code>default</code>。</li></ul><p><img src="F:/学习资源/设计模式/架构师进阶-设计模式/181_抽丝剥茧设计模式精讲/文档_5/md文档/img/06.jpg" alt="image-20220530160637842" style="zoom: 50%;" />  </p><h3 id="在UML类图中表示抽象类"><a href="#在UML类图中表示抽象类" class="headerlink" title="在UML类图中表示抽象类"></a>在UML类图中表示抽象类</h3><p>抽象类在UML类图中同样用矩形框表示，但是抽象类的类名以及抽象方法的名字都用斜体字表示，如图所示。</p><p><img src="https://bu.dusays.com/2024/06/18/6670f5a3cd3e3.jpg" alt="image-20220530160637842" style="zoom: 50%;" /> </p><h3 id="在UML类图中表示接口"><a href="#在UML类图中表示接口" class="headerlink" title="在UML类图中表示接口"></a>在UML类图中表示接口</h3><p>接口在类图中也是用矩形框表示，但是与类的表示法不同的是，接口在类图中的第一层顶端用构造型 &lt;<interface>&gt;表示，下面是接口的名字，第二层是方法。</p><p><img src="https://bu.dusays.com/2024/06/18/6670f5a7e1428.jpg" alt="image-20220530160637842" style="zoom: 50%;" /> </p><blockquote><p>此外，接口还有另一种表示法，俗称棒棒糖表示法，就是类上面的一根棒棒糖（圆圈+实线）。圆圈旁为接口名称，接口方法在实现类中出现。</p></blockquote><h3 id="在类图中表示关系"><a href="#在类图中表示关系" class="headerlink" title="在类图中表示关系"></a>在类图中表示关系</h3><p>类和类、类和接口、接口和接口之间存在一定关系，UML类图中一般会有连线指明它们之间的关系。</p><p>关系共有六种类型 ,如下图: </p><p><img src="https://bu.dusays.com/2024/06/18/6670f5a0f358d.jpg" alt="image-20220530160637842" style="zoom: 50%;" />  </p><h4 id="实现关系"><a href="#实现关系" class="headerlink" title="实现关系"></a>实现关系</h4><p>实现关系是接口与实现类之间的关系。在这种关系中，类实现了接口，类中的操作实现了接口中所声明的所有的抽象操作。</p><p>在 UML 类图中，实现关系使用带空心三角箭头的虚线来表示，箭头从实现类指向接口。</p><p>例如，汽车和船实现了交通工具，其类图:</p><p><img src="https://bu.dusays.com/2024/06/18/6670f5ad72dd7.jpg" alt="image-20220530160637842" style="zoom: 90%;" /> </p><h4 id="泛化关系"><a href="#泛化关系" class="headerlink" title="泛化关系"></a>泛化关系</h4><p>泛化关系是对象之间耦合度最大的一种关系，表示一般与特殊的关系，是父类与子类之间的关系，是一种继承关系。</p><p>在 UML 类图中，泛化关系用带空心三角箭头的实线来表示，箭头从子类指向父类。在代码实现时，使用面向对象的继承机制来实现泛化关系。</p><p>例如，Student 类和 Teacher 类都是 Person 类的子类，其类图如下图所示：</p><p><img src="https://bu.dusays.com/2024/06/18/6670f5b22f7b6.jpg" alt="image-20220530160637842" style="zoom: 90%;" /> </p><h4 id="关联关系"><a href="#关联关系" class="headerlink" title="关联关系"></a>关联关系</h4><p>关联关系是对象之间的一种引用关系，用于表示一类对象与另一类对象之间的联系，如老师和学生、师傅和徒弟、丈夫和妻子等。关联关系是类与类之间最常用的一种关系，分为一般关联关系、聚合关系和组合关系。</p><p>我们先介绍一般关联关系, 一般关联关系又可以分为单向关联，双向关联，自关联。</p><p><strong>1) 单向关联</strong></p><p><img src="https://bu.dusays.com/2024/06/18/6670f5d2820f7.jpg" alt="image-20220530160637842" style="zoom: 100%;" /> </p><p>在UML类图中单向关联用一个带箭头的实线表示。上图表示每个顾客都有一个地址，这通过让Customer类持有一个类型为Address的成员变量类实现。</p><p><strong>2) 双向关联</strong></p><p>​                                <img src="https://bu.dusays.com/2024/06/18/6670f5d89b1d4.jpg" alt="image-20220530160637842" style="zoom: 100%;" /> </p><p>从上图中我们很容易看出，所谓的双向关联就是双方各自持有对方类型的成员变量。</p><p>在UML类图中，双向关联用一个不带箭头的直线表示。上图中在Customer类中维护一个List\<Product>，表示一个顾客可以购买多个商品；在Product类中维护一个Customer类型的成员变量表示这个产品被哪个顾客所购买。</p><p><strong>3) 自关联</strong></p><p>​                                                        <img src="https://bu.dusays.com/2024/06/18/6670f5eb103cd.jpg" alt="image-20220530160637842" style="zoom: 100%;" /> </p><p>自关联在UML类图中用一个带有箭头且指向自身的线表示。上图的意思就是Node类包含类型为Node的成员变量，也就是“自己包含自己”。</p><h5 id="聚合关系"><a href="#聚合关系" class="headerlink" title="聚合关系"></a>聚合关系</h5><p>聚合关系是关联关系的一种，表示一种弱的‘拥有’关系，体现的是A对象可以包含B对象，但是B对象不是A对象的一部分</p><blockquote><p>在代码中: 比如A 类对象包含 B 类对象，B 类对象的生命周期可以不依赖 A 类对象的生命周期，也就是说可以单独销毁 A 类对象而不影响 B 对象</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A</span>&#123;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> B b;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">A</span><span class="params">(B b)</span>&#123;</span><br><span class="line"><span class="built_in">this</span>.b = b;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在 UML 类图中，聚合关系可以用带空心菱形的实线来表示，菱形指向整体。</p><p>下图所示是大学和教师的关系图：                            </p><p><img src="https://bu.dusays.com/2024/06/18/6670f5eea1c8f.jpg" alt="image-20220530160637842" style="zoom: 100%;" /></p><h5 id="组合关系"><a href="#组合关系" class="headerlink" title="组合关系"></a>组合关系</h5><p>组合关系是一种强‘拥有’关系，体现了严格的部分和整体的关系，部分和整体的声明周期一样</p><blockquote><p>在代码中: 比如A 类对象包含 B 类对象，B 类对象的生命周期依赖A 类对象的生命周期，B 类对象不可以单独存在</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A</span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> B b;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">A</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.b = <span class="keyword">new</span> <span class="title class_">B</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在 UML 类图中，组合关系用带实心菱形的实线来表示，菱形指向整体。</p><p>下图所示是头和嘴的关系图：</p><p>​                                <img src="https://bu.dusays.com/2024/06/18/6670f5fda43de.jpg" alt="image-20220530160637842" style="zoom: 100%;" /> </p><h5 id="依赖关系"><a href="#依赖关系" class="headerlink" title="依赖关系"></a>依赖关系</h5><p>依赖关系是一种使用关系，它是对象之间耦合度最弱的一种关联方式，是临时性的关联。</p><p>在代码中，某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类（被依赖类）中的某些方法来完成一些职责。</p><p>在 UML 类图中，依赖关系使用带箭头的虚线来表示，箭头从使用类指向被依赖的类。</p><p>下图所示是司机和汽车的关系图，司机驾驶汽车：</p><p>​                            <img src="https://bu.dusays.com/2024/06/18/6670f5fdaa6d2.jpg" alt="image-20220530160637842" style="zoom: 100%;" /> </p><h2 id="UML类图总结"><a href="#UML类图总结" class="headerlink" title="UML类图总结"></a>UML类图总结</h2><p>之前我们一共学习了六种类关系的表示方式</p><ul><li>实现关系</li><li>泛化关系</li><li>关联关系</li><li>聚合关系</li><li>组合关系</li><li>依赖关系</li></ul><p>为了节省学习成本, 方便记忆,我们可以将类和类之间的关系重新调整一下,使其更加贴近编程的角度:</p><ul><li>实现关系</li><li>泛化关系</li><li>依赖关系</li><li>组合关系 (组合关系+关联关系+聚合关系)</li></ul><blockquote><p>我们也没有必要去区分组合与聚合这两个概念,只需要记住一点就是: 多用组合少用继承 </p><p>也就是说只要 B 类对象是 A 类对象的成员变量，那我们就称，A 类跟 B 类是组合关 系.</p></blockquote><h1 id="六大设计原则-SOLID"><a href="#六大设计原则-SOLID" class="headerlink" title="六大设计原则 (SOLID)"></a>六大设计原则 (SOLID)</h1><h2 id="设计原则概述"><a href="#设计原则概述" class="headerlink" title="设计原则概述"></a>设计原则概述</h2><blockquote><p>古人云: 有道无术,术可求.有术无道,止于术. </p><p>而设计模式通常需要遵循一些设计原则，在设计原则的基础之上衍生出了各种各样的设计模式。设计原则是设计要求，设计模式是设计方案，使用设计模式的代码则是具体的实现。</p></blockquote><p>设计模式中主要有六大设计原则，简称为SOLID ，是由于各个原则的首字母简称合并的来(两个L算一个,solid 稳定的)，六大设计原则分别如下：</p><p>​    <strong>1、单一职责原则（Single Responsibitity Principle）</strong></p><p>​    <strong>2、开放封闭原则（Open Close Principle）</strong></p><p>​    <strong>3、里氏替换原则（Liskov Substitution Principle）</strong></p><p>​    <strong>4、接口分离原则（Interface Segregation Principle）</strong></p><p>​    <strong>5、依赖倒置原则（Dependence Inversion Principle）</strong></p><p>​    <strong>6、迪米特法则（Law Of Demter）</strong></p><p>软件开发中我们要基于这六个原则,设计建立稳定、灵活、健壮的程序.</p><h2 id="单一职责原则"><a href="#单一职责原则" class="headerlink" title="单一职责原则"></a>单一职责原则</h2><h3 id="官方定义"><a href="#官方定义" class="headerlink" title="官方定义"></a>官方定义</h3><p><strong>单一职责原则</strong>，英文缩写SRP，全称 Single Responsibility Principle。</p><p>在&lt;&lt;架构整洁之道&gt;&gt;一书中 关于这个原则的英文描述是这样的：A class or module should have a single responsibility 。如果我们把它翻译成中文，那就是<strong>：一个类或者模块只负责完成一个职责（或者功能）</strong>。</p><h3 id="通俗解释"><a href="#通俗解释" class="headerlink" title="通俗解释"></a>通俗解释</h3><p>单一职责原则的定义描述非常简单，也不难理解。<strong>一个类只负责完成一个职责或者功能。</strong></p><p>也就是说在类的设计中 我们不要设计大而全的类,而是要设计粒度小、功能单一的类. </p><blockquote><p>比如 我们设计一个类里面既包含了用户的一些操作,又包含了支付的一些操作,那这个类的职责就不够单一,应该将该类进行拆分,拆分成多个功能更加单一的,粒度更细的类.</p></blockquote><h3 id="场景示例"><a href="#场景示例" class="headerlink" title="场景示例"></a>场景示例</h3><p>那么该如何判断一个类的职责是否单一 ?</p><blockquote><p>其实在软件设计中，要真正用好单一职责原则并不简单，因为遵循这一原则最关键的地方在于职责的划分，而职责的划分是根据需求定的，同一个类（接口）的设计，在不同的需求里面，可能职责的划分并不一样.</p></blockquote><p>我们来看下面这个例子:</p><p>在一个社交媒体产品中,我们使用UserInfo去记录用户的信息,包括如下的属性.</p><p><img src="https://bu.dusays.com/2024/06/18/6670f5fdba73f.jpg" alt="image-20220530160637842" style="zoom: 50%;" />   </p><p>请问上面的UserInfo类是否满足单一职责原则呢 ?</p><ul><li>观点1: 满足,因为记录的都是跟用户相关的信息</li><li>观点2: 不满足,因为地址信息应该被拆分出来,单独放到地址表中保存.</li></ul><p>正确答案: 根据实际业务场景选择是否拆分</p><ul><li>该社交产品的有用户信息只是用来展示的,那么这个类这样设计就没有问题</li><li>假设后面这个社交产品又添加了电商模块, 那就需要将地址信息提取出来,单独设计一个类</li></ul><p><img src="https://bu.dusays.com/2024/06/18/6670f5fdb18a9.jpg" alt="image-20220530160637842" style="zoom: 50%;" /> </p><p><strong>总结: 不同的应用场景、不同阶段的需求背景下，对同一个类的职责是否单一的判定，可能都是不一样的,最好的方式就是: </strong></p><blockquote><p>我们可以先写一个粗粒度的类，满足业务需求。随着业务的发展，如果粗粒度的类越来越庞大，代码越来越多，这个时候，我们就可以将这个粗粒度的类，拆分成几个更细粒度的类。这就是所谓的持续重构</p></blockquote><p><strong>如何判断一个类的职责是否单一?</strong></p><p>这里没有一个具体的金科玉律，但从实际代码开发经验上，有一些可执行性的侧面判断指标，可供参考：</p><ul><li><p>类中的代码行数、函数、或者属性过多；</p></li><li><p>类依赖的其他类过多</p></li><li><p>私有方法过多</p></li><li><p>类中大量的方法都是集中操作类中的几个属性</p></li></ul><h2 id="开闭原则"><a href="#开闭原则" class="headerlink" title="开闭原则"></a>开闭原则</h2><h3 id="官方定义-1"><a href="#官方定义-1" class="headerlink" title="官方定义"></a>官方定义</h3><p>一般认为最早提出开闭原则（Open-Close Principle，OCP）的是伯特兰·迈耶。他在1988 年发表的《面向对象软件构造》中给出的。在面向对象编程领域中，</p><p>开闭原则规定软件中的对象、类、模块和函数对扩展应该是开放的，但对于修改是封闭的。这意味着应该用抽象定义结构，用具体实现扩展细节，以此确保软件系统开发和维护过程的可靠性。</p><h3 id="通俗解释-1"><a href="#通俗解释-1" class="headerlink" title="通俗解释"></a>通俗解释</h3><p>定义：<strong>对扩展开放，对修改关闭</strong></p><blockquote><p>对扩展开放和对修改关闭表示当一个类或一个方法有新需求或者需求发生改变时应该采用扩展的方式而不应该采用修改原有逻辑的方式来实现。因为扩展了新的逻辑如果有问题只会影响新的业务，不会影响老业务；而如果采用修改的方式，很有可能就会影响到老业务受影响。</p><p>开闭原则是所有设计模式的最核心目标，也是最难实现的目标，但是所有的软件设计模式都应该以开闭原则当作标准，才能使软件更加的稳定和健壮。</p></blockquote><p>优点：</p><ol><li><p>新老逻辑解耦，需求发生改变不会影响老业务的逻辑</p></li><li><p>改动成本最小，只需要追加新逻辑，不需要改的老逻辑</p></li><li><p>提供代码的稳定性和可扩展性</p></li></ol><h4 id="场景示例-1"><a href="#场景示例-1" class="headerlink" title="场景示例"></a>场景示例</h4><p>系统A与系统B之间进行数据传输使用的是427版本的协议，一年以后对427版本的协议进行了修正。</p><p>设计时应该考虑的数据传输协议的可变性，抽象出具有报文解译、编制、校验等所有版本协议使用的通用方法，调用方针对接口进行编程即可，如上述示例设计类图如下</p><p><img src="https://bu.dusays.com/2024/06/18/6670f5ffbc8fd.jpg" alt="image-20220530160637842" style="zoom: 50%;" /> </p><p>调用方依赖于报文接口，报文接口是稳定的，而不针对具体的427协议或427修正协议。利用接口多态技术，实现了开闭原则。</p><p><strong>顶层设计思维</strong></p><ul><li>抽象意识</li><li>封装意识</li><li>扩展意识</li></ul><blockquote><p>在写代码的时候后，我们要多花点时间往前多思考一下，这段代码未来可能有哪些需求变 更、如何设计代码结构，事先留好扩展点，以便在未来需求变更的时候，不需要改动代码整 体结构、做到最小代码改动的情况下，新的代码能够很灵活地插入到扩展点上，做到“对扩 展开放、对修改关闭”。</p></blockquote><h2 id="里氏替换原则"><a href="#里氏替换原则" class="headerlink" title="里氏替换原则"></a>里氏替换原则</h2><h3 id="官方定义-2"><a href="#官方定义-2" class="headerlink" title="官方定义"></a>官方定义</h3><p>里氏替换原则（Liskov Substitution Principle，LSP）是由麻省理工学院计算机科学系教授芭芭拉·利斯科夫于 1987 年在“面向对象技术的高峰会议”（OOPSLA）上发表的一篇论文《数据抽象和层次》（Data Abstractionand Hierarchy）里提出的.</p><p>她在论文中提到：如果S是T的子类型，对于S类型的任意对象，如果将他们看作是T类型的对象，则对象的行为也理应与期望的行为一致。</p><blockquote><p>子类对象能够替换程序中父类对象出现的任何地方，并且保证原来程序的逻辑行为不变及正确性不被破坏。</p></blockquote><h3 id="通俗解释-2"><a href="#通俗解释-2" class="headerlink" title="通俗解释"></a>通俗解释</h3><p><strong>如何理解里氏替换原则？</strong></p><p>要理解里氏替换原则，其实就是要理解两个问题：</p><ul><li>什么是替换？</li><li>什么是与期望行为一致的替换（Robert Martin所说的“必须能够替换”）？</li></ul><p><strong>1 ) 什么是替换 ?</strong></p><p>替换的前提是面向对象语言所支持的多态特性，同一个行为具有多个不同表现形式或形态的能力。</p><blockquote><p>以JDK的集合框架为例，<code>List</code>接口的定义为有序集合，<code>List</code>接口有多个派生类，比如大家耳熟能详的<code>ArrayList</code>, <code>LinkedList</code>。那当某个方法参数或变量是<code>List</code>接口类型时，既可以是<code>ArrayList</code>的实现, 也可以是<code>LinkedList</code>的实现，这就是替换。</p></blockquote><p><strong>2 ) 什么是与期望行为一致的替换？</strong> </p><p>在不了解派生类的情况下，仅通过接口或基类的方法，即可清楚的知道方法的行为，而不管哪种派生类的实现，都与接口或基类方法的期望行为一致。</p><blockquote><p>不需要关心是哪个类对接口进行了实现,因为不管底层如何实现,最终的结果都会符合接口中关于方法的描述(也就是与接口中方法的期望行为一致).</p><p>或者说接口或基类的方法是一种契约，使用方按照这个契约来使用，派生类也按照这个契约来实现。这就是与期望行为一致的替换。</p></blockquote><h3 id="场景示例-2"><a href="#场景示例-2" class="headerlink" title="场景示例"></a>场景示例</h3><p>里氏替换原则要求我们在编码时使用基类或接口去定义对象变量，使用时可以由具体实现对象进行赋值，实现变化的多样性，完成代码对修改的封闭，扩展的开放。</p><p>比如在一个商城项目中, 定义结算接口Istrategy，该接口有三个具体实现类，分别为 PromotionalStrategy （满减活动，两百以上百八折）、RebateStrategy （打折活动）、 ReduceStrategy（返现活动）</p><p><img src="https://bu.dusays.com/2024/06/18/6670f60376b6a.jpg" alt="image-20220530160637842" style="zoom: 50%;" /> </p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Istrategy</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">realPrice</span><span class="params">(<span class="type">double</span> consumePrice)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PromotionalStrategy</span> <span class="keyword">implements</span> <span class="title class_">Istrategy</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">realPrice</span><span class="params">(<span class="type">double</span> consumePrice)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (consumePrice &gt; <span class="number">200</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">200</span> + (consumePrice - <span class="number">200</span>) * <span class="number">0.8</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> consumePrice;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RebateStrategy</span> <span class="keyword">implements</span> <span class="title class_">Istrategy</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">double</span> rate;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">RebateStrategy</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.rate = <span class="number">0.8</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">realPrice</span><span class="params">(<span class="type">double</span> consumePrice)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> consumePrice * <span class="built_in">this</span>.rate;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReduceStrategy</span> <span class="keyword">implements</span> <span class="title class_">Istrategy</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">realPrice</span><span class="params">(<span class="type">double</span> consumePrice)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (consumePrice &gt;= <span class="number">1000</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> consumePrice - <span class="number">200</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> consumePrice;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>调用方为Context，在此类中使用接口定义了一个对象。</p><p> <img src="https://bu.dusays.com/2024/06/18/6670f60723c98.jpg" alt="image-20220530160637842" style="zoom: 50%;" /> </p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Context</span> &#123;</span><br><span class="line">    <span class="comment">//使用基类定义对象变量</span></span><br><span class="line">    <span class="keyword">private</span> Istrategy strategy;</span><br><span class="line">    <span class="comment">// 注入当前活动使用的具体对象</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setStrategy</span><span class="params">(Istrategy strategy)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.strategy = strategy;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 计算并返回费用</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">cul</span><span class="params">(<span class="type">double</span> consumePrice)</span> &#123;</span><br><span class="line">        <span class="comment">// 使用具体商品促销策略获得实际消费金额</span></span><br><span class="line">        <span class="type">double</span> <span class="variable">realPrice</span> <span class="operator">=</span> <span class="built_in">this</span>.strategy.realPrice(consumePrice);</span><br><span class="line">        <span class="comment">// 格式化保留小数点后1位，即：精确到角</span></span><br><span class="line">        <span class="type">BigDecimal</span> <span class="variable">bd</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(realPrice);</span><br><span class="line">        bd = bd.setScale(<span class="number">1</span>, BigDecimal.ROUND_DOWN);</span><br><span class="line">        <span class="keyword">return</span> bd.doubleValue();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Context 中代码使用接口定义对象变量，这个对象变量可以是实现了lStrategy接口的PromotionalStrategy、RebateStrategy 、 ReduceStrategy任意一个。</p><p>里氏代换原则与多态的区别 ?</p><blockquote><p>虽然从定义描述和代码实现上 来看，多态和里式替换有点类似，但它们关注的角度是不一样的。多态是面向对象编程的一 大特性，也是面向对象编程语言的一种语法。它是一种代码实现的思路。而里式替换是一种 设计原则，用来指导继承关系中子类该如何设计，子类的设计要保证在替换父类的时候，不 改变原有程序的逻辑及不破坏原有程序的正确性。</p></blockquote><p>里氏替换原则和依赖倒置原则，构成了面向接口编程的基础，正因为里氏替换原则，才使得程序呈现多样性。</p><h2 id="接口隔离原则"><a href="#接口隔离原则" class="headerlink" title="接口隔离原则"></a>接口隔离原则</h2><h3 id="官方定义-3"><a href="#官方定义-3" class="headerlink" title="官方定义"></a>官方定义</h3><p>&lt;&lt;代码整洁之道&gt;&gt;作者<strong>罗伯特 C·马丁</strong> 为 “接口隔离原则” 的定义是：客户端不应该被迫依赖于它不使用的方法（Clients should not be forced to depend on methods they do not use）。</p><p>该原则还有另外一个定义：一<strong>个类对另一个类的依赖应该建立在最小的接口上</strong></p><h3 id="通俗解释-3"><a href="#通俗解释-3" class="headerlink" title="通俗解释"></a>通俗解释</h3><p>上面两个定义的含义用一句话概括就是：<strong>要为各个类建立它们需要的专用接口，而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。</strong> </p><p><strong>接口隔离原则与单一职责原则的区别</strong></p><p>接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性，体现了封装的思想，但两者是不同的：</p><ul><li>单一职责原则注重的是职责，而接口隔离原则注重的是对接口依赖的隔离。</li><li>单一职责原则主要是约束类，它针对的是程序中的实现和细节；接口隔离原则主要约束接口，主要针对抽象和程序整体框架的构建。</li></ul><h3 id="场景示例-3"><a href="#场景示例-3" class="headerlink" title="场景示例"></a>场景示例</h3><p>微服务用户系统提供了一组跟用户相关的 API 给其他系统 使用，比如：注册、登录、获取用户信息等。</p><p><img src="https://bu.dusays.com/2024/06/18/6670f60adc0f8.jpg" alt="image-20220530160637842" style="zoom: 50%;" /> </p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">register</span><span class="params">(String cellphone, String password)</span>;</span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">login</span><span class="params">(String cellphone, String password)</span>;</span><br><span class="line">    UserInfo <span class="title function_">getUserInfoById</span><span class="params">(<span class="type">long</span> id)</span>;</span><br><span class="line">    UserInfo <span class="title function_">getUserInfoByCellphone</span><span class="params">(String cellphone)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line"><span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>需求: 后台管理系统要实现删除用户的功能，希望用户系统提供一个删除用户的接口,应该如何设计这个接口(假设这里我们不去考虑使用鉴权框架).</p><ul><li><p>方案1: 直接在UserService接口中添加一个删除用户的接口</p><blockquote><p>这个方法可以解决问题，但是也隐藏了一些安全隐患。删除用户是一个非常慎重的操作，我们只希望通过后台管理系统来执行，所以这个接口只限于给后台管理系统使用。如果我们把它放到 UserService 中，那所有使用到 UserService<br>的系统，都可以调用这个接口。不加限制地被其他业务系统调用，就有可能导致误删用户。</p></blockquote></li><li><p>方案2: 遵照接口隔离原则,为依赖接口的类定制服务。只提供调用者需要的方法，屏蔽不需要的方法。</p></li></ul><p>​      将删除接口单独放到另外 一个接口 RestrictedUserService 中, 然后将 RestrictedUserService 只打包提供给后台管理系统来      使用。</p><p>​     <img src="https://bu.dusays.com/2024/06/18/6670f60e5948e.jpg" alt="image-20220530160637842" style="zoom: 50%;" />     </p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">register</span><span class="params">(String cellphone, String password)</span>;</span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">login</span><span class="params">(String cellphone, String password)</span>;</span><br><span class="line">    UserInfo <span class="title function_">getUserInfoById</span><span class="params">(<span class="type">long</span> id)</span>;</span><br><span class="line">    UserInfo <span class="title function_">getUserInfoByCellphone</span><span class="params">(String cellphone)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">RestrictedUserService</span> &#123;</span><br><span class="line"> <span class="type">boolean</span> <span class="title function_">deleteUserByCellphone</span><span class="params">(String cellphone)</span>;</span><br><span class="line"> <span class="type">boolean</span> <span class="title function_">deleteUserById</span><span class="params">(<span class="type">long</span> id)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">UserService</span>, RestrictedUserService &#123;</span><br><span class="line"><span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>遵循接口隔离原则的优势</p><ol><li>将臃肿庞大的接口分解为多个粒度小的接口，可以预防外来变更的扩散，提高系统的灵活性和可维护性。</li><li>使用多个专门的接口还能够体现对象的层次，因为可以通过接口的继承，实现对总接口的定义。</li><li>能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法，当实现这个接口的时候，被迫设计冗余的代码.</li></ol><h2 id="依赖倒置原则"><a href="#依赖倒置原则" class="headerlink" title="依赖倒置原则"></a>依赖倒置原则</h2><h3 id="官方定义-4"><a href="#官方定义-4" class="headerlink" title="官方定义"></a>官方定义</h3><p>依赖倒置原则是Robert C.Martin于1996年在C++Report上发表的文章中提出的。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">High level modules should not depend upon low level modules. Both should depend upon abstractions.</span><br><span class="line">Abstractions should not depend upon details. Details should depend upon abstractions</span><br></pre></td></tr></table></figure><p>依赖倒置原则（Dependence Inversion Principle，DIP）是指在设计代码架构时，高层模块不应该依赖于底层模块，二者都应该依赖于抽象。抽象不应该依赖于细节，细节应该依赖于抽象。</p><h3 id="通俗解释-4"><a href="#通俗解释-4" class="headerlink" title="通俗解释"></a>通俗解释</h3><p>依赖倒置原则是实现开闭原则的重要途径之一，它降低了客户与实现模块之间的耦合。</p><blockquote><ol><li>高层级的模块应该依赖的是低层级的模块的行为的抽象，取决于具体编程语言，可以是抽象类或者接口等技术；</li><li>第2句话其实很简单，只有一个意思：只要依赖了实现，就是耦合了代码，所以我们需要始终依赖的是抽象，而不是实现。</li></ol></blockquote><ul><li><p><strong>传统的自定向下的设计</strong></p><blockquote><p>传统设计方式采用自顶向下的原则， 逐级依赖，中层模块和高层模块的耦合度很高，如果需要修改其中的一个模块，则可能会导致其它很多模块也需要修改，牵一发动全身，不易于维护。</p><p>不使用依赖反转的系统构架，控制流和依赖关系流的依赖箭头是一个方向的，由高层指向底层，也就是高层依赖底层</p></blockquote></li></ul><p><img src="https://bu.dusays.com/2024/06/18/6670f61214723.jpg" alt="image-20220530160637842" style="zoom: 50%;" />    </p><ul><li><p>依赖倒置原则</p><blockquote><p>依赖倒置原则的好处:</p><ul><li>减少类间的耦合性，提高系统的稳定性 . (根据类与类之间的耦合度从弱到强排列：依赖关系、关联关系、聚合关系、组合关系、泛化关系和实现关系 )</li><li>降低并行开发引起的风险 (两个类之间有依赖关系，只要制定出两者之间的接口（或抽象类）就可以独立开发了)</li><li>提高代码的可读性和可维护性</li></ul></blockquote></li></ul><p>​        <img src="https://bu.dusays.com/2024/06/18/6670f6149fff9.jpg" alt="image-20220530160637842" style="zoom: 50%;" /> </p><h3 id="场景示例-4"><a href="#场景示例-4" class="headerlink" title="场景示例"></a>场景示例</h3><p>假设我们现在要组装一台电脑,需要的配件有 cpu，硬盘，内存条。只有这些配置都有了，计算机才能正常的运行。选择cpu有很多选择，如Intel，AMD等，硬盘可以选择希捷，西数等，内存条可以选择金士顿，海盗船等。</p><p><img src="https://bu.dusays.com/2024/06/18/6670f616a82e8.jpg" alt="image-20220530160637842" style="zoom: 50%;" /> </p><p>代码如下：</p><p><strong>希捷硬盘类（XiJieHardDisk）:</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">XiJieHardDisk</span> <span class="keyword">implements</span> <span class="title class_">HardDisk</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(String data)</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;使用希捷硬盘存储数据&quot;</span> + data);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">get</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;使用希捷希捷硬盘取数据&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;数据&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>Intel处理器（IntelCpu）：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">IntelCpu</span> <span class="keyword">implements</span> <span class="title class_">Cpu</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;使用Intel处理器&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>金士顿内存条（KingstonMemory）：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">KingstonMemory</span> <span class="keyword">implements</span> <span class="title class_">Memory</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;使用金士顿作为内存条&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>电脑（Computer）：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Computer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> XiJieHardDisk hardDisk;</span><br><span class="line">    <span class="keyword">private</span> IntelCpu cpu;</span><br><span class="line">    <span class="keyword">private</span> KingstonMemory memory;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> IntelCpu <span class="title function_">getCpu</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> cpu;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setCpu</span><span class="params">(IntelCpu cpu)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.cpu = cpu;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> KingstonMemory <span class="title function_">getMemory</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> memory;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setMemory</span><span class="params">(KingstonMemory memory)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.memory = memory;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> XiJieHardDisk <span class="title function_">getHardDisk</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> hardDisk;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setHardDisk</span><span class="params">(XiJieHardDisk hardDisk)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.hardDisk = hardDisk;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;计算机工作&quot;</span>);</span><br><span class="line">        cpu.run();</span><br><span class="line">        memory.save();</span><br><span class="line">        <span class="type">String</span> <span class="variable">data</span> <span class="operator">=</span> hardDisk.get();</span><br><span class="line">        System.out.println(<span class="string">&quot;从硬盘中获取的数据为：&quot;</span> + data);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>测试类（TestComputer）：</strong></p><p>测试类用来组装电脑。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestComputer</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Computer</span> <span class="variable">computer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Computer</span>();</span><br><span class="line">        computer.setHardDisk(<span class="keyword">new</span> <span class="title class_">XiJieHardDisk</span>());</span><br><span class="line">        computer.setCpu(<span class="keyword">new</span> <span class="title class_">IntelCpu</span>());</span><br><span class="line">        computer.setMemory(<span class="keyword">new</span> <span class="title class_">KingstonMemory</span>());</span><br><span class="line"></span><br><span class="line">        computer.run();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面代码可以看到已经组装了一台电脑，但是似乎组装的电脑的cpu只能是Intel的，内存条只能是金士顿的，硬盘只能是希捷的，这对用户肯定是不友好的，用户有了机箱肯定是想按照自己的喜好，选择自己喜欢的配件。</p><p>根据依赖倒转原则进行改进：</p><p>代码我们需要修改Computer类，让Computer类依赖抽象（各个配件的接口），而不是依赖于各个组件具体的实现类。</p><p><strong>类图如下：</strong></p><p><img src="https://bu.dusays.com/2024/06/18/6670f61ab59c5.jpg" alt="image-20220530160637842" style="zoom: 50%;" />  </p><p><strong>电脑（Computer）：</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Computer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> HardDisk hardDisk;</span><br><span class="line">    <span class="keyword">private</span> Cpu cpu;</span><br><span class="line">    <span class="keyword">private</span> Memory memory;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//getter/setter......</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;计算机工作&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>关于依赖倒置、依赖注入、控制反转这三者之间的区别与联系</strong>   </p><p>1 )  依赖倒置原则</p><p>依赖倒置是一种通用的软件设计原则, 主要用来指导框架层面的设计。</p><blockquote><p>高层模块不依赖低层模块，它们共同依赖同一个抽象。抽象不要依赖具体实现细节，具体实现细节依赖抽象。</p></blockquote><p>2 ) 控制反转</p><p>控制反转与依赖倒置有一些相似, 它也是一种框架设计常用的模式,但并不是具体的方法。</p><blockquote><p>“控制”指的是对程序执行流程的控制，而“反转”指的是在没有使用框架之前，程序员自己控制整个程序的执行。在使用框架之后，整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。</p><p>Spring框架，核心模块IoC容器,就是通过控制反转这一种思想进行设计的</p></blockquote><p>3 ) 依赖注入</p><p>依赖注入是实现控制反转的一个手段，它是一种具体的编码技巧。</p><blockquote><p>我们不通过 new 的方式在类内部创建依赖的对象，而是将依赖的对象在外部创建好之后，通过构造函数等<br>方式传递（或注入）进来, 给类来使用。</p><p>依赖注入真正实现了面向接口编程的愿景，可以很方便地替换同一接口的不同实现，而不会影响到依赖这个接口的客户端。</p></blockquote><h2 id="迪米特法则"><a href="#迪米特法则" class="headerlink" title="迪米特法则"></a>迪米特法则</h2><h3 id="官方定义-5"><a href="#官方定义-5" class="headerlink" title="官方定义"></a>官方定义</h3><p>1987年秋天，迪米特法则由美国Northeastern University的Ian Holland(伊恩 霍兰德)提出，被UML的创始者之一Booch(布奇)等人普及。后来，因为经典著作The PragmaticProgrammer &lt;&lt;程序员修炼之道&gt;&gt;而广为人知。</p><p>迪米特法则（LoD：Law of Demeter）又叫最少知识原则（LKP：Least Knowledge Principle ），指的是一个类/模块对其他的类/模块有越少的了解越好。简言之：talk only to your immediate friends（只跟你最亲密的朋友交谈），不跟陌生人说话。</p><h3 id="通俗解释-5"><a href="#通俗解释-5" class="headerlink" title="通俗解释"></a>通俗解释</h3><p>大部分设计原则和思想都非常抽象，有各种各样的解读，要想灵活地应用到 实际的开发中，需要有实战经验的积累。迪米特法则也不例外。</p><p>简单来说迪米特法则想要表达的思想就是:  <strong>不该有直接依赖关系的类之间，不要有依赖；有依赖关系的类之间，尽量只依赖必要的接口。</strong></p><blockquote><p>如果两个软件实体无须直接通信，那么就不应当发生直接的相互调用，可以通过第三方转发该调用。其目的是降低类之间的耦合度，提高模块的相对独立性。</p></blockquote><h3 id="场景示例-5"><a href="#场景示例-5" class="headerlink" title="场景示例"></a>场景示例</h3><p>我们一起来看下面这个例子: </p><p>明星由于全身心投入艺术，所以许多日常事务由经纪人负责处理，如和粉丝的见面会，和媒体公司的业务洽淡等。这里的经纪人是明星的朋友，而粉丝和媒体公司是陌生人，所以适合使用迪米特法则。</p><p><img src="https://bu.dusays.com/2024/06/18/6670f62a2e270.jpg" alt="image-20220530160637842" style="zoom: 50%;" /> </p><blockquote><p>迪米特法则的独特之处在于它简洁而准确的定义，它允许在编写代码时直接应用，几乎自动地应用了适当的封装、高内聚和低耦合。</p><p>但是，过度使用迪米特法则会使系统产生大量的中介类，从而增加系统的复杂性，使模块之间的通信效率降低。所以，在釆用迪米特法则时需要反复权衡，确保高内聚和低耦合的同时，保证系统的结构清晰。</p></blockquote><p>代码如下：</p><p><strong>明星类（Star）</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Star</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Star</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name=name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>粉丝类（Fans）</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Fans</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Fans</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name=name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>媒体公司类（Company）</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Company</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Company</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.name=name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>经纪人类（Agent）</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Agent</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Star star;</span><br><span class="line">    <span class="keyword">private</span> Fans fans;</span><br><span class="line">    <span class="keyword">private</span> Company company;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setStar</span><span class="params">(Star star)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.star = star;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setFans</span><span class="params">(Fans fans)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.fans = fans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setCompany</span><span class="params">(Company company)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.company = company;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">meeting</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(fans.getName() + <span class="string">&quot;与明星&quot;</span> + star.getName() + <span class="string">&quot;见面了。&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">business</span><span class="params">()</span> &#123;</span><br><span class="line">        System.out.println(company.getName() + <span class="string">&quot;与明星&quot;</span> + star.getName() + <span class="string">&quot;洽淡业务。&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="设计原则总结"><a href="#设计原则总结" class="headerlink" title="设计原则总结"></a>设计原则总结</h2><p>我们之前给的大家介绍了评判代码质量的标准,比如可读性、可复用性、可扩展性等等,这是从代码的整体质量的角度来评判.</p><p>而设计原则就是我们要使用到的更加具体的对于代码进行评判的标准,比如, 我们说这段代码的可扩展性比较差，主要原因是违背了开闭原则。</p><p>我们所学习的SOLID 原则它包含了：</p><ol><li>单一职责原则（SRP）</li><li>开闭原则（OCP）</li><li>里氏替换原则（LSP）</li><li>接口隔离原则（ISP）</li><li>依赖倒置原则（DIP）</li><li>迪米特法则 (LKP)</li></ol><p>这里我们只需要重点关注三个常用的原则即可:</p><p><strong>1 ) 单一职责原则</strong></p><p>单一职责原则是类职责划分的重要参考依据，是保证代码”高内聚“的有效手段，是我们在进行面向对象设计时的主要指导原则。</p><blockquote><p>单一职责原则的难点在于，对代码职责是否足够单一的判定。这要根据具体的场景来具体分析。同一个类的设计，在不同的场景下，对职责是否单一的判定，可能是不同的。</p></blockquote><p><strong>2 ) 开闭原则</strong></p><p>开闭原则是保证代码可扩展性的重要指导原则，是对代码扩展性的具体解读。很多设计模式诞生的初衷都是为了提高代码的扩展性，都是以满足开闭原则为设计目的的。</p><blockquote><p>开闭原则是所有设计模式的最核心目标，也是最难实现的目标，但是所有的软件设计模式都应该以开闭原则当作标准，才能使软件更加的稳定和健壮。</p></blockquote><p><strong>3 ) 依赖倒置原则</strong></p><p>依赖倒置原则主要用来指导框架层面的设计。高层模块不依赖低层模块，它们共同依赖同一个抽象。</p><blockquote><p>依赖倒置原则其实也是实现开闭原则的重要途径之一，它降低了类之间的耦合，提高了系统的稳定性和可维护性，同时这样的代码一般更易读，且便于传承。</p></blockquote>]]></content>
    
    
    <summary type="html">代码质量好坏如何评价?</summary>
    
    
    
    <category term="学习笔记" scheme="https://blog.serms.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    <category term="设计模式" scheme="https://blog.serms.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
    
    <category term="设计模式" scheme="https://blog.serms.top/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    
  </entry>
  
  <entry>
    <title>如何排查慢SQL?</title>
    <link href="https://blog.serms.top/posts/9935d352.html"/>
    <id>https://blog.serms.top/posts/9935d352.html</id>
    <published>2024-05-13T09:25:41.000Z</published>
    <updated>2025-05-28T15:08:01.900Z</updated>
    
    <content type="html"><![CDATA[<h2 id="回答"><a href="#回答" class="headerlink" title="回答"></a>回答</h2><p>如果是在项目中，可以通过SpringAOP去查询这个接口运行的时间；</p><p>如果是一个sql，可以通过explain的指令去查这个sql的执行计划。</p><p>如果有数据库终端的话，也可以通过开启mysql的慢日志查询，设置好时间阈值，进行捕获。</p><h2 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h2><p>来源评论区补充：<a href="https://gnezdd.github.io/2023/07/10/慢SQL治理实战总结/">补充链接</a></p><p>在企业内部对于慢查SQL的优化主要经历以下的几个步骤：</p><p><img src="https://oss.serms.top/blog/202405131703219.png?x-oss-process=style/serms_watermark" alt="202310251903040"></p><p>在慢SQL的优化过程中，可以从以下五个角度去进行思考优化：<strong>SQL优化、资源占用、业务改造、数据减少、源头替换</strong>。</p><p>在治理慢查的过程中，SQL语句的使用问题是导致慢SQL的主要因素，因此本文主要从SQL优化角度出发，对慢SQL的常见原因和特征进行分析，介绍慢SQL的优化过程以及一些有效的调优技巧和工具，希望能够提供一些有用的方法和策略，帮助大家更好地应对慢SQL问题，并最终实现提升系统性能和优化用户体验的目标。</p><h3 id="SQL优化"><a href="#SQL优化" class="headerlink" title="SQL优化"></a>SQL优化</h3><p>SQL语句的优化方式主要是通过选择合适的索引、优化查询语句、避免全表扫描等提高查询效率，减少慢SQL的出现</p><h4 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h4><p>索引主要用于加快数据的查询速度，有了正确的索引，数据库就可以根据索引的数据结构快速定位到匹配的数据行，从而提高查询效率和响应速度。在慢SQL中由于索引导致的主要有两个方面：<strong>索引缺失 与 索引失效</strong></p><h5 id="索引缺失"><a href="#索引缺失" class="headerlink" title="索引缺失"></a>索引缺失</h5><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> t1 <span class="keyword">where</span> text3 <span class="operator">=</span> <span class="string">&#x27;text898&#x27;</span></span><br></pre></td></tr></table></figure><p>text3列没有索引的情况下：</p><p><img src="https://oss.serms.top/blog/202405131705364.png?x-oss-process=style/serms_watermark" alt="202310251904540"></p><p>text3列增加了索引：<img src="https://oss.serms.top/blog/202405131709155.png?x-oss-process=style/serms_watermark" alt="202310251905902"></p><p><strong>建议</strong></p><p>一般在以下场景中需要为相应的列创建索引：</p><ul><li><strong>字段有唯一性限制</strong></li><li><strong>经常用于where查询条件的字段</strong></li><li><strong>经常用于group by 和 order by的字段，可以避免排序</strong></li></ul><p>但是并不是需要为每个字段都添加上索引，有一些场景下添加上索引反而会加重DB的负担：</p><ul><li>不用于查询条件的字段</li><li>字段中存在大量重复的数据</li><li>数据量太少</li><li>频繁更新的字段（考虑）</li></ul><p>注意：随着数据的增长和变化，索引的有效性可能会下降。定期评估和优化现有索引是十分必要的。可以通过删除不再使用的索引、调整索引的顺序和选择适当的索引类型等方式来进行索引维护和优化</p><h5 id="索引失效"><a href="#索引失效" class="headerlink" title="索引失效"></a>索引失效</h5><p>索引失效会导致SQL的执行变为全表扫描或选择错误的索引，在explain中一般是type=ALL或type=index</p><p><strong>索引失效原因：</strong></p><ul><li>索引字段发送隐式转换<ul><li>数字转换为字符串会发生隐式转换失效</li><li>字符串转换为数字是自动转换，不会导致索引失效</li><li><strong>通过在explain语句后增加extended<code>explain extended sql语句</code>，再执行show warnings查看是否存在隐式转换以及哪个字段存在隐式转换</strong></li></ul></li><li>使用 非/不等于（!=、not in）查询时可能会导致索引失效<ul><li>在满足索引覆盖的情况下可能会走索引</li></ul></li><li>在查询条件中对索引使用函数或表达式计算<ul><li>比如from_unixtime(create_time) = ’2019-12-01’就不能使用到索引：需要先做一次全表扫描，将字段上的所有值使用表达式作用后再进行匹配，从而会导致Mysql放弃走索引。所以语句应该写成create_time = unix_timestamp(’2019-12-01’);</li></ul></li><li>没有遵循最左前缀匹配原则，比如联合索引中没有使用到第一列索引、使用左右模糊匹配</li><li>在where子句中，or一些条件列是索引列，一些不是，会导致索引失效，直接全表扫描</li><li>索引的可选择性差（数据发布严重倾斜或区分度不高）<ul><li>MYSQL查询优化器可能认为返回的数据量本身就很多，通过索引扫描并不能减少多少开销，此时选择全表扫描的权重会提高很多</li><li>一般认为区分度 &gt; 0.1的查询字段可以建立索引（经验性指标，一般要进行实际的测试，比如使用前缀索引时，不断尝试直到选择到合适的前缀长度以及合适的区分度）</li></ul></li><li>IS NOT NULL 或 IS NULL条件查询也可能导致索引失效<ul><li>当索引字段不可以为空（null）时<ul><li>is null 不会使用索引，因为条件失效无法查询</li><li>只有使用is not null 返回的结果集中只包含索引字段时，才使用索引，因为实现索引覆盖，优化器认为此时成本较小</li></ul></li><li>当索引字段可以为空（null）时<ul><li>使用 is null 会使用索引，因为NULL值在SQL中被认为是列中最小的值，存储在最左边，所以可以通过索引快速定位</li><li>使用 is not null 返回的结果集中只包含索引字段时，才会使用索引，因为实现索引覆盖</li></ul></li></ul></li></ul><p>总结来说，<strong>要让避免索引失效的原则为：遵循最左前缀原则、避免使用函数和表达式、避免隐式转换、尽量实现索引覆盖</strong></p><p><strong>建议</strong></p><ul><li>修改SQL语句</li><li>force index强制使用索引</li><li>ignore index忽略特定索引</li></ul><h3 id="SQL语句优化"><a href="#SQL语句优化" class="headerlink" title="SQL语句优化"></a>SQL语句优化</h3><h3 id="分页写法（深翻页）"><a href="#分页写法（深翻页）" class="headerlink" title="分页写法（深翻页）"></a>分页写法（深翻页）</h3><p>最常见的分页写法就是使用limit，在分页查询时，会在 LIMIT 后面传两个参数，一个是偏移量(offset)，一个是获取的条数(limit)。</p><p>实现方式是先查询offset+limit条数据，再将offset条数据丢弃给用户返回剩下的limit条数据。比如limit 10000,10实际上是mysql查找到前10010条数据，之后丢弃前面的10000行后再返回</p><p>这样子当偏移量很小时，查询速度很快，但是随着 offset 变大时，查询速度会越来越慢，因为查找的数据越来越多</p><p>在limit0,10的情况下执行速度很快，基本可以忽略不计</p><p><img src="https://oss.serms.top/blog/202405131710570.png?x-oss-process=style/serms_watermark" alt="202310251905273"></p><p>但是当limit n，n是值越来越大时，就导致查询时间增加了</p><p><img src="https://bu.dusays.com/2024/05/13/6641d977c5902.png" alt="202310251908548"></p><p><strong>建议</strong></p><ul><li>方式一：</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> t1 <span class="keyword">where</span> id <span class="operator">&gt;=</span> <span class="number">300000</span> <span class="keyword">order</span> <span class="keyword">by</span> id limit <span class="number">10</span></span><br></pre></td></tr></table></figure><p><img src="https://oss.serms.top/blog/202405131711851.png?x-oss-process=style/serms_watermark" alt="202310251909738"></p><p>避免了扫描前offset条记录</p><p>但是每次查询都需要拿到上一页的最大/小id。比如当前在第3页，需要查询第5页的数据就没办法了</p><ul><li>方式二</li></ul><p>结合普通limit与方式一，解决方式二的问题，但是offset要尽量小</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * from t1 where id &gt; 300000 order by id limit 10, 10</span><br></pre></td></tr></table></figure><p><img src="https://oss.serms.top/blog/202405131711270.png?x-oss-process=style/serms_watermark" alt="202310251907531"></p><ul><li>方式三：</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * from t1 as a inner join (select id from t1 order by id limit 300000, 10) as b on a.id = b.id order by a.id</span><br></pre></td></tr></table></figure><p>由于内部的子查询只扫描了id字段，而不是全表，所以性能会比较强</p><p><img src="https://oss.serms.top/blog/202405131713001.png?x-oss-process=style/serms_watermark" alt="202310251907545"></p><p>这种情况下还是扫描聚簇索引树，可能难以理解并且优化效果不是很明显。在order by换成其他字段达到索引覆盖的情况下会比较容易理解</p><ul><li>方式四：</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * from t1 where id &gt; (select id from t1 order by id limit 300000, 1) limit 10</span><br></pre></td></tr></table></figure><p>同样是通过子查询扫描字段id，但是性能会略好于方式三，因为它不需要进行表的关联，而是一个简单的比较，在不知道上一页最大id的情况下，是比较推荐的用法</p><p><img src="https://oss.serms.top/blog/202405131713995.png?x-oss-process=style/serms_watermark" alt="202310251908991"></p><h4 id="最大最小写法"><a href="#最大最小写法" class="headerlink" title="最大最小写法"></a>最大最小写法</h4><p>MySQL提供了max()和min()用于获取最大最小值，但是优化得不是很好</p><p>text1没有索引，因此会全表扫描获取最小的id</p><p><img src="https://oss.serms.top/blog/202405131714485.png?x-oss-process=style/serms_watermark" alt="202310251910645"></p><p><strong>建议：</strong></p><p>由于id是主键我们可以知道第一次找到的记录对应的id就是我们需要的结果，所以可以根据结果的有序性修改SQL语句</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select id from t1 where text1 = &#x27;dd&#x27; limit 1</span><br></pre></td></tr></table></figure><p><img src="https://oss.serms.top/blog/202405131714180.png?x-oss-process=style/serms_watermark" alt="202310251911283"></p><p>最大值同理</p><h4 id="order-by排序问题"><a href="#order-by排序问题" class="headerlink" title="order by排序问题"></a>order by排序问题</h4><p>MySQL进行排序是一个成本比较高的操作：</p><ul><li>全字段排序会在sort_buffer中建立临时表进行排序</li><li>基于rowid排序不仅需要建立临时表，还会涉及回表操作</li></ul><p>在需要排序时会在explain的Extra字段中出现Using filesort</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> t1 <span class="keyword">where</span> v1 <span class="operator">&lt;</span> <span class="number">100</span> <span class="keyword">order</span> <span class="keyword">by</span> v1</span><br></pre></td></tr></table></figure><p><img src="https://oss.serms.top/blog/202405131715405.png?x-oss-process=style/serms_watermark" alt="202310251912862"></p><p><strong>建议：</strong></p><p>对于索引是本来就是有序的，所以可以给order by字段加上索引</p><ul><li>如果order by后面的字段是单个索引，需要order by 条件要与where中条件一致，否则order by不会利用索引进行排序</li><li>如果order by 最后的字段是组合索引的一部分，需要把放在索引组合顺序的最后</li></ul><p><img src="https://bu.dusays.com/2024/05/13/6641dab233192.png" alt="202310251912164"></p><h4 id="group-by临时表问题"><a href="#group-by临时表问题" class="headerlink" title="group by临时表问题"></a>group by临时表问题</h4><ul><li>内存占用：group by语句由于可能会建立内部临时表，用于保存和统计中间结果。首先会使用内存临时表，但是内存临时表的大小是有限制的，由参数 tmp_table_size 控制，当超过此限制时会把内存临时表转成磁盘临时表。因此内部临时表的存在会影响内存和磁盘的空间，且需要构造的是一个带唯一索引的表，执行代价都是比较高的。因此需要尽量避免内部临时表的建立</li><li>额外排序：group by column默认会根据column排序，因此还会触发排序开销问题</li></ul><p><img src="https://bu.dusays.com/2024/05/13/6641dad7246f5.png" alt="202310251912542"></p><p><strong>建议：</strong></p><ul><li>让 group by 字段用上表的索引，确认方法是 explain 的Extra结果里有没有 Using temporary 和 Using filesort；通过索引建立，只需要顺序扫描到数据结束，就可以拿到 group by 的结果，不需要临时表，也不需要再额外排序</li></ul><p><img src="https://oss.serms.top/blog/202405131718823.png?x-oss-process=style/serms_watermark" alt="202310251913287"></p><ul><li>如果对 group by 语句的结果没有排序要求，要在语句后面加 order by null</li><li>如果 group by 需要统计的数据量不大，尽量只使用内存临时表；可以通过适当调大tmp_table_size 参数，来避免用到磁盘临时表</li><li>如果数据量实在太大，使用 SQL_BIG_RESULT 这个hint，来告诉优化器直接使用排序算法得到 group by 的结果</li></ul><h4 id="join"><a href="#join" class="headerlink" title="join"></a>join</h4><p>当关联被驱动表上使用到索引时，会使用 Index Nested-Loop Join （NLJ）算法</p><p>当关联被驱动表上没有使用到索引时（即t2的字段a无索引），会使用 Block Nested-Loop Join（BNL）算法</p><p><strong>建议：</strong></p><p>NLJ算法优化：小表驱动大表，在join的时候如果明确知道哪张表是小表时可以使用straight_join写法固定连接驱动方式</p><p>BNL算法优化：</p><ul><li>给被驱动表的join字段加上索引，把BNL算法转成NLJ算法</li><li>无法设置索引的情况可以通过设置join_buffer_size参数来控制Join Buffer的大小，以减少分段查询次数</li></ul><p>Hash Join算法优化：增加 join_buffer_size值避免生成文件</p><h4 id="in-amp-exists"><a href="#in-amp-exists" class="headerlink" title="in &amp; exists"></a>in &amp; exists</h4><p>in执行流程：查询子查询的表且内外表有关联时，先执行内层表的子查询，然后将内表和外表做一个笛卡尔积，然后按照条件进行筛选，得到结果集。所以相对内表比较小的时候，in的速度较快</p><p>exists执行流程：指定一个子查询，检测行的存在。遍历循环外表，然后看外表中的记录有没有和内表的数据一样的，匹配上就将结果放入结果集中</p><p><strong>建议：</strong></p><p>遵循小表驱动大表：<strong>exists是以外层表为驱动表、IN是先执行内层表的**</strong>子查询**。如果子查询得出的结果集记录较少，主查询中的表较大且又有索引时应该用in；反之如果外层的主查询记录较少，子查询中的表大且又有索引时使用exists</p><h4 id="not-in-amp-not-exists"><a href="#not-in-amp-not-exists" class="headerlink" title="not in &amp; not exists"></a>not in &amp; not exists</h4><p>not in使用的是全表扫描没有用到索引；而not exists在子查询依然能用到表上的索引</p><p><strong>建议：</strong></p><p>使用not exists代替not in</p><h4 id="查询记录是否存在"><a href="#查询记录是否存在" class="headerlink" title="查询记录是否存在"></a>查询记录是否存在</h4><p>在很多时候开发人员判断某一条件对应的记录是否存在时会采用select count(*)，但是这样子会导致扫描所有符合条件的数据</p><p><strong>建议：</strong></p><p>改用limit 1，这样子数据库查询到一条符合条件的记录就会返回，不需要再继续查找还有多少条记录</p><h3 id="资源占用"><a href="#资源占用" class="headerlink" title="资源占用"></a>资源占用</h3><ul><li>锁资源等待：在读写很热的表上，通常会发生锁资源争夺，从而导致慢查询的情况<ul><li>谨慎使用for update</li><li>增删改尽量使用到索引</li><li>降低并发，避免对同一条数据进行反复修改</li></ul></li><li>网络波动：往客户端发送数据时发生网络波动导致的慢查询</li><li>硬件配置：CPU利用率高，磁盘IO经常满载，导致慢查询</li></ul><p>在高并发、高流量下，数据库所在机器的负载load过高也会导致SQL整体执行时间过长，这时可能需要从机器和实例的分配，分布式部署，分库分表，读写分离等角度进行优化</p><h3 id="业务改造"><a href="#业务改造" class="headerlink" title="业务改造"></a>业务改造</h3><ul><li>是不是真的需要全部查出来，还是取其中的top N就能够满足需求了</li><li>查询条件过多的情况下，能否前端页面提示限制过多的查询条件的使用</li><li>针对实时导出的数据，涉及到实时查DB导出大量数据时，限制导出数据量 or 走T+1的离线导出是不是也是可以的</li><li>现在业务上需要做数据搜索，使用了 LIKE “%关键词%” 做全模糊查询，从而导致了慢SQL。是不是可以让业务方妥协下，最右模糊匹配，这样就可以利用上索引了</li></ul><h3 id="源头替换"><a href="#源头替换" class="headerlink" title="源头替换"></a>源头替换</h3><p>Mysql并不是任何的查询场景都是适合的，如需要支持全模糊搜索时，全模糊的like是无法走到索引的。同时结合数据本身的生命周期，对于热点数据，可以考虑存储到缓存解决。因此针对不适合mysql数据源的情况，我们需要替代新的存储介质</p><ul><li>有like的全模糊的查询，比如基于文本内容去查订单信息，需要接搜索引擎解决</li><li>有热点数据的查询，考虑是否要接缓存解决</li><li>针对复杂条件的海量数据查询，可以考虑切换到OLAP(Online Analytical Processing)，可以考虑接Hybrid DB或ADB通道</li><li>有些场景Mysql不适用，需要用K-V的数据库，HBASE等列式存储的存储引擎</li></ul><h3 id="数据减少"><a href="#数据减少" class="headerlink" title="数据减少"></a>数据减少</h3><p>SQL本身的性能已经到达极限了，但是耗时仍然很长，可能由于数据量或索引数据都比较大了。因此需要从数据量级减少的角度去处理</p><ul><li>使用分库分表。由于单表的数据量过大，例如达到千万级别的数据了，需要使用分库分表技术拆分后减轻单库单表的单点压力</li><li>定时清理终态数据。针对已经状态为终态的业务单据或明显信息，可以使用idb历史数据清理的方式配置定时自动清理。如针对我们的仓储库存操作明细为完结状态的数据，我们只保留最近1天的数据在db中，其他直接删除，减少db查询压力</li><li>统计类查询可以单独维护汇总数据表。参考数据仓库中的数据分层设计，基于明细数据，抽出一张指标汇总表，或7天/15天等的视图数据进行预计算。此类汇总表数据量级相比明细表下降很多，从而避免直接根据大量明细查询聚合造成慢sql</li></ul><h3 id="实践举例"><a href="#实践举例" class="headerlink" title="实践举例"></a>实践举例</h3><ul><li>SQL语句分析</li><li>分析sql时间点发现固定db某个示例会导致RT尖峰抖动，发现磁盘也有相应问题。怀疑DB某些库磁盘问题导致，联系DBA确认后进行主备切换解决</li><li>核销慢sql查询迟迟难以解决。发现库存核销记录每天增量数据达到百万级别，但是核销创建状态记录只有20%~30%左右，因此对完结状态的核销记录idb配置定时清理，由15天缩短到2天，减少db数据量</li><li>库存sn查询涉及复杂查询，采用切换到OLAP链路，通过数据同步中间件完成从db到HybridDB一键同步，切换数据源后问题解决</li></ul><h3 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h3><p><a href="https://blog.csdn.net/chuixue24/article/details/100031812">MySQL中IS NULL、IS NOT NULL、!=是否走索引</a></p>]]></content>
    
    
    <summary type="html">如何排查一条慢SQL？可以从哪些方面入手？</summary>
    
    
    
    <category term="后端开发" scheme="https://blog.serms.top/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/"/>
    
    <category term="数据库" scheme="https://blog.serms.top/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
    <category term="Mysql" scheme="https://blog.serms.top/tags/Mysql/"/>
    
    <category term="八股文" scheme="https://blog.serms.top/tags/%E5%85%AB%E8%82%A1%E6%96%87/"/>
    
  </entry>
  
  <entry>
    <title>Docker部署Redis</title>
    <link href="https://blog.serms.top/posts/f64403ff.html"/>
    <id>https://blog.serms.top/posts/f64403ff.html</id>
    <published>2024-03-20T15:27:25.000Z</published>
    <updated>2024-03-20T15:27:25.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Docker部署Redis"><a href="#Docker部署Redis" class="headerlink" title="Docker部署Redis"></a>Docker部署Redis</h2><h3 id="创建数据卷映射目录"><a href="#创建数据卷映射目录" class="headerlink" title="创建数据卷映射目录"></a>创建数据卷映射目录</h3><p>这里我存放的路径为<code>data/app</code>为例，按需创建data、conf、log目录</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mkdir -p /data/app/redis/data</span><br><span class="line">mkdir -p /data/app/redis/conf</span><br><span class="line">mkdir -p /data/app/redis/log</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/03/20/65fa80eb56a33.png" alt="image-20240320142226287"></p><h3 id="获取配置文件"><a href="#获取配置文件" class="headerlink" title="获取配置文件"></a>获取配置文件</h3><p>关于拉取配置文件这里建议访问<a href="https://redis.io/docs/management/config/">redisConfig</a>看一下,当前稳定的版本是哪些</p><p><img src="https://bu.dusays.com/2024/03/20/65fa9b2c31d62.png" alt="image-20240320161533213"></p><p>截止至此文发布时<strong>最新版本为<code>7.2</code></strong>，这里直接拿<code>7.2</code>做演示，在<a href="https://github.com/redis/redis/blob/7.2/redis.conf">RedisGithub</a>仓库中找到对应版本的<code>redis.conf</code>文件，<strong>点击复制</strong></p><p><img src="https://bu.dusays.com/2024/03/20/65fa9c9329092.png" alt="image-20240320162052637"></p><p>在<code>/data/app/redis/conf/</code>路径下创建<code>redis.conf</code>文件，随后修改文件，直接将复制的内容粘贴进去</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">touch</span> redis.conf</span><br><span class="line">vim redis.conf</span><br><span class="line">或</span><br><span class="line">vi redis.conf</span><br></pre></td></tr></table></figure><h3 id="修改配置文件"><a href="#修改配置文件" class="headerlink" title="修改配置文件"></a>修改配置文件</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">vim redis.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># 常见的配置项 这里仅作参考</span></span><br><span class="line">daemonize <span class="built_in">yes</span> <span class="comment">#是否以守护进程方式运行</span></span><br><span class="line">port 6379 <span class="comment"># Redis服务器监听的端口</span></span><br><span class="line"><span class="built_in">bind</span> 127.0.0.1 <span class="comment"># 绑定的主机IP地址</span></span><br><span class="line">requirepass <span class="comment">#设置密码</span></span><br></pre></td></tr></table></figure><details class="folding-tag" green><summary> 常用注释说明 </summary>              <div class='content'>              <figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 通用设置</span></span><br><span class="line">daemonize no                  <span class="comment"># 是否以守护进程方式运行 Redis</span></span><br><span class="line">pidfile /var/run/redis_6379.pid  <span class="comment"># Redis 进程 ID 文件路径</span></span><br><span class="line">port 6379                     <span class="comment"># Redis 监听的端口号</span></span><br><span class="line"><span class="built_in">bind</span> 127.0.0.1                <span class="comment"># Redis 监听的地址（默认为本地回环地址）</span></span><br><span class="line">requirepass<span class="comment"># 密码配置</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 连接设置</span></span><br><span class="line"><span class="built_in">timeout</span> 0                     <span class="comment"># Redis 客户端连接超时时间</span></span><br><span class="line">tcp-keepalive 0               <span class="comment"># 是否开启 TCP keepalive</span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># 通信协议设置</span></span><br><span class="line">tcp-backlog 511               <span class="comment"># TCP listen() 函数的等待队列长度</span></span><br><span class="line">tcp-keepalive 300             <span class="comment"># TCP keepalive 时间间隔，单位为秒</span></span><br><span class="line">unixsocket /var/run/redis/redis.sock  <span class="comment"># Unix 套接字文件路径</span></span><br><span class="line">unixsocketperm 700            <span class="comment"># Unix 套接字文件的权限</span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># 通用配置项</span></span><br><span class="line">loglevel notice               <span class="comment"># 日志记录级别</span></span><br><span class="line">logfile /var/log/redis/redis.log  <span class="comment"># 日志文件路径</span></span><br><span class="line">syslog-enabled no             <span class="comment"># 是否将日志输出到系统日志</span></span><br><span class="line">syslog-ident redis            <span class="comment"># 系统日志标识符</span></span><br><span class="line">syslog-facility local0        <span class="comment"># 系统日志设施</span></span><br><span class="line">databases 16                  <span class="comment"># Redis 数据库数量</span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># 快照配置项</span></span><br><span class="line">save 900 1                    <span class="comment"># 触发快照保存的条件（900 秒内至少有一个键被修改）</span></span><br><span class="line">save 300 10                   <span class="comment"># 触发快照保存的条件（300 秒内至少有 10 个键被修改）</span></span><br><span class="line">save 60 10000                 <span class="comment"># 触发快照保存的条件（60 秒内至少有 10000 个键被修改）</span></span><br><span class="line">stop-writes-on-bgsave-error <span class="built_in">yes</span>  <span class="comment"># 快照保存出错时是否停止写入操作</span></span><br><span class="line">rdbcompression <span class="built_in">yes</span>            <span class="comment"># 是否开启 RDB 文件压缩</span></span><br><span class="line">rdbchecksum <span class="built_in">yes</span>               <span class="comment"># 是否开启 RDB 文件校验</span></span><br><span class="line">dbfilename dump.rdb           <span class="comment"># RDB 文件名</span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># AOF 配置项</span></span><br><span class="line">appendonly no                 <span class="comment"># 是否开启 AOF 持久化</span></span><br><span class="line">appendfilename <span class="string">&quot;appendonly.aof&quot;</span>  <span class="comment"># AOF 持久化文件名</span></span><br><span class="line">appendfsync everysec          <span class="comment"># AOF 持久化的同步方式</span></span><br><span class="line">no-appendfsync-on-rewrite no  <span class="comment"># 是否在 AOF 重写时禁止同步</span></span><br><span class="line">auto-aof-rewrite-percentage 100  <span class="comment"># 触发 AOF 重写的条件（AOF 文件大小增长率达到 100%）</span></span><br><span class="line">auto-aof-rewrite-min-size 64mb  <span class="comment"># 触发 AOF 重写的条件（AOF 文件最小大小）</span></span><br><span class="line">aof-load-truncated <span class="built_in">yes</span>        <span class="comment"># 是否在 AOF 文件被截断时继续加载剩余内容</span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># 主从复制配置项</span></span><br><span class="line">slave-serve-stale-data <span class="built_in">yes</span>    <span class="comment"># 从节点是否在主节点不可用时仍然向客户端提供服务</span></span><br><span class="line">slave-read-only <span class="built_in">yes</span>           <span class="comment"># 从节点是否只读</span></span><br><span class="line">repl-diskless-sync no         <span class="comment"># 是否在复制时避免使用磁盘</span></span><br><span class="line">repl-diskless-sync-delay 5    <span class="comment"># 复制时磁盘同步延迟时间，单位为秒</span></span><br><span class="line">repl-ping-slave-period 10     <span class="comment"># 主节点发送 ping 命令的时间间隔，单位为秒</span></span><br><span class="line">repl-timeout 60               <span class="comment"># 主从节点之间的心跳超时时间，单位为秒</span></span><br><span class="line">repl-disable-tcp-nodelay no   <span class="comment"># 是否禁用 TCP_NODELAY 算法</span></span><br><span class="line">slave-priority 100            <span class="comment"># 从节点优先级</span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># Sentinel 配置项</span></span><br><span class="line">sentinel deny-scripts-reconfig <span class="built_in">yes</span>  <span class="comment"># 是否允许 Sentinel 脚本重新配置 Redis</span></span><br><span class="line">sentinel monitor mymaster 127.0.0.1 6379 2  <span class="comment"># 监控的主节点名称、IP 地址、端口号、quorum</span></span><br><span class="line">sentinel down-after-milliseconds mymaster 30000  <span class="comment"># 主节点失效判定时间，单位为毫秒</span></span><br><span class="line">sentinel failover-timeout mymaster 180000</span><br><span class="line"></span><br><span class="line"><span class="comment"># 附加配置项</span></span><br><span class="line">always-show-logo</span><br><span class="line"><span class="comment"># 是否在命令行启动时显示 Redis Logo，默认为 yes。可以将该选项设置为 no，以屏蔽 Redis Logo 的显示。</span></span><br><span class="line">hz</span><br><span class="line"><span class="comment"># 控制 Redis 在每秒钟运行的循环次数。默认为 10，表示每秒钟运行 10 次循环。可以将该选项设置为一个正整数，以控制 Redis 运行的速度</span></span><br><span class="line">include</span><br><span class="line"><span class="comment"># 允许在配置文件中包含其他文件，默认为 &quot;&quot;，表示不包含其他文件。可以将该选项设置为实际文件名，以包含其他文件的配置项。</span></span><br><span class="line">lua-time-limit</span><br><span class="line"><span class="comment"># 控制 Lua 脚本的执行时间限制，默认为 5000 毫秒。可以将该选项设置为一个正整数，以控制 Lua 脚本的最长执行时间。</span></span><br><span class="line">repl-backlog-size</span><br><span class="line"><span class="comment"># 控制 Redis 复制功能的回放缓冲区大小，默认为 1MB。可以将该选项设置为一个正整数，以控制回放缓冲区的大小。</span></span><br><span class="line">repl-diskless-sync</span><br><span class="line"><span class="comment"># 是否在复制时避免使用磁盘，即将数据直接从主节点传输到从节点的内存中，默认为 no。可以将该选项设置为 yes，以避免磁盘的使用。</span></span><br><span class="line">repl-ping-slave-period</span><br><span class="line"><span class="comment"># 控制主节点发送 ping 命令的时间间隔，默认为 10 秒。可以将该选项设置为一个正整数，以控制 ping 命令的发送间隔。</span></span><br><span class="line">repl-timeout</span><br><span class="line"><span class="comment"># 控制主从节点之间的心跳超时时间，默认为 60 秒。可以将该选项设置为一个正整数，以控制心跳超时时间。</span></span><br><span class="line">appendonly</span><br><span class="line"><span class="comment"># 是否开启 AOF 持久化，默认为 no。AOF 持久化可以保证 Redis 在宕机时不会丢失数据，但会增加额外的磁盘写入操作。</span></span><br><span class="line">appendfilename</span><br><span class="line"><span class="comment"># AOF 持久化文件名，默认为 appendonly.aof。可以将该选项设置为实际文件名。</span></span><br><span class="line">appendfsync</span><br><span class="line"><span class="comment"># AOF 持久化的同步方式，默认为 everysec。可以将该选项设置为 always、everysec 或 no，分别表示每个写入命令时同步、每秒同步一次或不同步。</span></span><br><span class="line">save</span><br><span class="line"><span class="comment"># RDB 持久化的触发条件，默认为 save 900 1，表示如果 900 秒内至少有一个键被修改，则执行 RDB 持久化。可以设置多个触发条件，例如 save 300 10 表示如果 300 秒内至少有 10 个键被修改，则执行 RDB 持久化。</span></span><br><span class="line">rdbcompression</span><br><span class="line"><span class="comment"># 是否开启 RDB 文件压缩，默认为 yes。开启压缩可以减少磁盘空间的占用，但会增加 CPU 的负载。</span></span><br><span class="line">maxmemory-policy</span><br><span class="line"><span class="comment"># Redis 内存达到最大限制后的淘汰策略，默认为 noeviction，表示停止写入操作。其他可选策略包括 volatile-lru、volatile-ttl、volatile-random、allkeys-lru、allkeys-random 等。</span></span><br><span class="line">client-output-buffer-limit</span><br><span class="line"><span class="comment"># 控制客户端输出缓冲区的大小，默认为 client-output-buffer-limit normal 0 0 0，表示不限制缓冲区大小。可以将该选项设置为实际缓冲区大小，以避免客户端输出缓冲区溢出。</span></span><br><span class="line">notify-keyspace-events</span><br><span class="line"><span class="comment"># 开启键空间通知功能，默认为 &quot;&quot;，表示关闭键空间通知功能。可以将该选项设置为 KEA、KgE、lsh、E、gxE 等，表示监听不同类型的键空间事件。</span></span><br></pre></td></tr></table></figure>              </div>            </details><h3 id="启动redis容器"><a href="#启动redis容器" class="headerlink" title="启动redis容器"></a>启动redis容器</h3><p>在启动Redis容器前，请确定你的数据卷映射。在下属示例中，数据卷映射的路径为<code>/data/app/redis/xxxx</code>，如果不一样请记得修改。启动容器后，系统将自动从远程仓库拉取相应版本的Redis镜像。请务必确保所拉取的Redis版本与之前复制的配置文件版本一致。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">docker run -p 6379:6379 \</span><br><span class="line">    --name redis \</span><br><span class="line">    -v /data/app/redis/conf/redis.conf:/etc/redis/redis.conf \</span><br><span class="line">    -v /data/app/redis/data:/data \</span><br><span class="line">    -v /data/app/redis/logs:/logs \</span><br><span class="line">    --restart always \</span><br><span class="line">    -d redis:7.2 redis-server /etc/redis/redis.conf \</span><br><span class="line">    --appendonly <span class="built_in">yes</span></span><br></pre></td></tr></table></figure><p>或不指定配置文件启动，<strong>不要/etc/redis/redis.conf</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">docker run -p 6379:6379 \</span><br><span class="line">--name redis \</span><br><span class="line">-v /data/app/redis/conf/redis.conf:/etc/redis/redis.conf \</span><br><span class="line">-v /data/app/redis/data:/data \</span><br><span class="line">-v /data/app/redis/log:/var/log/redis \</span><br><span class="line">--restart always \</span><br><span class="line">-d redis:7.2 redis-server \</span><br><span class="line">--appendonly <span class="built_in">yes</span></span><br></pre></td></tr></table></figure><p><strong>必须：</strong></p><ul><li>-p 6379:6379 端口映射：前表示主机部分，：后表示容器部分。</li><li>–name myredis 指定该容器名称，查看和进行操作都比较方便。</li></ul><p><strong>可选</strong>：</p><ul><li>-v 挂载目录，规则与端口映射相同。</li><li>-d redis 表示后台启动redis</li><li>redis-server /etc/redis/redis.conf 以配置文件启动redis，加载容器内的conf文件，最终找到的是挂载的录/data/app/redis/conf/redis.conf</li><li>appendonly yes 开启redis 持久化</li><li>—restart always 表示开机启动</li><li>—network serms-net 网卡 </li></ul><p><img src="https://bu.dusays.com/2024/03/20/65faac440dda4.png" alt="image-20240320172830013"></p><h3 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h3><ul><li><strong>进入redis容器使用redis-cli命令进行连接</strong></li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker exec -it redis redis-cli</span><br></pre></td></tr></table></figure><ul><li><strong>查看启动日志</strong></li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">docker logs myredis <span class="comment"># 后面跟容器名 or 容器ID 都可以</span></span><br><span class="line">docker logs --since 30m &lt;容器名&gt; <span class="comment"># --since 30m 是查看此容器30分钟之内的日志情况。</span></span><br></pre></td></tr></table></figure><ul><li>Docker 停止、删除、重启、启动容器</li></ul><p>正常删除容器，一般是先停止容器，再进行删除</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">docker stop [容器名|容器ID] <span class="comment">#停止容器</span></span><br><span class="line">docker start   [容器名|容器ID]  <span class="comment">#启动停止的容器</span></span><br><span class="line">docker restart  [容器名|容器ID]  <span class="comment"># 将容器重新启动</span></span><br><span class="line">docker <span class="built_in">kill</span> [容器名|容器ID] <span class="comment">#强行终止</span></span><br><span class="line">docker <span class="built_in">rm</span> [容器名|容器ID]   <span class="comment"># 删除停止的容器</span></span><br></pre></td></tr></table></figure><h2 id="常见报错"><a href="#常见报错" class="headerlink" title="常见报错"></a>常见报错</h2><p>如果还有其他问题可留言，博主会尽力帮你解决</p><h3 id="Redis-Client-On-Error-Error-write-ECONNABORTED-Config-right"><a href="#Redis-Client-On-Error-Error-write-ECONNABORTED-Config-right" class="headerlink" title="Redis Client On Error: Error: write ECONNABORTED Config right?"></a>Redis Client On Error: Error: write ECONNABORTED Config right?</h3><ol><li><p>方法一</p><blockquote><p>bind 0.0.0.0 注释 或者设置为<code>bind 0.0.0.0</code></p><p>protected-mode 设置为<code>no</code></p></blockquote><p><img src="https://bu.dusays.com/2024/03/20/65fafb3599bc3.png" alt="image-20240320230513561"></p></li><li><p>方法二</p><p>设置密码</p><p><img src="https://bu.dusays.com/2024/03/20/65fafb9806b29.png" alt="image-20240320230659087"></p></li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;Docker部署Redis&quot;&gt;&lt;a href=&quot;#Docker部署Redis&quot; class=&quot;headerlink&quot; title=&quot;Docker部署Redis&quot;&gt;&lt;/a&gt;Docker部署Redis&lt;/h2&gt;&lt;h3 id=&quot;创建数据卷映射目录&quot;&gt;&lt;a href=&quot;#</summary>
      
    
    
    
    <category term="运维部署" scheme="https://blog.serms.top/categories/%E8%BF%90%E7%BB%B4%E9%83%A8%E7%BD%B2/"/>
    
    <category term="Docker" scheme="https://blog.serms.top/categories/%E8%BF%90%E7%BB%B4%E9%83%A8%E7%BD%B2/Docker/"/>
    
    
    <category term="Docker" scheme="https://blog.serms.top/tags/Docker/"/>
    
    <category term="Redis" scheme="https://blog.serms.top/tags/Redis/"/>
    
  </entry>
  
  <entry>
    <title>Docker部署Oracle11g</title>
    <link href="https://blog.serms.top/posts/3bf450c5.html"/>
    <id>https://blog.serms.top/posts/3bf450c5.html</id>
    <published>2024-03-18T04:36:48.000Z</published>
    <updated>2024-03-18T04:36:48.000Z</updated>
    
    <content type="html"><![CDATA[<div class="note blue anzhiyufont anzhiyu-icon-bullhorn flat"><p><mark class="hl-label blue">Docker安装请看这篇:</mark>  <a class="btn-anzhiyu outline" href="https://blog.serms.top/post/53d552cb"   title="Docker安装部署"><span>Docker安装部署</span></a></p></div><h2 id="安装部署"><a href="#安装部署" class="headerlink" title="安装部署"></a>安装部署</h2><blockquote><p>oracle镜像来自registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g 账户：root/helowin</p></blockquote><h3 id="拉取镜像"><a href="#拉取镜像" class="headerlink" title="拉取镜像"></a>拉取镜像</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g</span><br><span class="line">或 </span><br><span class="line">docker pull akaiot/oracle_11g</span><br></pre></td></tr></table></figure><h3 id="创建并启动容器"><a href="#创建并启动容器" class="headerlink" title="创建并启动容器"></a>创建并启动容器</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 无数据卷挂载</span></span><br><span class="line">docker run -d -p 1521:1521 --name oracle11g akaiot/oracle_11g</span><br><span class="line"><span class="comment"># 有数据卷挂载</span></span><br><span class="line">docker run -d --name oracle11g -p 1521:1521 --privileged=<span class="literal">true</span>  -v /data/app/oracle/oradata:/serms/oracle/oradata akaiot/oracle_11g</span><br></pre></td></tr></table></figure><h3 id="进入oracle11g容器进行配置"><a href="#进入oracle11g容器进行配置" class="headerlink" title="进入oracle11g容器进行配置"></a>进入oracle11g容器进行配置</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker exec -it oracle11g bash</span><br></pre></td></tr></table></figure><h3 id="切换到root用户下进行配置"><a href="#切换到root用户下进行配置" class="headerlink" title="切换到root用户下进行配置"></a>切换到root用户下进行配置</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">su root</span><br><span class="line">密码为：helowin</span><br></pre></td></tr></table></figure><h3 id="编辑profile文件配置ORACLE环境变量"><a href="#编辑profile文件配置ORACLE环境变量" class="headerlink" title="编辑profile文件配置ORACLE环境变量"></a>编辑profile文件配置ORACLE环境变量</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vi /etc/profile</span><br></pre></td></tr></table></figure><p>在最下边添加如下内容:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">export ORACLE_HOME=/home/oracle/app/oracle/product/11.2.0/dbhome_2</span><br><span class="line">export ORACLE_SID=helowin</span><br><span class="line">export PATH=$ORACLE_HOME/bin:$PATH</span><br></pre></td></tr></table></figure><h3 id="让配置立刻生效"><a href="#让配置立刻生效" class="headerlink" title="让配置立刻生效"></a>让配置立刻生效</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">source /etc/profile</span><br></pre></td></tr></table></figure><h3 id="创建软连接"><a href="#创建软连接" class="headerlink" title="创建软连接"></a>创建软连接</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ln -s $ORACLE_HOME/bin/sqlplus /usr/bin</span><br></pre></td></tr></table></figure><h3 id="切换到oracle-用户"><a href="#切换到oracle-用户" class="headerlink" title="切换到oracle 用户"></a>切换到oracle 用户</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">su - oracle</span><br></pre></td></tr></table></figure><h3 id="登录sqlplus并修改sys、system用户密码并刷新权限"><a href="#登录sqlplus并修改sys、system用户密码并刷新权限" class="headerlink" title="登录sqlplus并修改sys、system用户密码并刷新权限"></a>登录sqlplus并修改sys、system用户密码并刷新权限</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">sqlplus <span class="operator">/</span>nolog</span><br><span class="line">conn <span class="operator">/</span><span class="keyword">as</span> sysdba</span><br><span class="line"># 修改<span class="keyword">system</span>用户的密码为&quot;root&quot;</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">user</span> <span class="keyword">system</span> identified <span class="keyword">by</span> root;</span><br><span class="line"># 修改sys用户的密码为&quot;root&quot;</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">user</span> sys identified <span class="keyword">by</span> root;</span><br><span class="line"># 修改默认配置文件中密码有效期策略，将密码的有效期设置为永久有效（UNLIMITED）</span><br><span class="line"><span class="keyword">ALTER</span> PROFILE <span class="keyword">DEFAULT</span> LIMIT PASSWORD_LIFE_TIME UNLIMITED;</span><br></pre></td></tr></table></figure><p>退出：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">exit;</span><br></pre></td></tr></table></figure><h3 id="查看一下oracle实例状态"><a href="#查看一下oracle实例状态" class="headerlink" title="查看一下oracle实例状态"></a>查看一下oracle实例状态</h3><p>lsnrctl status</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">[oracle@4b4795fdc52f ~]$ lsnrctl status</span><br><span class="line"></span><br><span class="line">LSNRCTL for Linux: Version 11.2.0.1.0 - Production on 18-MAR-2024 11:16:46</span><br><span class="line"></span><br><span class="line">Copyright (c) 1991, 2009, Oracle.  All rights reserved.</span><br><span class="line"></span><br><span class="line">Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1521)))</span><br><span class="line">STATUS of the LISTENER</span><br><span class="line">------------------------</span><br><span class="line">Alias                     LISTENER</span><br><span class="line">Version                   TNSLSNR for Linux: Version 11.2.0.1.0 - Production</span><br><span class="line">Start Date                18-MAR-2024 11:04:17</span><br><span class="line">Uptime                    0 days 0 hr. 12 min. 29 sec</span><br><span class="line">Trace Level               off</span><br><span class="line">Security                  ON: Local OS Authentication</span><br><span class="line">SNMP                      OFF</span><br><span class="line">Listener Parameter File   /home/oracle/app/oracle/product/11.2.0/dbhome_2/network/admin/listener.ora</span><br><span class="line">Listener Log File         /home/oracle/app/oracle/diag/tnslsnr/4b4795fdc52f/listener/alert/log.xml</span><br><span class="line">Listening Endpoints Summary...</span><br><span class="line">  (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))</span><br><span class="line">  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=4b4795fdc52f)(PORT=1521)))</span><br><span class="line">Services Summary...</span><br><span class="line">Service &quot;helowin&quot; has 1 instance(s).</span><br><span class="line">  Instance &quot;helowin&quot;, status READY, has 1 handler(s) for this service...</span><br><span class="line">Service &quot;helowinXDB&quot; has 1 instance(s).</span><br><span class="line">  Instance &quot;helowin&quot;, status READY, has 1 handler(s) for this service...</span><br><span class="line">The command completed successfully</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="修改字符集编码为GBK"><a href="#修改字符集编码为GBK" class="headerlink" title="修改字符集编码为GBK"></a>修改字符集编码为GBK</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">connect</span> sys<span class="operator">/</span>root <span class="keyword">as</span> SYSDBA;</span><br><span class="line">SHUTDOWN IMMEDIATE;</span><br><span class="line">STARTUP MOUNT;</span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">SYSTEM</span> ENABLE RESTRICTED SESSION;</span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">SYSTEM</span> <span class="keyword">SET</span> JOB_QUEUE_PROCESSES<span class="operator">=</span><span class="number">0</span>;</span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">SYSTEM</span> <span class="keyword">SET</span> AQ_TM_PROCESSES<span class="operator">=</span><span class="number">0</span>;</span><br><span class="line"><span class="keyword">ALTER</span> DATABASE <span class="keyword">OPEN</span>;</span><br><span class="line"><span class="operator">/</span><span class="operator">/</span> 跳过超子集检测</span><br><span class="line"><span class="keyword">ALTER</span> DATABASE <span class="keyword">CHARACTER SET</span> INTERNAL_USE ZHS16GBK; </span><br><span class="line">SHUTDOWN IMMEDIATE;</span><br><span class="line">STARTUP;</span><br></pre></td></tr></table></figure><h2 id="集成平台Oracle配置"><a href="#集成平台Oracle配置" class="headerlink" title="集成平台Oracle配置"></a>集成平台Oracle配置</h2><p>Oracle的部署至此已经部署完毕,下述为个人项目需要配置的内容,有需要可以做参考:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">docker <span class="built_in">exec</span> -it oracle11g bash</span><br><span class="line">su - oracle</span><br><span class="line"><span class="comment">#passowrd oracle</span></span><br><span class="line">sqlplus / as sysdba</span><br></pre></td></tr></table></figure><h3 id="设置Process最大进程数"><a href="#设置Process最大进程数" class="headerlink" title="设置Process最大进程数"></a>设置Process最大进程数</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">alter</span> <span class="keyword">system</span> <span class="keyword">set</span> processes<span class="operator">=</span><span class="number">800</span> <span class="keyword">scope</span> <span class="operator">=</span> spfile;</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">system</span> <span class="keyword">set</span> processes<span class="operator">=</span><span class="number">1600</span> <span class="keyword">scope</span> <span class="operator">=</span> spfile;</span><br></pre></td></tr></table></figure><h3 id="修改账号密码修改期限"><a href="#修改账号密码修改期限" class="headerlink" title="修改账号密码修改期限"></a>修改账号密码修改期限</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Alter</span> PROFILE <span class="keyword">DEFAULT</span> LIMIT PASSWORD_LIFE_TIME UNLIMITED;</span><br></pre></td></tr></table></figure><h3 id="设置参数SQLNET-EXPIRE-TIME"><a href="#设置参数SQLNET-EXPIRE-TIME" class="headerlink" title="设置参数SQLNET.EXPIRE_TIME"></a>设置参数SQLNET.EXPIRE_TIME</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> <span class="variable">$ORACLE_HOME</span>/network/admin/</span><br><span class="line">vi sqlnet.ora</span><br><span class="line">SQLNET.EXPIRE_TIME=20</span><br><span class="line"><span class="comment"># 设置完后需重启服务</span></span><br></pre></td></tr></table></figure><p>设置游标数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">alter system <span class="built_in">set</span> open_cursors=2000;</span><br></pre></td></tr></table></figure><p>对Oracle Users表空增加dbf文件</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">alter</span> tablespace USERS  <span class="keyword">add</span> datafile <span class="string">&#x27;/home/oracle/app/oracle/oradata/helowin/USERS02.DBF&#x27;</span> size <span class="number">500</span>M autoextend <span class="keyword">on</span> next <span class="number">50</span>M maxsize unlimited ;</span><br><span class="line"><span class="keyword">alter</span> tablespace USERS  <span class="keyword">add</span> datafile <span class="string">&#x27;/home/oracle/app/oracle/oradata/helowin/USERS03.DBF&#x27;</span> size <span class="number">500</span>M autoextend <span class="keyword">on</span> next <span class="number">50</span>M maxsize unlimited ;</span><br><span class="line"><span class="keyword">alter</span> tablespace USERS  <span class="keyword">add</span> datafile <span class="string">&#x27;/home/oracle/app/oracle/oradata/helowin/USERS04.DBF&#x27;</span> size <span class="number">500</span>M autoextend <span class="keyword">on</span> next <span class="number">50</span>M maxsize unlimited ;</span><br><span class="line"><span class="keyword">alter</span> tablespace USERS  <span class="keyword">add</span> datafile <span class="string">&#x27;/home/oracle/app/oracle/oradata/helowin/USERS05.DBF&#x27;</span> size <span class="number">500</span>M autoextend <span class="keyword">on</span> next <span class="number">50</span>M maxsize unlimited ;</span><br><span class="line"><span class="keyword">alter</span> tablespace USERS  <span class="keyword">add</span> datafile <span class="string">&#x27;/home/oracle/app/oracle/oradata/helowin/USERS06.DBF&#x27;</span> size <span class="number">500</span>M autoextend <span class="keyword">on</span> next <span class="number">50</span>M maxsize unlimited ;</span><br><span class="line"><span class="keyword">alter</span> tablespace USERS  <span class="keyword">add</span> datafile <span class="string">&#x27;/home/oracle/app/oracle/oradata/helowin/USERS07.DBF&#x27;</span> size <span class="number">500</span>M autoextend <span class="keyword">on</span> next <span class="number">50</span>M maxsize unlimited ;</span><br><span class="line"><span class="keyword">alter</span> tablespace USERS  <span class="keyword">add</span> datafile <span class="string">&#x27;/home/oracle/app/oracle/oradata/helowin/USERS08.DBF&#x27;</span> size <span class="number">500</span>M autoextend <span class="keyword">on</span> next <span class="number">50</span>M maxsize unlimited ;</span><br><span class="line"><span class="keyword">alter</span> tablespace USERS  <span class="keyword">add</span> datafile <span class="string">&#x27;/home/oracle/app/oracle/oradata/helowin/USERS09.DBF&#x27;</span> size <span class="number">500</span>M autoextend <span class="keyword">on</span> next <span class="number">50</span>M maxsize unlimited ;</span><br><span class="line"><span class="keyword">alter</span> tablespace USERS  <span class="keyword">add</span> datafile <span class="string">&#x27;/home/oracle/app/oracle/oradata/helowin/USERS10.DBF&#x27;</span> size <span class="number">500</span>M autoextend <span class="keyword">on</span> next <span class="number">50</span>M maxsize unlimited ;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/03/18/65f7c4f3e4c47.png" alt="image-20240318095901584"></p><p>链接Docker中Oracle服务,执行如下命令,创建用户并授予权限</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">user</span> datamanager  identified <span class="keyword">by</span> data  <span class="keyword">default</span> tablespace USERS  temporary tablespace TEMP;</span><br><span class="line"><span class="keyword">create</span> <span class="keyword">user</span> datacenter  identified <span class="keyword">by</span> data  <span class="keyword">default</span> tablespace USERS  temporary tablespace TEMP;</span><br><span class="line"></span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">system</span> <span class="keyword">set</span> job_queue_processes<span class="operator">=</span><span class="number">20</span>; </span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">system</span> <span class="keyword">set</span> aq_tm_processes<span class="operator">=</span><span class="number">10</span>; </span><br><span class="line"><span class="comment">------------------datamanager给dataceneter授权</span></span><br><span class="line"><span class="keyword">grant</span> dba <span class="keyword">to</span> datamanager;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">connect</span> <span class="keyword">to</span> datamanager;</span><br><span class="line"><span class="keyword">grant</span> resource <span class="keyword">to</span> datamanager;</span><br><span class="line"><span class="keyword">grant</span> dba <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">connect</span> <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">grant</span> resource <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">system</span> <span class="keyword">set</span> job_queue_processes<span class="operator">=</span><span class="number">20</span>;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">create</span> <span class="keyword">view</span> <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">EXECUTE</span> <span class="keyword">ON</span> dbms_sql  <span class="keyword">To</span> datacenter;</span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">SELECT</span> <span class="keyword">ON</span> dba_jobs_running <span class="keyword">TO</span> datacenter;</span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">SELECT</span> <span class="keyword">ON</span> dba_jobs <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">execute</span> <span class="keyword">on</span> dbms_ddl <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">select</span> <span class="keyword">on</span> dba_users <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">EXECUTE</span> <span class="keyword">ON</span> dbms_sql  <span class="keyword">To</span> datacenter;</span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">SELECT</span> <span class="keyword">ON</span> dba_jobs_running <span class="keyword">TO</span> datacenter;</span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">SELECT</span> <span class="keyword">ON</span> dba_jobs <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">create</span> <span class="keyword">any</span> <span class="keyword">table</span> <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">select</span> <span class="keyword">any</span> <span class="keyword">table</span> <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">select</span> <span class="keyword">on</span> DBA_DATA_FILES  <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">alter</span> tablespace <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">CREATE</span> MATERIALIZED <span class="keyword">VIEW</span> <span class="keyword">TO</span> datacenter;</span><br><span class="line"><span class="comment">--------------导入数据后执行--------------</span></span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">select</span>, <span class="keyword">insert</span>, <span class="keyword">update</span>, <span class="keyword">delete</span>, <span class="keyword">references</span>, <span class="keyword">alter</span>, index <span class="keyword">on</span> datamanager.EAPARAM <span class="keyword">to</span> DATACENTER;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">select</span> <span class="keyword">on</span> DATACENTER.v_dept <span class="keyword">to</span> datamanager <span class="keyword">with</span> <span class="keyword">grant</span> option;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">select</span> <span class="keyword">on</span> DATACENTER.v_empl <span class="keyword">to</span> datamanager <span class="keyword">with</span> <span class="keyword">grant</span> option;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">select</span> <span class="keyword">on</span> datamanager.Eauser <span class="keyword">to</span> datacenter;</span><br><span class="line"><span class="keyword">grant</span> <span class="keyword">select</span> <span class="keyword">on</span> datamanager.v_eadept <span class="keyword">to</span> datacenter <span class="keyword">with</span> <span class="keyword">grant</span> option;</span><br></pre></td></tr></table></figure><h3 id="导入dmp数据文件"><a href="#导入dmp数据文件" class="headerlink" title="导入dmp数据文件"></a>导入dmp数据文件</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">imp userid=DATACENTER/data file=/serms/oracle/oradata/dmp/1.dmp full=y</span><br></pre></td></tr></table></figure><p> 导入数据后处理（空表不能导出处理，在datacenter下执行）：</p><p>select ‘alter table ‘||table_name||’ allocate extent;’ from user_tables where num_rows=0</p><p>把查询结果在PLSQL中执行，以空行数据进行导出处理。</p><p>T_TERM_DETAIL</p>]]></content>
    
    
    <summary type="html">Docker整活~部署Oracle11G!</summary>
    
    
    
    <category term="运维部署" scheme="https://blog.serms.top/categories/%E8%BF%90%E7%BB%B4%E9%83%A8%E7%BD%B2/"/>
    
    <category term="Docker" scheme="https://blog.serms.top/categories/%E8%BF%90%E7%BB%B4%E9%83%A8%E7%BD%B2/Docker/"/>
    
    
    <category term="Docker" scheme="https://blog.serms.top/tags/Docker/"/>
    
    <category term="Oracle11g" scheme="https://blog.serms.top/tags/Oracle11g/"/>
    
  </entry>
  
  <entry>
    <title>一招教你解锁Jetbrains全家桶</title>
    <link href="https://blog.serms.top/posts/8d3fe41f.html"/>
    <id>https://blog.serms.top/posts/8d3fe41f.html</id>
    <published>2024-03-11T07:45:16.000Z</published>
    <updated>2024-03-11T07:45:16.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="今天教大家永久解锁-Jetbrains所有产品"><a href="#今天教大家永久解锁-Jetbrains所有产品" class="headerlink" title="今天教大家永久解锁 Jetbrains所有产品"></a>今天教大家永久解锁 Jetbrains所有产品</h2><p>先上效果图</p><p><img src="https://bu.dusays.com/2024/03/11/65eeb14105f62.png" alt="image-20240311152208036" style="zoom:50%;" /></p><p><img src="https://bu.dusays.com/2024/03/11/65eeb175264b0.png" alt="image-20240311152330767" style="zoom:50%;" /></p><h2 id="正片开始"><a href="#正片开始" class="headerlink" title="正片开始"></a>正片开始</h2><h3 id="下载Jetbrains产品"><a href="#下载Jetbrains产品" class="headerlink" title="下载Jetbrains产品"></a>下载Jetbrains产品</h3><p>这里我那Idea来做演示</p><p>直接去<a href="https://www.jetbrains.com.cn/idea/download/?section=windows">官网</a>下载最新版,不要犹豫,直接点下载即可, 这里只需要注意你的系统版本即可,Mac的需要对应下载Mac版本的Idea</p><p><img src="https://bu.dusays.com/2024/03/11/65eeb20c43912.png" alt="image-20240311152602405"></p><h3 id="下载破解包"><a href="#下载破解包" class="headerlink" title="下载破解包"></a>下载破解包</h3><p><a href="https://gateway.pinata.cloud/ipfs/bafybeia4nrbuvpfd6k7lkorzgjw3t6totaoko7gmvq5pyuhl2eloxnfiri/">点我跳转</a>,下载 <strong>jetbra.zip</strong>文件:</p><p><img src="https://bu.dusays.com/2024/03/11/65eeb3115003d.png" alt="image-20240311153023578"></p><h3 id="破解安装"><a href="#破解安装" class="headerlink" title="破解安装"></a>破解安装</h3><p>先装idea，如果之前已有idea或别的Jetbrains产品<strong>请先卸载</strong>，卸载这里我推荐使用<a href="https://geekuninstaller.com/">Geek</a>软件，谁用谁知道，装完之后不要打开</p><p>接着<strong>解压jetbra.zip</strong>，进入到 <strong>scripts</strong>文件，安装顺执行<strong>install-all-users.vbs</strong>、<strong>install-current-user.vbs</strong>两个文件</p><p><img src="https://bu.dusays.com/2024/03/11/65eeb46bb7c97.png" alt="image-20240311153610275"></p><p>如果怕idea卸载有残留，可以先执行 <strong>uninstall</strong></p><p><img src="https://bu.dusays.com/2024/03/11/65eeb48303839.png" alt="image-20240311153633619"></p><p>在刚刚的网站上找到你所需要破解的Jetbrains产品，点击复制密钥</p><p><img src="https://bu.dusays.com/2024/03/11/65eeb4d4dd48d.png" alt="image-20240311153753964"></p><p>打开Idea，输入密钥即可完成注册</p><p><img src="https://bu.dusays.com/2024/03/11/65eeb52d45da9.png" alt="image-20240311153923669"></p><p>可以看到所注册时间到2026年，其实这个时间是“假的”，而你电脑上现在的idea已经实现理论永久了，时间无需在意</p><p><img src="https://bu.dusays.com/2024/03/11/65eeb58353696.png" alt="image-20240311154049590"></p><h2 id="拓展"><a href="#拓展" class="headerlink" title="拓展"></a>拓展</h2><p>其他Jetbrains产品也是类似着这样破解，温馨提示：<strong>install-all-users.vbs</strong>、<strong>install-current-user.vbs</strong> 文件只需要执行一次，如果电脑需要装多个Jetbrains的产品，那么直接再去网站上复制产品密钥进行激活即可，无需在执行文件</p><p>其次，网站上还有很多的插件密钥，大家可以去尝试激活，具体如下</p><p><img src="https://bu.dusays.com/2024/03/11/65eeb5b457f74.png" alt="image-20240311154138800"></p>]]></content>
    
    
    <summary type="html">教你解锁Jetbrains全家桶， 让你在代码的世界里畅游~</summary>
    
    
    
    <category term="工具与软件" scheme="https://blog.serms.top/categories/%E5%B7%A5%E5%85%B7%E4%B8%8E%E8%BD%AF%E4%BB%B6/"/>
    
    <category term="IDE配置" scheme="https://blog.serms.top/categories/%E5%B7%A5%E5%85%B7%E4%B8%8E%E8%BD%AF%E4%BB%B6/IDE%E9%85%8D%E7%BD%AE/"/>
    
    
    <category term="Jetbrains" scheme="https://blog.serms.top/tags/Jetbrains/"/>
    
  </entry>
  
  <entry>
    <title>ElasticSearch高级操作</title>
    <link href="https://blog.serms.top/posts/d7f046b9.html"/>
    <id>https://blog.serms.top/posts/d7f046b9.html</id>
    <published>2024-02-01T02:29:50.000Z</published>
    <updated>2025-05-28T15:08:01.896Z</updated>
    
    <content type="html"><![CDATA[<h2 id="数据准备"><a href="#数据准备" class="headerlink" title="数据准备"></a>数据准备</h2><ul><li>将ES中的索引全部删除</li><li>添加索引： <code>PUT hotel</code></li><li>添加几个文档</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line">PUT /hotel</span><br><span class="line">&#123;</span><br><span class="line">  &quot;mappings&quot;: &#123;</span><br><span class="line">    &quot;properties&quot;: &#123;</span><br><span class="line">      &quot;id&quot;:&#123;</span><br><span class="line">        &quot;type&quot;: &quot;keyword&quot;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;name&quot;:&#123;</span><br><span class="line">        &quot;type&quot;: &quot;keyword&quot;,</span><br><span class="line">        &quot;analyzer&quot;: &quot;ik_max_word&quot;,</span><br><span class="line">        &quot;copy_to&quot;: &quot;all&quot;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;address&quot;:&#123;</span><br><span class="line">        &quot;type&quot;: &quot;keyword&quot;,</span><br><span class="line">        &quot;index&quot;: false</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;price&quot;:&#123;</span><br><span class="line">        &quot;type&quot;: &quot;integer&quot;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;score&quot;:&#123;</span><br><span class="line">        &quot;type&quot;: &quot;integer&quot;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;brand&quot;:&#123;</span><br><span class="line">        &quot;type&quot;: &quot;keyword&quot;,</span><br><span class="line">        &quot;copy_to&quot;: &quot;all&quot;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;city&quot;:&#123;</span><br><span class="line">        &quot;type&quot;: &quot;keyword&quot;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;starName&quot;:&#123;</span><br><span class="line">        &quot;type&quot;: &quot;keyword&quot;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;business&quot;:&#123;</span><br><span class="line">        &quot;type&quot;: &quot;keyword&quot;,</span><br><span class="line">        &quot;copy_to&quot;: &quot;all&quot;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;location&quot;:&#123;</span><br><span class="line">        &quot;type&quot;: &quot;geo_point&quot;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;pic&quot;:&#123;</span><br><span class="line">        &quot;type&quot;: &quot;keyword&quot;,</span><br><span class="line">        &quot;index&quot;: false</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;all&quot;:&#123;</span><br><span class="line">        &quot;type&quot;: &quot;keyword&quot;,</span><br><span class="line">        &quot;analyzer&quot;: &quot;ik_max_word&quot;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">GET /hotel/_search</span><br></pre></td></tr></table></figure><p>可以注意到Hotel映射结构中：<code>name</code>、<code>brand</code>、<code>business</code>三个字段分别使用了copy_to属性，<strong>它允许你将一个或多个字段的原始值复制到另一个指定的字段</strong>，优势在于：<strong>合并内容、简化查询、分析一致性</strong></p><p><img src="https://bu.dusays.com/2024/02/01/65bb07a708de6.png" alt="Hotel查询结果"></p><h2 id="高级查询"><a href="#高级查询" class="headerlink" title="高级查询"></a>高级查询</h2><h3 id="全文检索查询"><a href="#全文检索查询" class="headerlink" title="全文检索查询"></a>全文检索查询</h3><ul><li>请求方式：GET 索引名/_search</li><li>请求体内容</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"># 全文检索查询</span><br><span class="line"># match对用户输入的内容进行分词然后去倒排索引库检索</span><br><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;match&quot;: &#123;</span><br><span class="line">      &quot;name&quot;: &quot;如家&quot;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb093aae5fb.png" alt="image-20240201110008926"></p><p>使用合并内容字段<code>all</code>进行查询，查询外滩如家将分词为 外滩、如家，即包含两个其中一个都命中结果</p><p><img src="https://bu.dusays.com/2024/02/01/65bb0c441684d.png" alt="image-20240201111306170"></p><h3 id="查询所有文档"><a href="#查询所有文档" class="headerlink" title="查询所有文档"></a>查询所有文档</h3><ul><li>请求方式：GET 索引名/_search</li><li>请求体内容</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;match_all&quot;: &#123;&#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb0cc87ced3.png" alt="image-20240201111516906"></p><h3 id="字段匹配查询-multi-match"><a href="#字段匹配查询-multi-match" class="headerlink" title="字段匹配查询:multi_match"></a>字段匹配查询:multi_match</h3><p><strong>查询的字段越多效率越慢</strong></p><ul><li><code>multi_match</code> 与 <code>match</code> 类似，不同的是它可以在多个字段中查询。</li><li>请求方式：GET 索引名/_search</li><li>请求体内容</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"># multi_match 多个字段搜索，参与查询字段越多，查询性能越差copy_to </span><br><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;multi_match&quot;: &#123;</span><br><span class="line">      &quot;query&quot;: &quot;外滩如家&quot;,</span><br><span class="line">      &quot;fields&quot;: [&quot;brand&quot;,&quot;name&quot;,&quot;business&quot;]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb0d027a87a.png" alt="image-20240201111616840"></p><h3 id="单关键字精确查询-term"><a href="#单关键字精确查询-term" class="headerlink" title="单关键字精确查询:term"></a>单关键字精确查询:term</h3><ul><li><code>term</code> 查询，精确的关键词匹配查询，不对查询条件进行分词，即只能单关键字精确查询。</li><li>请求方式：GET 索引名/_search</li><li>请求体内容</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">#精确查询 一般查找KeyWord、数值、日期、Boolean</span><br><span class="line"># term</span><br><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;term&quot;: &#123;</span><br><span class="line">      &quot;city&quot;: &#123;</span><br><span class="line">        &quot;value&quot;: &quot;深圳&quot;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb0d4368dd1.png" alt="image-20240201111721811"></p><h3 id="多关键字精确查询-terms"><a href="#多关键字精确查询-terms" class="headerlink" title="多关键字精确查询:terms"></a>多关键字精确查询:terms</h3><ul><li>terms 查询和 term 查询一样，但它允许你指定多值进行匹配。如果这个字段包含了指定值中的任何一个值，那么这个文档满足条件，类似于 mysql 的 in</li><li>请求方式：GET 索引名/_search</li><li>请求体内容</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;terms&quot;: &#123;</span><br><span class="line">      &quot;name&quot;: [</span><br><span class="line">        &quot;上海&quot;,</span><br><span class="line">        &quot;深圳&quot;</span><br><span class="line">      ]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb0da4deea2.png" alt="image-20240201111859144"></p><h3 id="指定字段查询-source"><a href="#指定字段查询-source" class="headerlink" title="指定字段查询:_source"></a>指定字段查询:_source</h3><ul><li>默认情况下，Elasticsearch 在搜索的结果中，会把文档中保存在 <code>_source</code> 的所有字段都返回。</li><li>如果我们只想获取其中的部分字段，我们可以添加 <code>_source</code> 的过滤</li><li>请求方式：GET 索引名/_search</li><li>请求体内容</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;_source&quot;: [</span><br><span class="line">    &quot;name&quot;,</span><br><span class="line">    &quot;price&quot;,</span><br><span class="line">    &quot;address&quot;</span><br><span class="line">  ],</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;terms&quot;: &#123;</span><br><span class="line">      &quot;name&quot;: [</span><br><span class="line">        &quot;上海&quot;,</span><br><span class="line">        &quot;深圳&quot;</span><br><span class="line">      ]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb0e66e179b.png" alt="image-20240201112213271"></p><h3 id="过滤字段-includes-amp-excludes"><a href="#过滤字段-includes-amp-excludes" class="headerlink" title="过滤字段:includes&amp;excludes"></a>过滤字段:includes&amp;excludes</h3><ul><li>includes：来指定想要显示的字段</li><li>excludes：来指定不想要显示的字段</li><li>请求方式：GET 索引名/_search</li><li>请求体内容</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;_source&quot;: &#123;</span><br><span class="line">    &quot;includes&quot;: [</span><br><span class="line">      &quot;name&quot;,</span><br><span class="line">      &quot;price&quot;</span><br><span class="line">    ]</span><br><span class="line">  &#125;,</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;terms&quot;: &#123;</span><br><span class="line">      &quot;name&quot;: [</span><br><span class="line">        &quot;上海&quot;</span><br><span class="line">      ]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb0f8a7a8fc.png" alt="查询名称包含上海的酒店，返回name、price信息"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;_source&quot;: &#123;</span><br><span class="line">    &quot;excludes&quot;: [</span><br><span class="line">      &quot;name&quot;,</span><br><span class="line">      &quot;price&quot;</span><br><span class="line">    ]</span><br><span class="line">  &#125;,</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;terms&quot;: &#123;</span><br><span class="line">      &quot;name&quot;: [</span><br><span class="line">        &quot;上海&quot;</span><br><span class="line">      ]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="C:\Users\SerMs\AppData\Roaming\Typora\typora-user-images\image-20240201112749170.png" alt="查询名称为上海的酒店信息，排除name和价格"></p><h3 id="组合查询-bool"><a href="#组合查询-bool" class="headerlink" title="组合查询:bool"></a>组合查询:bool</h3><ul><li><code>bool</code> 把各种其它查询通过 <code>must</code>（必须，类似 and）、<code>must_not</code>（必须不，类似 not）、<code>should</code>（应该 类似 or）的方式进行组合</li><li>请求方式：GET 索引名/_search</li><li>请求体内容</li></ul><p>查询名称必须包含如家，价格不能超过400，并且在我位置10km范围内的酒店，且结果按价格高到低排序</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;bool&quot;: &#123;</span><br><span class="line">      &quot;must&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">          &quot;match&quot;: &#123;</span><br><span class="line">            &quot;name&quot;: &quot;如家&quot;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      ],</span><br><span class="line">      &quot;must_not&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">          &quot;range&quot;: &#123;</span><br><span class="line">            &quot;price&quot;: &#123;</span><br><span class="line">              &quot;gt&quot;: 400</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      ],</span><br><span class="line">      &quot;filter&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">          &quot;geo_distance&quot;: &#123;</span><br><span class="line">            &quot;distance&quot;: &quot;10km&quot;,</span><br><span class="line">            &quot;location&quot;: &#123;</span><br><span class="line">              &quot;lat&quot;: 31.21,</span><br><span class="line">              &quot;lon&quot;: 121.5</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      ]</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  &quot;sort&quot;: [</span><br><span class="line">    &#123;</span><br><span class="line">      &quot;price&quot;: &quot;asc&quot;</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb1126d9877.png" alt="image-20240201113356960"></p><h3 id="范围查询-range"><a href="#范围查询-range" class="headerlink" title="范围查询:range"></a>范围查询:range</h3><ul><li><code>range</code> 查询找出那些落在指定区间内的数字或者时间。<code>range</code> 查询允许以下字符</li></ul><div class="table-container"><table><thead><tr><th style="text-align:center"><strong>操作符</strong></th><th style="text-align:center"><strong>说明</strong></th></tr></thead><tbody><tr><td style="text-align:center">gt</td><td style="text-align:center">&gt;</td></tr><tr><td style="text-align:center">gte</td><td style="text-align:center">&gt;=</td></tr><tr><td style="text-align:center">lt</td><td style="text-align:center">&lt;</td></tr><tr><td style="text-align:center">lte</td><td style="text-align:center">&lt;=</td></tr></tbody></table></div><p>查询价格1000-3000的酒店信息</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"># range 范围查询</span><br><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;range&quot;: &#123;</span><br><span class="line">      &quot;price&quot;: &#123;</span><br><span class="line">        &quot;gte&quot;: 1000,</span><br><span class="line">        &quot;lte&quot;: 3000</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb117e71741.png" alt="image-20240201113524705"></p><h3 id="模糊查询-fuzzy"><a href="#模糊查询-fuzzy" class="headerlink" title="模糊查询:fuzzy"></a>模糊查询:fuzzy</h3><p><code>fuzzy</code>：返回包含与搜索字词相似的字词的文档，更多<code>fuzzy</code>有关解释请查看<a href="https://www.elastic.co/guide/cn/elasticsearch/guide/current/fuzziness.html">官方文档  (opens new window)</a> -编辑距离是将一个术语转换为另一个术语所需的一个字符更改的次数。这些更改可以包括：</p><p>更改字符（box → fox）</p><p>删除字符（black → lack）</p><p>插入字符（sic → sick）</p><p>转置两个相邻字符（act → cat）</p><p>为了找到相似的术语，fuzzy 查询会在指定的编辑距离内创建一组搜索词的所有可能的变体或扩展。然后查询返回每个扩展的完全匹配。</p><p>通过 <code>fuzziness</code> 修改编辑距离。一般使用默认值 <code>AUTO</code>，根据术语的长度生成编辑距离。</p><p>请求方式：GET 索引名/_search</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;fuzzy&quot;: &#123;</span><br><span class="line">      &quot;all&quot;: &#123;</span><br><span class="line">        &quot;value&quot;: &quot;乡&quot;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb11f7498bb.png" alt="image-20240201113725623"></p><h3 id="精确查询地理位置-geo-distance"><a href="#精确查询地理位置-geo-distance" class="headerlink" title="精确查询地理位置:geo_distance"></a>精确查询地理位置:geo_distance</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"># 精确查询地理位置</span><br><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;geo_distance&quot;:&#123;</span><br><span class="line">      &quot;distance&quot;:&quot;2km&quot;,</span><br><span class="line">      &quot;location&quot;:&quot;31.21,121.5&quot;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb126d2c71f.png" alt="image-20240201113923477"></p><h3 id="复合查询-function-score"><a href="#复合查询-function-score" class="headerlink" title="复合查询:function_score"></a>复合查询:function_score</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"># 复合查询</span><br><span class="line"># function score 查询</span><br><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;function_score&quot;: &#123;</span><br><span class="line">      &quot;query&quot;: &#123;</span><br><span class="line">        &quot;match&quot;: &#123;</span><br><span class="line">          &quot;all&quot;: &quot;外滩&quot;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;functions&quot;: [</span><br><span class="line">        &#123;</span><br><span class="line">          &quot;filter&quot;: &#123;</span><br><span class="line">            &quot;term&quot;: &#123;</span><br><span class="line">              &quot;brand&quot;: &quot;如家&quot;</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;,</span><br><span class="line">          &quot;weight&quot;: 10</span><br><span class="line">        &#125;</span><br><span class="line">      ],</span><br><span class="line">      &quot;boost_mode&quot;: &quot;sum&quot;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb12db273fa.png" alt="image-20240201114113309"></p><h3 id="前缀查询-prefix"><a href="#前缀查询-prefix" class="headerlink" title="前缀查询:prefix"></a>前缀查询:prefix</h3><ul><li>请求方式：GET 索引名/_search</li><li>请求体内容</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">GET /hotel/_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;prefix&quot;: &#123;</span><br><span class="line">      &quot;name&quot;: &#123;</span><br><span class="line">        &quot;value&quot;: &quot;上&quot;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/02/01/65bb1341943c0.png" alt="image-20240201114255956"></p>]]></content>
    
    
    <summary type="html">ElasticSreach Api操作</summary>
    
    
    
    <category term="运维部署" scheme="https://blog.serms.top/categories/%E8%BF%90%E7%BB%B4%E9%83%A8%E7%BD%B2/"/>
    
    <category term="学习笔记" scheme="https://blog.serms.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    <category term="微服务" scheme="https://blog.serms.top/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
    <category term="搜索服务" scheme="https://blog.serms.top/categories/%E6%90%9C%E7%B4%A2%E6%9C%8D%E5%8A%A1/"/>
    
    
    <category term="ElasticSearch" scheme="https://blog.serms.top/tags/ElasticSearch/"/>
    
  </entry>
  
  <entry>
    <title>ElasticSearch基本操作</title>
    <link href="https://blog.serms.top/posts/9d6997a.html"/>
    <id>https://blog.serms.top/posts/9d6997a.html</id>
    <published>2024-01-24T02:57:09.000Z</published>
    <updated>2025-05-28T15:08:01.896Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>注意本教程基于Es7.x版本Api编写</p></blockquote><h2 id="启动Es-amp-Kibana"><a href="#启动Es-amp-Kibana" class="headerlink" title="启动Es&amp;Kibana"></a>启动Es&amp;Kibana</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">docker start es</span><br><span class="line">docker logs -f es</span><br></pre></td></tr></table></figure><p>当日志中输出一推的<code>successfully</code>时，基本就启动成功了</p><p><img src="https://bu.dusays.com/2024/01/24/65b0b5404c334.png" alt="image-20240124145911318"></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">docker start kibana</span><br><span class="line">docker logs -f kibana</span><br></pre></td></tr></table></figure><p>出现5601的链接地址时表明启动成功</p><p><img src="https://bu.dusays.com/2024/01/24/65b0b5b735bef.png" alt="image-20240124150110181"></p><h2 id="数据格式"><a href="#数据格式" class="headerlink" title="数据格式"></a>数据格式</h2><p>ES在使用时，会涉及到五个核心概念：索引（Index）、映射（Mapping）、域    （Field）、文档（Document）、倒排索引。以一张MySQL中数据表为例。</p><p><code>Elasticsearch</code> 是面向文档型数据库，一条数据在这里就是一个文档。我们可以把 <code>Elasticsearch</code> 里存储文档数据和关系型数据库 <code>MySQL</code> 存储数据的概念进行一个类比。 <code>ES</code> 里的 <code>Index</code> 可以看做一个库，而 <code>Types</code> 相当于表，<code>Documents</code> 则相当于表的行。</p><p><img src="https://bu.dusays.com/2024/01/24/65b0b40c34442.png" alt="ElasticSearch">这里 Types 的概念已经被逐渐弱化，Elasticsearch 6.X 中，一个 index 下已经只能包含一个type，Elasticsearch 7.X 中, Type 的概念已经被删除了。</p><h2 id="索引操作"><a href="#索引操作" class="headerlink" title="索引操作"></a>索引操作</h2><p>索引相当于关系型数据库中的一张表，一个index包含若干document，通过Index代表一类类似的或者相同的document。</p><ul><li>打开 <code>kibana</code> 的 <code>Dev Tools</code> 菜单</li></ul><p><img src="https://bu.dusays.com/2024/01/24/65b0b62a8b157.png" alt="image-20240124150305755"></p><h3 id="创建索引-PUT"><a href="#创建索引-PUT" class="headerlink" title="创建索引-PUT"></a>创建索引-PUT</h3><ul><li>对比关系型数据库，创建索引就等同于创建数据库</li><li>PUT 索引名</li></ul><p><img src="https://bu.dusays.com/2024/01/24/65b0b7c6d3107.png" alt="image-20240124150958095"></p><ul><li>发送请求后，服务器返回了这样的响应</li></ul><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;acknowledged&quot;</span> <span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;shards_acknowledged&quot;</span> <span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;index&quot;</span> <span class="punctuation">:</span> <span class="string">&quot;test&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><ul><li>如果重复添加索引呢？重复添加则会返回索引已经存在的错误信息</li></ul><p><img src="https://bu.dusays.com/2024/01/24/65b0b8067ae25.png" alt="image-20240124151101833"></p><h3 id="查看全部索引-GET"><a href="#查看全部索引-GET" class="headerlink" title="查看全部索引-GET"></a>查看全部索引-GET</h3><blockquote><p>GET _cat/indices?v</p><p>GET _all</p></blockquote><p><img src="https://bu.dusays.com/2024/01/24/65b0baf936afe.png" alt="image-20240124152336430"></p><p><code>_cat</code>：表示查看的意思；</p><p><code>indices</code>： 表示索引</p><p><code>health</code>：当前服务器健康状态：<code>green</code>(集群完整)、<code>yellow</code>(单点正常、集群不完整)、<code>red</code>(单点不正常)</p><p><code>status</code>：索引打开、关闭状态</p><p><code>index</code>：索引名</p><p><code>uuid</code>：索引统一编号</p><p><code>pri</code>：主分片数量</p><p><code>rep</code>：副本数量</p><p><code>docs.count</code>：可用文档数量</p><p><code>docs.deleted</code>：文档删除状态（逻辑删除）</p><p><code>store.size</code>：主分片和副分片整体占空间大小</p><p><code>pri.store.size</code>：主分片占空间大小</p><h3 id="查看单个索引-GET"><a href="#查看单个索引-GET" class="headerlink" title="查看单个索引-GET"></a>查看单个索引-GET</h3><p><img src="https://bu.dusays.com/2024/01/24/65b0bb50114f0.png" alt="image-20240124152503380"></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="string">&quot;user&quot;</span>【索引名】<span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="string">&quot;aliases&quot;</span>【别名】<span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;mappings&quot;</span>【映射】<span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;settings&quot;</span>【设置】<span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="string">&quot;index&quot;</span>【设置 - 索引】<span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">        <span class="string">&quot;routing&quot;</span>【设置 - 索引路由】<span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;allocation&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">            <span class="attr">&quot;include&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">              <span class="attr">&quot;_tier_preference&quot;</span><span class="punctuation">:</span> <span class="string">&quot;data_content&quot;</span></span><br><span class="line">            <span class="punctuation">&#125;</span></span><br><span class="line">          <span class="punctuation">&#125;</span></span><br><span class="line">        <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="string">&quot;number_of_shards&quot;</span>【设置 - 索引 - 主分片数量】<span class="punctuation">:</span> <span class="string">&quot;1&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="string">&quot;provided_name&quot;</span>【设置 - 索引 - 名称】<span class="punctuation">:</span> <span class="string">&quot;user&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="string">&quot;creation_date&quot;</span>【设置 - 索引 - 创建时间】<span class="punctuation">:</span> <span class="string">&quot;1659678930693&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="string">&quot;number_of_replicas&quot;</span>【设置 - 索引 - 副分片数量】<span class="punctuation">:</span> <span class="string">&quot;1&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="string">&quot;uuid&quot;</span>【设置 - 索引 - 唯一标识】<span class="punctuation">:</span> <span class="string">&quot;P0pIpPyTSa-zS7kJCeE7Ng&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="string">&quot;version&quot;</span>【设置 - 索引版本号】<span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">          <span class="attr">&quot;created&quot;</span><span class="punctuation">:</span> <span class="string">&quot;8030399&quot;</span></span><br><span class="line">        <span class="punctuation">&#125;</span></span><br><span class="line">      <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="查询多个索引"><a href="#查询多个索引" class="headerlink" title="查询多个索引"></a>查询多个索引</h3><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># 查询多个索引信息 GET 索引名称<span class="punctuation">,</span>索引名称</span><br><span class="line">PUT person1</span><br><span class="line">GET person<span class="punctuation">,</span>person1</span><br></pre></td></tr></table></figure><h3 id="删除索引-DELETE"><a href="#删除索引-DELETE" class="headerlink" title="删除索引-DELETE"></a>删除索引-DELETE</h3><p><img src="https://bu.dusays.com/2024/01/24/65b0bb7f6b5a2.png" alt="image-20240124152550891"></p><h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><p><strong>字符串</strong></p><ul><li>text：会进行分词，如华为手机，会分成：华为，手机。 <strong>被分出来的每一个词，称为term（词条）</strong></li><li>keyword：不会进行分词，如华为手机，只有一个词条，即华为手机。</li></ul><p><strong>数值</strong></p><ul><li>long：带符号64位整数</li><li>integer：带符号32位整数</li><li>short：带符号16位整数</li><li>byte：带符号8位整数</li><li>double：双精度64位浮点数</li><li>float：单精度32位浮点数</li><li>half_float：半精度16位浮点数</li></ul><p><strong>布尔：</strong></p><ul><li>boolean</li></ul><p><strong>二进制：</strong></p><ul><li>binary</li></ul><p><strong>日期：</strong></p><ul><li>date</li></ul><p><strong>范围类型：</strong></p><ul><li>integer_range</li><li>float_range</li><li>long_range</li><li>double_range</li><li>date_range</li></ul><p><strong>数组</strong></p><p><strong>对象</strong></p><h2 id="文档操作"><a href="#文档操作" class="headerlink" title="文档操作"></a>文档操作</h2><h3 id="创建文档-POST"><a href="#创建文档-POST" class="headerlink" title="创建文档-POST"></a>创建文档-POST</h3><p>先创建好一个索引，接下来我们来创建文档，并添加数据。这里的文档可以类比为关系型数据库中的表数据，添加的数据格式为<code>JSON</code>格式</p><p><img src="https://bu.dusays.com/2024/01/24/65b0bbe6286d6.png" alt="image-20240124152733502"></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="string">&quot;_index&quot;</span>【索引】<span class="punctuation">:</span> <span class="string">&quot;user&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="string">&quot;_id&quot;</span>【唯一标识，支持自定义】<span class="punctuation">:</span> <span class="string">&quot;C1yqbIIBJVfoW_YKu2D5&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="string">&quot;_version&quot;</span>【版本】<span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">  <span class="string">&quot;result&quot;</span>【结果】<span class="punctuation">:</span> <span class="string">&quot;created&quot;</span><span class="punctuation">,</span>#这里的 create 表示创建成功</span><br><span class="line">  <span class="string">&quot;_shards&quot;</span>【分片】<span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="string">&quot;total&quot;</span>【分片 - 总数】<span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;successful&quot;</span>【分片 - 成功】<span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">    <span class="string">&quot;failed&quot;</span>【分片 - 失败】<span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_seq_no&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_primary_term&quot;</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><ul><li>自定义唯一标识：POST 索引名/_doc/(自定义ID)</li></ul><p><img src="https://bu.dusays.com/2024/01/24/65b0bc5704bea.png" alt="image-20240124152926213"></p><h3 id="查看文档-GET"><a href="#查看文档-GET" class="headerlink" title="查看文档-GET"></a>查看文档-GET</h3><ul><li>查看文档时，需要指明文档的唯一性标识，类似于 MySQL 中数据的主键查询</li><li>查看指定文档：GET 索引名/_doc/(唯一标识)</li><li>查看所有数据：GET 索引名/_search</li></ul><p><img src="https://bu.dusays.com/2024/01/24/65b0bc8fa4103.png" alt="查询单个数据"></p><p><img src="https://bu.dusays.com/2024/01/24/65b0bcb4e9c34.png" alt="查询多个数据"></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="string">&quot;_index&quot;</span>【索引】<span class="punctuation">:</span> <span class="string">&quot;user&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_id&quot;</span><span class="punctuation">:</span> <span class="string">&quot;C1yqbIIBJVfoW_YKu2D5&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_version&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_seq_no&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_primary_term&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">  <span class="string">&quot;found&quot;</span>【查询结果】<span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="string">&quot;_source&quot;</span>【文档源信息】<span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;userName&quot;</span><span class="punctuation">:</span> <span class="string">&quot;A佳技术&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;age&quot;</span><span class="punctuation">:</span> <span class="string">&quot;28&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="修改文档-POST"><a href="#修改文档-POST" class="headerlink" title="修改文档-POST"></a>修改文档-POST</h3><p><strong>全局修改</strong></p><p>POST 索引名/_doc/(唯一标识)</p><p><img src="https://bu.dusays.com/2024/01/24/65b0bd5650e1a.png" alt="image-20240124153341586"></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;_index&quot;</span><span class="punctuation">:</span> <span class="string">&quot;user&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_id&quot;</span><span class="punctuation">:</span> <span class="string">&quot;C1yqbIIBJVfoW_YKu2D5&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_version&quot;</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line">  <span class="string">&quot;result&quot;</span>【更新操作】<span class="punctuation">:</span> <span class="string">&quot;updated&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_shards&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;total&quot;</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;successful&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;failed&quot;</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_seq_no&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_primary_term&quot;</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><strong>局部修改</strong></p><p><img src="https://bu.dusays.com/2024/01/24/65b0bdf285e7f.png" alt="image-20240124153617694"></p><h3 id="删除文档-DELETE"><a href="#删除文档-DELETE" class="headerlink" title="删除文档-DELETE"></a>删除文档-DELETE</h3><ul><li><p>删除一个文档不会立即从磁盘上移除，它只是被标记成已删除（逻辑删除）</p></li><li><p>DELETE 索引名/_doc/(唯一标识)</p><p><img src="https://bu.dusays.com/2024/01/24/65b0be3ee6e3e.png" alt="image-20240124153734162"></p></li></ul><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;_index&quot;</span><span class="punctuation">:</span> <span class="string">&quot;user&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_id&quot;</span><span class="punctuation">:</span> <span class="string">&quot;C1yqbIIBJVfoW_YKu2D5&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="string">&quot;_version&quot;</span>【版本<span class="punctuation">:</span>对数据的操作，都会更新版本】<span class="punctuation">:</span> <span class="number">5</span><span class="punctuation">,</span></span><br><span class="line">  <span class="string">&quot;result&quot;</span>【结果】<span class="punctuation">:</span> <span class="string">&quot;deleted&quot;</span><span class="punctuation">,</span># deleted 表示数据被标记为删除</span><br><span class="line">  <span class="attr">&quot;_shards&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;total&quot;</span><span class="punctuation">:</span> <span class="number">2</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;successful&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;failed&quot;</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_seq_no&quot;</span><span class="punctuation">:</span> <span class="number">4</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;_primary_term&quot;</span><span class="punctuation">:</span> <span class="number">1</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/01/24/65b0be713fb3d.png" alt="image-20240124153824479"></p><h3 id="条件删除文档-POST"><a href="#条件删除文档-POST" class="headerlink" title="条件删除文档-POST"></a>条件删除文档-POST</h3><p>首先分别增加多条数据，这里我添加了3条数据</p><p><img src="https://bu.dusays.com/2024/01/24/65b0bedbb8a7f.png" alt="ElasticSearch"></p><p>POST 索引名/_delete_by_query + 请求体条件</p><p><img src="https://bu.dusays.com/2024/01/24/65b0beee35d30.png" alt="ElasticSearch"></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="string">&quot;took&quot;</span>【耗时】<span class="punctuation">:</span> <span class="number">17</span><span class="punctuation">,</span></span><br><span class="line">  <span class="string">&quot;timed_out&quot;</span>【是否超时】<span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="string">&quot;total&quot;</span>【总数】<span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">  <span class="string">&quot;deleted&quot;</span>【删除数量】<span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;batches&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;version_conflicts&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;noops&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;retries&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;bulk&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;search&quot;</span><span class="punctuation">:</span> <span class="number">0</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;throttled_millis&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;requests_per_second&quot;</span><span class="punctuation">:</span> <span class="number">-1</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;throttled_until_millis&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;failures&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>查看文档数据情况，年龄20已经删除</p><p><img src="https://bu.dusays.com/2024/01/24/65b0bf04cc5be.png" alt="ElasticSearch"></p><h2 id="映射基本操作"><a href="#映射基本操作" class="headerlink" title="映射基本操作"></a>映射基本操作</h2><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><p>有了索引库，等于有了数据库中的 <code>database</code>。</p><p>接下来就需要建索引库(<code>index</code>)中的映射了，类似于数据库(<code>database</code>)中的表结构(<code>table</code>)。创建数据库表需要设置字段名称，类型，长度，约束等；索引库也一样，需要知道这个类型下有哪些字段，每个字段有哪些约束信息，这就叫做映射(<code>mapping</code>)。</p><h3 id="先创建索引-PUT"><a href="#先创建索引-PUT" class="headerlink" title="先创建索引-PUT"></a>先创建索引-PUT</h3><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">PUT hotel</span><br></pre></td></tr></table></figure><h3 id="创建映射-PUT"><a href="#创建映射-PUT" class="headerlink" title="创建映射-PUT"></a>创建映射-PUT</h3><p><strong>为已经存在的索引库添加映射</strong></p><p>PUT 索引名/_mapping + 请求体内容</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">PUT student/_mapping</span><br><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;properties&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;text&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;sex&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;keyword&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span><span class="literal"><span class="keyword">true</span></span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;age&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span><span class="string">&quot;keyword&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span><span class="literal"><span class="keyword">false</span></span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/01/24/65b0c0d3e86da.png" alt="image-20240124154835123"></p><p><strong>创建索引并添加映射</strong></p><blockquote><p>PUT /索引名称<br>{</p><p>​    “mappings”：{</p><p>​        “properties”: {}    </p><p>​    }</p><p>}</p></blockquote><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line">PUT /hotel</span><br><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;mappings&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;properties&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;id&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;keyword&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;keyword&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;analyzer&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ik_max_word&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;copy_to&quot;</span><span class="punctuation">:</span> <span class="string">&quot;all&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;address&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;keyword&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;price&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;integer&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;score&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;integer&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;brand&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;keyword&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;copy_to&quot;</span><span class="punctuation">:</span> <span class="string">&quot;all&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;city&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;keyword&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;starName&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;keyword&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;business&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;keyword&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;copy_to&quot;</span><span class="punctuation">:</span> <span class="string">&quot;all&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;location&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;geo_point&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;pic&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;keyword&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;index&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">false</span></span></span><br><span class="line">      <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;all&quot;</span><span class="punctuation">:</span><span class="punctuation">&#123;</span></span><br><span class="line">        <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;keyword&quot;</span><span class="punctuation">,</span></span><br><span class="line">        <span class="attr">&quot;analyzer&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ik_max_word&quot;</span></span><br><span class="line">      <span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><strong>映射数据说明：</strong></p><ul><li>字段名：任意填写</li><li><code>type</code>：类型，<code>Elasticsearch</code> 中支持的数据类型非常丰富，说几个关键的：<ul><li><code>String类型</code>，又分两种<ul><li><code>text</code>：可分词，支持模糊查询，支持准确查询，不支持聚合查询</li><li><code>keyword</code>：不可分词，数据会作为完整字段进行匹配，支持模糊查询，支持准确查询，支持聚合查询。</li></ul></li><li><code>Numerical</code>：数值类型，分两类<ul><li>基本数据类型：<code>long</code>、<code>integer</code>、<code>short</code>、<code>byte</code>、<code>double</code>、<code>float</code>、<code>half_float</code></li><li>浮点数的高精度类型：<code>scaled_float</code></li></ul></li><li><code>Date</code>：日期类型</li><li><code>Array</code>：数组类型</li><li><code>Object</code>：对象</li></ul></li><li><code>index</code>：是否索引，默认为 true，也就是说你不进行任何配置，所有字段都会被索引。<ul><li>true：字段会被索引，则可以用来进行搜索</li><li>false：字段不会被索引，不能用来搜索</li></ul></li><li><code>store</code>：是否将数据进行独立存储，默认为 false。</li></ul><p>原始的文本会存储在<code>_source</code> 里面，默认情况下其他提取出来的字段都不是独立存储的，是从<code>_source</code>里面提取出来的。当然你也可以独立的存储某个字段，只要设置 <code>&quot;store&quot;: true</code> 即可，获取独立存储的字段要比从 <code>_source</code> 中解析快得多，但是也会占用更多的空间，所以要根据实际业务需求来设置。</p><ul><li><code>analyzer</code>：分词器，这里的 <code>ik_max_word</code> 即使用 <code>ik 分词器</code></li></ul><h3 id="查看映射-GET"><a href="#查看映射-GET" class="headerlink" title="查看映射-GET"></a>查看映射-GET</h3><blockquote><p>GET /索引/_mapping</p></blockquote><p><img src="https://bu.dusays.com/2024/01/24/65b0c1a896684.png" alt="image-20240124155207724"></p><h3 id="添加字段"><a href="#添加字段" class="headerlink" title="添加字段"></a><strong>添加字段</strong></h3><p>对于映射，只能进行字段添加，不能对字段进行修改或删除，如有需要，则重新创建映射。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">PUT user/_mapping</span><br><span class="line">&#123;</span><br><span class="line">  &quot;properties&quot;:&#123;</span><br><span class="line">    &quot;name&quot;: &#123;</span><br><span class="line">        &quot;type&quot;: &quot;text&quot;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;age&quot;: &#123;</span><br><span class="line">        &quot;type&quot;: &quot;integer&quot;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;address&quot;:&#123;</span><br><span class="line">        &quot;type&quot;:&quot;text&quot;</span><br><span class="line">      &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">ElasticSreach基本操作</summary>
    
    
    
    <category term="运维部署" scheme="https://blog.serms.top/categories/%E8%BF%90%E7%BB%B4%E9%83%A8%E7%BD%B2/"/>
    
    <category term="学习笔记" scheme="https://blog.serms.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    <category term="微服务" scheme="https://blog.serms.top/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
    <category term="搜索服务" scheme="https://blog.serms.top/categories/%E6%90%9C%E7%B4%A2%E6%9C%8D%E5%8A%A1/"/>
    
    
    <category term="ElasticSearch" scheme="https://blog.serms.top/tags/ElasticSearch/"/>
    
  </entry>
  
  <entry>
    <title>ElasticSearch概念与安装</title>
    <link href="https://blog.serms.top/posts/5e525b05.html"/>
    <id>https://blog.serms.top/posts/5e525b05.html</id>
    <published>2024-01-24T02:52:38.000Z</published>
    <updated>2025-05-28T15:08:01.896Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Elasticsearch-是什么"><a href="#Elasticsearch-是什么" class="headerlink" title="Elasticsearch 是什么"></a>Elasticsearch 是什么</h2><p><img src="https://bu.dusays.com/2024/01/24/65b06050ecae3.png" alt="ElasticSearch"></p><p>The Elastic Stack, 包括 Elasticsearch、Kibana、Beats 和 Logstash（也称为 ELK Stack）。能够安全可靠地获取任何来源、任何格式的数据，然后实时地对数据进行搜索、分析和可视化。Elaticsearch，简称为 ES，ES 是一个<strong>开源的高扩展的分布式全文搜索引擎</strong>，是整个 Elastic Stack 技术栈的核心。它可以近乎实时的存储、检索数据；本身扩展性很好，可以扩展到上百台服务器，处理 PB 级别的数据。</p><p>PS：elasticsearch是一款非常强大的开源搜索引擎，具备非常多强大功能，可以帮助我们从海量数据中快速找到需要的内容</p><h2 id="全文搜索引擎"><a href="#全文搜索引擎" class="headerlink" title="全文搜索引擎"></a>全文搜索引擎</h2><p>Google，百度类的网站搜索，它们都是根据网页中的关键字生成索引，我们在搜索的时候输入关键字，它们会将该关键字即索引匹配到的所有网页返回；还有常见的项目中应用日志的搜索等等。对于这些非结构化的数据文本，关系型数据库搜索不是能很好的支持。</p><p>一般传统数据库，全文检索都实现的很鸡肋，因为一般也没人用数据库存文本字段。进行全文检索需要扫描整个表，如果数据量大的话即使对 SQL 的语法优化，也收效甚微。建立了索引，但是维护起来也很麻烦，对于 insert 和 update 操作都会重新构建索引。</p><p>基于以上原因可以分析得出，在一些生产环境中，使用常规的搜索方式，性能是非常差的：</p><ul><li>搜索的数据对象是大量的非结构化的文本数据。</li><li>文件记录量达到数十万或数百万个甚至更多。</li><li>支持大量基于交互式文本的查询。</li><li>需求非常灵活的全文搜索查询。</li><li>对高度相关的搜索结果的有特殊需求，但是没有可用的关系数据库可以满足。</li><li>对不同记录类型、非文本数据操作或安全事务处理的需求相对较少的情况。为了解决结构化数据搜索和非结构化数据搜索性能问题，我们就需要专业，健壮，强大的全文搜索引擎。</li></ul><p>这里说到的全文搜索引擎指的是目前广泛应用的主流搜索引擎。它的工作原理是计算机索引程序通过扫描文章中的每一个词，对每一个词建立一个索引，指明该词在文章中出现的次数和位置，当用户查询时，检索程序就根据事先建立的索引进行查找，并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。</p><h2 id="elasticsearch和lucene"><a href="#elasticsearch和lucene" class="headerlink" title="elasticsearch和lucene"></a>elasticsearch和lucene</h2><p>elasticsearch底层是基于<strong>lucene</strong>来实现的。</p><p><strong>Lucene</strong>是一个Java语言的搜索引擎类库，是Apache公司的顶级项目，由DougCutting于1999年研发。官网地址：<a href="https://lucene.apache.org/">https://lucene.apache.org/</a> 。</p><p><img src="https://bu.dusays.com/2024/01/24/65b0b36ed6794.png" alt="image-20210720194547780"></p><p><strong>elasticsearch</strong>的发展历史：</p><ul><li>2004年Shay Banon基于Lucene开发了Compass</li><li>2010年Shay Banon 重写了Compass，取名为Elasticsearch。</li></ul><p><img src="https://bu.dusays.com/2024/01/24/65b0b36ebb775.png" alt="image-20210720195001221"></p><h2 id="Elasticsearch-And-Solr"><a href="#Elasticsearch-And-Solr" class="headerlink" title="Elasticsearch And Solr"></a>Elasticsearch And Solr</h2><p>Lucene 是 Apache 软件基金会 Jakarta 项目组的一个子项目，提供了一个简单却强大的应用程式接口，能够做全文索引和搜寻。在 Java 开发环境里 Lucene 是一个成熟的免费开源工具。就其本身而言，Lucene 是当前以及最近几年最受欢迎的免费 Java 信息检索程序库。但 Lucene 只是一个提供全文搜索功能类库的核心工具包，而真正使用它还需要一个完善的服务框架搭建起来进行应用。</p><p>目前市面上流行的搜索引擎软件，主流的就两款：Elasticsearch 和 Solr,这两款都是基于 Lucene 搭建的，可以独立部署启动的搜索引擎服务软件。由于内核相同，所以两者除了服务器安装、部署、管理、集群以外，对于数据的操作 修改、添加、保存、查询等等都十分类似。</p><p>在使用过程中，一般都会将 Elasticsearch 和 Solr 这两个软件对比，然后进行选型。这两个搜索引擎都是流行的，先进的的开源搜索引擎。它们都是围绕核心底层搜索库 - Lucene构建的 - 但它们又是不同的。像所有东西一样，每个都有其优点和缺点：</p><p><img src="https://bu.dusays.com/2024/01/24/65b060473246d.png" alt="ElasticSearch"></p><h2 id="Elasticsearch-Or-Solr"><a href="#Elasticsearch-Or-Solr" class="headerlink" title="Elasticsearch Or Solr"></a>Elasticsearch Or Solr</h2><p>Elasticsearch 和 Solr 都是开源搜索引擎，那么我们在使用时该如何选择呢？</p><ul><li>Google 搜索趋势结果表明，与 Solr 相比，Elasticsearch 具有很大的吸引力，但这并不意味着 Apache Solr 已经死亡。虽然有些人可能不这么认为，但 Solr 仍然是最受欢迎的搜索引擎之一，拥有强大的社区和开源支持。</li><li>与 Solr 相比，Elasticsearch 易于安装且非常轻巧。此外，你可以在几分钟内安装并运行Elasticsearch。但是，如果 Elasticsearch 管理不当，这种易于部署和使用可能会成为一个问题。基于 JSON 的配置很简单，但如果要为文件中的每个配置指定注释，那么它不适合您。总的来说，如果你的应用使用的是 JSON，那么 Elasticsearch 是一个更好的选择。否则，请使用 Solr，因为它的 schema.xml 和 solrconfig.xml 都有很好的文档记录。</li><li>Solr 拥有更大，更成熟的用户，开发者和贡献者社区。ES 虽拥有的规模较小但活跃的 用户社区以及不断增长的贡献者社区。</li><li>Solr 贡献者和提交者来自许多不同的组织，而 Elasticsearch 提交者来自单个公司。</li><li>Solr 更成熟，但 ES 增长迅速，更稳定。</li><li>Solr 是一个非常有据可查的产品，具有清晰的示例和 API 用例场景。 Elasticsearch 的文档组织良好，但它缺乏好的示例和清晰的配置说明。</li></ul><h2 id="ELK技术栈"><a href="#ELK技术栈" class="headerlink" title="ELK技术栈"></a>ELK技术栈</h2><p>elasticsearch结合kibana、Logstash、Beats，也就是elastic stack（ELK）。被广泛应用在日志数据分析、实时监控等领域：</p><p><img src="https://bu.dusays.com/2024/01/24/65b0b34cce36d.png" alt="image-20210720194008781"></p><p>而elasticsearch是elastic stack的核心，负责存储、搜索、分析数据。</p><p><img src="https://bu.dusays.com/2024/01/24/65b0b34c7426c.png" alt="image-20210720194230265"></p><h2 id="为什么不是其他搜索技术？"><a href="#为什么不是其他搜索技术？" class="headerlink" title="为什么不是其他搜索技术？"></a>为什么不是其他搜索技术？</h2><p>目前比较知名的搜索引擎技术排名：</p><p><img src="https://bu.dusays.com/2024/01/24/65b0b38b4a3de.png" alt="image-20210720195142535"></p><p>虽然在早期，Apache Solr是最主要的搜索引擎技术，但随着发展elasticsearch已经渐渐超越了Solr，独占鳌头：</p><p><img src="https://bu.dusays.com/2024/01/24/65b0b38b2a4cb.png" alt="image-20210720195306484"></p><h2 id="Windows上安装ES"><a href="#Windows上安装ES" class="headerlink" title="Windows上安装ES"></a>Windows上安装ES</h2><h3 id="下载软件"><a href="#下载软件" class="headerlink" title="下载软件"></a>下载软件</h3><p><a href="https://www.elastic.co/cn/">ElasticSearch官方地址</a></p><p><a href="https://www.elastic.co/cn/downloads/past-releases/elasticsearch-8-12-0">ElasticSearch目前最新版本下载地址</a></p><p><img src="https://bu.dusays.com/2024/01/24/65b06342ad0a9.png" alt="image-20240124090921134"></p><h3 id="安装软件"><a href="#安装软件" class="headerlink" title="安装软件"></a>安装软件</h3><p>Windows 版的 Elasticsearch 的安装很简单，解压即安装完毕，这里我下载的目前最新版8.12.0，解压后的 Elasticsearch 的目录结构如下</p><p><img src="https://bu.dusays.com/2024/01/24/65b06467d57a8.png" alt="image-20240124091414474"></p><div class="table-container"><table><thead><tr><th style="text-align:center">目录</th><th style="text-align:center">含义</th></tr></thead><tbody><tr><td style="text-align:center">bin</td><td style="text-align:center">可执行脚本</td></tr><tr><td style="text-align:center">config</td><td style="text-align:center">配置目录</td></tr><tr><td style="text-align:center">jdk</td><td style="text-align:center">内置JDK目录</td></tr><tr><td style="text-align:center">lib</td><td style="text-align:center">类库</td></tr><tr><td style="text-align:center">logs</td><td style="text-align:center">日志目录</td></tr><tr><td style="text-align:center">modules</td><td style="text-align:center">模块目录</td></tr><tr><td style="text-align:center">plugins</td><td style="text-align:center">插件目录</td></tr></tbody></table></div><p><strong>解压后，我们先来修改一些配置：</strong></p><ul><li><p>配置启动内存，修改配置文件 <code>config/jvm.options</code></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">################################################################</span><br><span class="line">## IMPORTANT: JVM heap size</span><br><span class="line">################################################################</span><br><span class="line">## 配置启动内存，默认是4G</span><br><span class="line">-Xms1g</span><br><span class="line">-Xmx1g</span><br><span class="line">################################################################</span><br></pre></td></tr></table></figure></li><li><p>暂时禁止掉再次启动时更新地图的一些数据库操作，修改 <code>config/elasticsearch.yml</code> 配置</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># 添加配置：暂时禁止掉再次启动时更新地图的一些数据库操作</span><br><span class="line">ingest.geoip.downloader.enabled: false</span><br></pre></td></tr></table></figure></li><li><p>进入 <code>bin</code> 文件目录，点击 <code>elasticsearch.bat</code> 文件启动服务 启动完成后，打开浏览器，输入：<a href="http://localhost:9200/">http://localhost:9200/</a></p><p><img src="https://bu.dusays.com/2024/01/24/65b066b941d44.png" alt="image-20240124092407817"></p><p><img src="https://bu.dusays.com/2024/01/24/65b066df67522.png" alt="image-20240124092446062"></p></li></ul><h2 id="Windows上安装Kibana"><a href="#Windows上安装Kibana" class="headerlink" title="Windows上安装Kibana"></a>Windows上安装Kibana</h2><p>Kibana 是一个免费且开放的用户界面，能够让你对 Elasticsearch 数据进行可视化，并 让你在 Elastic Stack 中进行导航。你可以进行各种操作，从跟踪查询负载，到理解请求如 何流经你的整个应用，都能轻松完成。</p><p><strong>下载时尽量下载与 ElasicSearch 一致的版本。</strong></p><p>前往<a href="https://www.elastic.co/cn/downloads/kibana">官网下载 Windows 版本</a></p><p>下载后进行解压，目录如图：</p><p><img src="https://bu.dusays.com/2024/01/24/65b067393be3f.png" alt="image-20240124092615693"></p><h3 id="启动kibana"><a href="#启动kibana" class="headerlink" title="启动kibana"></a>启动kibana</h3><ul><li>启动 <code>Kibana</code> 之前要启动 <code>Elasticsearch</code></li><li>进入 <code>bin</code> 目录，双击 <code>kibana.bat</code> 启动服务</li><li>启动成功后，复制控制台的地址进行访问<a href="http://localhost:5601">http://localhost:5601</a></li></ul><p><img src="https://bu.dusays.com/2024/01/24/65b067f63ce80.png" alt="image-20240124092924553"></p><ul><li>8.x版本访问时，需要先生成一个<code>token</code>，我们在 <code>ES</code> 的 <code>bin</code> 目录下执行以下命令生成 <code>token</code></li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">es\bin&gt;elasticsearch-create-enrollment-token.bat --scope kibana</span><br><span class="line">warning: ignoring JAVA_HOME=G:\java\java8\jdk; using bundled JDK</span><br><span class="line">eyJ2ZXIiOiI4LjMuMyIsImFkciI6WyIxNzIuMjAuMTAuNDo5MjAwIl0sImZnciI6ImUzOWZlYzRmM2MyMDg2ZTEyOGIxOTU3OTAyOTVlZTc5OTlkZmI1MDlmMzM5NjNiZWE2Y2Q1ZTRjMWVhOTdlNmMiLCJrZXkiOiJfbHdsYklJQkpWZm9XX1lLNlY5NTo2czNnMFJZYVRfYXNCX19BZVZ6MDVnIn0=</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li><p>将 <code>token</code> 复制到 <code>kibana</code>登录页中 </p><p><img src="https://bu.dusays.com/2024/01/24/65b068bf07cbc.png" alt="ElasticSearch"></p></li></ul><p><img src="es/es13.png" alt="ElasticSearch"></p><h3 id="修改界面语言"><a href="#修改界面语言" class="headerlink" title="修改界面语言"></a>修改界面语言</h3><p>访问界面是英文，可修改成中文，进入根目录下的 <code>config</code> 目录，打开 <code>kibana.yml</code> 文件，在最末尾处加入以下配置，添加完成后，重新启动即可</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 默认端口</span></span><br><span class="line"><span class="comment"># server.port: 5601</span></span><br><span class="line"><span class="comment"># ES 服务器的地址，经过上一步的操作，这些会自动生成，所以不需要改动</span></span><br><span class="line"><span class="comment"># elasticsearch.hosts: [&quot;https://127.0.0.1:9200&quot;]</span></span><br><span class="line"><span class="comment"># 索引名：可改可不改</span></span><br><span class="line"><span class="comment"># kibana.index: &quot;.kibana&quot;</span></span><br><span class="line"><span class="comment"># 支持中文</span></span><br><span class="line"><span class="attr">i18n.locale:</span> <span class="string">&quot;zh-CN&quot;</span></span><br></pre></td></tr></table></figure><h2 id="Docker安装Es"><a href="#Docker安装Es" class="headerlink" title="Docker安装Es"></a>Docker安装Es</h2><p><strong>需提前装好Centos系统及Docker</strong></p><ul><li>因为我们还需要部署kibana容器，因此需要让es和kibana容器互联。这里先创建一个网络：</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker network create es-net</span><br></pre></td></tr></table></figure><ul><li>调整机器的vm.max_map_count参数至262144,调整前可以将原文件备份</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">// 备份文件</span><br><span class="line">cp /etc/sysctl.conf /etc/sysctl.conf.bak</span><br><span class="line">// 编辑文件</span><br><span class="line">vim /etc/sysctl.conf</span><br><span class="line">// 在文件的尾部添加以下参数</span><br><span class="line">vm.max_map_count=262144</span><br><span class="line">// 使修改后的文件生效</span><br><span class="line">sysctl -p</span><br></pre></td></tr></table></figure><ul><li>修改虚拟内存区域大小，否则会因为过小而无法启动:</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sysctl -w vm.max_map_count=262144</span><br></pre></td></tr></table></figure><ul><li>使用docker命令启动</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">docker run -d \</span><br><span class="line">  --name elasticsearch \</span><br><span class="line">  -e &quot;cluster.name=elasticsearch&quot; \</span><br><span class="line">  -e &quot;discovery.type=single-node&quot; \</span><br><span class="line">  -e &quot;ES_JAVA_OPTS=-Xms512m -Xmx1024m&quot; \</span><br><span class="line">  -v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \</span><br><span class="line">  -v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \</span><br><span class="line">  --network es-net\ \</span><br><span class="line">  --privileged \</span><br><span class="line">  -p 9200:9200 \</span><br><span class="line">  -p 9300:9300 \</span><br><span class="line">  elasticsearch:7.17.3</span><br></pre></td></tr></table></figure><ul><li>启动时会发现/usr/share/elasticsearch/data目录没有访问权限，只需要修改/mydata/elasticsearch/data目录的权限，再重新启动。</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chmod 777 /mydata/elasticsearch/data/</span><br></pre></td></tr></table></figure><h3 id="部署Kiban"><a href="#部署Kiban" class="headerlink" title="部署Kiban"></a>部署Kiban</h3><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">docker <span class="keyword">run</span><span class="language-bash"> -d \</span></span><br><span class="line"><span class="language-bash">  --name kibana \</span></span><br><span class="line"><span class="language-bash">  --<span class="built_in">link</span> elasticsearch:es \</span></span><br><span class="line"><span class="language-bash">  -e ELASTICSEARCH_HOSTS=http://es:9200 \</span></span><br><span class="line"><span class="language-bash">  --network cscz-net \</span></span><br><span class="line"><span class="language-bash">  -p 5601:5601 \</span></span><br><span class="line"><span class="language-bash">  kibana:7.17.3</span></span><br></pre></td></tr></table></figure><p>查看日志</p><figure class="highlight docker"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker logs -f kibana</span><br></pre></td></tr></table></figure><ul><li>开启防火墙：</li></ul><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">firewall-cmd --zone=public --add-port=5601/tcp --permanent firewall-cmd --reload</span><br></pre></td></tr></table></figure><h2 id="安装分词器"><a href="#安装分词器" class="headerlink" title="安装分词器"></a>安装分词器</h2><h3 id="前述"><a href="#前述" class="headerlink" title="前述"></a>前述</h3><p>为了能够更好地对中文进行搜索和查询，就需要在Elasticsearch中集成好的分词器插件， 而 <code>IK</code> 分词器就是用于对中文提供支持得插件。</p><h2 id="集成IK分词器"><a href="#集成IK分词器" class="headerlink" title="集成IK分词器"></a>集成IK分词器</h2><h3 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h3><ul><li><a href="https://github.com/medcl/elasticsearch-analysis-ik/releases">IK分词器下载  (opens new window)</a></li><li>注意版本需要对应，目前<code>IK</code>分词器还没有<code>8.3.3</code>版本，所以就先重新下一个<code>8.2.3</code>版本的<code>ES</code></li></ul><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><ul><li>将下载的IK压缩包直接解压到 <code>elasticsearch-8.2.3</code> 的 <code>plugins</code>目录下，重启<code>ES</code></li></ul><h3 id="使用-IK-分词器"><a href="#使用-IK-分词器" class="headerlink" title="使用 IK 分词器"></a>使用 IK 分词器</h3><p>IK 分词器提供了两个分词算法：</p><p>▶️ <code>ik_smart</code>: 会做最粗粒度的拆分，适合 Phrase 查询</p><p>▶️ <code>Ik_max_word</code>:会将文本做最细粒度的拆分,会穷尽各种可能的组合,适合 Term Query</p><ul><li>为索引指定默认IK分词器</li></ul><p><strong>这样我们在索引中就不用创建每一个字段，可以通过动态字段映射，将String类型的字段映射为text类型，同时分词器指定为ik_max_word</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">PUT ik_index</span><br><span class="line">&#123;</span><br><span class="line">  &quot;settings&quot;: &#123;</span><br><span class="line">    &quot;analysis&quot;: &#123;</span><br><span class="line">      &quot;analyzer&quot;: &#123;</span><br><span class="line">        &quot;default&quot;: &#123;</span><br><span class="line">          &quot;type&quot;: &quot;ik_max_word&quot;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="自定义分词效果"><a href="#自定义分词效果" class="headerlink" title="自定义分词效果"></a>自定义分词效果</h3><ul><li>我们在使用 <code>IK</code> 分词器时会发现其实有时候分词的效果也并不是我们所期待的,有时一些特 殊得术语会被拆开，但实际上我们希望不要拆开。</li><li><code>IK</code> 插件给我们提供了自定义分词字典，我们可以添加自己想要保留得字了。</li></ul><p><strong>自定义分词</strong></p><ul><li>首先在 <code>elasticsearch-analysis-ik-8.2.3</code>的<code>config</code>目录下新建一个<code>my_self.dic</code>文件，输入自己希望不想被拆开的术语，比如<code>分词词语</code></li><li>接下来我们修改配置文件：<code>config/IKAnalyzer.cfg.xml</code></li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">properties</span> <span class="keyword">SYSTEM</span> <span class="string">&quot;http://java.sun.com/dtd/properties.dtd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">comment</span>&gt;</span>IK Analyzer 扩展配置<span class="tag">&lt;/<span class="name">comment</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!--用户可以在这里配置自己的扩展字典多个文件用&#x27;;&#x27;分隔开 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">entry</span> <span class="attr">key</span>=<span class="string">&quot;ext_dict&quot;</span>&gt;</span>my_self.dic<span class="tag">&lt;/<span class="name">entry</span>&gt;</span></span><br><span class="line"> <span class="comment">&lt;!--用户可以在这里配置自己的扩展停止词字典--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">entry</span> <span class="attr">key</span>=<span class="string">&quot;ext_stopwords&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">entry</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!--用户可以在这里配置远程扩展字典 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- &lt;entry key=&quot;remote_ext_dict&quot;&gt;words_location&lt;/entry&gt; --&gt;</span></span><br><span class="line"><span class="comment">&lt;!--用户可以在这里配置远程扩展停止词字典--&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- &lt;entry key=&quot;remote_ext_stopwords&quot;&gt;words_location&lt;/entry&gt; --&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="热更新-IK-分词使用方法"><a href="#热更新-IK-分词使用方法" class="headerlink" title="热更新 IK 分词使用方法"></a>热更新 IK 分词使用方法</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--用户可以在这里配置远程扩展字典 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">entry</span> <span class="attr">key</span>=<span class="string">&quot;remote_ext_dict&quot;</span>&gt;</span>location<span class="tag">&lt;/<span class="name">entry</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!--用户可以在这里配置远程扩展停止词字典--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">entry</span> <span class="attr">key</span>=<span class="string">&quot;remote_ext_stopwords&quot;</span>&gt;</span>location<span class="tag">&lt;/<span class="name">entry</span>&gt;</span></span><br></pre></td></tr></table></figure><p>其中 <code>location</code> 是指一个 url，比如 <code>http://yoursite.com/getCustomDict</code>，该请求只需满足以下两点即可完成分词热更新。</p><ul><li>1、该 http 请求需要返回两个头部(header)，一个是 <code>Last-Modified</code>，一个是 <code>ETag</code>，这两者都是字符串类型，只要有一个发生变化，该插件就会去抓取新的分词进而更新词库。</li><li>2、该 http 请求返回的内容格式是一行一个分词，换行符用 <code>\n</code>即可。</li></ul><p>满足上面两点要求就可以实现热更新分词了，不需要重启 ES 实例。</p><p>可以将需自动更新的热词放在一个 <code>UTF-8</code> 编码的 <code>.txt</code> 文件里，放在 <code>nginx</code> 或其他简易 <code>http server</code> 下，当 .<code>txt</code> 文件修改时，<code>http server</code> 会在客户端请求该文件时自动返回相应的 <code>Last-Modified</code> 和 <code>ETag</code>。可以另外做一个工具来从业务系统提取相关词汇，并更新这个 <code>.txt</code> 文件。</p>]]></content>
    
    
    <summary type="html">ElasticSreach概念与安装</summary>
    
    
    
    <category term="运维部署" scheme="https://blog.serms.top/categories/%E8%BF%90%E7%BB%B4%E9%83%A8%E7%BD%B2/"/>
    
    <category term="微服务" scheme="https://blog.serms.top/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
    <category term="后端开发" scheme="https://blog.serms.top/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/"/>
    
    <category term="搜索服务" scheme="https://blog.serms.top/categories/%E6%90%9C%E7%B4%A2%E6%9C%8D%E5%8A%A1/"/>
    
    
    <category term="ElasticSearch" scheme="https://blog.serms.top/tags/ElasticSearch/"/>
    
  </entry>
  
  <entry>
    <title>Nacos学习</title>
    <link href="https://blog.serms.top/posts/8b3d7021.html"/>
    <id>https://blog.serms.top/posts/8b3d7021.html</id>
    <published>2024-01-23T09:31:15.000Z</published>
    <updated>2025-05-28T15:08:01.897Z</updated>
    
    <content type="html"><![CDATA[<h2 id="微服务架构"><a href="#微服务架构" class="headerlink" title="微服务架构"></a>微服务架构</h2><p>为了适应企业的业务发展，软件架构现如今也做了升级和优化，将一个独立的系统拆分成若干个小的服务，每个服务都运行在不同的进程中，服务和服务之间采用RESTFul, RPC等协议传输数据，每个服务所拥有的功能具有独立性强的特点，这样的设计实现了服务之间的低耦合、服务内部的高内聚。</p><h2 id="服务发现流程"><a href="#服务发现流程" class="headerlink" title="服务发现流程"></a>服务发现流程</h2><p>在很多架构中，当微服务很多时，可能会出现A服务调用B服务，B服务调用C服务，C服务调用A服务，从而服务循环；微服务之间的调用需要有各个服务的地址，当某个服务出现升级或宕机的情况，服务实例网址可能会发生变化，因此就需要有服务发现。</p><p>服务发现流程引入了一个<strong>服务发现中心</strong>。</p><ol><li>在每个服务启动时会向服务中心上报自己的网络地址，这样在服务发现中心内部会形成一个<strong>服务注册表</strong>，<strong>服务注册表</strong>是服务发现的核心部分，是包含所有服务实例的网络地址的数据库；</li><li><strong>服务发现客户端</strong>会定期的从<strong>服务中心</strong>同步<strong>服务注册表</strong>，并缓存在客户端；</li><li>当需要对某个服务进行请求时，服务实例通过注册表，定位目标服务网络地址，若目标服务存在多个网络地址，则使用负载均衡算法从多个服务实例中选择一个，然后发出请求。</li></ol><h2 id="Nacos"><a href="#Nacos" class="headerlink" title="Nacos"></a>Nacos</h2><p>目前市面上用得比较多的服务中心有：Nacos、Eureka、Consul 和 Zookeeper</p><p><img src="https://bu.dusays.com/2024/01/19/65a9dc38b5a0c.png" alt="image-20240119101927536"></p><p>Nacos作为服务中心，相比较其他软件具有更多的功能特性，且从长远角度来看，Nacos会在以后的版本中会支持SpringCloud和Kubernetes的组合，填补两者之间的鸿沟。另外，Nacos计划实现Service Mesh，也是未来微服务发展的趋势。</p><h3 id="特性"><a href="#特性" class="headerlink" title="特性"></a>特性</h3><ul><li><p>服务发现与服务健康检查</p><p>Nacos使服务更容易注册，通过DNS和http端口更容易发现其他服务，Nacos提供服务的实时健康检查，以防止向不健康的主机或服务实例发送请求。</p></li><li><p>动态配置管理</p><p>动态配置服务允许您在所有环境中以集中和动态的方式管理所有服务的配置。消除了在更新配置时重新部署应用程序，这使配置的更改更加高效灵活。</p></li><li><p>动态DNS服务</p><p>Nacos提供基于DNS协议的服务发现能力，旨在支持异构语言的服务发现，支持将注册在Nacos上的服务以域名的方式暴露端口，让三方应用方便的查阅和发现。</p></li><li><p>服务和元数据管理</p><p>Nacos能让你以微服务平台建设的视角管理数据中心的所有服务及元数据，包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康检查、服务的流量管理、路由及安全策略。</p></li></ul><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>Nacos依赖Java环境来运行需提前配置JAVA_HOME&gt;1.8+，下载地址：<a href="https://github.com/alibaba/nacos/releases">https://github.com/alibaba/nacos/releases</a></p><p><strong>启动服务器：</strong></p><p>Nacos的默认端口是8848，需要保证8848端口没有被其他进程占用；</p><p>进入安装程序的bin目录，启动命令（standalone代表单机模式运行，cluster代表集群模式）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">startup.cmd -m standalone</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/01/19/65a9e28e8dc26.png" alt="image-20240119104636690"></p><p>默认用户名/密码：nacos/nacos</p><h3 id="简单的使用"><a href="#简单的使用" class="headerlink" title="简单的使用"></a>简单的使用</h3><p>在SpringBoot工程中的pom.xml中引入客户端依赖</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.alibaba.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-alibaba-nacos-discovery<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>配置application.yml文件</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">nacos:</span></span><br><span class="line">    <span class="comment"># nacos服务地址</span></span><br><span class="line">      <span class="attr">server-addr:</span> <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span><span class="string">:8848</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">xxx-service</span></span><br></pre></td></tr></table></figure><p>请求示范</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Order <span class="title function_">queryOrderById</span><span class="params">(Long orderId)</span> &#123;</span><br><span class="line">    <span class="type">RestTemplate</span> <span class="variable">restTemplate</span> <span class="operator">=</span>  <span class="keyword">new</span> <span class="title class_">RestTemplate</span>();</span><br><span class="line">    <span class="type">Order</span> <span class="variable">order</span> <span class="operator">=</span> orderMapper.findById(orderId);</span><br><span class="line">    <span class="comment">// 此处user-service为另外一个服务的服务名称</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> <span class="string">&quot;http://user-service/user/&quot;</span> + order.getUserId();</span><br><span class="line">    <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> restTemplate.getForObject(url, User.class);</span><br><span class="line">    order.setUser(user);</span><br><span class="line">    <span class="keyword">return</span> order;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="领域模型"><a href="#领域模型" class="headerlink" title="领域模型"></a>领域模型</h3><p><img src="https://bu.dusays.com/2024/01/19/65a9e4511d4b3.png" alt="image-20240119105407174"></p><p><strong>NameSpace</strong>：命名空间，默认的NameSpace是<strong>public</strong>。比如，我们开发，测试环境共用一个nacos，必定我们的接口地址也是不同，而且你在开发过程中，也是不建议随意配置测试环境的，这时我们就应该用namespace来隔离我们的空间。</p><p><img src="https://bu.dusays.com/2024/01/19/65a9e38f252cd.png" alt="image-20240119105053186"></p><p><strong>group</strong>：分组。也是用来隔离的，打个比方啊，在系统中有用户服务，订单服务，仓储服务和物流服务四个服务，<strong>订单服务</strong>中有一个接口叫<strong>getData</strong>，<strong>仓储服务</strong>中也有一个接口叫<strong>getData</strong>，我们的用户服务只想调用到我们的订单服务的getData，不想调用到仓储服务的getData，这时我们可以用group分组来隔离<br><img src="https://bu.dusays.com/2024/01/19/65a9e3f821b99.png" alt="image-20240119105238408"></p><p><strong>cluster</strong>：集群。打个比方，我们现在有两组集群，一组是北京的订单服务集群，北京的商品服务集群，还有一组是南京的订单服务集群，南京的商品服务集群。 我们希望北京的订单集群，优先去调用北京的商品系统，南京的优先调用南京的集群服务。并不希望我们跨地区远程调用（如果组内实在没有服务了，也可以调用，但是优先考虑同一集群的）</p><p><img src="https://bu.dusays.com/2024/01/19/65a9e49049382.png" alt="image-20240119105510440"></p><h4 id="Nacos服务分级存储模型"><a href="#Nacos服务分级存储模型" class="headerlink" title="Nacos服务分级存储模型"></a>Nacos服务分级存储模型</h4><ul><li>一级是服务，例如userservice</li><li>二级是集群，例如杭州或上海</li><li>三级是实例，例如杭州机房的某台部署了userservice的服务器</li></ul><h3 id="集群配置"><a href="#集群配置" class="headerlink" title="集群配置"></a>集群配置</h3><p>spring.cloud.nacos.discovery.cluster-name属性即可</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">nacos:</span></span><br><span class="line">      <span class="attr">server-addr:</span> <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span><span class="string">:8848</span></span><br><span class="line"><span class="comment">#        服务发现配置</span></span><br><span class="line">      <span class="attr">discovery:</span></span><br><span class="line"><span class="comment">#        集群配置</span></span><br><span class="line">        <span class="attr">cluster-name:</span> <span class="string">$&#123;cname&#125;</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">user-service</span></span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2024/01/19/65a9e5032d199.png" alt="image-20240119105705265"></p><h3 id="集群负载均衡"><a href="#集群负载均衡" class="headerlink" title="集群负载均衡"></a><strong>集群负载均衡</strong></h3><p>在客户端的yml中配置</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">user-service:</span> <span class="comment"># 集群名称</span></span><br><span class="line">  <span class="attr">ribbon:</span></span><br><span class="line">    <span class="attr">NFLoadBalancerRuleClassName:</span> <span class="string">com.alibaba.cloud.nacos.ribbon.NacosRule</span></span><br></pre></td></tr></table></figure><h4 id="更改默认策略规则"><a href="#更改默认策略规则" class="headerlink" title="更改默认策略规则"></a>更改默认策略规则</h4><p>配置文件的方式：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">user-service:</span></span><br><span class="line">  <span class="attr">ribbon:</span></span><br><span class="line">    <span class="attr">NFLoadBalancerRuleClassName:</span> <span class="string">com.netflix.loadbalancer.RandomRule</span></span><br></pre></td></tr></table></figure><p>通过代码的方式</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="meta">@LoadBalanced</span></span><br><span class="line"><span class="keyword">public</span> RestTemplate <span class="title function_">restTemplate</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RestTemplate</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> IRule <span class="title function_">randomRule</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RandomRule</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="统一配置管理"><a href="#统一配置管理" class="headerlink" title="统一配置管理"></a>统一配置管理</h3><p>抽取共同的配置交给Nacos管理</p><p><img src="https://bu.dusays.com/2024/01/19/65a9e7f779841.png" alt="image-20240119110941442"></p><ul><li>Data ID：实际上就是配置文件名，可以以 【服务名称-环境.文件后缀名】格式命名</li></ul><p><img src="https://bu.dusays.com/2024/01/19/65a9e7ffd1deb.png" alt="image-20240119110949869"></p><p>实际上并不是所有的配置都适合放到配置中心，建议将一些关键参数，需要运行时随时可调整的参数放到 nacos 配置中心，一些比较固定的配置则可以放在项目本身的配置文件当中。</p><h4 id="服务拉取配置"><a href="#服务拉取配置" class="headerlink" title="服务拉取配置"></a>服务拉取配置</h4><p>一个服务如果以 nacos 作为配置中心，则会先拉取 nacos 中管理的配置，然后与本地的配置文件比如 application.yml 中的配置合并，最后作为项目的完整配置，启动项目。</p><ul><li><p>没有nacos管理配置文件的情况下的项目启动流程：</p><p><img src="https://bu.dusays.com/2024/01/19/65a9e861c12c7.png" alt="image-20240119111128049"></p></li><li><p>使用nacos管理配置时项目的启动流程</p><p><img src="https://bu.dusays.com/2024/01/19/65a9e881415d8.png" alt="image-20240119111159338"></p></li></ul><p>项目启动的时候需要提前知道 nacos 的环境信息，而 application.yml 在读取 nacos 配置后才会读取，所以无法把 nacos 的相关信息配置在 application.yml 中，此时我们可以使用 bootstrap.yml文件。bootstrap.ym是一个引导文件，优先级高于application.yml，它会在application.yml之前被读取</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.alibaba.cloud<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-cloud-starter-a libaba-nacos-config<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>创建bootstart.yml文件，将有关Nacos的配置项以及服务名称都需写进这个文件</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">userservice</span> <span class="comment"># 服务名称</span></span><br><span class="line">  <span class="attr">profiles:</span></span><br><span class="line">    <span class="attr">active:</span> <span class="string">dev</span> <span class="comment">#开发环境，这里是dev </span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">nacos:</span></span><br><span class="line">      <span class="attr">server-addr:</span> <span class="string">localhost:8848</span> <span class="comment"># Nacos地址</span></span><br><span class="line">      <span class="attr">config:</span></span><br><span class="line">        <span class="attr">file-extension:</span> <span class="string">yaml</span> <span class="comment"># 文件后缀名</span></span><br></pre></td></tr></table></figure><p>这里会根据 spring.cloud.nacos.server-addr 来得到 nacos 配置中心的地址，再根据 【spring.application.name】-【spring.profiles.active】.【spring.cloud.nacos.config.file-extension】 作为配置的 DATA ID来从配置中心读取配置，也就是读取了 userservice-dev.yaml 配置。</p><h4 id="多环境配置共享"><a href="#多环境配置共享" class="headerlink" title="多环境配置共享"></a>多环境配置共享</h4><p>实际上微服务启动时，会去 nacos 配置中心读取多个配置文件，也就是不仅仅会读取指定的 profile 的配置文件，还会读取多个环境共享的配置文件。</p><p>当 nacos 和项目的本地配置文件中存在相同配置时，优先级如下： 服务名-profile.yaml &gt; 服务名.yaml &gt; 本地配置，如下</p><p><img src="https://bu.dusays.com/2024/01/19/65a9e9eb9228c.png" alt="image-20240119111801836"></p><h4 id="配置热更新"><a href="#配置热更新" class="headerlink" title="配置热更新"></a>配置热更新</h4><p>默认情况下，修改了 nacos 配置中心的配置，微服务的配置不会随之更新的，需要重启微服务才能读到新配置。</p><p>通过 @Value 和通过 @ConfigurationProperties 来读取配置时，实现热更新的方式不同。</p><h5 id="RefreshScope"><a href="#RefreshScope" class="headerlink" title="RefreshScope"></a>RefreshScope</h5><p>如果是通过 @Value 来读取配置的，此时只需在使用 @Value 注入的变量所在类上添加注解 @RefreshScope 即可，如下：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RefreshScope</span></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserController</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> UserService userService;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;pattern.dateformat&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String dateformat;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@GetMapping(&quot;test01&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">now</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> dateformat;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="ConfigurationProperties"><a href="#ConfigurationProperties" class="headerlink" title="ConfigurationProperties"></a>ConfigurationProperties</h5><h2 id="nacos的坑"><a href="#nacos的坑" class="headerlink" title="nacos的坑"></a>nacos的坑</h2><h3 id="安装环节"><a href="#安装环节" class="headerlink" title="安装环节"></a>安装环节</h3><ol><li>解压安装时最好不要有中文路径及空格，否则会报错</li></ol><h3 id="启动环节"><a href="#启动环节" class="headerlink" title="启动环节"></a>启动环节</h3><ol><li><h4 id="java-io-IOException-java-lang-IllegalArgumentException-db-num-is-null"><a href="#java-io-IOException-java-lang-IllegalArgumentException-db-num-is-null" class="headerlink" title="java.io.IOException: java.lang.IllegalArgumentException: db.num is null"></a>java.io.IOException: java.lang.IllegalArgumentException: db.num is null</h4><p>这个是在集群模式下启动的时候缺少数据库文件，要么单机启动<code>startup.cmd -m standalone</code>,要么加载数据库</p><p>或者编辑startup.cmd文件把其中的set MODE = “cluster” 修改为以下内容（默认集群启动，修改为单机启动）</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">rem <span class="built_in">set</span> MODE=<span class="string">&quot;cluster&quot;</span></span><br><span class="line"><span class="built_in">set</span> MODE=<span class="string">&quot;standalone&quot;</span></span><br></pre></td></tr></table></figure></li><li><p>Java环境需配置JAVA_HOME且大于1.8</p></li></ol>]]></content>
    
    
    <summary type="html">Nacos学习总结</summary>
    
    
    
    <category term="运维部署" scheme="https://blog.serms.top/categories/%E8%BF%90%E7%BB%B4%E9%83%A8%E7%BD%B2/"/>
    
    <category term="学习笔记" scheme="https://blog.serms.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    <category term="微服务" scheme="https://blog.serms.top/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
    <category term="服务治理" scheme="https://blog.serms.top/categories/%E6%9C%8D%E5%8A%A1%E6%B2%BB%E7%90%86/"/>
    
    
    <category term="Nacos" scheme="https://blog.serms.top/tags/Nacos/"/>
    
  </entry>
  
  <entry>
    <title>Git的奇技淫巧</title>
    <link href="https://blog.serms.top/posts/cdc384c3.html"/>
    <id>https://blog.serms.top/posts/cdc384c3.html</id>
    <published>2023-11-20T07:31:56.000Z</published>
    <updated>2023-11-20T07:31:56.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="取消跟踪文件"><a href="#取消跟踪文件" class="headerlink" title="取消跟踪文件"></a>取消跟踪文件</h2><p>顾名思义也可以理解为：从 Git 中删除文件夹但本地保留</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">rm</span> -r --cached FileName  <span class="comment">## 这个命令用于从 Git 的版本控制中删除文件，但保留在工作目录中。它会将文件从暂存区（索引）中删除，但不会删除实际的文件。这意味着文件不再被跟踪，但仍然存在于你的工作目录中。</span></span><br><span class="line">git commit -m <span class="string">&quot;Remove FileName folder from Git&quot;</span>  <span class="comment">## 提交删除文件夹的更改到Git历史记录中</span></span><br><span class="line">git push origin &lt;branch-name&gt;  <span class="comment">## push到远程分支 </span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">## 扩展</span></span><br><span class="line">git <span class="built_in">rm</span> FielName <span class="comment">## 执行命令之后，会将git暂存区文件和本地的彻底清除，慎用！</span></span><br></pre></td></tr></table></figure><p>当你运行 <code>git rm --cached</code> 命令时，Git 会将你指定的文件或文件夹从 Git 仓库中删除，但不会删除实际的文件或文件夹。这意味着文件或文件夹将不再被 Git 跟踪，但仍然存在于你的本地文件系统中。如下图，仓库中存在一个Nginx目录，我不想让他在远程仓库中展示出来但本地又需要用到他，则我可以执行上述命令将它移除git，在提交至远程分支</p><p><img src="https://bu.dusays.com/2023/11/20/655b0c36e0680.png" alt="image-20231120153511097"></p><h2 id="回滚"><a href="#回滚" class="headerlink" title="回滚"></a>回滚</h2><h3 id="保留历史记录"><a href="#保留历史记录" class="headerlink" title="保留历史记录"></a>保留历史记录</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git revert</span><br></pre></td></tr></table></figure><ul><li>如果想要撤销某个或某些提交的更改，但保留这些提交的历史记录，可以使用 <code>git revert</code> 命令。</li><li>运行 <code>git revert &lt;commit&gt;</code>，其中 <code>&lt;commit&gt;</code> 是你想要回滚的提交的标识符（commit identifier）。</li><li>Git 将创建一个新的提交，该提交将撤销指定提交的更改。这样可以保留原始提交的历史记录，并且其他人在拉取你的更改时不会受到影响。</li></ul><h3 id="不保留历史记录"><a href="#不保留历史记录" class="headerlink" title="不保留历史记录"></a>不保留历史记录</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reset</span><br></pre></td></tr></table></figure><ul><li>如果需要彻底删除某个或某些提交，并且不保留它们的历史记录，可以使用 <code>git reset</code> 命令。</li><li>运行 <code>git reset --hard &lt;commit&gt;</code>，其中 <code>&lt;commit&gt;</code> 是你想要回滚到的目标提交的标识符。</li><li>Git 将移动当前分支的 HEAD 指针和分支引用到目标提交，并重置工作目录和暂存区以匹配目标提交的状态。这将丢失目标提交之后的所有提交。</li></ul><p>请注意，在使用 <code>git revert</code> 或 <code>git reset</code> 进行回滚之前，确保你理解了回滚操作的影响，并且已经备份了重要的代码。如果你在一个共享的代码库中工作，最好在与团队讨论后再进行回滚操作，以确保不会对其他人的工作产生不良影响。</p><h3 id="救命操作"><a href="#救命操作" class="headerlink" title="救命操作"></a>救命操作</h3><p>如果你向我一样，没备份文件就直接执行了git reset，那请按照如下找回在 回滚</p><ol><li><p>使用 <code>git reflog</code> 查找被重置的提交的标识符：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reflog</span><br></pre></td></tr></table></figure><p>这将显示你的仓库历史中的操作记录，包括重置操作。查找之前的提交的标识符（commit identifier），它应该类似于 <code>HEAD@&#123;n&#125;</code>，其中 <code>n</code> 是一个数字。</p></li><li><p>使用 <code>git reset</code> 恢复到之前的提交：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reset --hard HEAD@&#123;n&#125;</span><br></pre></td></tr></table></figure><p>将 <code>&lt;n&gt;</code> 替换为你在第一步中找到的数字。这将移动当前分支的 HEAD 指针和分支引用到之前的提交，并恢复工作目录和暂存区的状态。</p></li></ol><p><img src="https://bu.dusays.com/2023/11/20/655b11d4e1cd9.png" alt="image-20231120155916245"></p><h3 id="撤销提交"><a href="#撤销提交" class="headerlink" title="撤销提交"></a>撤销提交</h3><p>在本地执行了git add 及 git commit 操作后，想撤销这两步</p><ol><li><p>撤销 <code>git add</code> 操作：</p><ul><li>执行 <code>git reset</code> 命令可以将暂存区的文件撤销到工作区。例如，如果你执行了 <code>git add file1.txt</code> 将文件添加到暂存区，可以使用命令 <code>git reset file1.txt</code> 将其撤销。</li><li>如果你想要撤销所有的 <code>git add</code> 操作，可以使用 <code>git reset</code> 或者 <code>git reset .</code> 命令。</li></ul></li><li><p>撤销 <code>git commit</code> 操作：</p><ul><li><p>如果你想要撤销最近一次的 <code>git commit</code>，可以使用 <code>git reset HEAD^</code> 命令进行撤销。这将撤销最后一次提交，并将暂存区的文件移回到工作区，以便你可以重新修改和提交。</p></li><li><p>如果你想要完全删除最后一次提交，包括提交的文件内容，可以使用 <code>git reset --hard HEAD^</code> 命令进行撤销。请注意，这个操作是不可逆的，会永久删除最后一次提交。</p></li></ul></li></ol>]]></content>
    
    
    <summary type="html">探索Git版本控制的奇妙世界，提升代码管理效率。</summary>
    
    
    
    <category term="学习笔记" scheme="https://blog.serms.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    <category term="工具与软件" scheme="https://blog.serms.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/%E5%B7%A5%E5%85%B7%E4%B8%8E%E8%BD%AF%E4%BB%B6/"/>
    
    <category term="版本控制" scheme="https://blog.serms.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/%E5%B7%A5%E5%85%B7%E4%B8%8E%E8%BD%AF%E4%BB%B6/%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/"/>
    
    
    <category term="Git" scheme="https://blog.serms.top/tags/Git/"/>
    
  </entry>
  
  <entry>
    <title>Mysql随笔</title>
    <link href="https://blog.serms.top/posts/df7fb2d2.html"/>
    <id>https://blog.serms.top/posts/df7fb2d2.html</id>
    <published>2023-11-13T03:44:42.000Z</published>
    <updated>2023-11-13T03:44:42.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="InnoDB引擎"><a href="#InnoDB引擎" class="headerlink" title="InnoDB引擎"></a>InnoDB引擎</h2><h3 id="逻辑存储结构"><a href="#逻辑存储结构" class="headerlink" title="逻辑存储结构"></a>逻辑存储结构</h3><p><img src="https://bu.dusays.com/2023/11/13/65519c16690f0.png" alt="image-20231113114622728"></p><h4 id="表空间"><a href="#表空间" class="headerlink" title="表空间"></a>表空间</h4><p>表空间是InnoDB存储引擎的最高层，在磁盘中每个表都有一张表空间文件（xxx.ibd），一个MYSQL可以有多个表空间，表空间主要用于存储记录、索引等数据。</p><h4 id="段"><a href="#段" class="headerlink" title="段"></a>段</h4><p>段，分为：数据段、索引段、回滚段，InnoDB是索引组织表，数据段也就是B+树的叶子节点，索引段为B+树的非叶子节点，段用来管理多个区。</p><h4 id="区"><a href="#区" class="headerlink" title="区"></a>区</h4><p>区，是表空间的单元结构，每个区大小为1M，默认情况下Mysql的InnoDB引擎页大小为16K，也就是一个区中一共有64个连续的页。</p><h4 id="页"><a href="#页" class="headerlink" title="页"></a>页</h4><p>页，是InnoDB最小的磁盘管理单元，每个也为16KB，为了保证页的连续性，InnoDB引擎在申请页大小的时候会连续申请4-5个连续的页。</p><h4 id="行"><a href="#行" class="headerlink" title="行"></a>行</h4><p>行，InnoDB 存储引擎数据是按行进行存放的。</p><p>在行中，默认有两个隐藏字段： </p><ul><li>Trx_id：每次对某条记录进行改动时，都会把对应的事务id赋值给trx_id隐藏列。 </li><li>Roll_pointer：每次对某条引记录进行改动时，都会把旧的版本写入到undo日志中，然后这个 隐藏列就相当于一个指针，可以通过它来找到该记录修改前的信息。</li></ul><h3 id="架构"><a href="#架构" class="headerlink" title="架构"></a>架构</h3><p>之所以Mysql采用InnoDB作为默认的数据引擎，我想是以为InnoDB出众的事务处理能力，具有崩溃恢复的特性。</p><h4 id="8-0"><a href="#8-0" class="headerlink" title="8.0"></a>8.0</h4><p><img src="https://bu.dusays.com/2023/11/13/65519ec40187b.png" alt="image-20231113115754673"></p><h4 id="5-5"><a href="#5-5" class="headerlink" title="5.5"></a>5.5</h4><p><img src="https://bu.dusays.com/2023/11/13/65519ee0203cf.png" alt="image-20231113115823194"></p><h4 id="内存结构"><a href="#内存结构" class="headerlink" title="内存结构"></a>内存结构</h4><h5 id="Buffer-Pool"><a href="#Buffer-Pool" class="headerlink" title="Buffer Pool"></a>Buffer Pool</h5><p>是一个缓冲区，由于物理磁盘和内存之间的访问速度差距很大，为了尽可能的弥补两者之间的IO差值，就需要把经常用到的数据加载到缓冲池中，避免每次访问都进行磁 盘I/O。</p><p>在InnoDB的缓冲池中不仅缓存了<strong>索引页</strong>和数据页，还包含了<strong>undo页</strong>、插入缓存、<strong>自适应哈希索引</strong>以及 <strong>InnoDB的锁</strong>信息等等。</p><p>缓冲池 Buffer Pool，是主内存中的一个区域，里面可以缓存磁盘上经常操作的真实数据，在执行增 删改查操作时，先操作缓冲池中的数据（若缓冲池没有数据，则从磁盘加载并缓存），然后再以一定频 率刷新到磁盘，从而减少磁盘IO，加快处理速度。</p><p>缓冲池以Page页为单位，底层采用链表数据结构管理Page。根据状态，将Page分为三种类型： </p><ul><li>free page：空闲page，未被使用。 </li><li>clean page：被使用page，数据没有被修改过。 </li><li>dirty page：脏页，被使用page，数据被修改过，也中数据与磁盘的数据产生了不一致。</li></ul><h2 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h2><ul><li>原子性（Atomicity）：事务是不可分割的最小操作单元，要么全部成功，要么全部失败。 </li><li>一致性（Consistency）：事务完成时，必须使所有的数据都保持一致状态。 </li><li>隔离性（Isolation）：数据库系统提供的隔离机制，保证事务在不受外部并发操作影响的独立环 境下运行。 </li><li>持久性（Durability）：事务一旦提交或回滚，它对数据库中的数据的改变就是永久的。</li></ul><p>上述四大特性分为两部分：</p><ul><li>原子性、一致性、持久性是由InnoDB中的redo log、undo log日志来保证的。</li><li>持久性是通过数据库的锁加上MVCC机制来实现的</li></ul><p><img src="https://bu.dusays.com/2023/11/13/6551cda0e085a.png" alt="image-20231113151752283"></p><h3 id="redo-log"><a href="#redo-log" class="headerlink" title="redo log"></a>redo log</h3><p>个人理解：</p><ol><li><p>缓冲数据：</p><p>当我们进行一批SQL操作的时候，例如update、delete操作等，内存中的缓冲池（Buffer Pool）通过后台线程从磁盘结构中读取ibd文件中的数据缓冲到 Buffer Pool当中。</p></li><li><p>执行SQL语句</p><blockquote><p>没有RedoLog：</p><p>直接执行SQL语句操作缓冲区中的数据，此时缓冲区数据产生脏页，然后MySQL会通过一定的时候讲脏页刷新到磁盘当中，从而保证了数据的一致，但是脏页的数据并不是实时刷新的，而是通过一段时间后通过后台线程将脏页数据刷新到磁盘中，如果这时脏页数据写入磁盘出错呢？如何保证数据的持久性？一致性？</p></blockquote></li></ol><p>直接执行SQL语句操作缓冲区中的数据，此时缓冲区数据产生脏页，然后就会记录在RedoLogBuffer中，RedoLogBuffer中就会记录数据页的变化，当事务在提交的时候，会将数据页直接刷新到磁盘当中，持久化的保存在磁盘当中，如果在BufferPoll将脏页刷新数据到磁盘出错了，可以通过数据页来恢复数据，redoLog中主要是记录了当次数据的变化，所以能通过RedoLog日志文件进行数据恢复，总结来说RedoLog就是在缓冲区中脏页往磁盘中刷新数据出错的时候进行数据恢复，如果说每一次的事务操作都直接从缓冲区写入到磁盘，会导致IO效率问题，因为每一个事务操作操作一组数据的时候通常会操作很多条记录，这些记录都是随机操作数据页的，这个时候就会涉及到大量的随机磁盘IO，导致性能降低，如果通过RedoLog日志文件将每次的操作记录都异步的追加到磁盘中，那么他就是顺序的磁盘IO操作了，性能是要高于随机磁盘IO的，这种也叫做WAL（先写日志），</p><p>总结：</p><p>在Mysql中RedoLog是一种事务日志，用于记录已经提交的事务所做的修改操作。它的主要作用是在数据库发生崩溃或意外关闭时，通过重新执行已提交的事务来恢复数据库的一致性。</p><p>当一个事务开始时，MySQL会将该事务所做的修改操作记录到redo log中，而不是立即将数据写入磁盘。这样可以提高数据库的性能，因为磁盘写入是比较耗时的操作</p><p>redo log的写入是顺序的，按照事务的顺序将日志写入磁盘。这样可以减少磁盘寻道的时间，提高写入性能</p><p>整理执行过程是这样子的，在进行一个事务操作的时候，首先内存中的bufferPoll会从磁盘中的ibd文件获取数据到缓冲池，在执行增删改操作，执行完了之后，会将缓冲池中的脏页也就是已修改的数据页，记录到RedoLog日志文件中去，当事务提交完了之后过了一段时间，缓冲池中的脏页才会写入到磁盘中去，如果此时磁盘写入出错，那么就会重新执行事务，将redolog中的数据从磁盘中读取到缓冲区在重新执行写入到磁盘的操作，这样就保证了数据的持久性。</p><p>MySQL会定期将redo log的数据刷新到磁盘，以保证数据的持久性。刷新操作可以在事务提交时触发，当redo log的空间不足时触发，或者由后台线程定期触发。</p><p>当数据库发生崩溃或意外关闭时，MySQL可以通过redo log来恢复数据的一致性。在数据库重新启动时，MySQL会检查redo log，将未完成的事务重新执行，将数据恢复到崩溃前的状态。综上所述，redo log在MySQL中起着重要的作用，用于记录已提交事务的修改操作，并在数据库崩溃时保证数据的一致性恢复。</p><p>因为在业务操作中，我们操作数据一般都是随机读写磁盘的，而不是顺序读写磁盘。 而redo log在 往磁盘文件中写入数据，由于是日志文件，所以都是顺序写的。顺序写的效率，要远大于随机写。</p><h3 id="undo-log"><a href="#undo-log" class="headerlink" title="undo log"></a>undo log</h3><p>回滚日志，用于记录数据被修改前的信息 , 作用包含两个 : </p><ul><li>提供回滚(保证事务的原子性) 和 MVCC(多版本并发控制) 。 undo log和redo log记录物理日志不一样，它是逻辑日志。可以认为当delete一条记录时，undo log中会记录一条对应的insert记录，反之亦然，当update一条记录时，它记录一条对应相反的 update记录。当执行rollback时，就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。 </li><li>Undo log销毁：undo log在事务执行时产生，事务提交时，并不会立即删除undo log，因为这些 日志可能还用于MVCC。 Undo log存储：undo log采用段的方式进行管理和记录，存放在前面介绍的 rollback segment 回滚段中，内部包含1024个undo log segment。</li></ul><h3 id="MVCC"><a href="#MVCC" class="headerlink" title="MVCC"></a>MVCC</h3><h4 id="当前读"><a href="#当前读" class="headerlink" title="当前读"></a>当前读</h4><p>当前读读到的就是最新的数据，</p><p><img src="C:/Users/iipcloud0821/Desktop/%E5%BD%93%E5%89%8D%E8%AF%BB.gif" alt="当前读"></p><p>在测试中我们可以看到，即使是在默认的RR隔离级别下，事务A中依然可以读取到事务B最新提交的内 容，因为在查询语句后面加上了 lock in share mode 共享锁，此时是当前读操作。当然，当我们 加排他锁的时候，也是当前读操作。</p><h4 id="快照读"><a href="#快照读" class="headerlink" title="快照读"></a>快照读</h4><p>简单的select（不加锁）就是快照读，快照读，读取的是记录数据的可见版本，有可能是历史数据， 不加锁，是非阻塞读。</p><ul><li>Read Committed：每次select，都生成一个快照读。 </li><li>Repeatable Read：开启事务后第一个select语句才是快照读的地方。 </li><li>Serializable：快照读会退化为当前读。</li></ul><h4 id="MVCC-1"><a href="#MVCC-1" class="headerlink" title="MVCC"></a>MVCC</h4><p>MVCC主要实现依赖于表中的三个隐式字段，undolog ，readview</p><p><img src="https://bu.dusays.com/2023/11/13/6551efca08517.png" alt="image-20231113174337202"></p><p>而上述的前两个字段是肯定会添加的， 是否添加最后一个字段DB_ROW_ID，得看当前表有没有主键， 如果有主键，则不会添加该隐藏字段。</p><h4 id="undolog"><a href="#undolog" class="headerlink" title="undolog"></a>undolog</h4><p>回滚日志，在insert、update、delete的时候产生的便于数据回滚的日志。 当insert的时候，产生的undo log日志只在回滚时需要，在事务提交后，可被立即删除。 而update、delete的时候，产生的undo log日志不仅在回滚时需要，在快照读时也需要，不会立即 被删除。</p><h4 id="版本链"><a href="#版本链" class="headerlink" title="版本链"></a>版本链</h4><p><img src="https://bu.dusays.com/2023/11/13/6551f0738241d.png" alt="image-20231113174626924"></p><p>RC：每一次提交都会生成一个新的ReadView</p><p>RR：可重复读也就是因为每次都是复用一个ReadView</p><h4 id="readview"><a href="#readview" class="headerlink" title="readview"></a>readview</h4><p><img src="https://bu.dusays.com/2023/11/13/6551f085252cf.png" alt="image-20231113174644430"></p><p><img src="https://bu.dusays.com/2023/11/13/6551f08f18aaa.png" alt="image-20231113174654543"></p><p>trx_id 代表当前undolog版本链对应事务ID。</p><p>不同的隔离级别，生成ReadView的时机不同： </p><ul><li>READ COMMITTED ：在事务中每一次执行快照读时生成ReadView。 </li><li>REPEATABLE READ：仅在事务中第一次执行快照读时生成ReadView，后续复用该ReadView。</li></ul><h4 id="RC隔离级别"><a href="#RC隔离级别" class="headerlink" title="RC隔离级别"></a>RC隔离级别</h4><p>RC隔离级别下，在事务中每一次执行快照读时生成ReadView。</p><p><img src="https://bu.dusays.com/2023/11/13/6551f0e0912b2.png" alt="image-20231113174815901"></p><h4 id="RR隔离级别"><a href="#RR隔离级别" class="headerlink" title="RR隔离级别"></a>RR隔离级别</h4><p>RR隔离级别下，仅在事务中第一次执行快照读时生成ReadView，后续复用该ReadView。 而RR 是可 重复读，在一个事务中，执行两次相同的select语句，查询到的结果是一样的。</p><p><img src="https://bu.dusays.com/2023/11/13/6551f0f76652e.png" alt="image-20231113174838621"></p>]]></content>
    
    
    <summary type="html">Mysql学习笔记</summary>
    
    
    
    <category term="后端开发" scheme="https://blog.serms.top/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/"/>
    
    <category term="数据库" scheme="https://blog.serms.top/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
    <category term="Mysql" scheme="https://blog.serms.top/tags/Mysql/"/>
    
  </entry>
  
  <entry>
    <title>SQL优化</title>
    <link href="https://blog.serms.top/posts/b69403ff.html"/>
    <id>https://blog.serms.top/posts/b69403ff.html</id>
    <published>2023-11-09T10:07:15.000Z</published>
    <updated>2023-11-09T10:07:15.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="插入数据"><a href="#插入数据" class="headerlink" title="插入数据"></a>插入数据</h2><h3 id="批量插入数据"><a href="#批量插入数据" class="headerlink" title="批量插入数据"></a>批量插入数据</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Insert into tb_test values(1,&#x27;Tom&#x27;),(2,&#x27;Cat&#x27;),(3,&#x27;Jerry&#x27;);</span><br></pre></td></tr></table></figure><h3 id="手动控制事务"><a href="#手动控制事务" class="headerlink" title="手动控制事务"></a>手动控制事务</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">start transaction;</span><br><span class="line">insert into tb_test values(1,&#x27;Tom&#x27;),(2,&#x27;Cat&#x27;),(3,&#x27;Jerry&#x27;);</span><br><span class="line">insert into tb_test values(4,&#x27;Tom&#x27;),(5,&#x27;Cat&#x27;),(6,&#x27;Jerry&#x27;);</span><br><span class="line">insert into tb_test values(7,&#x27;Tom&#x27;),(8,&#x27;Cat&#x27;),(9,&#x27;Jerry&#x27;);</span><br><span class="line">commit;</span><br></pre></td></tr></table></figure><h3 id="主键顺序插入，性能要高于乱序插入"><a href="#主键顺序插入，性能要高于乱序插入" class="headerlink" title="主键顺序插入，性能要高于乱序插入"></a>主键顺序插入，性能要高于乱序插入</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">主键乱序插入 : 8 1 9 21 88 2 4 15 89 5 7 3</span><br><span class="line">主键顺序插入 : 1 2 3 4 5 7 8 9 15 21 88 89</span><br></pre></td></tr></table></figure><h2 id="大批量插入数据"><a href="#大批量插入数据" class="headerlink" title="大批量插入数据"></a>大批量插入数据</h2><p>如果一次性需要插入大批量数据(比如: 几百万的记录)，使用insert语句插入性能较低，此时可以使 用MySQL数据库提供的load指令进行插入。操作如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">-- 客户端连接服务端时，加上参数 -–local-infile</span><br><span class="line">mysql –-local-infile -u root -p</span><br><span class="line">-- 设置全局参数local_infile为1，开启从本地加载文件导入数据的开关</span><br><span class="line">set global local_infile = 1;</span><br><span class="line">-- 执行load指令将准备好的数据，加载到表结构中</span><br><span class="line">load data local infile &#x27;/root/app/tb_sku1.log&#x27; into table tb_user fields terminated by &#x27;,&#x27; lines terminated by &#x27;\n&#x27; ;</span><br></pre></td></tr></table></figure><h2 id="order-by优化"><a href="#order-by优化" class="headerlink" title="order by优化"></a>order by优化</h2><p>MySQL的排序有两种方式： </p><ul><li>Using filesort : 通过表的索引或全表扫描，读取满足条件的数据行，然后在排序缓冲区sort buffer中完成排序操作，所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。 </li><li>Using index : 通过有序索引顺序扫描直接返回有序数据，这种情况即为 using index，不需要 额外排序，操作效率高。 </li></ul><p>对于以上的两种排序方式，Using index的性能高，而Using filesort的性能低，我们在优化排序 操作时，尽量要优化为 Using index。</p><p>执行SQL</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">explain select id,age,phone from tb_user order by age ;</span><br><span class="line">explain select id,age,phone from tb_user order by age, phone ;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654cc4aa3ed91.png" alt="image-20231109193816874"></p><p>由于 age, phone 都没有索引，所以此时再排序时，出现Using filesort， 排序性能较低。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">-- 创建索引</span><br><span class="line">create index idx_user_age_phone_aa on tb_user(age,phone);</span><br><span class="line">explain select id,age,phone from tb_user order by age;</span><br><span class="line">explain select id,age,phone from tb_user order by age , phone;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654cc4f31d755.png" alt="image-20231109193929633"></p><p>建立索引之后，再次进行排序查询，就由原来的Using filesort， 变为了 Using index，性能 就是比较高的了。</p><p><strong>创建索引后，根据age, phone进行降序排序</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain select id,age,phone from tb_user order by age desc , phone desc ;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654cc57851a25.png" alt="image-20231109194143567"></p><p>也出现 Using index， 但是此时Extra中出现了 Backward index scan，这个代表反向扫描索 引，因为在MySQL中我们创建的索引，默认索引的叶子节点是从小到大排序的，而此时我们查询排序 时，是从大到小，所以，在扫描时，就是反向扫描，就会出现 Backward index scan。 在 MySQL8版本中，支持降序索引，我们也可以创建降序索引。</p><p><strong>根据phone，age进行升序排序，phone在前，age在后。</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain select id,age,phone from tb_user order by phone , age;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654cc648030f0.png" alt="image-20231109194510851"></p><p>排序时,也需要满足最左前缀法则,否则也会出现 filesort。因为在创建索引的时候， age是第一个 字段，phone是第二个字段，所以排序时，也就该按照这个顺序来，否则就会出现 Using filesort。</p><p><strong>根据age, phone进行降序一个升序，一个降序</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain select id,age,phone from tb_user order by age asc , phone desc ;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654cc5de771fa.png" alt="image-20231109194325680"></p><p>因为创建索引时，如果未指定顺序，默认都是按照升序排序的，而查询时，一个升序，一个降序，此时 就会出现Using filesort。</p><p><img src="https://bu.dusays.com/2023/11/09/654cc6cc0bd31.png" alt="image-20231109194722035"></p><p>为了解决上述的问题，我们可以创建一个索引，这个联合索引中 age 升序排序，phone 倒序排序。</p><p> <strong>创建联合索引(age 升序排序，phone 倒序排序)</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">create index idx_user_age_phone_ad on tb_user(age asc ,phone desc);</span><br><span class="line">explain select id,age,phone from tb_user order by age asc , phone desc ;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654cc75fafc13.png" alt="image-20231109194950627"></p><p>order by优化原则: </p><ul><li>根据排序字段建立合适的索引，多字段排序时，也遵循最左前缀法则。 </li><li>尽量使用覆盖索引。 </li><li>多字段排序, 一个升序一个降序，此时需要注意联合索引在创建时的规则（ASC/DESC）。 </li><li>如果不可避免的出现filesort，大数据量排序时，可以适当增大排序缓冲区大小 sort_buffer_size(默认256k)。</li></ul><h2 id="group-by优化"><a href="#group-by优化" class="headerlink" title="group by优化"></a>group by优化</h2><p>在没有索引的情况下，执行如下SQL，查询执行计划：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain select profession , count(*) from tb_user group by profession ;</span><br></pre></td></tr></table></figure><p>然后，我们在针对于 profession ， age， status 创建一个联合索引。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">create index idx_user_pro_age_sta on tb_user(profession , age , status);</span><br></pre></td></tr></table></figure><p>紧接着，再执行前面相同的SQL查看执行计划。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain select profession , count(*) from tb_user group by profession ;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654cc9758a96d.png" alt="image-20231109195841052"></p><p>我们发现，如果仅仅根据age分组，就会出现 Using temporary ；</p><p>而如果是 根据 profession,age两个字段同时分组，则不会出现 Using temporary。原因是因为对于分组操作， 在联合索引中，也是符合最左前缀法则的</p><p>所以，在分组操作中，我们需要通过以下两点进行优化，以提升性能： </p><ul><li>在分组操作时，可以通过索引来提高效率。 </li><li>分组操作时，索引的使用也是满足最左前缀法则的。</li></ul><h2 id="limit优化"><a href="#limit优化" class="headerlink" title="limit优化"></a>limit优化</h2><p>在数据量比较大时，如果进行limit分页查询，在查询时，越往后，分页查询效率越低。</p><p>优化思路: 一般分页查询时，通过创建 覆盖索引 能够比较好地提高性能，可以通过覆盖索引加子查 询形式进行优化。</p>]]></content>
    
    
    <summary type="html">常见的SQL优化方案</summary>
    
    
    
    <category term="后端开发" scheme="https://blog.serms.top/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/"/>
    
    <category term="数据库" scheme="https://blog.serms.top/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
    <category term="Mysql" scheme="https://blog.serms.top/tags/Mysql/"/>
    
  </entry>
  
  <entry>
    <title>Mysql索引详解</title>
    <link href="https://blog.serms.top/posts/2bf0e697.html"/>
    <id>https://blog.serms.top/posts/2bf0e697.html</id>
    <published>2023-11-09T02:17:23.000Z</published>
    <updated>2023-11-09T02:17:23.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>文章由本人通过互联网自学、整理、总结的教程，仅作为学习笔记记录，并分享给有需要的同学。</p><h2 id="索引概述"><a href="#索引概述" class="headerlink" title="索引概述"></a>索引概述</h2><p>在我理解看来，索引是具有高效查询的一种数据结构，也可以理解为满足特定查找算法的数据结构，这种数据结构以某种方式指向数据。</p><h2 id="索引特点"><a href="#索引特点" class="headerlink" title="索引特点"></a>索引特点</h2><p><img src="https://bu.dusays.com/2023/11/09/654c5051978a1.png" alt="image-20231109112145106"></p><h2 id="索引结构"><a href="#索引结构" class="headerlink" title="索引结构"></a>索引结构</h2><p>MySQL的索引是在存储引擎层实现的，不同的存储引擎有不同的索引结构，主要包含以下几种：</p><p><img src="https://bu.dusays.com/2023/11/09/654c507c829b0.png" alt="image-20231109112235443"></p><p>上述是MySQL中所支持的所有的索引结构，接下来，我们再来看看不同的存储引擎对于索引结构的支持情况</p><p><img src="https://bu.dusays.com/2023/11/09/654c51f2dc382.png" alt="image-20231109112842604"></p><blockquote><p> 注意： 我们平常所说的索引，如果没有特别指明，都是指B+树结构组织的索引。</p></blockquote><h3 id="B-Tree"><a href="#B-Tree" class="headerlink" title="B-Tree"></a>B-Tree</h3><p>B-Tree，B树是一种多叉路衡查找树，相对于二叉树，B树每个节点可以有多个分支，即多叉。 以一颗最大度数（max-degree）为5(5阶)的b-tree为例，那这个B树每个节点最多存储4个key，5 个指针：</p><p><img src="https://bu.dusays.com/2023/11/09/654c52428e040.png" alt="image-20231109113008883"></p><blockquote><p>知识小贴士: 树的度数指的是一个节点的子节点个数。</p></blockquote><p>我们可以通过一个数据结构可视化的网站来演示：<a href="https://www.cs.usfca.edu/~galles/visualization/BTree.html">https://www.cs.usfca.edu/~galles/visualization/BTree.html</a></p><h3 id="B-Tree-1"><a href="#B-Tree-1" class="headerlink" title="B+Tree"></a>B+Tree</h3><p>B+Tree是B-Tree的变种，我们以一颗最大度数（max-degree）为4（4阶）的b+tree为例，来看一 下其结构示意图：</p><p><img src="https://bu.dusays.com/2023/11/09/654c5486bc34b.png" alt="image-20231109113948669"></p><p>我们可以看到，两部分： </p><ul><li>绿色框框起来的部分，是索引部分，仅仅起到索引数据的作用，不存储数据。 </li><li>红色框框起来的部分，是数据存储部分，在其叶子节点中要存储具体的数据。</li></ul><p><a href="https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html">https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html</a></p><p>最终我们看到，B+Tree 与 B-Tree相比，主要有以下三点区别：</p><ul><li>所有的数据都会出现在叶子节点。 </li><li>叶子节点形成一个单向链表。 </li><li>非叶子节点仅仅起到索引数据作用，具体的数据都是在叶子节点存放的。</li></ul><p>上述我们所看到的结构是标准的B+Tree的数据结构，接下来，我们再来看看MySQL中优化之后的 B+Tree。</p><p>MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上，增加一个指向相邻叶子节点 的链表指针，就形成了带有顺序指针的B+Tree，提高区间访问的性能，利于排序。</p><p><img src="https://bu.dusays.com/2023/11/09/654c55d51f494.png" alt="image-20231109114524046"></p><h3 id="Hash"><a href="#Hash" class="headerlink" title="Hash"></a>Hash</h3><p>MySQL中除了支持B+Tree索引，还支持一种索引类型—-Hash索引。</p><p>哈希索引就是采用一定的hash算法，将键值换算成新的hash值，映射到对应的槽位上，然后存储在 hash表中。</p><p><img src="https://bu.dusays.com/2023/11/09/654c55fe9e4d9.png" alt="image-20231109114605502"></p><p>如果两个(或多个)键值，映射到一个相同的槽位上，他们就产生了hash冲突（也称为hash碰撞），可 以通过链表来解决。</p><p><img src="https://bu.dusays.com/2023/11/09/654c560b5a526.png" alt="image-20231109114618382"></p><p>特点 </p><ul><li>Hash索引只能用于对等比较(=，in)，不支持范围查询（between，&gt;，&lt; ，…）  </li><li>无法利用索引完成排序操作 </li><li>查询效率高，通常(不存在hash冲突的情况)只需要一次检索就可以了，效率通常要高于B+tree索引</li></ul><h3 id="存储引擎支持"><a href="#存储引擎支持" class="headerlink" title="存储引擎支持"></a>存储引擎支持</h3><ul><li><p>在MySQL中，支持hash索引的是Memory存储引擎。 </p></li><li><p>而InnoDB中具有自适应hash功能，hash索引是 InnoDB存储引擎根据B+Tree索引在指定条件下自动构建的。</p></li></ul><p>为什么InnoDB存储引擎选择使用B+tree索引结构?</p><ul><li>相对于二叉树，层级更少，搜索效率高；</li><li>对于B-tree，无论是叶子节点还是非叶子节点，都会保存数据，这样导致一页中存储 的键值减少，指针跟着减少，要同样保存大量数据，只能增加树的高度，导致性能降低；这里说的性能大部分是指查询性能。</li><li>相对Hash索引，B+tree支持范围匹配及排序操作；</li></ul><h2 id="索引分类"><a href="#索引分类" class="headerlink" title="索引分类"></a>索引分类</h2><p>在MySQL数据库，将索引的具体类型主要分为以下几类：主键索引、唯一索引、常规索引、全文索引。</p><p><img src="https://bu.dusays.com/2023/11/09/654c56b866813.png" alt="image-20231109114911302"></p><h2 id="聚集索引-amp-二级索引"><a href="#聚集索引-amp-二级索引" class="headerlink" title="聚集索引&amp;二级索引"></a>聚集索引&amp;二级索引</h2><p>而在在InnoDB存储引擎中，根据索引的存储形式，又可以分为以下两种：</p><p><img src="https://bu.dusays.com/2023/11/09/654c56cd2ca8e.png" alt="image-20231109114932113"></p><h3 id="聚集索引选取规则"><a href="#聚集索引选取规则" class="headerlink" title="聚集索引选取规则"></a>聚集索引选取规则</h3><ul><li>如果存在主键，主键索引就是聚集索引。</li><li>如果不存在主键，将使用第一个唯一（UNIQUE）索引作为聚集索引。</li><li>如果表没有主键，或没有合适的唯一索引，则InnoDB会自动生成一个rowid作为隐藏的聚集索引。</li></ul><p>聚集索引和二级索引的具体结构如下：</p><p><img src="https://bu.dusays.com/2023/11/09/654c57337c269.png" alt="image-20231109115114287"></p><ul><li>聚集索引的叶子节点下挂的是这一行的数据 。</li><li>二级索引的叶子节点下挂的是该字段值对应的主键值、主键Id。</li></ul><p>当我们执行如下的SQL语句时，具体的查询过程如下：</p><p><img src="https://bu.dusays.com/2023/11/09/654c576b4d8db.png" alt="image-20231109115210259"></p><p>具体过程如下: </p><ul><li>由于是根据name字段进行查询，所以先根据name=’Arm’到name字段的二级索引中进行匹配查找。但是在二级索引中只能查找到 Arm 对应的主键值 10。</li><li>由于查询返回的数据是*，所以此时，还需要根据主键值10，到聚集索引中查找10对应的记录，最 终找到10对应的行row。</li><li>最终拿到这一行的数据，直接返回即可。</li></ul><blockquote><p>这种先到二级索引中查找数据，找到主键值，然后再到聚集索引中根据主键值，获取 数据的方式，就称之为回表查询。</p></blockquote><h2 id="索引语法"><a href="#索引语法" class="headerlink" title="索引语法"></a>索引语法</h2><h3 id="创建索引"><a href="#创建索引" class="headerlink" title="创建索引"></a>创建索引</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name (index_col_name,... ) ;</span><br></pre></td></tr></table></figure><h3 id="查看索引"><a href="#查看索引" class="headerlink" title="查看索引"></a>查看索引</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SHOW INDEX FROM TABLE_NAME;</span><br></pre></td></tr></table></figure><h3 id="删除索引"><a href="#删除索引" class="headerlink" title="删除索引"></a>删除索引</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DROP INDEX INDEX_NAME ON TABLE_NAME;</span><br></pre></td></tr></table></figure><h3 id="演示操作"><a href="#演示操作" class="headerlink" title="演示操作"></a>演示操作</h3><h4 id="创建表"><a href="#创建表" class="headerlink" title="创建表"></a>创建表</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create table</span> tb_user(</span><br><span class="line">    id <span class="type">int</span> <span class="keyword">primary key</span> auto_increment comment <span class="string">&#x27;主键&#x27;</span>,</span><br><span class="line">    name <span class="type">varchar</span>(<span class="number">50</span>) <span class="keyword">not null</span> comment <span class="string">&#x27;用户名&#x27;</span>,</span><br><span class="line">    phone <span class="type">varchar</span>(<span class="number">11</span>) <span class="keyword">not null</span> comment <span class="string">&#x27;手机号&#x27;</span>,</span><br><span class="line">    email <span class="type">varchar</span>(<span class="number">100</span>) comment <span class="string">&#x27;邮箱&#x27;</span>,</span><br><span class="line">    profession <span class="type">varchar</span>(<span class="number">11</span>) comment <span class="string">&#x27;专业&#x27;</span>,</span><br><span class="line">    age tinyint unsigned comment <span class="string">&#x27;年龄&#x27;</span>,</span><br><span class="line">    gender <span class="type">char</span>(<span class="number">1</span>) comment <span class="string">&#x27;性别 , 1: 男, 2: 女&#x27;</span>,</span><br><span class="line">    status <span class="type">char</span>(<span class="number">1</span>) comment <span class="string">&#x27;状态&#x27;</span>,</span><br><span class="line">    createtime datetime comment <span class="string">&#x27;创建时间&#x27;</span></span><br><span class="line">) comment <span class="string">&#x27;系统用户表&#x27;</span>;</span><br></pre></td></tr></table></figure><h4 id="创建索引-1"><a href="#创建索引-1" class="headerlink" title="创建索引"></a>创建索引</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">CREATE INDEX idx_user_name ON tb_user(name);</span><br><span class="line">CREATE UNIQUE INDEX idx_user_phone ON tb_user(phone);</span><br></pre></td></tr></table></figure><h4 id="创建联合索引"><a href="#创建联合索引" class="headerlink" title="创建联合索引"></a>创建联合索引</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CREATE INDEX idx_user_pro_age_sta ON tb_user(profession,age,status);</span><br></pre></td></tr></table></figure><h4 id="查看索引-1"><a href="#查看索引-1" class="headerlink" title="查看索引"></a>查看索引</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">show index from tb_user;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c59d3e09bf.png" alt="image-20231109120226885"></p><h2 id="SQL性能分析"><a href="#SQL性能分析" class="headerlink" title="SQL性能分析"></a>SQL性能分析</h2><p>MySQL 客户端连接成功后，通过 show [session|global] status 命令可以提供服务器状态信 息。通过如下指令，可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">-- session 是查看当前会话 ;</span><br><span class="line">-- global 是查询全局数据 ;</span><br><span class="line">SHOW GLOBAL STATUS LIKE &#x27;Com_______&#x27;;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c5a31cc0f6.png" alt="image-20231109120400997"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Com_delete: 删除次数</span><br><span class="line">Com_insert: 插入次数</span><br><span class="line">Com_select: 查询次数</span><br><span class="line">Com_update: 更新次数</span><br></pre></td></tr></table></figure><blockquote><p>通过上述指令，我们可以查看到当前数据库到底是以查询为主，还是以增删改为主，从而为数据库优化提供参考依据。 如果是以增删改为主，我们可以考虑不对其进行索引的优化。 如果是以 查询为主，那么就要考虑对数据库的索引进行优化了。</p></blockquote><p>通过SQL执行的频率，就能知道是以查询为主还是增删改为主，如果是以查询为主,我们可以查看慢查询日志来分析。</p><h3 id="慢查询日志"><a href="#慢查询日志" class="headerlink" title="慢查询日志"></a>慢查询日志</h3><p>慢查询日志记录了所有执行时间超过指定参数（long_query_time，单位：秒，默认10秒）的所有 SQL语句的日志。</p><p>MySQL的慢查询日志默认没有开启，我们可以查看一下系统变量 slow_query_log。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; show variables like &#x27;slow_query_log&#x27;;</span><br><span class="line">+----------------+-------+</span><br><span class="line">| Variable_name  | Value |</span><br><span class="line">+----------------+-------+</span><br><span class="line">| slow_query_log | OFF    |</span><br><span class="line">+----------------+-------+</span><br><span class="line">1 row in set (0.00 sec)</span><br></pre></td></tr></table></figure><h4 id="开启慢查询"><a href="#开启慢查询" class="headerlink" title="开启慢查询"></a>开启慢查询</h4><p>在MySQL的配置文件（/etc/my.cnf）中配置如下信息：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 开启MySQL慢日志查询开关</span></span><br><span class="line">slow_query_log=1</span><br><span class="line"><span class="comment"># 设置慢日志的时间为2秒，SQL语句执行时间超过2秒，就会视为慢查询，记录慢查询日志</span></span><br><span class="line">long_query_time=2</span><br></pre></td></tr></table></figure><p>配置完毕之后，通过以下指令重新启动MySQL服务器进行测试，查看慢日志文件中记录的信息</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl restart mysqld</span><br></pre></td></tr></table></figure><p>再次查看 <code>show variables like &#39;slow_query_log&#39;;</code></p><p>可以看到为ON了</p><p><img src="https://bu.dusays.com/2023/11/09/654c5bc7952ef.png" alt="image-20231109121046699"></p><p>查看日志文件: <code>/var/lib/mysql/localhost-slow.log。</code></p><h4 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h4><p>慢查询设置为2秒,为了达到2秒以上,可以使用<code>navicat Premium 16</code>进行数据生成,生成几百万条即可</p><p>当执行一条查询语句时间超过2秒以上时，我们再去查看<code>/var/lib/mysql/localhost-slow.log。</code></p><p><img src="https://bu.dusays.com/2023/11/09/654c5cbb83ae9.png" alt="image-20231109121450005"></p><p>慢查询的作用主要是定位，查询效率比较低的SQL，从而进行针对性的优化。</p><h3 id="profile"><a href="#profile" class="headerlink" title="profile"></a>profile</h3><p>show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling 参数，能够看到当前MySQL是否支持profile操作：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">SELECT @@have_profiling;## 查看是否支持Profile操作</span><br><span class="line">select  @@profiling;## 查看Prifile是否开启</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c5dccbaaac.png" alt="image-20231109121923855"></p><p>可以看到，当前MySQL是支持 profile操作的，但是开关是关闭的。可以通过set语句在 session/global级别开启profiling：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SET profiling = 1;</span><br></pre></td></tr></table></figure><p>开关已经打开了，接下来，我们所执行的SQL语句，都会被MySQL记录，并记录执行时间消耗到哪儿去 了。 我们直接执行如下的SQL语句：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">select * from tb_user;</span><br><span class="line">select * from tb_user where id = 1;</span><br><span class="line">select * from tb_user where name = &#x27;白起&#x27;;</span><br><span class="line">select count(*) from tb_sku;</span><br></pre></td></tr></table></figure><p>执行一系列的业务SQL的操作，然后通过如下指令查看指令的执行耗时：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">-- 查看每一条SQL的耗时基本情况</span><br><span class="line">show profiles;</span><br><span class="line">-- 查看指定query_id的SQL语句各个阶段的耗时情况</span><br><span class="line">show profile for query query_id;</span><br><span class="line">-- 查看指定query_id的SQL语句CPU的使用情况</span><br><span class="line">show profile cpu for query query_id;</span><br></pre></td></tr></table></figure><p>查看每一条SQL的耗时情况:</p><p><img src="https://bu.dusays.com/2023/11/09/654c5ec6a6272.png" alt="image-20231109122333639"></p><p>查看指定SQL各个阶段的耗时情况:</p><p><img src="https://bu.dusays.com/2023/11/09/654c5ed88a634.png" alt="image-20231109122350833"></p><p>可以看到<code>select * from tb_user where name = &#39;白起&#39;;</code>这sql语句查询的时间是比较久的,因为他是先通过的二级索引查找,找到对应的ID值,在通过ID查找的所有数据。</p><h3 id="explain"><a href="#explain" class="headerlink" title="explain"></a>explain</h3><p>EXPLAIN 或者 DESC命令获取 MySQL 如何执行 SELECT 语句的信息，包括在 SELECT 语句执行 过程中表如何连接和连接的顺序。</p><p>语法: </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">-- 直接在select语句之前加上关键字 explain / desc</span><br><span class="line">EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件 ;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c705a021bd.png" alt="image-20231109133833235"></p><p>Explain 执行计划中各个字段的含义:</p><p><img src="https://bu.dusays.com/2023/11/09/654c706cb08e3.png" alt="image-20231109133851504"></p><p>利用<code>explain</code>我们可以查看sql查询语句是否有走索引,</p><h2 id="索引使用规范"><a href="#索引使用规范" class="headerlink" title="索引使用规范"></a>索引使用规范</h2><h3 id="最左前缀法则"><a href="#最左前缀法则" class="headerlink" title="最左前缀法则"></a>最左前缀法则</h3><p>如果索引了多列（联合索引），要遵守最左前缀法则。最左前缀法则指的是<strong>查询从索引的最左列开始</strong>， 并且不跳过索引中的列。如果跳跃某一列，索引将会部分<strong>失效(后面的字段索引失效)</strong>。</p><h5 id="举例"><a href="#举例" class="headerlink" title="举例"></a>举例</h5><p><img src="https://bu.dusays.com/2023/11/09/654c803958e8f.png" alt="image-20231109144615734"></p><p>在<code>tb_user</code>表中 profession 、 age 、status 组建了联合索引为<code>idx_user_pro_age_sta</code>，可以看到<code>Seq_in_index</code> 列的顺序从上往下依次为1-3</p><p>分别执行一下查询语句：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">explain <span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> tb_user <span class="keyword">where</span> profession <span class="operator">=</span> <span class="string">&#x27;软件工程&#x27;</span> <span class="keyword">and</span> age <span class="operator">=</span> <span class="number">31</span> <span class="keyword">and</span> status <span class="operator">=</span> <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">explain <span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> tb_user <span class="keyword">where</span> profession <span class="operator">=</span> <span class="string">&#x27;软件工程&#x27;</span> <span class="keyword">and</span> age <span class="operator">=</span> <span class="number">31</span>;</span><br><span class="line">explain <span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> tb_user <span class="keyword">where</span> profession <span class="operator">=</span> <span class="string">&#x27;软件工程&#x27;</span>;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c827e32708.png" alt="image-20231109145557362"></p><p>三次执行过程结果分析：</p><p>以上的这三组测试中，我们发现只要联合索引最左边的字段 profession存在，索引就会生效，只不 过索引的长度不同。 而且由以上三组测试，我们也可以推测出profession字段索引长度为36、age 字段索引长度为2、status字段索引长度为4。</p><p>分别执行一下查询语句：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user where age = 31 and status = &#x27;0&#x27;;</span><br><span class="line">explain select * from tb_user where status = &#x27;0&#x27;;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c83ce73d66.png" alt="image-20231109150133458"></p><p>而通过上面的这两组测试，我们也可以看到索引并未生效，原因是因为不满足最左前缀法则，联合索引最左边的列profession不存在,所以那type为ALL走的是全局搜索。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user where profession = &#x27;软件工程&#x27; and status = &#x27;0&#x27;;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c842b8fd1c.png" alt="image-20231109150306948"></p><p>上述的SQL查询时，存在profession字段，最左边的列是存在的，索引满足最左前缀法则的基本条件。但是查询时，跳过了age这个列，所以后面的列索引是不会使用的，也就是索引部分生效，所以索引的长度就是36。</p><p>思考：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user where age = 31 and status = &#x27;0&#x27; and profession = &#x27;软件工程&#x27;;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c852d8c72e.png" alt="image-20231109150724913"></p><p>可以看到也走了索引，所以条件得顺序并不影响，同样也是满足最左前缀法则。</p><blockquote><p> 最左前缀法则中指的最左边的列，是指在查询时，联合索引的最左边的字段(即是 第一个字段)必须存在，与我们编写SQL时，条件编写的先后顺序无关。</p></blockquote><h3 id="范围查询"><a href="#范围查询" class="headerlink" title="范围查询"></a>范围查询</h3><p>联合索引中，出现范围查询(&gt;,&lt;)，范围查询右侧的列索引失效。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user where profession = &#x27;软件工程&#x27; and age &gt; 30 and status = &#x27;0&#x27;;</span><br><span class="line">explain select * from tb_user where profession = &#x27;软件工程&#x27; and age &gt;= 30 and status = &#x27;0&#x27;;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c85bb8e358.png" alt="image-20231109150946897"></p><p>当范围查询使用<code>&gt;</code> 或 <code>&lt;</code> 时，走联合索引了，但是索引的长度为38，就说明范围查询右边的status字 段是没有走索引的。</p><p>当范围查询使用<code>&gt;=</code> 或 <code>&lt;=</code>时，走联合索引了，索引的长度为42，就说明所有的字段都是走索引的。</p><p>所以，在业务允许的情况下，尽可能的使用类似于 &gt;= 或 &lt;= 这类的范围查询，而避免使用 &gt; 或 &lt; 。</p><h3 id="索引失效情况"><a href="#索引失效情况" class="headerlink" title="索引失效情况"></a>索引失效情况</h3><h4 id="索引列运算"><a href="#索引列运算" class="headerlink" title="索引列运算"></a>索引列运算</h4><p>不要在索引列上进行运算操作， 索引将失效。</p><p>在tb_user表中，除了前面介绍的联合索引之外，还有一个索引，是phone字段的单列索引。</p><p> 当根据phone字段进行等值匹配查询时, 索引生效。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user where phone = &#x27;17799990015&#x27;;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c86c92b18a.png" alt="image-20231109151416401"></p><p> 当根据phone字段进行函数运算操作之后，索引失效。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user where substring(phone,10,2) = &#x27;15&#x27;;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c87084e0c7.png" alt="image-20231109151519704"></p><h4 id="字符串不加引号"><a href="#字符串不加引号" class="headerlink" title="字符串不加引号"></a>字符串不加引号</h4><p>字符串类型字段使用时，不加引号，索引将失效。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user where profession = &#x27;软件工程&#x27; and age = 31 and status = &#x27;0&#x27;;</span><br><span class="line">explain select * from tb_user where profession = &#x27;软件工程&#x27; and age = 31 and status = 0;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c847630e9c.png" alt="image-20231109150421615"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user where phone = &#x27;17799990015&#x27;;</span><br><span class="line">explain select * from tb_user where phone = 17799990015;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c876ff1226.png" alt="image-20231109151703332"></p><h4 id="模糊查询"><a href="#模糊查询" class="headerlink" title="模糊查询"></a>模糊查询</h4><p>如果仅仅是尾部模糊匹配，索引不会失效。如果是头部模糊匹配，索引失效。</p><p>由于下面查询语句中，都是根据profession字段查询，符合最左前缀法则，联合索引是可以生效的， 我们主要看一下，模糊查询时，%加在关键字之前，和加在关键字之后的影响。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user where profession like &#x27;软件%&#x27;;</span><br><span class="line">explain select * from tb_user where profession like &#x27;%工程&#x27;;</span><br><span class="line">explain select * from tb_user where profession like &#x27;%工%&#x27;;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c87ce083de.png" alt="image-20231109151837361"></p><p>经过上述的测试，我们发现，在like模糊查询中，在关键字后面加%，索引可以生效。而如果在关键字 前面加了%，索引将会失效。</p><h4 id="or连接条件"><a href="#or连接条件" class="headerlink" title="or连接条件"></a>or连接条件</h4><p>用or分割开的条件， 如果or前的条件中的列有索引，而后面的列中没有索引，那么涉及的索引都不会被用到。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user where id = 10 or age = 23;</span><br><span class="line">explain select * from tb_user where phone = &#x27;17799990017&#x27; or age = 23;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c88479536a.png" alt="image-20231109152038899"></p><h3 id="数据分布影响"><a href="#数据分布影响" class="headerlink" title="数据分布影响"></a>数据分布影响</h3><p>如果MySQL评估使用索引比全表更慢，则不使用索引。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user where profession is null;</span><br><span class="line">explain select * from tb_user where profession is not null;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c8b6ed73bd.png" alt="image-20231109153406023"></p><p>在查询SQl时，MYSQL会评估是走索引快，还是走全表扫描快，如果全表 扫描更快，则放弃索引走全表扫描。 因此，is null 、is not null是否走索引，得具体情况具体 分析，并不是固定的。</p><p><strong>假设</strong><br>表中有1000条数据，age字段有15条是大于10的数据，此时MYSQL大概率是会走索引的，因为只有一部分的数据满足条件，所以MYSQL评估走索引可能更快</p><p>如果大于10的数据有900条，那MYSQL大概率是会走全表扫描不会走索引的，因为大多树数据都满足条件</p><h3 id="SQL提示"><a href="#SQL提示" class="headerlink" title="SQL提示"></a>SQL提示</h3><p>tb_user表数据如下</p><p><img src="https://bu.dusays.com/2023/11/09/654c9186dd45b.png" alt="image-20231109160005676"></p><p><img src="https://bu.dusays.com/2023/11/09/654c919adfde7.png" alt="image-20231109160026086"></p><p>这里可以看到，profession有两个索引，一个联合索引一个单列索引</p><p>执行SQL</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">eplain select * from tb_user where profession = &#x27;软件工程&#x27;;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c920665a3a.png" alt="image-20231109160213618"></p><p>测试结果，我们可以看到，possible_keys中 idx_user_pro_age_sta,idx_user_pro 这两个 索引都可能用到，最终MySQL选择了idx_user_pro_age_sta索引。这是MySQL<strong>自动选择的结果</strong>。</p><p>SQL提示，是优化数据库的一个重要手段，简单来说，就是在SQL语句中加入一些人为的提示来达到优 化操作的目的。</p><p>use index ： 建议MySQL使用哪一个索引完成此次查询（仅仅是建议，mysql内部还会再次进 行评估）。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user use index(idx_user_pro) where profession = &#x27;软件工程&#x27;;</span><br></pre></td></tr></table></figure><p>ignore index ： 忽略指定的索引。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user ignore index(idx_user_pro) where profession = &#x27;软件工程&#x27;;</span><br></pre></td></tr></table></figure><p>force index ： 强制使用索引。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain select * from tb_user force index(idx_user_pro) where profession = &#x27;软件工程&#x27;;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654c92ddd1746.png" alt="image-20231109160548918"></p><h3 id="覆盖索引"><a href="#覆盖索引" class="headerlink" title="覆盖索引"></a>覆盖索引</h3><p>尽量使用覆盖索引，减少select *。 那么什么是覆盖索引呢？ 覆盖索引是指 查询使用了索引，并且需要返回的列，在该索引中已经全部能够找到 。</p><p>使用select * 很容易出现回表查询</p><h3 id="前缀索引"><a href="#前缀索引" class="headerlink" title="前缀索引"></a>前缀索引</h3><p>故名意思就是把某个字段的前缀作为索引进行查询，通常用来处理大文本类型的如：varchar，text，longtext等，这些文本类型的字段如果不做前缀处理，那么会让索引变得很大，查询时，浪费大量的磁盘IO操作，影响查询效率，此时可以只将字符串的一部分前缀，建 立索引，这样可以大大节约索引空间，从而提高索引效率。</p><h4 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">create index idx_xxxx on table_name(column(n)) ;</span><br></pre></td></tr></table></figure><h4 id="前缀长度"><a href="#前缀长度" class="headerlink" title="前缀长度"></a>前缀长度</h4><p>可以根据索引的选择性来决定，而选择性是指不重复的索引值（基数）和数据表的记录总数的比值， 索引选择性越高则查询效率越高， 唯一索引的选择性是1，这是最好的索引选择性，性能也是最好的。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">select count(distinct email) / count(*) from tb_user ;</span><br><span class="line">select count(distinct substring(email,1,5)) / count(*) from tb_user ;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/09/654ca1c7e2988.png" alt="image-20231109170924275"></p><p><img src="https://bu.dusays.com/2023/11/09/654ca24437673.png" alt="image-20231109171130375"></p><h2 id="索引设计原则"><a href="#索引设计原则" class="headerlink" title="索引设计原则"></a>索引设计原则</h2><ul><li>针对于数据量较大，且查询比较频繁的表建立索引。 </li><li>针对于常作为查询条件（where）、排序（order by）、分组（group by）操作的字段建立索引。 </li><li>尽量选择区分度高的列作为索引，尽量建立唯一索引，区分度越高，使用索引的效率越高。 </li><li>如果是字符串类型的字段，字段的长度较长，可以针对于字段的特点，建立前缀索引。 </li><li>尽量使用联合索引，减少单列索引，查询时，联合索引很多时候可以覆盖索引，节省存储空间， 避免回表，提高查询效率。 </li><li>要控制索引的数量，索引并不是多多益善，索引越多，维护索引结构的代价也就越大，会影响增 删改的效率。</li><li>如果索引列不能存储NULL值，请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含 NULL值时，它可以更好地确定哪个索引最有效地用于查询。</li></ul>]]></content>
    
    
    <summary type="html">什么是索引？索引失效？</summary>
    
    
    
    <category term="后端开发" scheme="https://blog.serms.top/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/"/>
    
    <category term="数据库" scheme="https://blog.serms.top/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
    
    <category term="Mysql" scheme="https://blog.serms.top/tags/Mysql/"/>
    
  </entry>
  
  <entry>
    <title>Idea远程调试的几种方式</title>
    <link href="https://blog.serms.top/posts/a50538bc.html"/>
    <id>https://blog.serms.top/posts/a50538bc.html</id>
    <published>2023-11-05T10:27:20.000Z</published>
    <updated>2023-11-05T10:27:20.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在微服务项目中,假设项目在服务器部署上线了，此时该如何进行调式和排查问题呢？</p><h2 id="Jvm-Debug"><a href="#Jvm-Debug" class="headerlink" title="Jvm Debug"></a>Jvm Debug</h2><p>在<code>idea</code>中配置<code>Jvm Debug</code>，注意这里<code>Host</code>写自己主机ip地址,Port默认<code>5005</code>即可,模块选择你需要调试的模块</p><p><img src="https://bu.dusays.com/2023/11/05/65476f3ea2048.png" alt="image-20231105183228918"></p><p>在Jenkins中设置构建配置信息,配置好后重新启动docker服务,</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh root@192.168.150.101 <span class="string">&quot;/usr/local/src/script/startup.sh -c tj-trade -n tj-trade -d tj-trade -p 8088 -a 5005&quot;</span></span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/05/6547700aa3595.png" alt="image-20231105183553154"></p><p>可以看到启动之后也带上了5005端口</p><p><img src="https://bu.dusays.com/2023/11/05/6547726f5d62b.png" alt="image-20231105184606151"></p><p>在启动idea的JvmDebug,此时可以看到我Idea中之启动了一个JvmDebug应用,其余服务都没有启动</p><p>并且当控制台打印<code>transport: &#39;socket&#39;</code>说明,链接成功,正在进行通信了</p><p><img src="https://bu.dusays.com/2023/11/05/654772c3a87e9.png" alt="image-20231105184729493"></p><h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><p>此时,只需要在调用一次接口就可以查看是否已经可以实现远程调试的作用了</p>]]></content>
    
    
    <summary type="html">本地如何进行服务器bug调试排查呢？</summary>
    
    
    
    <category term="学习笔记" scheme="https://blog.serms.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    <category term="Bug记录" scheme="https://blog.serms.top/categories/Bug%E8%AE%B0%E5%BD%95/"/>
    
    
    <category term="远程调式" scheme="https://blog.serms.top/tags/%E8%BF%9C%E7%A8%8B%E8%B0%83%E5%BC%8F/"/>
    
  </entry>
  
  <entry>
    <title>Nacos服务下线报错</title>
    <link href="https://blog.serms.top/posts/d1d3609c.html"/>
    <id>https://blog.serms.top/posts/d1d3609c.html</id>
    <published>2023-11-05T08:22:12.000Z</published>
    <updated>2023-11-05T08:22:12.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>记录一下调试远程和本地代码的时候，下线Nacos远程服务报错问题</p><h2 id="问题复现"><a href="#问题复现" class="headerlink" title="问题复现"></a>问题复现</h2><p><code>192.168.150.1</code>是本地运行的服务, <code>101</code>是服务器部署的服务,现在我需要让接口调用Nacos的时候走<code>192.168.150.1</code>有两种方法,一种是调整某个服务的 <strong>优先级</strong>,第二种方法是直接下线某个服务,这里我采用的是第二种,直接下线服务的方式,此时就出现了一下的问题</p><p><img src="https://bu.dusays.com/2023/11/04/654653b822f06.png" alt=""></p><h2 id="问题分析"><a href="#问题分析" class="headerlink" title="问题分析"></a>问题分析</h2><p>nacos 采用 <code>raft</code>选举算法计算 <code>Leader</code>，并记录上次启动的集群地址，服务器 IP 改变时(网络环境不稳定，如WIFI， IP 地址也经常变化)会导致 raft 记录的集群地址失效，导致选 Leader 出现问题。</p><h2 id="解决方法"><a href="#解决方法" class="headerlink" title="解决方法"></a>解决方法</h2><h3 id="Nacos在本地"><a href="#Nacos在本地" class="headerlink" title="Nacos在本地"></a>Nacos在本地</h3><ul><li>删除Nacos根目录下data文件中的protocol文件夹,然后重启即可</li></ul><h3 id="Nacos在服务器中"><a href="#Nacos在服务器中" class="headerlink" title="Nacos在服务器中"></a>Nacos在服务器中</h3><p>相关命令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">docker <span class="built_in">exec</span> -it nacos /bin/bash<span class="comment">## 进入nacos容器</span></span><br><span class="line"><span class="built_in">cd</span> data/<span class="comment">## 进入data文件夹</span></span><br><span class="line"><span class="built_in">rm</span> -rf protocol/<span class="comment">## 删除文件夹</span></span><br><span class="line"><span class="built_in">exit</span> </span><br><span class="line"></span><br><span class="line">docker restart nacos  <span class="comment">## 重启Nacos容器</span></span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/11/05/6547550800ca6.png" alt="image-20231105164038279"></p><p>等待Nacos重启之后再次点击下线成功,此问题解决</p><p><img src="https://bu.dusays.com/2023/11/05/654755c6c7302.png" alt="image-20231105164349284"></p>]]></content>
    
    
    <summary type="html">Nacos服务下线报错的常见原因和解决方法，一文搞定！</summary>
    
    
    
    <category term="运维部署" scheme="https://blog.serms.top/categories/%E8%BF%90%E7%BB%B4%E9%83%A8%E7%BD%B2/"/>
    
    <category term="微服务" scheme="https://blog.serms.top/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
    
    <category term="Bug记录" scheme="https://blog.serms.top/categories/Bug%E8%AE%B0%E5%BD%95/"/>
    
    <category term="服务治理" scheme="https://blog.serms.top/categories/%E6%9C%8D%E5%8A%A1%E6%B2%BB%E7%90%86/"/>
    
    
    <category term="Nacos" scheme="https://blog.serms.top/tags/Nacos/"/>
    
  </entry>
  
  <entry>
    <title>Java扩展第三方Jar包</title>
    <link href="https://blog.serms.top/posts/8388fdb4.html"/>
    <id>https://blog.serms.top/posts/8388fdb4.html</id>
    <published>2023-10-31T08:21:57.000Z</published>
    <updated>2023-10-31T01:33:11.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>今天在<a href="https://www.bilibili.com/video/BV16J411h7Rd/">B站大学</a>学习并发编程的时候，老师引用了一个第三方的jar包（jol-core），maven坐标如下，来打印锁对象的Mark Word字节码，从而更直观察地多线程下加偏向锁的情况。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.openjdk.jol<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>jol-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>0.17<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><p>这些都是挺常规的操作，接着老师的骚操作来了，扩展了jar包里面的方法。弹幕里面都是惊呼声，老师改了jar包，我们学习者没法复现了呀。</p><p>我这人就喜欢钻牛角尖，评论区翻遍了也没见得有这个Jar包修改方法的 <strong>好心人</strong>，那就只能自己动手咯。</p><h2 id="视频地址"><a href="#视频地址" class="headerlink" title="视频地址"></a>视频地址</h2><iframe src="//player.bilibili.com/player.html?aid=81461839&bvid=BV16J411h7Rd&cid=1235831559&p=83" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe><h2 id="Jol-Core依赖加载失败"><a href="#Jol-Core依赖加载失败" class="headerlink" title="Jol-Core依赖加载失败"></a>Jol-Core依赖加载失败</h2><blockquote><p>如果你没有这个问题可直接跳过</p></blockquote><p>在加载Maven依赖的时候，Jol-Core这个Jar包就是拉不下来，因此我重新更新了一下Maven <code>settings.xml</code>中的  <strong>mirror</strong>,有需要的直接在你的 mirrors中追加即可</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">mirror</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">id</span>&gt;</span>aliyunmaven<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>*<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">name</span>&gt;</span>阿里云公共仓库<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://maven.aliyun.com/repository/public<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">mirror</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">id</span>&gt;</span>mirrorId<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>repositoryId<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">name</span>&gt;</span>Human Readable Name for this Mirror.<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">url</span>&gt;</span>http://my.repository.com/repo/path<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">mirror</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">id</span>&gt;</span>alimaven<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">name</span>&gt;</span>aliyun maven<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://maven.aliyun.com/repository/central<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>central<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">mirror</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">id</span>&gt;</span>sprintio<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>central<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">name</span>&gt;</span>Human Readable Name for this Mirror.<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://repo.spring.io/libs-snapshot/<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">mirror</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">id</span>&gt;</span>huaweicloud<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">name</span>&gt;</span>mirror from maven huaweicloud<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://mirror.huaweicloud.com/repository/maven/<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>central<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">mirror</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">id</span>&gt;</span>maven-default-http-blocker<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>external:http:*<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">name</span>&gt;</span>Pseudo repository to mirror external repositories initially using HTTP.<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">url</span>&gt;</span>http://0.0.0.0/<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">     <span class="tag">&lt;<span class="name">blocked</span>&gt;</span>true<span class="tag">&lt;/<span class="name">blocked</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="拉取源码"><a href="#拉取源码" class="headerlink" title="拉取源码"></a>拉取源码</h2><p>我这边目前是用的最新版本,直接在Maven除选中依赖右击 <code>Download Soures</code></p><p><img src="https://bu.dusays.com/2023/10/31/6540d041b492e.png" alt="image-20231031180032110"></p><p>在左侧项目的外部库下面展开对应jar包，然后右键选打开于Explore</p><p><img src="https://bu.dusays.com/2023/10/31/6540d2904537d.png" alt="image-20231031181021905"></p><p>找到<code>jol-core-0.17-sources.jar</code>包右击解压出来，我这边是用的<code>7-Zip</code> </p><p><img src="https://bu.dusays.com/2023/10/31/6540d2e5b507f.png" alt="image-20231031181148520"></p><h2 id="新建项目"><a href="#新建项目" class="headerlink" title="新建项目"></a>新建项目</h2><ol><li>新建一个项目</li><li>将解压出来的<code>org</code>文件夹复制到新建项目中的 <code>src/main/Java</code>下</li><li>将<code>META-INF</code>文件夹放入<code>resource</code>文件夹中</li><li>将<code>META-INF</code>下的<code>pom.xml</code>文件复制到项目根目录下</li><li>加载pom.xml, 运行Maven加载依赖即可</li></ol><p><img src="https://bu.dusays.com/2023/10/31/6540d3af9b22f.png" alt="image-20231031181509946"></p><h2 id="修改源码"><a href="#修改源码" class="headerlink" title="修改源码"></a>修改源码</h2><p>打开<code>ClassLayout</code>文件,双击两下<code>shift</code>可快速查找文件,在<code>Ctrl+O</code>查找<code>toPrintable</code>方法,可以看到默认没有改源码之前只有一个<code>toPrintable</code>方法 </p><p><img src="https://bu.dusays.com/2023/10/31/6540d58e3f98e.png" alt="image-20231031182309171"></p><p>这里我是参考<code>toPrintable()</code>方法进行重写的,看似吊炸天其实也就这样,没啥太多的代码,也就一百多行而已,那么我就来带大家一起来解读一下这个方法的作用吧</p><h3 id="源码解读"><a href="#源码解读" class="headerlink" title="源码解读"></a>源码解读</h3><p>如果您不需要解读请往下滑</p><p> <code>toPrintable</code> 主要用于输出一个对象的内部布局信息，包括对象的标头（Mark Word 和 Class Word）、数组长度（如果对象是数组），字段信息以及空间损失等。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> String <span class="title function_">toPrintable</span><span class="params">(Object instance)</span> &#123;</span><br><span class="line">    <span class="type">StringWriter</span> <span class="variable">sw</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringWriter</span>();</span><br><span class="line">    <span class="type">PrintWriter</span> <span class="variable">pw</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PrintWriter</span>(sw);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 计算最长的类型名称</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">maxTypeLen</span> <span class="operator">=</span> <span class="string">&quot;TYPE&quot;</span>.length();</span><br><span class="line">    <span class="keyword">for</span> (FieldLayout f : fields()) &#123;</span><br><span class="line">        maxTypeLen = Math.max(f.typeClass().length(), maxTypeLen);</span><br><span class="line">    &#125;</span><br><span class="line">    maxTypeLen += <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义一些描述信息的字符串</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">MSG_OBJ_HEADER</span> <span class="operator">=</span> <span class="string">&quot;(object header)&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">MSG_MARK_WORD</span> <span class="operator">=</span> <span class="string">&quot;(object header: mark)&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">MSG_CLASS_WORD</span> <span class="operator">=</span> <span class="string">&quot;(object header: class)&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">MSG_ARR_LEN</span> <span class="operator">=</span> <span class="string">&quot;(array length)&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">MSG_FIELD_GAP</span> <span class="operator">=</span> <span class="string">&quot;(alignment/padding gap)&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">MSG_OBJ_GAP</span> <span class="operator">=</span> <span class="string">&quot;(object alignment gap)&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 计算最长的描述信息的长度</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">maxDescrLen</span> <span class="operator">=</span> <span class="string">&quot;DESCRIPTION&quot;</span>.length();</span><br><span class="line">    maxDescrLen = Math.max(maxDescrLen, MSG_OBJ_HEADER.length());</span><br><span class="line">    maxDescrLen = Math.max(maxDescrLen, MSG_MARK_WORD.length());</span><br><span class="line">    maxDescrLen = Math.max(maxDescrLen, MSG_CLASS_WORD.length());</span><br><span class="line">    maxDescrLen = Math.max(maxDescrLen, MSG_FIELD_GAP.length());</span><br><span class="line">    maxDescrLen = Math.max(maxDescrLen, MSG_OBJ_GAP.length());</span><br><span class="line">    <span class="keyword">for</span> (FieldLayout f : fields()) &#123;</span><br><span class="line">        maxDescrLen = Math.max(f.shortFieldName().length(), maxDescrLen);</span><br><span class="line">    &#125;</span><br><span class="line">    maxDescrLen += <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义格式化字符串</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">format</span> <span class="operator">=</span> <span class="string">&quot;%3d %3d %&quot;</span> + maxTypeLen + <span class="string">&quot;s %-&quot;</span> + maxDescrLen + <span class="string">&quot;s %s%n&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">formatS</span> <span class="operator">=</span> <span class="string">&quot;%3s %3s %&quot;</span> + maxTypeLen + <span class="string">&quot;s %-&quot;</span> + maxDescrLen + <span class="string">&quot;s %s%n&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 检查传入的实例是否为null</span></span><br><span class="line">    <span class="keyword">if</span> (instance != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 加载类，用于检查实例类型是否匹配</span></span><br><span class="line">            Class&lt;?&gt; klass = ClassUtils.loadClass(classData.name());</span><br><span class="line">            <span class="keyword">if</span> (!klass.isAssignableFrom(instance.getClass())) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;Passed instance type &quot;</span> + instance.getClass() + <span class="string">&quot; is not assignable from &quot;</span> + klass + <span class="string">&quot;.&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;Class is not found: &quot;</span> + classData.name() + <span class="string">&quot;.&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 输出类的名称和对象内部信息的标题行</span></span><br><span class="line">    pw.println(classData.name() + <span class="string">&quot; object internals:&quot;</span>);</span><br><span class="line">    pw.printf(formatS, <span class="string">&quot;OFF&quot;</span>, <span class="string">&quot;SZ&quot;</span>, <span class="string">&quot;TYPE&quot;</span>, <span class="string">&quot;DESCRIPTION&quot;</span>, <span class="string">&quot;VALUE&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始化标头、类标头和数组长度的字符串</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">markStr</span> <span class="operator">=</span> <span class="string">&quot;N/A&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">classStr</span> <span class="operator">=</span> <span class="string">&quot;N/A&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">arrLenStr</span> <span class="operator">=</span> <span class="string">&quot;N/A&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取标头、类标头和数组长度的大小</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">markSize</span> <span class="operator">=</span> model.markHeaderSize();</span><br><span class="line">    <span class="type">int</span> <span class="variable">classSize</span> <span class="operator">=</span> model.classHeaderSize();</span><br><span class="line">    <span class="type">int</span> <span class="variable">arrSize</span> <span class="operator">=</span> model.arrayLengthHeaderSize();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 计算标头、类标头和数组长度的偏移</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">markOffset</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">classOffset</span> <span class="operator">=</span> markOffset + markSize;</span><br><span class="line">    <span class="type">int</span> <span class="variable">arrOffset</span> <span class="operator">=</span> classOffset + classSize;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 如果实例不为null，获取标头信息</span></span><br><span class="line">    <span class="keyword">if</span> (instance != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="type">VirtualMachine</span> <span class="variable">vm</span> <span class="operator">=</span> VM.current();</span><br><span class="line">        <span class="keyword">if</span> (markSize == <span class="number">8</span>) &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">mark</span> <span class="operator">=</span> vm.getLong(instance, markOffset);</span><br><span class="line">            <span class="type">String</span> <span class="variable">decoded</span> <span class="operator">=</span> (classSize &gt; <span class="number">0</span>) ? parseMarkWord(mark) : <span class="string">&quot;(Lilliput)&quot;</span>;</span><br><span class="line">            markStr = toHex(mark) + <span class="string">&quot; &quot;</span> + decoded;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (markSize == <span class="number">4</span>) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">mark</span> <span class="operator">=</span> vm.getInt(instance, markOffset);</span><br><span class="line">            <span class="type">String</span> <span class="variable">decoded</span> <span class="operator">=</span> (classSize &gt; <span class="number">0</span>) ? parseMarkWord(mark) : <span class="string">&quot;(Lilliput)&quot;</span>;</span><br><span class="line">            markStr = toHex(mark) + <span class="string">&quot; &quot;</span> + decoded;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果类标头有大小，获取类标头信息</span></span><br><span class="line">        <span class="keyword">if</span> (classSize == <span class="number">8</span>) &#123;</span><br><span class="line">            classStr = toHex(vm.getLong(instance, classOffset));</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (classSize == <span class="number">4</span>) &#123;</span><br><span class="line">            classStr = toHex(vm.getInt(instance, classOffset));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 如果类是数组，获取数组长度信息</span></span><br><span class="line">        <span class="keyword">if</span> (classData.isArray()) &#123;</span><br><span class="line">            arrLenStr = Integer.toString(vm.getInt(instance, arrOffset));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 输出标头、类标头和数组长度的信息</span></span><br><span class="line">    pw.printf(format, markOffset, markSize, <span class="string">&quot;&quot;</span>, MSG_MARK_WORD, markStr);</span><br><span class="line">    <span class="keyword">if</span> (classSize &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        pw.printf(format, classOffset, classSize, <span class="string">&quot;&quot;</span>, MSG_CLASS_WORD, classStr);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (classData.isArray()) &#123;</span><br><span class="line">        pw.printf(format, arrOffset, arrSize, <span class="string">&quot;&quot;</span>, MSG_ARR_LEN, arrLenStr);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">long</span> <span class="variable">nextFree</span> <span class="operator">=</span> headerSize();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 输出字段信息</span></span><br><span class="line">    <span class="keyword">for</span> (FieldLayout f : fields()) &#123;</span><br><span class="line">        <span class="keyword">if</span> (f.offset() &gt; nextFree) &#123;</span><br><span class="line">            pw.printf(format, nextFree, (f.offset() - nextFree), <span class="string">&quot;&quot;</span>, MSG_FIELD_GAP, <span class="string">&quot;&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">Field</span> <span class="variable">fi</span> <span class="operator">=</span> f.data().refField();</span><br><span class="line">        pw.printf(format,</span><br><span class="line">                  f.offset(),</span><br><span class="line">                  f.size(),</span><br><span class="line">                  f.typeClass(),</span><br><span class="line">                  f.shortFieldName(),</span><br><span class="line">                  (instance != <span class="literal">null</span> &amp;&amp; fi != <span class="literal">null</span>) ? ObjectUtils.safeToString(ObjectUtils.value(instance, fi)) : <span class="string">&quot;N/A&quot;</span></span><br><span class="line">                 );</span><br><span class="line"></span><br><span class="line">        nextFree = f.offset() + f.size();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">long</span> <span class="variable">sizeOf</span> <span class="operator">=</span> (instance != <span class="literal">null</span>) ? VM.current().sizeOf(instance) : instanceSize();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 输出总大小和空间损失信息</span></span><br><span class="line">    <span class="keyword">if</span> (sizeOf != nextFree) &#123;</span><br><span class="line">        pw.printf(format, nextFree, lossesExternal, <span class="string">&quot;&quot;</span>, MSG_OBJ_GAP, <span class="string">&quot;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    pw.printf(<span class="string">&quot;Instance size: %d bytes%n&quot;</span>, sizeOf);</span><br><span class="line">    pw.printf(<span class="string">&quot;Space losses: %d bytes internal + %d bytes external = %d bytes total%n&quot;</span>, lossesInternal, lossesExternal, lossesTotal);</span><br><span class="line"></span><br><span class="line">    pw.close();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> sw.toString();</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="重构方法"><a href="#重构方法" class="headerlink" title="重构方法"></a>重构方法</h3><p>可以看出<code>toPrintable</code>方法不止输出了MarkWord信息还有 Class Word、数组长度（如果对象是数组），字段信息以及空间损失等。</p><p>所以我们新增一个方法参考上述方法写就行了</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> String <span class="title function_">toPrintableSimpleSerMs</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> toPrintableSimpleSerMs(classData.instance());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> String <span class="title function_">toPrintableSimpleSerMs</span><span class="params">(Object instance)</span> &#123;</span><br><span class="line">    <span class="type">StringBuilder</span> <span class="variable">sb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">    <span class="type">String</span> <span class="variable">markStr</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="type">String</span> <span class="variable">remind</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取标头的大小</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">markSize</span> <span class="operator">=</span> model.markHeaderSize();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置标头的偏移</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">markOffset</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 如果传入的实例不为null，获取标头信息</span></span><br><span class="line">    <span class="keyword">if</span> (instance != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="type">VirtualMachine</span> <span class="variable">vm</span> <span class="operator">=</span> VM.current();</span><br><span class="line">        <span class="keyword">if</span> (markSize == <span class="number">8</span>) &#123;</span><br><span class="line">            <span class="comment">// 如果标头大小为8字节，获取标头信息并将其转换为二进制字符串</span></span><br><span class="line">            <span class="type">long</span> <span class="variable">mark</span> <span class="operator">=</span> vm.getLong(instance, markOffset);</span><br><span class="line">            markStr = Long.toBinaryString(mark);</span><br><span class="line">            <span class="comment">// 解析标头，获取额外的信息</span></span><br><span class="line">            remind = parseMarkWord(mark);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (markSize == <span class="number">4</span>) &#123;</span><br><span class="line">            <span class="comment">// 如果标头大小为4字节，获取标头信息并将其转换为二进制字符串</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">mark</span> <span class="operator">=</span> vm.getInt(instance, markOffset);</span><br><span class="line">            markStr = Integer.toBinaryString(mark);</span><br><span class="line">            <span class="comment">// 解析标头，获取额外的信息</span></span><br><span class="line">            remind = parseMarkWord(mark);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 高位补0，确保二进制字符串长度为标头大小的倍数</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> (; i &lt;= <span class="number">8</span> * markSize - markStr.length(); i++) &#123;</span><br><span class="line">        sb.append(<span class="string">&#x27;0&#x27;</span>);</span><br><span class="line">        <span class="keyword">if</span> (i % <span class="number">8</span> == <span class="number">0</span>) &#123;</span><br><span class="line">            sb.append(<span class="string">&quot; &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将标头的二进制字符串添加到输出中</span></span><br><span class="line">    <span class="keyword">for</span> (; i &lt;= <span class="number">8</span> * markSize; i++) &#123;</span><br><span class="line">        sb.append(markStr.charAt(i - (<span class="number">8</span> * markSize - markStr.length()) - <span class="number">1</span>));</span><br><span class="line">        <span class="keyword">if</span> (i % <span class="number">8</span> == <span class="number">0</span>) &#123;</span><br><span class="line">            sb.append(<span class="string">&quot; &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 添加解析后的标头信息</span></span><br><span class="line">    sb.append(remind);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回包含标头信息的字符串</span></span><br><span class="line">    <span class="keyword">return</span> sb.toString();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>另外我还写了另外的一个简约版本,  只会输出MarkWord 头中的最后三位，也就是锁和锁的状态</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 重写toPrintable方法，只输出Mark word二进制形式 Opt优化</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">toPrintableSimpleSimplicity</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> toPrintableSimpleSimplicity(classData.instance());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 重写toPrintable方法，只输出Mark word二进制形式 Opt优化</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * https://serms.top</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> instance</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">toPrintableSimpleSimplicity</span><span class="params">(Object instance)</span> &#123;</span><br><span class="line">    <span class="type">StringWriter</span> <span class="variable">sw</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringWriter</span>();</span><br><span class="line">    <span class="type">PrintWriter</span> <span class="variable">pw</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PrintWriter</span>(sw);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (instance != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="type">VirtualMachine</span> <span class="variable">vm</span> <span class="operator">=</span> VM.current();</span><br><span class="line">        <span class="type">long</span> <span class="variable">markWord</span> <span class="operator">=</span> vm.getLong(instance, <span class="number">0</span>); <span class="comment">// Assuming the Mark Word is a long (64 bits)</span></span><br><span class="line"></span><br><span class="line">        pw.println(<span class="string">&quot;Mark Word Simplicity (binary):&quot;</span>);</span><br><span class="line">        pw.println(toBinary(markWord));</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        pw.println(<span class="string">&quot;Mark Word: N/A&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    pw.close();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> sw.toString();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> String <span class="title function_">toBinary</span><span class="params">(<span class="type">long</span> value)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> Long.toBinaryString(value);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="重构Jar包"><a href="#重构Jar包" class="headerlink" title="重构Jar包"></a>重构Jar包</h2><ol><li><p>修改完代码之后<code>Ctrl+ F9</code>编译，编译之后找到原来的Jar包打开</p><p><img src="https://bu.dusays.com/2023/10/31/6540d8a476001.png" alt="image-20231031183619237"></p></li><li><p>找到<code>ClassLayout.class</code>文件删除</p><p><img src="https://bu.dusays.com/2023/10/31/6540d8e9e9964.png" alt="image-20231031183728890"></p></li><li><p>将修改好的<code>ClassLayout.class</code>文件复制进去</p><p><img src="https://bu.dusays.com/2023/10/31/6540d94b93bfa.png" alt="image-20231031183906267"></p></li></ol><h2 id="测试代码"><a href="#测试代码" class="headerlink" title="测试代码"></a>测试代码</h2><h3 id="方法一"><a href="#方法一" class="headerlink" title="方法一"></a>方法一</h3><p>调用新增方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> NoSuchFieldException, InterruptedException, IllegalAccessException &#123;</span><br><span class="line">    test1();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test1</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">A</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">A</span>();</span><br><span class="line">    out.println(ClassLayout.parseInstance(a).toPrintableSimpleSerMs());</span><br><span class="line">    <span class="keyword">synchronized</span> (a) &#123;</span><br><span class="line">        out.println(ClassLayout.parseInstance(a).toPrintableSimpleSerMs());</span><br><span class="line">    &#125;</span><br><span class="line">    out.println(ClassLayout.parseInstance(a).toPrintableSimpleSerMs());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101 (biasable; age: 0)</span><br><span class="line">00000000 00000000 00000001 11111011 01001010 10110100 01100000 00000101 (biased: 0x000000007ed2ad18; epoch: 0; age: 0)</span><br><span class="line">00000000 00000000 00000001 11111011 01001010 10110100 01100000 00000101 (biased: 0x000000007ed2ad18; epoch: 0; age: 0)</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/10/31/6540ddc3f0580.png" alt="image-20231031185810713"></p><h3 id="方法二"><a href="#方法二" class="headerlink" title="方法二"></a>方法二</h3><p>上述说到我写了两个方法，一个是打印得比较全得，一个是简约的，这里做个对比</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test2</span><span class="params">()</span> <span class="keyword">throws</span> NoSuchFieldException, IllegalAccessException, InterruptedException &#123;</span><br><span class="line">    <span class="type">A</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">A</span>();</span><br><span class="line">    out.println(<span class="string">&quot;befor hash&quot;</span>);</span><br><span class="line">    <span class="comment">//没有计算HASHCODE之前的对象头</span></span><br><span class="line">    out.println(ClassLayout.parseInstance(a).toPrintableSimpleSimplicity());</span><br><span class="line">    out.println(ClassLayout.parseInstance(a).toPrintableSimpleSerMs());</span><br><span class="line">    <span class="comment">//JVM 计算的hashcode</span></span><br><span class="line">    out.println(<span class="string">&quot;jvm‐‐‐‐‐‐‐‐‐‐‐‐0x&quot;</span> + Integer.toHexString(a.hashCode()));</span><br><span class="line">    <span class="comment">//当计算完hashcode之后，我们可以查看对象头的信息变化</span></span><br><span class="line">    out.println(<span class="string">&quot;after hash&quot;</span>);</span><br><span class="line">    out.println(ClassLayout.parseInstance(a).toPrintableSimpleSimplicity());</span><br><span class="line">    out.println(ClassLayout.parseInstance(a).toPrintableSimpleSerMs());</span><br><span class="line">    <span class="keyword">synchronized</span> (a) &#123;</span><br><span class="line">        out.println(<span class="string">&quot;对象a 已加锁 ---------&quot;</span>);</span><br><span class="line">        out.println(ClassLayout.parseInstance(a).toPrintableSimpleSimplicity());</span><br><span class="line">        out.println(ClassLayout.parseInstance(a).toPrintableSimpleSerMs());</span><br><span class="line">    &#125;</span><br><span class="line">    out.println(<span class="string">&quot;对象a 解锁 ---------&quot;</span>);</span><br><span class="line">    out.println(ClassLayout.parseInstance(a).toPrintableSimpleSimplicity());</span><br><span class="line">    out.println(ClassLayout.parseInstance(a).toPrintableSimpleSerMs());</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>打印结果：</p><ul><li>toPrintableSimpleSimplicity()方法打印在上</li><li>toPrintableSimpleSerMs()方法打印在下</li></ul><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">befor <span class="built_in">hash</span></span><br><span class="line">Mark Word Simplicity (binary):</span><br><span class="line">101  </span><br><span class="line"></span><br><span class="line">00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101 (biasable; age: 0)</span><br><span class="line">jvm‐‐‐‐‐‐‐‐‐‐‐‐0x573f2bb1</span><br><span class="line">after <span class="built_in">hash</span></span><br><span class="line">Mark Word Simplicity (binary):</span><br><span class="line">101011100111111001010111011000100000001</span><br><span class="line"></span><br><span class="line">00000000 00000000 00000000 01010111 00111111 00101011 10110001 00000001 (<span class="built_in">hash</span>: 0x573f2bb1; age: 0)</span><br><span class="line">对象a 已加锁 ---------</span><br><span class="line">Mark Word Simplicity (binary):</span><br><span class="line">1101110100101111111111111001000111000</span><br><span class="line"></span><br><span class="line">00000000 00000000 00000000 00011011 10100101 11111111 11110010 00111000 (thin lock: 0x0000001ba5fff238)</span><br><span class="line">对象a 解锁 ---------</span><br><span class="line">Mark Word Simplicity (binary):</span><br><span class="line">101011100111111001010111011000100000001</span><br><span class="line"></span><br><span class="line">00000000 00000000 00000000 01010111 00111111 00101011 10110001 00000001 (<span class="built_in">hash</span>: 0x573f2bb1; age: 0)</span><br><span class="line"></span><br><span class="line">Process finished with <span class="built_in">exit</span> code 0</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/10/31/6540e2e241c7b.png" alt="image-20231031192001040"></p><h2 id="导出Jar包"><a href="#导出Jar包" class="headerlink" title="导出Jar包"></a>导出Jar包</h2><p>Maven直接Install然后引入到项目中即可</p><p><img src="https://bu.dusays.com/2023/10/31/6540e3894cd09.png" alt="image-20231031192247760"></p>]]></content>
    
    
    <summary type="html">修改第三方Jar源码</summary>
    
    
    
    <category term="后端开发" scheme="https://blog.serms.top/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/"/>
    
    <category term="Java基础" scheme="https://blog.serms.top/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/Java%E5%9F%BA%E7%A1%80/"/>
    
    
    <category term="Java" scheme="https://blog.serms.top/tags/Java/"/>
    
  </entry>
  
  <entry>
    <title>synchronized原理</title>
    <link href="https://blog.serms.top/posts/edf9312a.html"/>
    <id>https://blog.serms.top/posts/edf9312a.html</id>
    <published>2023-10-31T01:33:11.000Z</published>
    <updated>2023-10-31T01:33:11.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Monitor-概念回顾"><a href="#Monitor-概念回顾" class="headerlink" title="Monitor 概念回顾"></a>Monitor 概念回顾</h2><h3 id="Java对象头"><a href="#Java对象头" class="headerlink" title="Java对象头"></a>Java对象头</h3><p><strong>以 32 位虚拟机为例</strong></p><p>名词解释：</p><ol><li><strong>Object Header (64 bits)</strong>: 它包含了<strong>对象的元信息</strong>以及用于垃圾回收和同步的数据。</li><li><strong>Mark Word (32 bits）</strong>: 这个部分通常<strong>包含用于垃圾回收和同步的标记信息</strong>。标记字包含了<strong>对象的哈希码</strong>、<strong>锁定状态</strong>、<strong>垃圾回收标记</strong>等信息。</li><li><strong>Klass Word (32 bits) </strong>: 这个部分包含指向对象的<strong>类元数据的指针</strong>，它描述了对象<strong>属于哪个类</strong>，包括类的<strong>方法</strong>、字段等信息。</li></ol><h4 id="普通对象"><a href="#普通对象" class="headerlink" title="普通对象"></a>普通对象</h4><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">|<span class="params">--------------------------------------------------------------</span>| </span><br><span class="line">|<span class="params">                     Object Header (64 bits)                  </span>| </span><br><span class="line">|<span class="params">------------------------------------</span>|-------------------------|<span class="params"> </span></span><br><span class="line"><span class="params"></span>|        <span class="title class_">Mark</span> <span class="title class_">Word</span> (<span class="number">32</span> bits)         |<span class="params">    Klass Word (32 bits) </span>| </span><br><span class="line">|<span class="params">------------------------------------</span>|-------------------------|<span class="params"></span></span><br></pre></td></tr></table></figure><h4 id="数组对象"><a href="#数组对象" class="headerlink" title="数组对象"></a>数组对象</h4><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">|<span class="params">---------------------------------------------------------------------------------</span>|</span><br><span class="line">|<span class="params">                                 Object Header (96 bits)                         </span>|</span><br><span class="line">|<span class="params">--------------------------------</span>|-----------------------|<span class="params">------------------------</span>|</span><br><span class="line">|<span class="params">        Mark Word(32bits)       </span>|    <span class="title class_">Klass</span> <span class="title class_">Word</span>(32bits) |<span class="params">  array length(32bits)  </span>|</span><br><span class="line">|<span class="params">--------------------------------</span>|-----------------------|<span class="params">------------------------</span>|</span><br></pre></td></tr></table></figure><h4 id="其中-Mark-Word-结构为"><a href="#其中-Mark-Word-结构为" class="headerlink" title="其中 Mark Word 结构为"></a>其中 Mark Word 结构为</h4><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">|<span class="params">-------------------------------------------------------</span>|--------------------|<span class="params"></span></span><br><span class="line"><span class="params"></span>|                  <span class="title class_">Mark</span> <span class="title class_">Word</span> (<span class="number">32</span> bits)                  |<span class="params">       State        </span>| </span><br><span class="line">|<span class="params">-------------------------------------------------------</span>|--------------------|<span class="params"> </span></span><br><span class="line"><span class="params"></span>|  <span class="symbol">hashcode:</span><span class="number">25</span>         |<span class="params"> age:4 </span>| <span class="symbol">biased_lock:</span><span class="number">0</span> |<span class="params"> 01     </span>|       <span class="title class_">Normal</span>       |<span class="params"> </span></span><br><span class="line"><span class="params"></span>|-------------------------------------------------------|<span class="params">--------------------</span>|</span><br><span class="line">|<span class="params">  thread:23 </span>| <span class="symbol">epoch:</span><span class="number">2</span> |<span class="params"> age:4 </span>| <span class="symbol">biased_lock:</span><span class="number">1</span> |<span class="params"> 01     </span>|       <span class="title class_">Biased</span>       |<span class="params"></span></span><br><span class="line"><span class="params"></span>|-------------------------------------------------------|<span class="params">--------------------</span>| </span><br><span class="line">|<span class="params">               ptr_to_lock_record:30          </span>| <span class="number">00</span>     |<span class="params"> Lightweight Locked </span>|</span><br><span class="line">|<span class="params">-------------------------------------------------------</span>|--------------------|<span class="params"></span></span><br><span class="line"><span class="params"></span>|               <span class="symbol">ptr_to_heavyweight_monitor:</span><span class="number">30</span>  |<span class="params"> 10     </span>| <span class="title class_">Heavyweight</span> <span class="title class_">Locked</span> |<span class="params"></span></span><br><span class="line"><span class="params"></span>|-------------------------------------------------------|<span class="params">--------------------</span>|</span><br><span class="line">|<span class="params">                                              </span>| <span class="number">11</span>     |<span class="params">    Marked <span class="keyword">for</span> GC   </span>|</span><br><span class="line">|<span class="params">-------------------------------------------------------</span>|--------------------|<span class="params"></span></span><br><span class="line"><span class="params">        </span></span><br><span class="line"><span class="params">hashcode：这部分通常用于存储对象的哈希码（HashCode），它是一个用于快速查找对象的值。哈希码在对象创建时生成，然后在对象的生命周期中不会更改。</span></span><br><span class="line"><span class="params">age (4 bits): 这部分用于表示对象的年龄，通常在分代垃圾回收中使用。年龄是一个对象存活的时间的度量。</span></span><br><span class="line"><span class="params">biased_lock (1 bit): 这个位用于标识对象是否启用了偏向锁。当偏向锁被启用时，该位为1；否则，为0。</span></span><br><span class="line"><span class="params">01 State: 这个状态表示对象处于正常状态，未被锁定或标记。</span></span><br><span class="line"><span class="params">    </span></span><br><span class="line"><span class="params">thread (23 bits): 这一部分用于存储拥有锁的线程的ID。在偏向锁状态下，它表示偏向锁的线程ID。</span></span><br><span class="line"><span class="params">epoch (2 bits): 这一部分用于存储偏向时间戳（bias timestamp）。它用于检测是否应取消偏向锁。在不同时间偏向锁的情况下，该值可能不同。</span></span><br><span class="line"><span class="params">age (4 bits): 这一部分用于表示对象的年龄，通常在分代垃圾回收中使用。年龄是一个对象存活的时间的度量。</span></span><br><span class="line"><span class="params">biased_lock (1 bit): 这个位用于标识对象是否启用了偏向锁。当偏向锁被启用时，该位为1。</span></span><br><span class="line"><span class="params">01 State: 这个状态表示对象处于偏向锁状态，已经偏向某个线程。</span></span><br><span class="line"><span class="params"></span></span><br><span class="line"><span class="params">ptr_to_lock_record (30 bits): 这一部分用于指向偏向锁的记录（bias lock record），该记录包含了关于偏向锁的详细信息。这在取消偏向锁时使用。</span></span><br><span class="line"><span class="params">00 State: 这个状态表示对象处于轻量级锁状态。</span></span><br><span class="line"><span class="params"></span></span><br><span class="line"><span class="params">ptr_to_heavyweight_monitor (30 bits): 这一部分用于指向重量级锁的监视器对象。重量级锁通常涉及多个线程之间的同步。</span></span><br><span class="line"><span class="params">10 State: 这个状态表示对象处于重量级锁状态。</span></span><br><span class="line"><span class="params"></span></span><br><span class="line"><span class="params">11 State: 这个状态表示对象被标记为垃圾回收（GC Marked），通常在垃圾回收期间使用。</span></span><br><span class="line"><span class="params"></span></span><br></pre></td></tr></table></figure><h4 id="64位虚拟机-Mark-Word"><a href="#64位虚拟机-Mark-Word" class="headerlink" title="64位虚拟机 Mark Word"></a>64位虚拟机 Mark Word</h4><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">|<span class="params">--------------------------------------------------------------------</span>|--------------------|<span class="params"></span></span><br><span class="line"><span class="params"></span>|                        <span class="title class_">Mark</span> <span class="title class_">Word</span> (<span class="number">64</span> bits)                         |<span class="params">       State        </span>|</span><br><span class="line">|<span class="params">--------------------------------------------------------------------</span>|--------------------|<span class="params"> </span></span><br><span class="line"><span class="params"></span>| <span class="symbol">unused:</span><span class="number">25</span> |<span class="params"> hashcode:31 </span>| <span class="symbol">unused:</span><span class="number">1</span> |<span class="params"> age:4 </span>| <span class="symbol">biased_lock:</span><span class="number">0</span> |<span class="params"> 01    </span>|    <span class="title class_">Normal</span> 无锁状态   |<span class="params"> </span></span><br><span class="line"><span class="params"></span>|--------------------------------------------------------------------|<span class="params">--------------------</span>| </span><br><span class="line">|<span class="params"> thread:54 </span>| <span class="symbol">epoch:</span><span class="number">2</span>     |<span class="params"> unused:1 </span>| <span class="symbol">age:</span><span class="number">4</span> |<span class="params"> biased_lock:1 </span>| <span class="number">01</span>    |<span class="params"> Biased 偏向锁       </span>| </span><br><span class="line">|<span class="params">--------------------------------------------------------------------</span>|--------------------|<span class="params"> </span></span><br><span class="line"><span class="params"></span>|             <span class="symbol">ptr_to_lock_record:</span><span class="number">62</span>                          |<span class="params"> 00    </span>| <span class="title class_">Lightweight</span> <span class="title class_">Locked</span>  轻量级锁|<span class="params"> </span></span><br><span class="line"><span class="params"></span>|--------------------------------------------------------------------|<span class="params">--------------------</span>| </span><br><span class="line">|<span class="params">             ptr_to_heavyweight_monitor:62                  </span>| <span class="number">10</span>    |<span class="params"> Heavyweight Locked 重量级锁</span>| </span><br><span class="line">|<span class="params">--------------------------------------------------------------------</span>|--------------------|<span class="params"> </span></span><br><span class="line"><span class="params"></span>|                                                            |<span class="params"> 11    </span>|    <span class="title class_">Marked</span> <span class="keyword">for</span> <span class="variable constant_">GC</span>   |<span class="params"> </span></span><br><span class="line"><span class="params"></span>|--------------------------------------------------------------------|<span class="params">--------------------</span>|</span><br></pre></td></tr></table></figure><h2 id="synchronized加锁过程"><a href="#synchronized加锁过程" class="headerlink" title="synchronized加锁过程"></a>synchronized加锁过程</h2><ol><li><strong>无锁状态（Unlocked）</strong>：一开始，对象处于无锁状态。这意味着没有线程持有该对象的锁。</li><li><strong>偏向锁检测（Biased Lock Check）</strong>：在进入同步块之前，JVM 会尝试检测对象是否已经偏向某个线程。偏向锁是一种优化，旨在减少不必要的竞争。如果对象已经被偏向某个线程，且当前线程是偏向线程，那么它可以直接进入同步块，跳过后续步骤。</li><li><strong>自旋锁（Spin Locking）</strong>：如果对象不是偏向任何线程，或者当前线程不是偏向线程，JVM 会尝试使用自旋锁来避免进入重量级锁。自旋锁是一种快速的锁获取尝试，线程会短暂自旋等待锁的释放，而不进入阻塞状态。</li><li><strong>轻量级锁尝试（Lightweight Lock Attempt）</strong>：如果自旋锁不成功，当前线程将尝试使用轻量级锁。此时，JVM会尝试在对象头中的 <code>Mark Word</code> 中设置标志来表示当前线程持有该对象的锁。</li><li><strong>竞争（Contention）</strong>：如果轻量级锁尝试失败，表示可能有其他线程也在竞争同一个锁，进入竞争状态。这时，JVM 将使用适当的机制来处理竞争，通常会升级锁为重量级锁。</li><li><strong>重量级锁（Heavyweight Lock）</strong>：如果竞争仍然存在，JVM 将升级锁为重量级锁。重量级锁使用操作系统的原生同步机制，例如互斥量，来确保同一时刻只有一个线程可以进入同步块。其他线程将被阻塞，直到持有锁的线程释放它。</li><li><strong>执行同步块（Executing Synchronized Block）</strong>：一旦线程成功获取锁，它可以进入同步块内执行相应的代码。只有一个线程可以同时执行同步块内的代码。</li><li><strong>释放锁（Release Lock）</strong>：当线程退出同步块或抛出异常时，它会释放锁，允许其他线程竞争该锁。</li></ol><h2 id="轻量级锁"><a href="#轻量级锁" class="headerlink" title="轻量级锁"></a>轻量级锁</h2><p>轻量级锁的使用场景：如果一个对象虽然有多线程要加锁，但<strong>加锁的时间是错开的（也就是没有竞争）</strong>，那么可以 使用<strong>轻量级锁来优化</strong>。</p><p>轻量级锁对使用者是透明的，即语法仍然是 synchronized</p><p>假设有两个方法同步块，利用同一个对象加锁</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">method1</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">synchronized</span>( obj ) &#123;</span><br><span class="line">        <span class="comment">// 同步块 A</span></span><br><span class="line">        method2();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">method2</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">synchronized</span>( obj ) &#123;</span><br><span class="line">        <span class="comment">// 同步块 B</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>创建锁记录（Lock Record）对象，每个线程都的栈帧都会包含一个锁记录的结构，内部可以存储锁定对象的 Mark Word</li></ul><p><img src="https://bu.dusays.com/2023/10/31/65407ab1aaa5b.png" alt=""></p><ul><li>让锁记录中 Object reference 指向锁对象，并尝试用 cas 替换 Object 的 Mark Word，将 Mark Word 的值存 入锁记录</li></ul><p><img src="https://bu.dusays.com/2023/10/31/65407aec0b215.png" alt="image-20231031115626931"></p><ul><li><p>如果 cas 替换成功，对象头中存储了 锁记录地址和状态 00 ，表示由该线程给对象加锁，这时图示如下</p><p><img src="https://bu.dusays.com/2023/10/31/65409fcebb4e5.png" alt="image-20231031143349476"></p></li><li><p>如果 cas 失败，有两种情况 </p><ul><li>如果是其它线程已经持有了该 Object 的轻量级锁，这时表明有竞争，进入锁膨胀过程 </li><li>如果是自己执行了 synchronized 锁重入，那么再添加一条 Lock Record 作为重入的计数</li></ul></li></ul><p><img src="https://bu.dusays.com/2023/10/31/6540a0592d705.png" alt="image-20231031143607867"></p><ul><li><p>当退出 synchronized 代码块（解锁时）如果有取值为 null 的锁记录，表示有重入，这时重置锁记录，表示重 入计数减一</p><p><img src="https://bu.dusays.com/2023/10/31/6540a082d7bc0.png" alt="image-20231031143649632"></p></li><li><p>当退出 synchronized 代码块（解锁时）锁记录的值不为 null，这时使用 cas 将 Mark Word 的值恢复给对象 头 </p><ul><li>成功，则解锁成功 </li><li>失败，说明轻量级锁进行了锁膨胀或已经升级为重量级锁，进入重量级锁解锁流程</li></ul></li></ul><h2 id="锁膨胀"><a href="#锁膨胀" class="headerlink" title="锁膨胀"></a>锁膨胀</h2><p>如果在尝试加轻量级锁的过程中，CAS 操作无法成功，这时一种情况就是有其它线程为此对象加上了轻量级锁（有竞争），这时需要进行锁膨胀，将轻量级锁变为重量级锁。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">method1</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">synchronized</span>( obj ) &#123;</span><br><span class="line">        <span class="comment">// 同步块</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>当 Thread-1 进行轻量级加锁时，Thread-0 已经对该对象加了轻量级锁</li></ul><p><img src="https://bu.dusays.com/2023/10/31/6540a0face4e1.png" alt="image-20231031143825427"></p><ul><li><p>这时 Thread-1 加轻量级锁失败，进入锁膨胀流程     </p><ul><li>即为 Object 对象申请 Monitor 锁，让 Object 指向重量级锁地址 </li><li>然后自己进入 Monitor 的 EntryList BLOCKED</li></ul></li><li><p>当 Thread-0 退出同步块解锁时，使用 cas 将 Mark Word 的值恢复给对象头，失败。这时会进入重量级解锁 流程，即按照 Monitor 地址找到 Monitor 对象，设置 Owner 为 null，唤醒 EntryList 中 BLOCKED 线程</p></li></ul><h2 id="自旋优化"><a href="#自旋优化" class="headerlink" title="自旋优化"></a>自旋优化</h2><p>重量级锁竞争的时候，还可以使用自旋来进行优化，如果当前线程自旋成功（即这时候持锁线程已经退出了同步 块，释放了锁），这时当前线程就可以避免阻塞。</p><p> 自旋重试成功的情况</p><p><img src="https://bu.dusays.com/2023/10/31/6540a1666e84d.png" alt="image-20231031144036930"></p><p>自旋重试失败的情况</p><p><img src="https://bu.dusays.com/2023/10/31/6540a190d130b.png" alt="image-20231031144119594"></p><ul><li>自旋会占用 CPU 时间，单核 CPU 自旋就是浪费，多核 CPU 自旋才能发挥优势。</li><li>在 Java 6 之后自旋锁是自适应的，比如对象刚刚的一次自旋操作成功过，那么认为这次自旋成功的可能性会高，就多自旋几次；反之，就少自旋甚至不自旋，总之，比较智能。 </li><li>Java 7 之后不能控制是否开启自旋功能,由操作系统底层控制</li></ul><h2 id="偏向锁"><a href="#偏向锁" class="headerlink" title="偏向锁"></a>偏向锁</h2><p>轻量级锁在没有竞争时（就自己这个线程），每次重入仍然需要执行 CAS 操作。 </p><p>Java 6 中引入了偏向锁来做进一步优化：只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头，之后发现 这个线程 ID 是自己的就表示没有竞争，不用重新 CAS。</p><p>以后只要不发生竞争，这个对象就归该线程所有 例如：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">m1</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">synchronized</span>( obj ) &#123;</span><br><span class="line">        <span class="comment">// 同步块 A</span></span><br><span class="line">        m2();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">m2</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">synchronized</span>( obj ) &#123;</span><br><span class="line">        <span class="comment">// 同步块 B</span></span><br><span class="line">        m3();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">m3</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">synchronized</span>( obj ) &#123;</span><br><span class="line">        <span class="comment">// 同步块 C</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/10/31/6540a21006e00.png" alt="image-20231031144326659"></p><p><img src="https://bu.dusays.com/2023/10/31/6540a2172cd60.png" alt="image-20231031144334098"></p><h3 id="偏向状态"><a href="#偏向状态" class="headerlink" title="偏向状态"></a>偏向状态</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">|--------------------------------------------------------------------|--------------------|</span><br><span class="line">|                        Mark <span class="title function_">Word</span> <span class="params">(<span class="number">64</span> bits)</span>                         |       State        |</span><br><span class="line">|--------------------------------------------------------------------|--------------------| </span><br><span class="line">| unused:<span class="number">25</span> | hashcode:<span class="number">31</span> | unused:<span class="number">1</span> | age:<span class="number">4</span> | biased_lock:<span class="number">0</span> | <span class="number">01</span>    |    Normal          | </span><br><span class="line">|--------------------------------------------------------------------|--------------------| </span><br><span class="line">| thread:<span class="number">54</span> | epoch:<span class="number">2</span>     | unused:<span class="number">1</span> | age:<span class="number">4</span> | biased_lock:<span class="number">1</span> | <span class="number">01</span>    | Biased 偏向锁       | </span><br><span class="line">|--------------------------------------------------------------------|--------------------| </span><br><span class="line">|             ptr_to_lock_record:<span class="number">62</span>                          | <span class="number">00</span>    | Lightweight Locked | </span><br><span class="line">|--------------------------------------------------------------------|--------------------| </span><br><span class="line">|             ptr_to_heavyweight_monitor:<span class="number">62</span>                  | <span class="number">10</span>    | Heavyweight Locked | </span><br><span class="line">|--------------------------------------------------------------------|--------------------| </span><br><span class="line">|                                                            | <span class="number">11</span>    |    Marked <span class="keyword">for</span> GC   | </span><br><span class="line">|--------------------------------------------------------------------|--------------------|</span><br></pre></td></tr></table></figure><p>一个对象创建时： </p><ul><li>如果开启了偏向锁（默认开启），那么对象创建后，markword 值为 0x05 即最后 3 位为 101，这时它的 thread、epoch、age 都为 0 </li><li>偏向锁是默认是延迟的，不会在程序启动时立即生效，如果想避免延迟，可以加 VM 参数 <code>-XX:BiasedLockingStartupDelay=0</code> 来禁用延迟 </li><li>如果没有开启偏向锁，那么对象创建后，markword 值为 0x01 即最后 3 位为 001，这时它的 hashcode、 age 都为 0，第一次用到 hashcode 时才会赋值</li></ul><h3 id="测试偏向锁延迟特性"><a href="#测试偏向锁延迟特性" class="headerlink" title="测试偏向锁延迟特性"></a>测试偏向锁延迟特性</h3><p>默认<code>synchronized</code> 加锁是先加的偏向锁</p><p>首先配置: <code>-XX:BiasedLockingStartupDelay=0</code> 禁用延迟,如果不禁用,请用sleep方法让主线程睡不低于4s,因为这是偏向锁的特性,延迟加载</p><p><img src="https://bu.dusays.com/2023/11/01/6541b37b727fc.png" alt="image-20231101101002777"></p><h4 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test3</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">A</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">A</span>();</span><br><span class="line">    <span class="type">ClassLayout</span> <span class="variable">classLayout</span> <span class="operator">=</span> ClassLayout.parseInstance(a);</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;加锁前:&#123;&#125;&quot;</span>, classLayout.toPrintableSimpleSerMs());</span><br><span class="line">        <span class="keyword">synchronized</span> (a) &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;加锁之后：&#123;&#125;&quot;</span>, classLayout.toPrintableSimpleSerMs());</span><br><span class="line">        &#125;</span><br><span class="line">        log.debug(<span class="string">&quot;解锁后:&#123;&#125;&quot;</span>, classLayout.toPrintableSimpleSerMs());</span><br><span class="line">    &#125;, <span class="string">&quot;T1&quot;</span>).start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="打印结果"><a href="#打印结果" class="headerlink" title="打印结果"></a>打印结果</h4><ul><li>第一次打印锁状态为 <code>101</code> 为偏向锁状态</li><li>第二次对A对象进行加锁操作,最后三位可以看到还是<code>101</code>依然是偏向锁,不同的是后面的54为多了T1线程的ThreadId</li><li>第三次解锁之后打印的结果跟第二次一样,这也就是偏向锁的思想,偏向锁的对象解锁后，线程 id 仍存储于对象头中</li></ul><p><img src="https://bu.dusays.com/2023/11/01/6541b8a29b6fb.png" alt="image-20231101103202099"></p><p>上述的<code>toPrintableSimpleSerMs()</code>方法是基于Jol-core Jar包进行的扩展方法,详细可以看这篇文章</p><div calss='anzhiyu-tag-link'><a class="tag-Link" target="_blank" href="https://blog.serms.top/post/8388fdb4/">    <div class="tag-link-tips">引用站外地址</div>    <div class="tag-link-bottom">        <div class="tag-link-left" style="background-size: 60px 60px;" style="">          <i class="anzhiyufont anzhiyu-icon-link" style=""></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">https://bu.dusays.com/2023/10/11/65269ea6226c8.png </div>            <div class="tag-link-sitename"> Java扩展第三方Jar </div>        </div>        <i class="anzhiyufont anzhiyu-icon-angle-right"></i>    </div>    </a></div><h3 id="测试禁用偏向锁"><a href="#测试禁用偏向锁" class="headerlink" title="测试禁用偏向锁"></a>测试禁用偏向锁</h3><p>在运行配置中设置<code>VM Options</code> <code>-XX:-UseBiasedLocking</code></p><p><img src="https://bu.dusays.com/2023/11/01/6541b9d8787ae.png" alt="image-20231101103711769"></p><h4 id="打印结果-1"><a href="#打印结果-1" class="headerlink" title="打印结果"></a>打印结果</h4><ul><li>因禁用了偏向锁,所以第一次打印的最后三位为<code>001</code>处于无锁的状态</li><li>第二次是加锁,最后三位为<code>000</code>偏向锁被禁用了,只能升级为轻量级锁,前面54为依然为<code>ThreadId</code></li><li>第三次解锁之后,回到无锁状态<code>001</code></li></ul><p><img src="https://bu.dusays.com/2023/11/01/6541b9fb30913.png" alt="image-20231101103746789"></p><h3 id="测试HashCode"><a href="#测试HashCode" class="headerlink" title="测试HashCode"></a>测试HashCode</h3><p>运行配置改成取消延迟加载</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test3</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">A</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">A</span>();</span><br><span class="line">    <span class="type">ClassLayout</span> <span class="variable">classLayout</span> <span class="operator">=</span> ClassLayout.parseInstance(a);</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;加锁前:&#123;&#125;&quot;</span>, classLayout.toPrintableSimpleSerMs());</span><br><span class="line">        a.hashCode();</span><br><span class="line">        <span class="keyword">synchronized</span> (a) &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;加锁之后：&#123;&#125;&quot;</span>, classLayout.toPrintableSimpleSerMs());</span><br><span class="line">        &#125;</span><br><span class="line">        log.debug(<span class="string">&quot;解锁后:&#123;&#125;&quot;</span>, classLayout.toPrintableSimpleSerMs());</span><br><span class="line">    &#125;, <span class="string">&quot;T1&quot;</span>).start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="打印结果-2"><a href="#打印结果-2" class="headerlink" title="打印结果"></a>打印结果</h4><ul><li>第一次后三位为<code>101</code>,偏向锁状态</li><li>第二次加锁后升级为<code>轻量级锁</code>,后面的 62位记录的是偏向锁的详细信息,在取消加锁的时候会用到</li><li>第三次取消加锁之后,后三位为<code>001</code>无锁状态,前面的为HashCode值</li></ul><p><img src="https://bu.dusays.com/2023/11/01/6541bb9759168.png" alt="image-20231101104438972"></p>]]></content>
    
    
    <summary type="html">synchronized原理深度解析</summary>
    
    
    
    <category term="学习笔记" scheme="https://blog.serms.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
    <category term="源码解析" scheme="https://blog.serms.top/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
    
    <category term="synchronized" scheme="https://blog.serms.top/tags/synchronized/"/>
    
  </entry>
  
  <entry>
    <title>LeetCode209:MinSubArrayLen</title>
    <link href="https://blog.serms.top/posts/cbdb7b6f.html"/>
    <id>https://blog.serms.top/posts/cbdb7b6f.html</id>
    <published>2023-10-30T08:23:37.000Z</published>
    <updated>2023-10-30T08:23:37.000Z</updated>
    
    <content type="html"><![CDATA[<div class="tip home"><p><img src="https://bu.dusays.com/2023/10/25/6538926ef38e9.webp" alt=""><br><span class='p center logo large'>坚持就是胜利</span></p></div><h2 id="LeetCode地址"><a href="#LeetCode地址" class="headerlink" title="LeetCode地址"></a>LeetCode地址</h2><div calss='anzhiyu-tag-link'><a class="tag-Link" target="_blank" href="https://leetcode.cn/problems/minimum-size-subarray-sum/">    <div class="tag-link-tips">引用站外地址</div>    <div class="tag-link-bottom">        <div class="tag-link-left" style="background-size: 60px 60px;" style="background-image: url(https://bu.dusays.com/2023/10/25/653891265eac8.webp)">          <i class="anzhiyufont anzhiyu-icon-link" style="display: none"></i>        </div>        <div class="tag-link-right">            <div class="tag-link-title">LeetCode </div>            <div class="tag-link-sitename">209</div>        </div>        <i class="anzhiyufont anzhiyu-icon-angle-right"></i>    </div>    </a></div><h2 id="滑动窗口解题思路"><a href="#滑动窗口解题思路" class="headerlink" title="滑动窗口解题思路"></a>滑动窗口解题思路</h2><h3 id="初始化变量和窗口"><a href="#初始化变量和窗口" class="headerlink" title="初始化变量和窗口"></a>初始化变量和窗口</h3><ul><li>定义两个指针 left 和 right，分别表示窗口的左右边界，初始化为0。 </li><li>定义一个变量 sum 用于存储窗口内元素的和，初始值为0。 </li><li>定义一个变量 minlength 用于记录符合条件的最短子数组的长度，初始值为0。</li></ul><h3 id="移动右边界，扩大窗口"><a href="#移动右边界，扩大窗口" class="headerlink" title="移动右边界，扩大窗口"></a>移动右边界，扩大窗口</h3><ul><li>在一个 while 循环中，不断将 nums[right] 加到 sum 中，然后将右指针 right 向右移动，扩大窗口。<h3 id="移动左边界，缩小窗口"><a href="#移动左边界，缩小窗口" class="headerlink" title="移动左边界，缩小窗口"></a>移动左边界，缩小窗口</h3></li><li>如果当前窗口的和大于等于目标值 target，则在另一个内部的 while 循环中，不断将 nums[left] 从 sum 中减去，并将左指针 left 向右移动，缩小窗口，直到窗口内的和小于目标值。 </li><li>在这个过程中，不断更新 minlength，保持其为符合条件的最短子数组的长度。<h3 id="循环直到右指针到达数组末尾"><a href="#循环直到右指针到达数组末尾" class="headerlink" title="循环直到右指针到达数组末尾"></a>循环直到右指针到达数组末尾</h3></li><li>不断执行步骤2和步骤3，直到右指针 right 到达数组的末尾。<h3 id="返回结果"><a href="#返回结果" class="headerlink" title="返回结果"></a>返回结果</h3></li><li>返回 minlength，即为符合条件的最短子数组的长度。</li></ul><h2 id="代码展示"><a href="#代码展示" class="headerlink" title="代码展示"></a>代码展示</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">minSubArrayLen</span><span class="params">(<span class="type">int</span> target, <span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> <span class="number">0</span>; <span class="comment">// 左指针</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">right</span> <span class="operator">=</span> <span class="number">0</span>; <span class="comment">// 右指针</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>; <span class="comment">// 子数组和</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">minlength</span> <span class="operator">=</span> <span class="number">0</span>; <span class="comment">// 最小子数组长度</span></span><br><span class="line">        <span class="keyword">while</span> (right &lt; nums.length) &#123;</span><br><span class="line">            sum += nums[right]; <span class="comment">// 添加当前元素到和</span></span><br><span class="line">            <span class="keyword">while</span> (sum &gt;= target) &#123; <span class="comment">// 当和大于等于目标值时</span></span><br><span class="line">                <span class="keyword">if</span> (right - left + <span class="number">1</span> &lt; minlength || minlength == <span class="number">0</span>) &#123; <span class="comment">// 如果当前子数组长度小于之前的子数组长度</span></span><br><span class="line">                    minlength = right - left + <span class="number">1</span>; <span class="comment">// 更新最小子数组长度</span></span><br><span class="line">                &#125;</span><br><span class="line">                sum -= nums[left]; <span class="comment">// 从左边移除元素，减小和</span></span><br><span class="line">                left++; <span class="comment">// 左指针向右移动</span></span><br><span class="line">            &#125;</span><br><span class="line">            right++; <span class="comment">// 右指针向右移动</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> minlength; <span class="comment">// 返回最小子数组长度</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h2 id="双指针解题思路"><a href="#双指针解题思路" class="headerlink" title="双指针解题思路"></a>双指针解题思路</h2><h3 id="初始化变量和窗口-1"><a href="#初始化变量和窗口-1" class="headerlink" title="初始化变量和窗口"></a>初始化变量和窗口</h3><ul><li>size 表示数组的长度，ans 用于记录符合条件的最短子数组的长度，初始化为 size + 1，确保初始值大于任何可能的子数组长度。 </li><li>l 表示窗口的左边界，初始化为0。 </li><li>sum 表示窗口内元素的和，初始化为0。<h3 id="移动右边界，扩大窗口-1"><a href="#移动右边界，扩大窗口-1" class="headerlink" title="移动右边界，扩大窗口"></a>移动右边界，扩大窗口</h3></li><li>使用一个 for 循环，遍历数组，移动右指针 r，将 nums[r] 加到 sum 中，扩大窗口。<h3 id="移动左边界，缩小窗口-1"><a href="#移动左边界，缩小窗口-1" class="headerlink" title="移动左边界，缩小窗口"></a>移动左边界，缩小窗口</h3></li><li>在一个内部的 while 循环中，如果当前窗口的和大于等于目标值 target，则计算当前子数组的长度 r - l + 1，并更新 ans 为较小的值，即 Math.min(ans, r - l + 1)。 </li><li>然后将窗口的左边界向右移动，即 sum -= nums[l++]，缩小窗口。<h3 id="循环直到右指针到达数组末尾-1"><a href="#循环直到右指针到达数组末尾-1" class="headerlink" title="循环直到右指针到达数组末尾"></a>循环直到右指针到达数组末尾</h3></li><li>不断执行步骤2和步骤3，直到右指针 r 到达数组的末尾。<h3 id="返回结果-1"><a href="#返回结果-1" class="headerlink" title="返回结果"></a>返回结果</h3></li><li>返回 ans，即为符合条件的最短子数组的长度。如果 ans 的值没有被更新，说明没有符合条件的子数组，返回0。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">// 双指针</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">minSubArrayLen03</span><span class="params">(<span class="type">int</span> target, <span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> <span class="number">0</span>, right = <span class="number">0</span>, min = Integer.MAX_VALUE, sum = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (right &lt; nums.length) &#123;</span><br><span class="line">        sum += nums[right];</span><br><span class="line">        <span class="keyword">while</span> (sum &gt;= target) &#123;</span><br><span class="line">            min = Math.min(min, right - left + <span class="number">1</span>);</span><br><span class="line">            sum -= nums[left++];</span><br><span class="line">        &#125;</span><br><span class="line">        right++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> min &lt;= nums.length ? min : <span class="number">0</span>;</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure><span class='p center logo large'>如果您还有更好的解题思路请在下方评论区交流</span> </li></ul>]]></content>
    
    
    <summary type="html">LeetCode209长度最小的子数组</summary>
    
    
    
    <category term="数据结构与算法" scheme="https://blog.serms.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
    
    <category term="算法题解" scheme="https://blog.serms.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/%E7%AE%97%E6%B3%95%E9%A2%98%E8%A7%A3/"/>
    
    
    <category term="算法解析" scheme="https://blog.serms.top/tags/%E7%AE%97%E6%B3%95%E8%A7%A3%E6%9E%90/"/>
    
  </entry>
  
  <entry>
    <title>无需VIP，一键转存海量文件</title>
    <link href="https://blog.serms.top/posts/4180f06.html"/>
    <id>https://blog.serms.top/posts/4180f06.html</id>
    <published>2023-10-27T08:44:46.000Z</published>
    <updated>2023-10-27T08:44:46.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>最近在网上找到一批好的学习资料，想保存到网盘，奈何贫穷的我没有雄厚的财力无法支撑我去开网盘的VIP，不知道大家是否也有过这头疼的问题</p><p>因此给大家安利一波一个无需VIP，一键转存海量文件的工具，让你可以免费转存任意文件到百度网盘</p><h2 id="百度开发者"><a href="#百度开发者" class="headerlink" title="百度开发者"></a>百度开发者</h2><p>百度开放平台进行注册</p><p><img src="https://bu.dusays.com/2023/10/27/653b7a0a27468.png" alt=""></p><ol><li><p>注册之后点击<a href="https://pan.baidu.com/union/console/applist">控制台</a>创建应用,然后你就会得到如下信息</p><p><img src="https://bu.dusays.com/2023/10/27/653b7a92e9897.png" alt=""></p></li><li><p>进入到<a href="https://pan.baidu.com/union/doc/al0rwqzzl">百度开放平台</a>,找到<code>接入授权</code>下的<code>授权码模式授权</code>,当然你也可以不用进,因为接口在下方我以贴出来了</p><p><img src="https://bu.dusays.com/2023/10/27/653b7b3569154.png" alt=""></p></li></ol><h2 id="发起授权码Code请求"><a href="#发起授权码Code请求" class="headerlink" title="发起授权码Code请求"></a>发起授权码Code请求</h2><p>发起授权码 Code 请求依赖于以下链接</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">GET http://openapi.baidu.com/oauth/2.0/authorize?</span><br><span class="line">response_type=code&amp;</span><br><span class="line">client_id=您应用的AppKey&amp;</span><br><span class="line">redirect_uri=您应用的授权回调地址&amp;</span><br><span class="line">scope=basic,netdisk&amp;</span><br><span class="line">device_id=您应用的AppID</span><br><span class="line"></span><br><span class="line">**注意**:这里的回调地址如果你没有就直接写`redirect_uri=oob`</span><br><span class="line">以上链接示例中参数仅给出了必选参数，其中device_id为硬件应用下的必选参数。</span><br><span class="line">关于应用的相关信息，您可在控制台，点进去您对应的应用，查看应用详情获得。</span><br></pre></td></tr></table></figure><p>上述用到的<code>AppKey</code>、<code>AppId</code>可在控制台中的<a href="https://pan.baidu.com/union/console/applist">应用</a>里面查看,<a href="https://developer.baidu.com/wiki/index.php?title=docs/oauth/redirect">回调地址</a>请查看官方文档说明进行配置</p><p><strong>注意</strong>:这里的回调地址如果你没有就直接写<code>redirect_uri=oob</code></p><p><img src="https://bu.dusays.com/2023/10/27/653b7c467ff38.png" alt=""></p><p>直接将填写好的api接口放到<code>浏览器</code>中请求，浏览器默认就是<code>GET</code>请求,请求之后你会得到一个<strong>授权码</strong></p><p><img src="https://bu.dusays.com/2023/10/27/653b7c8d6faf9.png" alt="image-20231027170203296"></p><h2 id="换取AccessToken凭证"><a href="#换取AccessToken凭证" class="headerlink" title="换取AccessToken凭证"></a>换取AccessToken凭证</h2><p>拿到上面请求到的授权码接着调用这个接口</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">GET https://openapi.baidu.com/oauth/2.0/token?</span><br><span class="line">grant_type=authorization_code&amp;</span><br><span class="line">code=用户授权码 Code 值&amp;</span><br><span class="line">client_id=您应用的AppKey&amp;</span><br><span class="line">client_secret=您应用的SecretKey&amp;</span><br><span class="line">redirect_uri=您应用设置的授权回调地址</span><br><span class="line"></span><br><span class="line">**注意**:这里的回调地址如果你没有就直接写`redirect_uri=oob`</span><br><span class="line">以上链接示例中参数仅给出了必选参数。</span><br><span class="line">关于应用的相关信息，您可在控制台，点进去您对应的应用，查看应用详情获得。</span><br></pre></td></tr></table></figure><p>然后我们就会看到返回的结果当中有<code>access_token</code>了</p><p><img src="https://bu.dusays.com/2023/10/27/653b7d6d4a5c2.png" alt="image-20231027170547289"></p><h2 id="配置脚本"><a href="#配置脚本" class="headerlink" title="配置脚本"></a>配置脚本</h2><p>配置<a href="https://greasyfork.org/zh-CN/scripts/468633-%E7%99%BE%E5%BA%A6%E7%BD%91%E7%9B%98%E7%A7%92%E4%BC%A0%E8%BD%AC%E5%AD%98%E5%8A%A9%E6%89%8B-%E6%94%AF%E6%8C%81pc%E5%8F%8A%E7%A7%BB%E5%8A%A8%E7%AB%AF-%E6%B0%B8%E4%B9%85%E6%97%A0%E5%B9%BF%E5%91%8A%E7%BB%BF%E8%89%B2%E7%89%88">油猴插件脚本</a></p><p><img src="https://bu.dusays.com/2023/10/27/653b7e2e3e7b0.png" alt="image-20231027170859608"></p><p>还不知道油猴是什么的小伙伴可以,可以点击链接下载: <a href="https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo">油猴下载</a>地址</p><h2 id="使用教程"><a href="#使用教程" class="headerlink" title="使用教程"></a>使用教程</h2><p>勾选需要转存的文件,点击生成秒传</p><p><img src="https://bu.dusays.com/2023/10/27/653b7e8cc27ef.png" alt=""></p>]]></content>
    
    
    <summary type="html">你还在为转存文件收费而发愁吗？快来试试这个工具吧！</summary>
    
    
    
    <category term="工具与软件" scheme="https://blog.serms.top/categories/%E5%B7%A5%E5%85%B7%E4%B8%8E%E8%BD%AF%E4%BB%B6/"/>
    
    <category term="实用插件" scheme="https://blog.serms.top/categories/%E5%B7%A5%E5%85%B7%E4%B8%8E%E8%BD%AF%E4%BB%B6/%E5%AE%9E%E7%94%A8%E6%8F%92%E4%BB%B6/"/>
    
    
    <category term="油猴插件" scheme="https://blog.serms.top/tags/%E6%B2%B9%E7%8C%B4%E6%8F%92%E4%BB%B6/"/>
    
  </entry>
  
  <entry>
    <title>并发编程:共享模型之管程</title>
    <link href="https://blog.serms.top/posts/784dacca.html"/>
    <id>https://blog.serms.top/posts/784dacca.html</id>
    <published>2023-10-27T06:06:42.000Z</published>
    <updated>2023-10-27T06:06:42.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="什么是管程（Monitor）"><a href="#什么是管程（Monitor）" class="headerlink" title="什么是管程（Monitor）"></a>什么是管程（Monitor）</h2><p><strong>管程（Monitor）是一种并发编程的模型和技术，用于解决多线程之间的互斥访问和条件等待的问题</strong>。它是由荷兰计算机科学家 Edsger Dijkstra 在 1971 年提出的一种同步机制。</p><p>管程提供了一种方式让多个线程能够协调彼此的执行，确保在任意时刻只有一个线程可以访问共享资源，从而避免了线程间的竞争条件（Race Condition）和数据不一致的问题。</p><p>管程通常由以下几个要素组成：</p><p><strong>互斥锁（Mutex）</strong>：用于实现对共享资源的互斥访问，保证同一时刻只有一个线程可以进入管程进行操作。</p><p><strong>条件变量（Condition Variable）</strong>：用于实现线程间的条件等待和通知机制。线程可以在条件不满足的情况下等待，并在条件满足时被唤醒继续执行。</p><p><strong>入口队列（Entry Queue）</strong>：用于存放等待访问资源的线程队列。</p><p><strong>管程内部方法</strong>：包括对资源的操作和对条件变量的操作等。</p><p><strong>通过管程</strong>，可以将对共享资源的访问限制在管程的范围内，线程在进入管程之前需要获得管程的互斥锁，如果资源被占用，线程将进入入口队列等待；当某个线程完成对共享资源的操作后，会释放互斥锁并唤醒等待队列中的某个线程继续执行。</p><p>管程的主要优点是提供了一种高层次的抽象和封装，简化了多线程编程的复杂性，提供了一种结构化的方式来管理线程之间的同步和协作。它可以有效地避免死锁（Deadlock）和饥饿（Starvation）等并发编程中常见的问题。</p><h3 id="举例"><a href="#举例" class="headerlink" title="举例"></a>举例</h3><p>在多线程中有A、B两个线程同时需要修改某一个共享资源，线程A从内存中读取共享资源，但还未来得及提交修改，或者处于休眠状态；同时，线程B获取到同一个共享资源并进行了修改，然后将修改后的值放回内存中。接着，当线程A继续执行并将它的修改放回内存时，它会覆盖线程B之前的修改，从而导致最终的结果不一致</p><p>上述例子也就体现了，线程A和线程B同时竞争修改共享资源，其操作顺序可能会导致不一致的结果,因此我们可以采用同步机制，管程或锁机制来确保只有一个线程可以修改资源</p><h3 id="Java体现资源争抢"><a href="#Java体现资源争抢" class="headerlink" title="Java体现资源争抢"></a>Java体现资源争抢</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">int</span> <span class="variable">counter</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5000</span>; i++) &#123;</span><br><span class="line">            counter++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5000</span>; i++) &#123;</span><br><span class="line">            counter--;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t2&quot;</span>);</span><br><span class="line">    t1.start();</span><br><span class="line">    t2.start();</span><br><span class="line">    t1.join();</span><br><span class="line">    t2.join();</span><br><span class="line">    log.debug(<span class="string">&quot;&#123;&#125;&quot;</span>,counter);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上的结果可能为负数、正数或0，因为Java中对静态变量的自增或自减操作并不是原子性操作</p><h2 id="临界区-Critical-Section"><a href="#临界区-Critical-Section" class="headerlink" title="临界区 Critical Section"></a>临界区 Critical Section</h2><ul><li>一个程序运行多个线程本身是没有问题的 </li><li>问题出在多个线程访问<strong>共享资源</strong> <ul><li>多个线程<strong>读</strong>共享资源其实也没有问题 </li><li>在多个线程对共享资源<strong>读写操作</strong>时发生指令交错，就会出现问题 </li></ul></li><li>一段代码块内如果存在对共享资源的多线程读写操作，称这段代码块为<strong>临界区</strong> 例如，下面代码中的临界区</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">int</span> <span class="variable">counter</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">increment</span><span class="params">()</span> </span><br><span class="line">    <span class="comment">// 临界区</span></span><br><span class="line">&#123; </span><br><span class="line">    counter++;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">decrement</span><span class="params">()</span> </span><br><span class="line">    <span class="comment">// 临界区</span></span><br><span class="line">&#123; </span><br><span class="line">    counter--;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="竞态条件-Race-Condition"><a href="#竞态条件-Race-Condition" class="headerlink" title="竞态条件 Race Condition"></a>竞态条件 Race Condition</h2><p>多个线程在<strong>临界区内</strong>执行，由于代码的执行序列不同而导致结果无法预测，称之为发生了竞态条件</p><h2 id="synchronized"><a href="#synchronized" class="headerlink" title="synchronized"></a>synchronized</h2><p>为了避免临界区的竞态条件发生，有多种手段可以达到目的。 </p><ul><li>阻塞式的解决方案：synchronized，Lock </li><li>非阻塞式的解决方案：原子变量</li></ul><p>synchronized 即俗称的【对象锁】，它采用互斥的方式让同一 时刻至多<strong>只有一个线程</strong>能持有【<strong>对象锁</strong>】，其它线程再想获取这个【<strong>对象锁</strong>】时就会<strong>阻塞住</strong>。这样就能保证拥有锁的线程可以安全的执行临界区内的代码，不用担心线程上下文切换(也就是线程切换)</p><blockquote><p><strong>注意</strong> </p><p>虽然 java 中互斥和同步都可以采用 synchronized 关键字来完成，但它们还是有区别的： </p><ul><li>互斥是保证临界区的竞态条件发生，同一时刻只能有一个线程执行临界区代码 </li><li>同步是由于线程执行的先后、顺序不同、需要一个线程等待其它线程运行到某个点</li></ul></blockquote><p>语法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">synchronized</span>(对象) <span class="comment">// 线程1， 线程2(blocked)</span></span><br><span class="line">&#123;</span><br><span class="line">    临界区</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>解决</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: SerMs</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2023/10/27 11 58</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Email</span>: 1839928782@qq.com</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Blog</span>: https://serms.top</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Slf4j(topic = &quot;c.Test04&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test04</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="type">int</span> <span class="variable">counter</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">synchronized</span>  (Test04.class) &#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5000</span>; i++) &#123;</span><br><span class="line">                    counter++;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">synchronized</span>(Test04.class)&#123;</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">5000</span>; i++) &#123;</span><br><span class="line">                    counter--;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="string">&quot;t2&quot;</span>);</span><br><span class="line">        t1.start();</span><br><span class="line">        t2.start();</span><br><span class="line">        t1.join();</span><br><span class="line">        t2.join();</span><br><span class="line">        log.debug(<span class="string">&quot;&#123;&#125;&quot;</span>,counter);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="方法上的-synchronized"><a href="#方法上的-synchronized" class="headerlink" title="方法上的 synchronized"></a>方法上的 synchronized</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">等价于</span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Test</span>&#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">synchronized</span>(<span class="built_in">this</span>) &#123;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">等价于</span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Test</span>&#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">synchronized</span>(Test.class) &#123;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><h2 id="Monitor概念"><a href="#Monitor概念" class="headerlink" title="Monitor概念"></a>Monitor概念</h2><h3 id="Java-对象头"><a href="#Java-对象头" class="headerlink" title="Java 对象头"></a>Java 对象头</h3><p>普通对象</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">|--------------------------------------------------------------|</span><br><span class="line">| Object <span class="title function_">Header</span> <span class="params">(<span class="number">64</span> bits)</span> |</span><br><span class="line">|------------------------------------|-------------------------|</span><br><span class="line">| Mark <span class="title function_">Word</span> <span class="params">(<span class="number">32</span> bits)</span> | Klass <span class="title function_">Word</span> <span class="params">(<span class="number">32</span> bits)</span> |</span><br><span class="line">|------------------------------------|-------------------------|</span><br></pre></td></tr></table></figure><p>数组对象</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">|---------------------------------------------------------------------------------|</span><br><span class="line">| Object <span class="title function_">Header</span> <span class="params">(<span class="number">96</span> bits)</span> |</span><br><span class="line">|--------------------------------|-----------------------|------------------------|</span><br><span class="line">| Mark <span class="title function_">Word</span><span class="params">(32bits)</span> | Klass <span class="title function_">Word</span><span class="params">(32bits)</span> | array <span class="title function_">length</span><span class="params">(32bits)</span> |</span><br><span class="line">|--------------------------------|-----------------------|------------------------|</span><br></pre></td></tr></table></figure><p>其中 Mark Word 结构为</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">|-------------------------------------------------------|--------------------|</span><br><span class="line">| Mark <span class="title function_">Word</span> <span class="params">(<span class="number">32</span> bits)</span>                                   |        State       |</span><br><span class="line">|-------------------------------------------------------|--------------------|</span><br><span class="line">| hashcode:<span class="number">25</span> | age:<span class="number">4</span> | biased_lock:<span class="number">0</span> | <span class="number">01</span>              | Normal             |</span><br><span class="line">|-------------------------------------------------------|--------------------|</span><br><span class="line">| thread:<span class="number">23</span> | epoch:<span class="number">2</span> | age:<span class="number">4</span> | biased_lock:<span class="number">1</span> | <span class="number">01</span>      | Biased             |</span><br><span class="line">|-------------------------------------------------------|--------------------|</span><br><span class="line">| ptr_to_lock_record:<span class="number">30</span> | <span class="number">00</span>                            | Lightweight Locked |</span><br><span class="line">|-------------------------------------------------------|--------------------|</span><br><span class="line">| ptr_to_heavyweight_monitor:<span class="number">30</span> | <span class="number">10</span>                    | Heavyweight Locked |</span><br><span class="line">|-------------------------------------------------------|--------------------|</span><br><span class="line">| | <span class="number">11</span>                                                  | Marked <span class="keyword">for</span> GC       |</span><br><span class="line">|-------------------------------------------------------|--------------------|</span><br></pre></td></tr></table></figure><p>64位虚拟机Mark Word</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">|--------------------------------------------------------------------|--------------------|</span><br><span class="line">| Mark Word (64 bits) | State |</span><br><span class="line">|--------------------------------------------------------------------|--------------------|</span><br><span class="line">| unused:25 | hashcode:31 | unused:1 | age:4 | biased_lock:0 | 01 | Normal|</span><br><span class="line">|--------------------------------------------------------------------|--------------------|</span><br><span class="line">| thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | 01 |Biased  |</span><br><span class="line">|--------------------------------------------------------------------|--------------------|</span><br><span class="line">| ptr_to_lock_record:62 | 00 | Lightweight Locked |</span><br><span class="line">|--------------------------------------------------------------------|--------------------|</span><br><span class="line">| ptr_to_heavyweight_monitor:62 | 10 | Heavyweight Locked |</span><br><span class="line">|--------------------------------------------------------------------|--------------------|</span><br><span class="line">| | 11 | Marked <span class="keyword">for</span> GC |</span><br><span class="line">|--------------------------------------------------------------------|--------------------|</span><br></pre></td></tr></table></figure><p><img src="https://bu.dusays.com/2023/10/27/653b6554cf311.png" alt=""></p><p>上图所示,其实Monitor就是充当一个锁,只要是同一个对象,都会跟同一个Monitor相关联</p><ul><li>Monitor 被翻译为监视器或管程 </li><li>每个 Java 对象都可以关联一个 Monitor 对象，如果使用 synchronized 给对象上锁（重量级）之后，该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针</li></ul><p>Monitor 结构如下</p><p><img src="https://bu.dusays.com/2023/10/27/653b67b0c4372.png" alt=""></p><ul><li>刚开始 Monitor 中 Owner 为 null </li><li>当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2，Monitor中只能有一 个 Owner </li><li>在 Thread-2 上锁的过程中，如果 Thread-3，Thread-4，Thread-5 也来执行 synchronized(obj)，就会进入 EntryList BLOCKED </li><li>Thread-2 执行完同步代码块的内容，然后唤醒 EntryList 中等待的线程来竞争锁，竞争的时是非公平的 </li><li>图中 WaitSet 中的 Thread-0，Thread-1 是之前获得过锁，但条件不满足进入 WAITING 状态的线程，后面讲 wait-notify 时会分析</li></ul><blockquote><p><strong>注意</strong>：</p><ul><li>synchronized 必须是进入同一个对象的 monitor 才有上述的效果 </li><li>不加 synchronized 的对象不会关联监视器，不遵从以上规则</li></ul></blockquote><h3 id="Monitor原理之synchronize"><a href="#Monitor原理之synchronize" class="headerlink" title="Monitor原理之synchronize"></a>Monitor原理之synchronize</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="keyword">static</span> <span class="type">int</span> <span class="variable">counter</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123; </span><br><span class="line">  <span class="keyword">synchronized</span> (lock) &#123;</span><br><span class="line">counter++; </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//字节码</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(java.lang.String[])</span>; </span><br><span class="line">descriptor: ([Ljava/lang/String;)V </span><br><span class="line">flags: ACC_PUBLIC, ACC_STATIC</span><br><span class="line">Code:</span><br><span class="line">     stack=<span class="number">2</span>, locals=<span class="number">3</span>, args_size=<span class="number">1</span></span><br><span class="line">         <span class="number">0</span>: getstatic #<span class="number">2</span> <span class="comment">// &lt;- lock引用 （synchronized开始）</span></span><br><span class="line">         <span class="number">3</span>: dup</span><br><span class="line">         <span class="number">4</span>: astore_1 <span class="comment">// lock引用 -&gt; slot 1</span></span><br><span class="line">         <span class="number">5</span>: monitorenter <span class="comment">// 将 lock对象 MarkWord 置为 Monitor 指针</span></span><br><span class="line">         <span class="number">6</span>: getstatic #<span class="number">3</span> <span class="comment">// &lt;- i</span></span><br><span class="line">         <span class="number">9</span>: iconst_1 <span class="comment">// 准备常数 1</span></span><br><span class="line">         <span class="number">10</span>: iadd <span class="comment">// +1</span></span><br><span class="line">         <span class="number">11</span>: putstatic #<span class="number">3</span> <span class="comment">// -&gt; i</span></span><br><span class="line">         <span class="number">14</span>: aload_1 <span class="comment">// &lt;- lock引用</span></span><br><span class="line">         <span class="number">15</span>: monitorexit <span class="comment">// 将 lock对象 MarkWord 重置, 唤醒 EntryList</span></span><br><span class="line">         <span class="number">16</span>: <span class="keyword">goto</span> <span class="number">24</span></span><br><span class="line">         <span class="number">19</span>: astore_2 <span class="comment">// e -&gt; slot 2 </span></span><br><span class="line">         <span class="number">20</span>: aload_1 <span class="comment">// &lt;- lock引用</span></span><br><span class="line">         <span class="number">21</span>: monitorexit <span class="comment">// 将 lock对象 MarkWord 重置, 唤醒 EntryList</span></span><br><span class="line">         <span class="number">22</span>: aload_2 <span class="comment">// &lt;- slot 2 (e)</span></span><br><span class="line">         <span class="number">23</span>: athrow <span class="comment">// throw e</span></span><br><span class="line">         <span class="number">24</span>: <span class="keyword">return</span></span><br><span class="line">     Exception table:</span><br><span class="line">         from to target type</span><br><span class="line">         <span class="number">6</span> <span class="number">16</span>  <span class="number">19</span> any</span><br><span class="line">         <span class="number">19</span> <span class="number">22</span>   <span class="number">19</span> any</span><br><span class="line">     LineNumberTable:</span><br><span class="line">         line <span class="number">8</span>: <span class="number">0</span></span><br><span class="line">         line <span class="number">9</span>: <span class="number">6</span></span><br><span class="line">         line <span class="number">10</span>: <span class="number">14</span></span><br><span class="line">         line <span class="number">11</span>: <span class="number">24</span></span><br><span class="line">     LocalVariableTable:</span><br><span class="line">     Start Length Slot Name Signature</span><br><span class="line">     <span class="number">0</span> <span class="number">25</span> <span class="number">0</span> args [Ljava/lang/String;</span><br><span class="line">     StackMapTable: number_of_entries = <span class="number">2</span></span><br><span class="line">     frame_type = <span class="number">255</span> <span class="comment">/* full_frame */</span></span><br><span class="line">     offset_delta = <span class="number">19</span></span><br><span class="line">             locals = [ class <span class="string">&quot;[Ljava/lang/String;&quot;</span>, <span class="keyword">class</span> <span class="title class_">java</span>/lang/Object ]</span><br><span class="line">             stack = [ <span class="keyword">class</span> <span class="title class_">java</span>/lang/Throwable ]</span><br><span class="line">     frame_type = <span class="number">250</span> <span class="comment">/* chop */</span></span><br><span class="line">             offset_delta = <span class="number">4</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">并发编程基础到原理实战</summary>
    
    
    
    <category term="学习笔记" scheme="https://blog.serms.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    <category term="并发编程" scheme="https://blog.serms.top/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
    <category term="并发编程" scheme="https://blog.serms.top/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
</feed>
