<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.0">Jekyll</generator><link href="https://ftvbftvbq.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://ftvbftvbq.github.io/" rel="alternate" type="text/html" /><updated>2020-12-18T09:48:49+00:00</updated><id>https://ftvbftvbq.github.io/feed.xml</id><title type="html">思考的犀牛</title><subtitle>生活历练者</subtitle><entry><title type="html">如何成为一个还不错的测试开发</title><link href="https://ftvbftvbq.github.io/%E5%A6%82%E4%BD%95%E6%88%90%E4%B8%BA%E4%B8%80%E4%B8%AA%E8%BF%98%E4%B8%8D%E9%94%99%E7%9A%84%E6%B5%8B%E8%AF%95%E5%BC%80%E5%8F%91/" rel="alternate" type="text/html" title="如何成为一个还不错的测试开发" /><published>2018-08-24T00:00:00+00:00</published><updated>2018-08-24T00:00:00+00:00</updated><id>https://ftvbftvbq.github.io/%E5%A6%82%E4%BD%95%E6%88%90%E4%B8%BA%E4%B8%80%E4%B8%AA%E8%BF%98%E4%B8%8D%E9%94%99%E7%9A%84%E6%B5%8B%E8%AF%95%E5%BC%80%E5%8F%91</id><content type="html" xml:base="https://ftvbftvbq.github.io/%E5%A6%82%E4%BD%95%E6%88%90%E4%B8%BA%E4%B8%80%E4%B8%AA%E8%BF%98%E4%B8%8D%E9%94%99%E7%9A%84%E6%B5%8B%E8%AF%95%E5%BC%80%E5%8F%91/">&lt;p&gt;在测试行业中，测试开发人员一直是非常特殊地存在，他们比普通测试人员薪资高，比普通测试人员代码能力强，有人说他们是测试，有人说他们是开发，到底一个还不错的测试开发应该具有哪些能力？又应该具有哪些技能栈？在这里，我表示我并不是测开大牛，但我也希望能够借此机会与大家沟通并培养学习这些能力。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;以下内容仅代表我从业以来所积累的相关经验，我会从硬技能、软实力这些方面尽量阐述我所认为的 “还不错的测试开发” 应当做到哪些技能。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;技能树&quot;&gt;技能树&lt;/h3&gt;

&lt;p&gt;测试领域能力：作为一个测试开发，测试领域能力，非常容易被忽视，因为很容易想到的是平时工作主要是写代码，至于那些测试分析能力的培养，就会容易被束之高阁。缺少这些能力的人，代码写得再好，也是浮沙筑高台，最终还是一介匹夫。这里我们来举个栗子：如果测试微信朋友圈？到底该怎么测试呢？是这么测试呢？还是这么测试呢？［乖巧样］&lt;/p&gt;

&lt;p&gt;好吧，作为一个不错的测试开发，应该能想到这些内容：
&lt;img src=&quot;https://ftvbftvbq.github.io/images/朋友圈测试.png&quot; alt=&quot;朋友圈测试&quot; /&gt;&lt;/p&gt;

&lt;p&gt;说下这种测试领域能力的特征：是一种由表及里，层层深入，充分挖掘测试需求的能力。这种能力，一方面需要平时积累自己的思考习惯（多层面设问），另一方面也考验自己的需求分析能力。在此基础上，注意哦，这里我们强调这是基础，我们分解如下这些硬技能：&lt;/p&gt;

&lt;h4 id=&quot;1计算机基础&quot;&gt;1）计算机基础&lt;/h4&gt;
&lt;p&gt;这里我列了3项——操作系统、算法和数据结构和网络。做为吃这口饭的手艺人，基础技能是我们应该也必须掌握的，试用连入门级别的技能都没有，你怎么有脸说是一个不错的测试开发呢？&lt;/p&gt;

&lt;h4 id=&quot;2代码能力&quot;&gt;2）代码能力&lt;/h4&gt;
&lt;p&gt;这里我们强调的是代码的横向能力，以及工具语言代码的纵向能力。前者你应该涵盖这些方面：python脚本、java代码编写、性能脚本、前端、模式、IO、基础容器操作、多线程，并在此基础上对代码手艺有更高的追求，更好的代码编程习惯的养成。这是干活的基础，好好干吧，干巴爹！&lt;/p&gt;

&lt;h4 id=&quot;3运维&quot;&gt;3）运维&lt;/h4&gt;
&lt;p&gt;测试开发之外，别老想着让别人部署你的代码了，学点运维吧。LINUX这是必知必会的，相关的测试环境的搭建可是件体力活，另外，还需要容器化，以便能够快速部署，持续交付，此外，你还需要部署相关的监控，以便优化你的代码，并且以此提高你对代码的自信。祝你具有蜜汁自信！～&lt;/p&gt;

&lt;h4 id=&quot;4数据库&quot;&gt;4）数据库&lt;/h4&gt;
&lt;p&gt;一般来说，代码或平台的设计，势必设计到模型和模型的迁移，所以基础操作、建表，那是天然自带的技能属性，同时，为了表示自己是有追求的，你还的随时留心SQL的新能，防治和预测慢SQL的产生，当然，数据库线程数调整，也是你必然要去完成的。&lt;/p&gt;

&lt;h4 id=&quot;5分布式&quot;&gt;5）分布式&lt;/h4&gt;
&lt;p&gt;当你的测试代码或平台，被使用得越来越频繁，那么分布式是必然的选项。对于分布式天然的缺陷和典型的问题，再实施分布式前就需要去了解和掌握，比如最终一致性，比如高可用问题，这些都是做分布式必然需要去解决的问题。&lt;/p&gt;

&lt;h4 id=&quot;6懂点架构&quot;&gt;6）懂点架构&lt;/h4&gt;
&lt;p&gt;这里说的是懂点，并没说精通哦，是有区别的，这里我们强调技术选型对架构的影响以及架构中的使用方法对其的影响。这里不展开了，真的，因为我怕一本书讲不完，那么多书讲架构，买本看下，应该是必须的。&lt;/p&gt;

&lt;h4 id=&quot;7平台化一体化思维&quot;&gt;7）平台化、一体化思维&lt;/h4&gt;
&lt;p&gt;在互联网测试领域，现在有一个大家都很追求的东西，或者说是大势所趋的方向，平台化，测试平台化提了这么多年，成功的其实并不多，关键不在于技术的使用，而在于设计平台时候的局限，比如：无法估计到业务的发展、没有跟上技术的更迭，问题莫衷一是，所以，如果你从你编写代码开始，就有意识培养这种思维和意愿，相信在你能够引导测试技术化的实践过程中能够助你一臂之力。&lt;/p&gt;

&lt;p&gt;以上，是我列举的技能树，这个技能树长在了测试领域能力的根上，需要不断浇灌根基，才能最终成为一片参天巨树（场面太宏大，作者也瑟瑟发抖）。&lt;/p&gt;

&lt;h3 id=&quot;保持竞争力&quot;&gt;保持竞争力&lt;/h3&gt;
&lt;p&gt;聊完了技能树，我们再聊聊如何保持竞争力的问题。学习如逆水行舟，不进则退。基础扎实，不是学了之后就忘了，需要不停的去看，巩固，基础是万变不离其宗的。其次，关注行业发展、新技术、新动态至少不能落伍了，这里推荐vipTest，未来的测试开发之家。最后，有思考，有产出，在你工作中，多思考多产出，形成良性的自我输出能力。&lt;/p&gt;

&lt;h2 id=&quot;思维导图&quot;&gt;思维导图&lt;/h2&gt;
&lt;p&gt;最后结合上文形成了这样一份思维导图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://ftvbftvbq.github.io/images/还不错的测试开发.png&quot; alt=&quot;还不错的测试开发&quot; /&gt;&lt;/p&gt;

&lt;p&gt;写在最后：
   “秦失其鹿，天下共逐”，在测试开发领域，一个广阔的充满竞争又满是机会的领域，很遗憾，目前还没有独领风骚的领军人物出现，所以，希望会出现一个个的还不错的测试开发们，一起去创造一个属于这个领域的成功。&lt;/p&gt;</content><author><name></name></author><summary type="html">在测试行业中，测试开发人员一直是非常特殊地存在，他们比普通测试人员薪资高，比普通测试人员代码能力强，有人说他们是测试，有人说他们是开发，到底一个还不错的测试开发应该具有哪些能力？又应该具有哪些技能栈？在这里，我表示我并不是测开大牛，但我也希望能够借此机会与大家沟通并培养学习这些能力。</summary></entry><entry><title type="html">谈一谈最近的2件小事</title><link href="https://ftvbftvbq.github.io/%E8%B0%88%E4%B8%80%E8%B0%88%E6%9C%80%E8%BF%91%E7%9A%842%E4%BB%B6%E5%B0%8F%E4%BA%8B/" rel="alternate" type="text/html" title="谈一谈最近的2件小事" /><published>2018-05-20T00:00:00+00:00</published><updated>2018-05-20T00:00:00+00:00</updated><id>https://ftvbftvbq.github.io/%E8%B0%88%E4%B8%80%E8%B0%88%E6%9C%80%E8%BF%91%E7%9A%842%E4%BB%B6%E5%B0%8F%E4%BA%8B</id><content type="html" xml:base="https://ftvbftvbq.github.io/%E8%B0%88%E4%B8%80%E8%B0%88%E6%9C%80%E8%BF%91%E7%9A%842%E4%BB%B6%E5%B0%8F%E4%BA%8B/">&lt;p&gt;&lt;img src=&quot;https://ftvbftvbq.github.io/images/riben.jpeg&quot; alt=&quot;riben&quot; /&gt;&lt;/p&gt;

&lt;p&gt;2018年5月7日 摄于日本北海道神宫&lt;/p&gt;

&lt;p&gt;犀牛君最近比较忙，一方面需要控制好产品的交付质量，另一方面，本身也需要参与到具体到质量控制流程中，在非常忙碌的产品测试交付中发生了2件小事，分享出来，与大家一起看一看聊一聊。&lt;/p&gt;

&lt;p&gt;小事一：&lt;/p&gt;

&lt;p&gt;一个产品的商品排序接口在测试过程中发生了问题，问题其实挺简单的，排序并不是按照PRD的要求以时间升序对商品排序。很自然，测试人员上报了这个缺陷并且和对应开发一起定位具体原因。在沟通过程中，意外发现，开发将一个紧急的功能对应的数据加到了这个接口中，但是在开发和产品沟通的最后截止日，产品无法给出对应参数明确的数据原型，于是，该接口必须在这个迭代中以硬编码的方式被改造回去。于是出现了排序对原有数据的失效问题。这个修复历经大约半个小时。&lt;/p&gt;

&lt;p&gt;小事二：&lt;/p&gt;

&lt;p&gt;在开发提交完分支代码后，测试开始在jenkins中将代码打包并且将服务部署到对应的测试环境中，在jenkins中代码全部编译通过并且代码扫描符合相关的规范和约定，最后的结果在log中明确显示为成功。然后，意外的事情发生了，按照经验，部署完毕相关的服务会自动启动，但是，在服务器查找相关的服务时发现有一个jar包对应的服务一直处于失败。于是，仍然很自然地去看对应的log，查了半天没有发现异常，最后不得不找对应的开发来查看服务情况。在排查了所有可能性后，开发恍然大悟地表示，自己忘记修改对应的小版本号了，测试此时的心情百（cao）感（ni）交（ma）集。这个排查解决大约历经不到15分钟。&lt;/p&gt;

&lt;p&gt;犀牛对上面2件小事最后都给出了解决的大致时间，从时间上看，其实和解决一些技术含量高的问题所花费的时间来说，真的可算是九牛一毛，但是仔细想想，从技术、工程、团队协作等层面来考虑，滚雪球的效应也许在不久的将来可见一斑。&lt;/p&gt;

&lt;p&gt;首先，从技术和工程的角度来看，有这么几个问题：&lt;/p&gt;

&lt;p&gt;1）以硬编码的方式添加可能性。这里的可能性指的是承接多种数据的可能性。硬编码本身是没有问题的，但是，一旦和可能性衔接在一起，那么这种组合绝对是在给自己喝下毒药。&lt;/p&gt;

&lt;p&gt;2）接口没有版本概念。从第一个小事我们看到，接口其实需要做一次降级，从类似版本1.1.1降到1.1.0时候的版本，从而快速地完成对不确定性的抛弃。而目前能做到的是，在一个没有版本的接口中，通过调整原有所有代码的方式来进行降级。试问，如果问题复杂度比较大的话，这种降级可能就不止半个小时能解决了。从而进一步来想，如果接口不止对接一个端或者在之后的调整中需要兼容新老业务，那么没有版本概念绝对是一次灾难。&lt;/p&gt;

&lt;p&gt;3）开发对敏捷迭代工具不重视。CI和CD做到现在其实也经历了从开始的整体规划到整体使用推行的过程，在这个过程中，对一些必要的参数进行配置化是正常不过的事情。研发在平常的使用中，基本都只处在用的状态，通常来说，只要不是代码编译不过的问题，基本上不太关注后续的结果。从而我大胆推导目前一些公司的敏捷状态和结果是禁锢在各自角色中的，如果敏捷只是处在一种守（比如遵守3355这样的敏捷内容的Doing状态），到不了破（breaking状态）和离（Being状态），敏捷最后还是会走到可预见的负收益的最终结果。犀牛以前感叹，敏捷之殇，责任（能力）无法承受之重。&lt;/p&gt;

&lt;p&gt;从团队协作角度来看，其实也是一个老生常谈的问题：产品和技术的沟通，永远隔着一条巨大的鸿沟。还记得各个技术论坛、技术QQ群中那一张张充满恶意的动图吗？嗯，痛苦之所以存在，是在于处于两个世界的人并不是一定有机会能走到一起。所谓的人人都是产品经理，犀牛一直认为是一种对其价值的恶意评价。&lt;/p&gt;

&lt;p&gt;小事就讲到这里吧，问题出现了大家还得坐下来想办法、想对策，以积极的态度面对这些“小”事，从基本面去解决这些问题，和团队一起成长，并且充满好奇心，才能在不确定性中找到解决各种小事的方法论和工具集。&lt;/p&gt;</content><author><name></name></author><summary type="html"></summary></entry><entry><title type="html">有关mock的是是非非</title><link href="https://ftvbftvbq.github.io/%E6%9C%89%E5%85%B3Mock%E7%9A%84%E6%98%AF%E6%98%AF%E9%9D%9E%E9%9D%9E/" rel="alternate" type="text/html" title="有关mock的是是非非" /><published>2018-03-09T00:00:00+00:00</published><updated>2018-03-09T00:00:00+00:00</updated><id>https://ftvbftvbq.github.io/%E6%9C%89%E5%85%B3Mock%E7%9A%84%E6%98%AF%E6%98%AF%E9%9D%9E%E9%9D%9E</id><content type="html" xml:base="https://ftvbftvbq.github.io/%E6%9C%89%E5%85%B3Mock%E7%9A%84%E6%98%AF%E6%98%AF%E9%9D%9E%E9%9D%9E/">&lt;h2 id=&quot;什么是mock&quot;&gt;什么是Mock&lt;/h2&gt;

&lt;p&gt;在人类理解世界的时候，总是假定一个生命周期的主体即对象首先是存在的并且对象和对象之间是存在联系的，这样才能形成我们认知中的世界的样子。但当对象没有诞生或暂时缺失的时候，非常容易想到的是用其他方式模拟这个对象的存在，以便获取相关对象的状态，使互相的联系不至于丢失或导致后续的中断。这个模拟的行为我们称之为：Mock。&lt;/p&gt;

&lt;p&gt;可见Mock包含了这两个内容：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;模拟的是一个对象或对象的活动（方法）的预期；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;模拟的目的是为了完成衔接也即我们通常说的对象上下文的切换。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;为了更好地对后续Mock进行描述，这里先介绍下大家可能会使用的Mock工具：&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;1-支持单元测试级别的mock工具easymock-vs-mockito&quot;&gt;1) 支持单元测试级别的mock工具：EasyMock VS Mockito&lt;/h2&gt;

&lt;p&gt;EasyMock使用的是expect-run-verify的内部库，这种方式使人联想到TDD，如果中间出现失败，则需要及时做调整，具体的伪代码如下：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;import static org.easymock.classextension.EasyMock.*;
    List mock1 = createMock(List.class);
    expect(mock1.get(1)).andStubReturn(“one”);
    expect(mock1.get(2)).andStubReturn(“two”); 
    mock1.clear( );
    replay(mock1); 
    verify(mock1);&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;而Mockito没有使用这种内部库，而是使用了when-then-return，有点类似BDD，非常直观和有效。而且如果中间失败，只在最后显示相关的结果，无需开发在测试中间进行调整。Mockito具体伪代码如下：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;import static org.mockito.Mockito.*; 
    Listmock2 = mock(List.class);
    when(mock2.get(1)).thenReturn(“one”); 
    when(mock2.get(2)).thenReturn(“two”); 
    verify(mock2).clear( );&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;两者其实比较相近，但是还是有一定的区别，尤其是在失败后的错误输出上，Mockito相比EasyMock更加清晰地指出了具体的问题，而EasyMock只是抛出异常，非常难以定位到具体问题。&lt;/p&gt;

&lt;p&gt;EasyMock和Mockito的使用，非常依赖使用者的嗜好，如果用TDD的思维则选择EasyMock，如果趋向于BDD，则可以使用Mockito。两者的优劣其实非常明显，相比起来Mockito因为顺滑的API，更好的truble shooting被更多的使用者接受。&lt;/p&gt;

&lt;h2 id=&quot;2-支持集成测试级别的mock工具&quot;&gt;2) 支持集成测试级别的Mock工具&lt;/h2&gt;

&lt;p&gt;前面我们说到了支持单元测试代码级别的mock，主要是为了快速模拟出相关的对象或方法，但是当出现外部需要模拟的数据或服务的时候，尤其是前端需要后端数据或服务支持的时候，则需要进行搭建server级别的mock，这里说的工具以阿里开源的RAP最为典型。&lt;/p&gt;

&lt;p&gt;RAP提供了团队管理、项目管理、文档编写、Mock.js、可视化、接口过渡、文档历史版本、mock插件并且支持本地部署，可以非常好的对前端的提前联调、集成测试，都有非常好的支持。具体的使用这里不做过多的说明，RAP本身文档清晰，作者就不再画蛇添足了。当然，在实际的使用中为了满足的个性化需求，RAP完全可以被进行改造，打造自己的mockserver来支持平时的mock实施。&lt;/p&gt;

&lt;h2 id=&quot;从软件架构理解如何正确的mock&quot;&gt;从软件架构理解如何正确的Mock&lt;/h2&gt;

&lt;p&gt;软件架构告诉我们，代码分为了两个部分：表达业务生命周期的代码即业务逻辑，表达用户访问生命周期的代码即服务通道。当业务代码非常内聚的时候，业务代码只负责对业务核心逻辑的实现，而服务代码只负责对外提供访问业务逻辑的通道，和用户打交道并且通过上下文切换完成自己的工作。在切分清楚的情况下，两者是一种合作的关系。然而现实却并不如此。&lt;/p&gt;

&lt;p&gt;比如很多时候软件工程师错误地为了实现服务通道的重用，将业务逻辑散落在服务通道中，用户在使用服务的时候，也直接对业务进行了相关的处理。这个时候，如果对业务代码进行单元测试，就会发现业务代码需要模拟服务代码的实现，完成上下文切换才能进行单元测试。这就是我们通常说的Mock。这个时候的Mock是在原本错误的切分结果下进行的，势必非常艰难，以致于最后的结果也是错误的。我们要极力避免这种情况下的Mock。我们通过一个单元测试的实例来说明下这种情况。&lt;/p&gt;

&lt;p&gt;测试小A童鞋拿到了如下代码，并且准备进行单元测试：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;public ProductDAO getCustomerProduct(HttpRequest request) {&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;     String customerId=Request.getFunction(&quot;CustomerId&quot;);

     String ProductId=Request.getFunction(&quot;ProductId&quot;);                                 

     CustomerDAO customer=CustomerManager.getCustomer(CustomerId);      

     ProductDAO product =productManager.getProduct(ProductId); 

     if(product!=null&amp;amp;&amp;amp;product.getCustomerId!=null&amp;amp;&amp;amp;product.getCustomerId.equals(CustomerId)){
      product.setCustomer(Customer); 
      return product;
      }
       return null;
     }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;没有经验的小A开始mock，他先mock出了一个HttpRequest容器，以便能够不启动服务器就能够将代码跑起来，然后，发现不仅仅如此，还需要模拟出数据库来，获取数据将代码跑成功。&lt;/p&gt;

&lt;p&gt;这时候，mock已经超出了本身的意义，开始对代码进行侵入式的模拟。其实，这段代码的本意只是提供对外服务，而程序员将业务逻辑混入了服务代码，使模拟单元测试不得不通过mock才能跑起来。这就是我们所说的，服务和业务代码的切分不清晰，导致的单元测试的复杂度大大提高了。而对于我们来说，这里的mock其实根本没有必要，只要我们将业务逻辑从这段代码中拆出来（这里不讨论如何正确拆分，我们只说mock），这个时候单元测试根本不用过多地依赖于mock，只需要对服务代码进行顺序的组合，便能够顺利地完成单元测试。&lt;/p&gt;

&lt;p&gt;使用Mock的正确姿势&lt;/p&gt;

&lt;p&gt;一、在业务层代码逻辑内使用&lt;/p&gt;

&lt;p&gt;如上我们已知，如果模拟的逻辑只处在业务逻辑的内部的一个部分，这种模拟是没有问题的。比如，在业务代码过程中，互相依赖或调用的业务无法并行，这个时候使用Mock加速一个业务周期的代码实现。这里还是想啰嗦一句，请尽量让你的代码分层清晰，在混乱的代码中进行mock，对于项目和代码本身，也是一种消耗和自损。为了说明问题，我们先来看下面这个代码：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;public ProductDAO getCustomerProduct(HttpRequest request) {&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    String customerId =  Request.getFunction(&quot;CustomerId&quot;);
    
    String ProductId=Request.getFunction(&quot;ProductId&quot;);        
    
    CustomerDAO customer=CustomerManager.getCustomer(CustomerId);
    
    ProductDAO product=productManager.getProduct(ProductId); 
    
    return anotherFunction.....;
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;额？这不就是上面代码独立抽了部分出来吗？没错。我们前面说过，其实上面的代码原意是为了提供对外服务，所以混入业务逻辑是错误的。这种服务代码肯定是不存在内部Mock需求的，如果出现请先自己打脸哦！&lt;/p&gt;

&lt;p&gt;然后，我们再看另外一个使用的例子：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;public interface MyService {&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;string DoSomething(int i);    }  &amp;gt; public classMyClass {
private readonly MyService service; 

public MyClass(MyService service) {

this.service = service;
 } 

public string void Print( ) {
  var message = service.DoSomething(); Console.WriteLine(message); 

  return message;
   }    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对应的Mock测试代码如下：&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;public void TestSomething( ) {&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;var service = newMock&amp;lt; MyService &amp;gt;( );
  
service.Setup(x =&amp;gt; x.DoSomething(It.IsAny&amp;lt;int&amp;gt;())).Returns(&quot;XXXXX&quot;); 
  
Assert.AreEqual(&quot;XXXXX&quot;, new MyClass(service).Print( )); 
  }    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里的mock主要的作用就是代替在业务逻辑中的一个未完成的部分，为了后续对业务逻辑代码的支持而存在。从而我们可见，在单元测试中只有涉及纯粹业务逻辑的mock才会对我们提供有效的作用。&lt;/p&gt;

&lt;p&gt;二、对外部的模拟&lt;/p&gt;

&lt;p&gt;在代码联调或测试阶段，如果外部存在不稳定性或者外部的服务存在诸如安全、资金损失和沟通成本过大，那么通过模拟的方式在一个阶段内可以降低这些风险，并且使用软件工程师能够集中注意力解决业务的核心问题，完成对业务的模拟实现。对于外部数据的模拟需要注意几点：尽量靠近原数据的状态、对数据的类型和转换需要全面、外部模拟不能成为各方沟通的障碍。&lt;/p&gt;

&lt;p&gt;从以上描述，我们得出mock对我们的帮助：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;提供了跨团队并行开发的可能&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;有了Mock，前后端人员只需要定义好接口文档就可以开始并行工作，互不影响，只在最后的联调阶段往来密切；后端与后端之间如果有接口耦合，也同样能被Mock解决；测试过程中如果遇到依赖接口没有准备好，同样可以借助Mock；不会出现一个团队等待另一个团队的情况。这样的话，开发自测阶段就可以及早开展，从而发现缺陷的时机也提前了，有利于整个产品质量以及进度的保证。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;可以模拟那些因为外部原因不存在的对象&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;你需要调用一个第三方的服务，而第三方并没有准备资源来方便自己调试，就可以自己通过Mock来解决。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;提高测试覆盖率&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果一个接口需要返回几十个结果，其中有些结果是不可能存在的，但是又不得不测试这些情况下的返回情况，这个时候，你通过Mock就能够非常方便地解决这个问题，提高测试的覆盖率，保证后续集成测试的正确率。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;以接近TDD或BDD的方式来驱动开发&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;换种思路，当单元测试能够提供足够的开发质量保障，那么单元测试将编程开发过程中不可或缺的一环。当单元测试中出现不存的对象时，这个时候mock扮演着上下衔接的作用。间接驱动了业务的实现过程。&lt;/p&gt;

&lt;h2 id=&quot;mock在微服务测试中的实践&quot;&gt;Mock在微服务测试中的实践&lt;/h2&gt;

&lt;p&gt;众所周知，微服务因为自治的开发和运维方式，而被很多现在的主流技术公司所使用，但是也是因为这种特性，使针对它的测试存在很多不确定因素，按照作者的经验，一般存在的很大的问题是各个服务的联调以及边界问题的引入。我们假定有个金融类的平台，使用了微服务的方式来实现，为来不将问题复杂化，这里抽取用户、交易以及基础系统服务为例，详细描述Mock的应用。为了表述清楚，先看下内部的时序图如下：
&lt;img src=&quot;https://ftvbftvbq.github.io/images/mork-guanxishixu.png&quot; alt=&quot;关系时序&quot; /&gt;&lt;/p&gt;

&lt;p&gt;了解了三个服务交互过程的上下文情况，下面我们来说下Mock在局部和整体测试过程中所解决的问题。&lt;/p&gt;

&lt;h2 id=&quot;1-单服务领域内的实现&quot;&gt;1. 单服务领域内的实现&lt;/h2&gt;

&lt;p&gt;说起单个服务领域内，我们很容易想到单元测试，我们用用户系统的注册服务来说。注册本身属于用户系统提供的一个服务，但是，注册本身的逻辑中还包含对于依赖基础服务的短信验证码和图形验证码，这个时候注册本身的服务看起来是这样：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; doRegister {...

     getCodeReturn( );   //获取手机验证码的返回 

    getRandomcode( );  //获取图形验证码的返回        

    ... 
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;那么对于单个服务内进行逻辑的完善，这个时候对 getCodeReturn( )和getRandomcode()方法进行Mock来填补逻辑，使开发的专注力集中在注册本身的业务实现。&lt;/p&gt;

&lt;p&gt;另外我们通过时序图发现，交易系统如果想实现自己的业务，基础是获得用户系统传递的交易申请单，这个部分不是交易系统独自能在领域内完成的，因为这里涉及两个服务之间的约定的问题，也即协议。假定整个两个系统间约定高于一切，那么如果交易系统单独实现这个部分，那么在后续的集成测试中就会遇到用户系统和交易系统无法“对话“的问题。因此需要在此之前基于约定Mock出一定的返回和获取逻辑。用户系统和交易系统的约定内容如下：
&lt;img src=&quot;https://ftvbftvbq.github.io/images/mork-yueding.png&quot; alt=&quot;约定&quot; /&gt;&lt;/p&gt;

&lt;p&gt;那么在交易系统中我们需要根据约定，Mock用户系统的数据落地来对交易内部的业务进行模拟，代码看起来是这样的：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;doRecharge {
   …&lt;br /&gt;
   getRechargeID( );&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;getUserIDStaus( );&lt;/p&gt;

&lt;p&gt;isAmountOK( ); &lt;br /&gt;
    …
  }&lt;/p&gt;

&lt;p&gt;这三个方法的返回的数据应该是Mock出来的，例如：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;mock {&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;response {      

  headers {
  ...
  }   

 &quot;getMockData&quot;: {  

        &quot;userId&quot;: &quot;0001&quot;,                     

        &quot;RechargeId&quot;:&quot;0000001&quot;;                     

        &quot;status&quot;: &quot;1&quot;       
 },    ...   }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这个Mock是在双方系统约定的前提下在交易系统领域内实现。从而在单个服务领域内可以实现三种情况下的Mock:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;单元测试内的Mock( 前文有所阐述，这里不再举例)；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;对于其他服务依赖而实现的内部Mock；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;基于双方约定，单个服务内实现的Mock，主要为后续集成测试而服务，但不在集成测试时使用。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;2-跨服务领域的实现&quot;&gt;2. 跨服务领域的实现&lt;/h2&gt;

&lt;p&gt;在完成各自领域服务代码开发之后，服务和服务之间开始进行联调，之前基于独立服务的mock开始不在这个过程中起作用，而是需要对方实现一定的Mock服务，我们简单称为：“在它而不在我”。这里我们以用户系统和基础服务来详细阐述。&lt;/p&gt;

&lt;p&gt;前文所说，用户系统通过在自己服务内的mock完成原本需要基于基础服务提供的依赖，到了联调测试阶段，这部分的mock需要被废弃，是基于几个原因：&lt;/p&gt;

&lt;p&gt;用户系统无法知晓基础服务内部对于这些服务的提供方式是否有差异；&lt;/p&gt;

&lt;p&gt;基础系统提供的这些服务需要统一到自己的平台，以便能够支持在不同环境下的联调测试；&lt;/p&gt;

&lt;p&gt;基础系统之所以需要mock是为了联调测试屏蔽第三方的干扰。因此，基础服务自己需要实现诸如：短信、学历、图形验证码等mock，因此将这些服务进行收敛，形成一个基于基础系统的mock平台，结构如图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://ftvbftvbq.github.io/images/mork-pingtai.png&quot; alt=&quot;平台&quot; /&gt;&lt;/p&gt;

&lt;p&gt;开关这里的定义是：能够支持在联调环境下自由地切换真实和mock数据源，mock平台更多的是为联调测试而存在。&lt;/p&gt;

&lt;p&gt;另外，基础服务会通过各种各样的中间件对外发起rpc请求，可以通过平台配置的中间件隔离来设置，平台会对这些中间件进行aop处理实现自动的mock，不需要人工去配置具体的rpc接口，从而简化对外接入的难度，提供测试空间和时间上的保障。&lt;/p&gt;

&lt;h2 id=&quot;3-领域外的实现&quot;&gt;3. 领域外的实现&lt;/h2&gt;

&lt;p&gt;对于各个微服务来说，联调的难点在于问题的定位，所以在所有的微服务系统要求一旦出现错误，则需要迅速地失败，以便调用链服务能够定位到具体的错误业务和涉及的对应服务，因此这些错误和crash的Mock非常重要，这个Mock超出了具体的服务领域，属于公用的提供给所有服务使用的工具。我们来看下具体的实现：
&lt;img src=&quot;https://ftvbftvbq.github.io/images/mork-lingyu.png&quot; alt=&quot;领域&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这些error/crash的mock主要是以TXT文本存在，以JSON的格式提供各个业务场景下的问题汇总数据，这些数据来源于真实场景，并且这些数据被技术服务解析提供给各个服务来mock自己业务领域内的各种失败，某一个业务服务通过日志系统最终定位问题。&lt;/p&gt;

&lt;p&gt;在测试环境下的集成测试完成之后，这个error/crash的mock也可以在预发环境进行使用。例如将缓存错误进行回归测试，那么如果预发环境下读缓存时有数据，那么直接可以用这些缓存数据落地到该工具内进行mock即可，确保了在预发环境下回归测试的完整性和正确性。&lt;/p&gt;

&lt;p&gt;以上是Mock在微服务内的具体实践，包括了单领域内、跨领域和领域外公共的Mock设计和具体解决问题的思路。&lt;/p&gt;</content><author><name></name></author><summary type="html">什么是Mock</summary></entry><entry><title type="html">“聊聊架构”笔记和心得</title><link href="https://ftvbftvbq.github.io/%E8%81%8A%E8%81%8A%E6%9E%B6%E6%9E%84-%E7%AC%94%E8%AE%B0%E5%92%8C%E5%BF%83%E5%BE%97/" rel="alternate" type="text/html" title="“聊聊架构”笔记和心得" /><published>2018-03-08T00:00:00+00:00</published><updated>2018-03-08T00:00:00+00:00</updated><id>https://ftvbftvbq.github.io/%E2%80%9C%E8%81%8A%E8%81%8A%E6%9E%B6%E6%9E%84%E2%80%9D%E7%AC%94%E8%AE%B0%E5%92%8C%E5%BF%83%E5%BE%97</id><content type="html" xml:base="https://ftvbftvbq.github.io/%E8%81%8A%E8%81%8A%E6%9E%B6%E6%9E%84-%E7%AC%94%E8%AE%B0%E5%92%8C%E5%BF%83%E5%BE%97/">&lt;h4 id=&quot;infoq垂直号聊聊架构公众号收录作品&quot;&gt;[InfoQ垂直号“聊聊架构”公众号收录作品]&lt;/h4&gt;
&lt;p&gt;从远古开始，人类为了摆脱对时间的恐惧和对生活更高的追求，开始慢慢地进化和改进自己的工具，并且寻求人和人之间的合作，开始形成分工，分工使人能够在有限的时间内并行地完成相关的业务工作，从而慢慢形成了一个树状的架构。由于要更好地完成分工，制定分工的人（架构师）开始开始深入到实际工作中识别问题、分析问题，抽象出这些工作的个性和共性，从中将核心生命周期识别出来，将非核心的工作按照流程分派给不同角色来处理。这些组织内角色的分工又按照切分的原则，使人能够责权对等并且解决相关人在时间和空间上的负载过重。从而使这种架构在组织内部成功落地最终达到业务增长的最终目的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://ftvbftvbq.github.io/images/ziran.jpeg&quot; alt=&quot;自然架构&quot; /&gt;&lt;/p&gt;

&lt;p&gt;软件生命周期的核心是软件的访问生命周期，运维为了这个核心目标而保驾护航。运维最重要的是控制变化，控制变化又需要隔离环境以及控制变更，其中涉及了环境的控制、监控和预警机制的完善，并且预警软件本身也需要纳入监控和预警中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://ftvbftvbq.github.io/images/软件生命周期.png&quot; alt=&quot;软件生命周期&quot; /&gt;&lt;/p&gt;

&lt;p&gt;面向对象和面向过程是两种不同的思维方式，本质上还是为了描述主体在自然界的基本属性和流转程序，面向过程和面向对象是一个硬币的两面，互相不可分离。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://ftvbftvbq.github.io/images/面向对象和面向过程.png&quot; alt=&quot;面向对象和面向过程&quot; /&gt;&lt;/p&gt;

&lt;p&gt;代码在整个架构中分为业务代码和通道代码，业务代码内聚以便能够体现业务的核心逻辑，通道代码负责支持用户能够通过自己的行为，访问业务逻辑，并且对业务逻辑产生一定的变更影响。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://ftvbftvbq.github.io/images/代码.png&quot; alt=&quot;代码&quot; /&gt;&lt;/p&gt;

&lt;p&gt;设计本身是为了解决人的问题，延伸到设计模式就是通过一套方法找到所处领域的共性并且处理好对应领域的个性。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://ftvbftvbq.github.io/images/设计.png&quot; alt=&quot;设计&quot; /&gt;&lt;/p&gt;

&lt;p&gt;大数据是对大量数据的快速处理的工具和技术，技术人员需要通过一定的生命周期来理解数据，并且从中识别出关键数据。大数据通过集群到数据中心到数据池，以一个大型的方式组织起来，最终完成了大数据的空间分布。
&lt;img src=&quot;https://ftvbftvbq.github.io/images/大数据.png&quot; alt=&quot;大数据&quot; /&gt;&lt;/p&gt;

&lt;p&gt;产品是用户访问企业的通道，通过交易形成商品价值，而交易是对时间定价的过程。
&lt;img src=&quot;https://ftvbftvbq.github.io/images/产品和商品.png&quot; alt=&quot;产品和商品&quot; /&gt;&lt;/p&gt;</content><author><name></name></author><summary type="html">[InfoQ垂直号“聊聊架构”公众号收录作品] 从远古开始，人类为了摆脱对时间的恐惧和对生活更高的追求，开始慢慢地进化和改进自己的工具，并且寻求人和人之间的合作，开始形成分工，分工使人能够在有限的时间内并行地完成相关的业务工作，从而慢慢形成了一个树状的架构。由于要更好地完成分工，制定分工的人（架构师）开始开始深入到实际工作中识别问题、分析问题，抽象出这些工作的个性和共性，从中将核心生命周期识别出来，将非核心的工作按照流程分派给不同角色来处理。这些组织内角色的分工又按照切分的原则，使人能够责权对等并且解决相关人在时间和空间上的负载过重。从而使这种架构在组织内部成功落地最终达到业务增长的最终目的。</summary></entry><entry><title type="html">聊聊“幂等”</title><link href="https://ftvbftvbq.github.io/%E8%81%8A%E8%81%8A-%E5%B9%82%E7%AD%89/" rel="alternate" type="text/html" title="聊聊“幂等”" /><published>2018-03-07T00:00:00+00:00</published><updated>2018-03-07T00:00:00+00:00</updated><id>https://ftvbftvbq.github.io/%E8%81%8A%E8%81%8A%E2%80%9C%E5%B9%82%E7%AD%89%E2%80%9D</id><content type="html" xml:base="https://ftvbftvbq.github.io/%E8%81%8A%E8%81%8A-%E5%B9%82%E7%AD%89/">&lt;h2 id=&quot;幂等的定义&quot;&gt;幂等的定义&lt;/h2&gt;

&lt;p&gt;从数学的角度来说，就是无论处理多少次，返回的结果和处理一次是一样的，比如：1的n次方和1。幂等性是系统对外的一种承诺而不是实现，承诺只要接口调用成功，外部系统调用一次和多次的影响是一致的。声明为幂等的接口认为外部系统调用失败是常态，并且失败之后一定会有重试。&lt;/p&gt;

&lt;h2 id=&quot;操作的幂等说明&quot;&gt;操作的幂等说明&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;对数据的读操作，在数据不变的情况下，无论执行多少次，返回的结果也是一直的，是天然的幂等性；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;对数据的删除操作，也是幂等性的，因为不论是删除一次还是删除多次，实现的效果是一致的；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;对于数据的新增操作，因为可能向数据库中插入重复的多条记录，因为不是幂等性的；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;对于数据的更新操作，也会出现多次重复被更新的情况，一次也不是幂等性的；&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;幂等的业务场景&quot;&gt;幂等的业务场景&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;账户资金的加减操作；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;电商订单的创建；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;页面的多次提交问题；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;并发下的计数问题；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;大型系统中的消息消费问题；&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;幂等的解决方案&quot;&gt;幂等的解决方案&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;幂等只是一个承诺，一个概念，保证调用多次返回结果的一致性，具体实现需要考虑多种环境下的高并发情况，并根据不同的场景选择合适的方案；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;对于新增的幂等性问题，可以配合数据库的唯一索引进行控制；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;对于数据的更新幂等性问题，可以通过悲观锁，乐观锁，缓存的分布式锁来控制并发；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;对于页面的多次提交，可以通过token机制进行控制；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;消息消费的场景，可以通过在消息上设置一个taskid来进行控制；&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;幂等性是优秀程序员必备的一个基因，在实现业务过程中，是应该被首先考虑的问题，尤其是在银行，金融的系统里，要同时保证数据的高效和准确性。&lt;/p&gt;</content><author><name></name></author><summary type="html">幂等的定义</summary></entry><entry><title type="html">分布式应用异常测试一二说</title><link href="https://ftvbftvbq.github.io/%E5%88%86%E5%B8%83%E5%BC%8F%E5%BA%94%E7%94%A8%E5%BC%82%E5%B8%B8%E6%B5%8B%E8%AF%95%E4%B8%80%E4%BA%8C%E8%AF%B4/" rel="alternate" type="text/html" title="分布式应用异常测试一二说" /><published>2018-03-06T00:00:00+00:00</published><updated>2018-03-06T00:00:00+00:00</updated><id>https://ftvbftvbq.github.io/%E5%88%86%E5%B8%83%E5%BC%8F%E5%BA%94%E7%94%A8%E5%BC%82%E5%B8%B8%E6%B5%8B%E8%AF%95%E4%B8%80%E4%BA%8C%E8%AF%B4</id><content type="html" xml:base="https://ftvbftvbq.github.io/%E5%88%86%E5%B8%83%E5%BC%8F%E5%BA%94%E7%94%A8%E5%BC%82%E5%B8%B8%E6%B5%8B%E8%AF%95%E4%B8%80%E4%BA%8C%E8%AF%B4/">&lt;p&gt;异常测试按性质分为应用层的业务逻辑异常测试、系统硬件/网络/文件/数据库/缓存/中间件异常测试，其中包含了许多的场景（单机、分布式），但所有的场景均和这两项有直接的关系。业务逻辑异常测试体现在当上述的第二种异常发生时，是否能根据业务的需要或者架构的设计做出合理的业务处理反应，这是建立在第二种异常测试之上的，因此异常测试的关系也已经非常明确了，第一种测试根据业务的不同，范围和流程有不确定性，第二种测试则是在一些明确的规则和约定下进行&lt;/p&gt;

&lt;p&gt;当架构演进到分布式，往往在测试过程中给人无从下手的错觉，尤其在异常测试方面，其实不然，前面提到的单机和分布式看似是两种类型，单独看，单机的异常影响范围可能会小一些，但事实上他们在分布式环境中会产生互相影响：&lt;/p&gt;

&lt;p&gt;单机：从系统层面来说就是指单一进程，从异常测试角度来看，影响范围发生在线程或进程级别，线程级别的异常可能导致线程的结束，且没有启动新的线程来代替，进程级别可能导致线程锁的不释放，导致其他线程都挂起等待。&lt;/p&gt;

&lt;p&gt;分布式：分布式是一个协同工作的应用环境，这种异常往往容易引起其他进程的挂起，或者数据库、缓存、中间件的问题，主要有网络调用所占用的资源、数据库访问等。
单机的异常如果处理不当，会引起整个环境中的资源不可用，从而导致环境中的每个单机都出现异常。根据上述的一些概念，可以列出异常测试中最重要的一些场景：&lt;/p&gt;

&lt;h2 id=&quot;系统资源&quot;&gt;系统资源&lt;/h2&gt;

&lt;p&gt;cpu、内存使用率过高，能否能将请求切到到资源利用率低的服务器上；&lt;/p&gt;

&lt;h2 id=&quot;数据量大小和形式&quot;&gt;数据量大小和形式&lt;/h2&gt;

&lt;p&gt;数据到底应该注入多少满足后续的压力测试，各服务对数据格式的要求和转换；&lt;/p&gt;

&lt;h2 id=&quot;文件读写&quot;&gt;文件读写&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;本地写：对同一个文件打开的的数量过多，或者只打开不关闭，导致文件句柄数超过系统阈值；&lt;/li&gt;
  &lt;li&gt;本地读：打开一个不存在的文件，是否有对应处理逻辑；&lt;/li&gt;
  &lt;li&gt;网络存储：服务不可用；&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;应用连接&quot;&gt;应用连接&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;短连接：请求方未设置超时时间，长时间等待响应方的响应，从而导致请求的大量堆积，线程池的处理线程被用完，导致大量新的用户请求被拒绝；&lt;/li&gt;
  &lt;li&gt;长连接：在网络出现异常状况后，断开的连接是否能重新建立，请求方如拿到失效的连接，是否能处理异常；&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;数据库&quot;&gt;数据库&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;数据源切换：如果所切换的数据源连接处于不可用状态或宕机时，是否会长时间等待或重试；&lt;/li&gt;
  &lt;li&gt;表锁、行锁：长时间更新操作，导致其他对此表的修改操作被挂起；&lt;/li&gt;
  &lt;li&gt;慢SQL的预防：通过对SQL的提前分析，来预防慢SQL相关的问题，及时告知DBA进行优化；&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;缓存&quot;&gt;缓存&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;key的失效：在获取不到key后，是否能正常处理；&lt;/li&gt;
  &lt;li&gt;锁的释放：申请到锁的一方如果意外重启，是否能在重启后释放锁；&lt;/li&gt;
  &lt;li&gt;缓存服务不可用；&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;消息中间件&quot;&gt;消息中间件&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;消息记录表切换：是否丢失；&lt;/li&gt;
  &lt;li&gt;清除消息记录：是否丢失记录；&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;服务发现&quot;&gt;服务发现&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;服务不可用：是否有其他处理措施；&lt;/li&gt;
  &lt;li&gt;单台不可用：是否能重新选举，重新建立连接；&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;应用容器&quot;&gt;应用容器&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;连接数：配置优化；&lt;/li&gt;
  &lt;li&gt;请求处理线程：配置优化；&lt;/li&gt;
  &lt;li&gt;jvm堆栈大小：参数优化；&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;前端静态化页面&quot;&gt;前端静态化页面&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;后端服务不可用；&lt;/li&gt;
  &lt;li&gt;缓存不可用；&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;数据库中间件&quot;&gt;数据库中间件&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;数据访问是否在错误发生后进行了正确的转移；&lt;/li&gt;
  &lt;li&gt;对于上层业务来说是否进行了正确的向下隔离；&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><summary type="html">异常测试按性质分为应用层的业务逻辑异常测试、系统硬件/网络/文件/数据库/缓存/中间件异常测试，其中包含了许多的场景（单机、分布式），但所有的场景均和这两项有直接的关系。业务逻辑异常测试体现在当上述的第二种异常发生时，是否能根据业务的需要或者架构的设计做出合理的业务处理反应，这是建立在第二种异常测试之上的，因此异常测试的关系也已经非常明确了，第一种测试根据业务的不同，范围和流程有不确定性，第二种测试则是在一些明确的规则和约定下进行</summary></entry><entry><title type="html">分布式锁滥用线上事故回顾</title><link href="https://ftvbftvbq.github.io/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E6%BB%A5%E7%94%A8%E7%BA%BF%E4%B8%8A%E4%BA%8B%E6%95%85%E5%9B%9E%E9%A1%BE/" rel="alternate" type="text/html" title="分布式锁滥用线上事故回顾" /><published>2018-03-05T00:00:00+00:00</published><updated>2018-03-05T00:00:00+00:00</updated><id>https://ftvbftvbq.github.io/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E6%BB%A5%E7%94%A8%E7%BA%BF%E4%B8%8A%E4%BA%8B%E6%95%85%E5%9B%9E%E9%A1%BE</id><content type="html" xml:base="https://ftvbftvbq.github.io/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E6%BB%A5%E7%94%A8%E7%BA%BF%E4%B8%8A%E4%BA%8B%E6%95%85%E5%9B%9E%E9%A1%BE/">&lt;h2 id=&quot;分布式锁概念&quot;&gt;分布式锁概念&lt;/h2&gt;

&lt;p&gt;分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中，常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源，那么访问这些资源的时候，往往需要互斥来防止彼此干扰来保证一致性，在这种情况下，便需要使用到分布式锁。在分布式系统中，常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源，那么访问这些资源的时候，往往需要互斥来防止彼此干扰来保证一致性，这个时候，便需要使用到分布式锁。&lt;/p&gt;

&lt;h2 id=&quot;事故详情&quot;&gt;事故详情&lt;/h2&gt;

&lt;p&gt;背景：最近发生一个线上问题，A系统tomcat到codis的连接数几分钟内就能从0飙升到200，因为连接数超限会被自动踢出可用服务序列，导致A系统几台tomcat陆续无法提供服务，页面端显示产品列表无数据。导致tomcat和codis连接数只增不减，原因是：&lt;/p&gt;

&lt;p&gt;A系统给B系统提供的一个接口（从方法名看是推荐产品），用户一进入App就调用该接口，所以调用频率较高。这个接口使用分布式锁在lock和unlock代码中间处理的业务逻辑是从数据库读出推荐商品list然后放到缓存里面。某种原因（如：线程死掉、服务器重启、该tomcat和codis之间断网）导致没执行到unlock，redis锁的默认失效时间300秒，所以这个锁（也就是redis的key）这300秒内一直存活，如果说获取锁失败后立即释放链接也没问题，但是框架里面有个sleep 3秒后继续重试获取锁，这样就有个问题：&lt;/p&gt;

&lt;p&gt;如果获取锁失败那么这个codis连接就一直存在，并不断尝试获取锁，所以重试机制应该有个度。当新的用户访问到来tomcat一直再建连接，导致5分钟后连接数到200个。&lt;/p&gt;

&lt;h2 id=&quot;解决措施&quot;&gt;解决措施&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;首先这个因为逻辑不应该使用分布式锁，分布式锁是为分布式系统的多进程在处理共享资源时使用，互斥来防止彼此干扰来保持一致性，此时需要利用锁的技术控制某一时刻修改数据的进程。需求是把推荐的公共数据缓存不应该采用用户触发的被动式，应该采用主动刷新缓存的方式（调用次数比用户触发可控）。这个操作只是select数据放缓存不涉及数据干扰的问题，所以不应该画蛇添足的用锁。
设置codis的key默认过期时间为20秒，使锁尽快释放；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;各个系统梳理使用分布式锁的地方进行改进，并根据情况设置一个小于20秒的锁失效时间；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;类似数据库、codis的连接不能把connection一直持有做自己的业务逻辑，后续操作（比如重试）宁可断开后从连接池再获取，虽然耗费点资源也不能一直占有，因为自己的业务逻辑不知什么时候就超时或者异常。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><summary type="html">分布式锁概念</summary></entry><entry><title type="html">Mysql查询优化技术之数据库和关系代数</title><link href="https://ftvbftvbq.github.io/MYSQL%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96%E6%8A%80%E6%9C%AF%E4%B9%8B%E6%95%B0%E6%8D%AE%E5%BA%93%E5%92%8C%E5%85%B3%E7%B3%BB%E4%BB%A3%E6%95%B0/" rel="alternate" type="text/html" title="Mysql查询优化技术之数据库和关系代数" /><published>2018-03-04T00:00:00+00:00</published><updated>2018-03-04T00:00:00+00:00</updated><id>https://ftvbftvbq.github.io/MYSQL%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96%E6%8A%80%E6%9C%AF%E4%B9%8B%E6%95%B0%E6%8D%AE%E5%BA%93%E5%92%8C%E5%85%B3%E7%B3%BB%E4%BB%A3%E6%95%B0</id><content type="html" xml:base="https://ftvbftvbq.github.io/MYSQL%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96%E6%8A%80%E6%9C%AF%E4%B9%8B%E6%95%B0%E6%8D%AE%E5%BA%93%E5%92%8C%E5%85%B3%E7%B3%BB%E4%BB%A3%E6%95%B0/">&lt;h2 id=&quot;1-什么是关系型数据库&quot;&gt;1. 什么是关系型数据库？&lt;/h2&gt;
&lt;h3 id=&quot;11-什么是数据库管理系统&quot;&gt;1.1 什么是数据库管理系统？&lt;/h3&gt;
&lt;p&gt;1）是一种操纵和管理数据的大型软件，用于建立、使用和维护数据，简称DBMS；&lt;/p&gt;

&lt;p&gt;2）它对数据进行统一的管理和控制，以保证数据的安全性和完整性；&lt;/p&gt;

&lt;p&gt;3）用户通过DBMS访问数据库中的数据，数据库管理员也通过DBMS进行数据库的维护工作；&lt;/p&gt;

&lt;p&gt;4）它可使多个应用程序和用户用不同的方法在同时或不同时刻去建立，修改和询问数据；&lt;/p&gt;

&lt;p&gt;5）DBMS提供数据定义语言DDL（Data Definition Language）和数据操作语言DML（Data Manipulation Language），供用户定义数据库的模式结构与权限约束，实现对数据的追加、删除等操作；&lt;/p&gt;

&lt;p&gt;目前市面上有一种说法，只要做数据处理，软件规模达到一定程度，似乎都称为了数据库。如：HBase/MongoDB等等。而且我们通常听到的这些NOSQL或NEWSQL数据库，通常被认为是分布式数据库，一谈到分布式我们就需要了解CAP理论，也即：&lt;/p&gt;

&lt;p&gt;Consistency(一致性)：数据一致更新，所有数据变动都是同步的；&lt;/p&gt;

&lt;p&gt;Availability(可用性)：好的响应性能；&lt;/p&gt;

&lt;p&gt;Partition tolerance(分区容忍性)：可靠性；&lt;/p&gt;

&lt;p&gt;CAP理论实践证实在分布式中要达到三者是不可能的，必须牺牲其中的一点。所以目前绝大部分企业内部都放弃了P而仅仅达到CA的目的。所以我们认为其实这些称为的数据库其实并不是严格意义上的数据库，因为我们衡量是否是数据库的标准是ACID：&lt;/p&gt;

&lt;p&gt;ACID，是指在数据库管理系统（DBMS）中事务所具有的四个特性：&lt;/p&gt;

&lt;p&gt;1）原子性（Atomicity）&lt;/p&gt;

&lt;p&gt;2）一致性（Consistency）&lt;/p&gt;

&lt;p&gt;3）隔离性（Isolation）&lt;/p&gt;

&lt;p&gt;4）持久性（Durability）&lt;/p&gt;

&lt;h3 id=&quot;12-什么是关系型数据库管理系统&quot;&gt;1.2 什么是关系型数据库管理系统？&lt;/h3&gt;

&lt;p&gt;历史上的几种类型的数据库：&lt;/p&gt;

&lt;p&gt;1 层次型&lt;/p&gt;

&lt;p&gt;2 网状型&lt;/p&gt;

&lt;p&gt;3 关系型&lt;/p&gt;

&lt;p&gt;关系数据库，是建立在关系数据库模型基础上的数据库，借助于关系代数等概念和方法来处理数据库中的数据，同时也是一个被组织成一组拥有正式描述性的表格，该形式的表格作用的实质是装载着数据项的特殊收集体，这些表格中的数据能以许多不同的方式被存取或重新召集而不需要重新组织数据库表格。我们来看下MYSQL数据库的模型：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://ftvbftvbq.github.io/images/mysql_moddle.jpg&quot; alt=&quot;MYSQL模型&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们所说的优化主要集中在Parser和Optimizer两个部分上，前者主要是逻辑优化后者主要是物理优化（比如利用索引）&lt;/p&gt;

&lt;h3 id=&quot;13为什么我们需要学习mysqlpostgresql-&quot;&gt;1.3为什么我们需要学习MySQL/PostgreSQL ？&lt;/h3&gt;

&lt;p&gt;趋势1：云计算，淘汰大部分的运维人员；&lt;/p&gt;

&lt;p&gt;趋势2：云数据库，淘汰大部分以商业数据库为职业的DBA；&lt;/p&gt;

&lt;p&gt;趋势3：电商等兴起，对开源数据库技术的人员需求增多；&lt;/p&gt;

&lt;p&gt;趋势4：去IOE化(就是去掉IBM的小型机、Oracle数据库、EMC存储设备，代之在开源软件基础上开发的系统)等运动背后的商业成本和安全问题，促进开源产业发展；&lt;/p&gt;

&lt;p&gt;趋势5：MySQL和PostgreSQL为代表的开源数据库自身正蓬勃发展中。&lt;/p&gt;

&lt;h3 id=&quot;14为什么我们需要掌握查询优化技术-&quot;&gt;1.4为什么我们需要掌握查询优化技术 ？&lt;/h3&gt;

&lt;p&gt;从数据处理的角度看：&lt;/p&gt;

&lt;p&gt;数据库的OLAP（联机分析处理）型应用要多于OLTP（联机事务处理过程）型应用。大数据技术目前处理了一部分非结构化的数据，但分布式数据库技术的发展，将使得数据库技术进一步发展甚至出现革命性的进步进而与现行的hadoop/spark等技术争夺数据分析的阵地。而查询优化技术因此二者得益继续发展。&lt;/p&gt;

&lt;p&gt;从当下技术发展的趋势看：&lt;/p&gt;

&lt;p&gt;大数据背景下，盛行几十年的SQL查询技术焕发了新的生机，不仅在数据库中占有半壁江山，而且在大数据处理技术下，各种SQL查询接口/功能层异军突起。而查询优化技术因具有普适性得以继续使用。&lt;/p&gt;

&lt;h2 id=&quot;2-什么是关系代数和数据库之间的关系是怎么样的&quot;&gt;2.	什么是关系代数？和数据库之间的关系是怎么样的？&lt;/h2&gt;
&lt;p&gt;数学中，关系代数是支持叫做逆反(converse)的对合一元运算的剩余布尔代数。激发关系代数的例子是在集合 X 上的所有二元关系的代数 ，带有 R·S 被解释为平常的二元关系复合。关系代数的早期形式形成于十九世纪德·摩根、皮尔士和 Ernst Schröder 的工作。它今日的纯等式形式是阿尔弗雷德·塔斯基和他的学生在 1940 年代开发的。
1970年E.F. Codd发表了数据的关系模型论文。Codd提议这样一种代数作为数据库查询语言的基础，称为关系代数。&lt;/p&gt;

&lt;p&gt;Codd的关系代数的六个原始运算是“选择”、“投影”、笛卡尔积（也叫做“叉积”或“交叉连接”）、并集、差集和“重命名”。（实际上，Codd忽略了重命名，而ISBL的发明者显著的包括了它）。这六个运算在省略其中任何一个都要损失表达能力的意义上是基本的。已经依据这六个原始运算定义了很多其他运算。其中最重要的是交集、除法和自然连接。事实上ISBL显著的用自然连接替代了笛卡尔积，它是笛卡尔积的退化情况。
第一个基于Codd的代数的查询语言是ISBL，许多作者都认同这个先驱的工作展示了一个使Codd的想法成为有用语言的方式。&lt;/p&gt;

&lt;p&gt;关系代数是一种抽象的查询语言，用对关系的运算来表达查询，作为研究关系数据语言的数学工具。&lt;/p&gt;

&lt;p&gt;关系代数的运算对象是关系，运算结果亦为关系。关系代数用到的运算符包括四类：&lt;/p&gt;

&lt;p&gt;1）集合运算符&lt;/p&gt;

&lt;p&gt;2）专门的关系运算符&lt;/p&gt;

&lt;p&gt;3）算术比较符&lt;/p&gt;

&lt;p&gt;4）逻辑运算符&lt;/p&gt;

&lt;p&gt;比较运算符和逻辑运算符是用来辅助专门的关系运算符进行操作的，所以按照运算符的不同，主要将关系代数分为传统的集合运算和专门的关系运算两类。&lt;/p&gt;

&lt;h2 id=&quot;3-什么是查询优化技术&quot;&gt;3.	什么是查询优化技术？&lt;/h2&gt;
&lt;p&gt;从广义的查询优化来说，查询优化技术包括了几个方面：&lt;/p&gt;

&lt;p&gt;1）	查询重用；&lt;/p&gt;

&lt;p&gt;2）	查询重写规则；&lt;/p&gt;

&lt;p&gt;3）	查询算法优化技术；&lt;/p&gt;

&lt;p&gt;4）	并行查询优化技术；&lt;/p&gt;

&lt;p&gt;5）	分布式查询优化技术；&lt;/p&gt;

&lt;p&gt;从狭义来说，查询优化技术只涉及2个方面：&lt;/p&gt;

&lt;p&gt;1）	逻辑优化&lt;/p&gt;

&lt;p&gt;2）	物理优化&lt;/p&gt;

&lt;p&gt;前者更注重对SQL本身的优化，依据关系代数做一定的等价变换做一些逻辑优化，后者是根据数据读取、表连接方式、表连接顺序、排序等技术进行优化。&lt;/p&gt;

&lt;h2 id=&quot;4-什么是mysql的查询优化&quot;&gt;4.	什么是MYSQL的查询优化？&lt;/h2&gt;

&lt;p&gt;MYSQL的查询优化主要涉及几个方面：&lt;/p&gt;

&lt;p&gt;1）	查询重用；&lt;/p&gt;

&lt;p&gt;2）	查询重写规则；&lt;/p&gt;

&lt;p&gt;3）	查询算法优化技术；&lt;/p&gt;

&lt;p&gt;其实还有部分的并行优化，但是和ORACLE等商业数据库相比还是弱了些。所以这里不写入。&lt;/p&gt;

&lt;h2 id=&quot;5-什么是数据库调优&quot;&gt;5.	什么是数据库调优？&lt;/h2&gt;

&lt;p&gt;数据库调优可以使数据库应用运行得更快，其目标是使数据库：&lt;/p&gt;

&lt;p&gt;1）	有更高的吞吐量&lt;/p&gt;

&lt;p&gt;2）	更短的响应时间&lt;/p&gt;

&lt;p&gt;这里需要强调等是，被调优的对象是整个数据库管理系统总体。&lt;/p&gt;

&lt;p&gt;数据库调优的方式通常有如下几种：&lt;/p&gt;

&lt;p&gt;1）人工调优&lt;/p&gt;

&lt;p&gt;主要依赖于人，效率低下；要求操作者完全理解常识所依赖的原理，还需要对应用、数据库管理系统、操作系统以及硬件有广泛而深刻的理解；&lt;/p&gt;

&lt;p&gt;2）基于案例的调优&lt;/p&gt;

&lt;p&gt;总结典型应用案例情况中数据库参数的推荐配置值、数据逻辑层设计等情况，从而为用户的调优工作提供一定的参考和借鉴。但这种方式忽略了系统的动态性和不同系统间存在的差异；&lt;/p&gt;

&lt;p&gt;3）自调优&lt;/p&gt;

&lt;p&gt;为数据库系统建立一个模型，根据“影响数据库系统性能效率的因素”，数据库系统自动进行参数的配置。一些商业数据库，实现了部分自调优技术。&lt;/p&gt;

&lt;p&gt;数据库调优主要分为五个阶段：
&lt;img src=&quot;https://ftvbftvbq.github.io/images/youhua.jpg&quot; alt=&quot;数据库调优阶段&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;数据库调优五个阶段的主要技术&quot;&gt;数据库调优五个阶段的主要技术&lt;/h2&gt;

&lt;h3 id=&quot;第一阶段&quot;&gt;第一阶段：&lt;/h3&gt;

&lt;p&gt;1）应用情况的估算&lt;/p&gt;

&lt;p&gt;应用的使用方式（把业务逻辑转换为数据库的读写分布逻辑，以是读多写少还是读写均衡等来区分OLTP和OLAP；应用对数据库的并发情况、并发是否可以池化等）、数据量、对数据库的压力、峰值压力等做一个预估；&lt;/p&gt;

&lt;p&gt;2）系统选型策略&lt;/p&gt;

&lt;p&gt;确定什么样的数据库可以适用应用需求，并确定数据库是使用开源的还是商业的，是集使用群还是单机的系统，同时对操作系统、中间件、硬件、网络等进行选型。&lt;/p&gt;

&lt;h3 id=&quot;第二阶段&quot;&gt;第二阶段：&lt;/h3&gt;

&lt;p&gt;数据模型的设计&lt;/p&gt;

&lt;p&gt;主要是根据业务逻辑，从几个角度考虑表的逻辑结构，内容如下：&lt;/p&gt;

&lt;p&gt;1）E-R模型设计：遵循E-R模型设计原理。偶尔的适当程度的非规范化可以改善系统查询性能；&lt;/p&gt;

&lt;p&gt;2）数据逻辑分布策略：目的是减少数据请求的不必要的数据量，把用户需要的数据返回；可用的技术如分区、用E-R模型分表等（如互联网企业典型的用法，根据业务的不同，进行分库、分表等操作）；&lt;/p&gt;

&lt;p&gt;3）数据物理存储策略：目的是减少IO，如启用压缩技术、把索引和表数据的存储分开，不同的表数据分布于不同的表空间，不同表空间分布在不同的物理存储上（尤其是读写量大的表空间分布在不同的物理存储上）、日志、索引和数据分布在不同的物理存储上等；&lt;/p&gt;

&lt;p&gt;4）索引：在查询频繁的对象上建立合适的索引，使索引的正效应大于负效应（索引的维护存在消耗）。&lt;/p&gt;

&lt;h3 id=&quot;第三四阶段&quot;&gt;第三、四阶段：&lt;/h3&gt;

&lt;p&gt;1）SQL设计&lt;/p&gt;

&lt;p&gt;编写正确的、查询效率高的SQL语句。这依据的主要是“查询重写规则”，编写语句的过程中要注意，要有意识地保障SQL能利用到索引。&lt;/p&gt;

&lt;p&gt;2）数据库功能的启用&lt;/p&gt;

&lt;p&gt;数据库为提高性能提供了一些功能，可合理使用，具体如下：&lt;/p&gt;

&lt;p&gt;2.1）查询重用：根据实际情况进行配置，可缓存查询执行计划、查询结果等；&lt;/p&gt;

&lt;p&gt;2.2）数据库参数的设置：可设置合适的参数如数据缓冲区等；&lt;/p&gt;

&lt;p&gt;2.3）模型系统预运行。在备用系统上模拟实际运行环境，加大压力进行系统测试，提前发现问题。&lt;/p&gt;

&lt;h3 id=&quot;第五阶段&quot;&gt;第五阶段：&lt;/h3&gt;

&lt;p&gt;1）系统监控与分析。在工业环境下，加强对系统的运行监控和日常的分析工作，具体如下：&lt;/p&gt;

&lt;p&gt;1.1）应用系统表现：收集用户对应用系统的使用意见、系统存在问题等，因为这些可能是用户在第一时间发现的；&lt;/p&gt;

&lt;p&gt;1.2）OS环境监控：实时监控CPU、内存、IO等，并对比实时情况与历史正常情况；&lt;/p&gt;

&lt;p&gt;1.3）据库内部状况监控：一些数据库提供系统表、视图、工具等手段，向用户提供数据库运行过程中内部状况的信息，如锁的情况，这些都需要实时监控，并对比实时情况与历史正常情况；&lt;/p&gt;

&lt;p&gt;1.4）日志分析：在数据库的日志、操作系统的日志中找出异常事件，定位问题。&lt;/p&gt;</content><author><name></name></author><summary type="html">1. 什么是关系型数据库？ 1.1 什么是数据库管理系统？ 1）是一种操纵和管理数据的大型软件，用于建立、使用和维护数据，简称DBMS； 2）它对数据进行统一的管理和控制，以保证数据的安全性和完整性； 3）用户通过DBMS访问数据库中的数据，数据库管理员也通过DBMS进行数据库的维护工作； 4）它可使多个应用程序和用户用不同的方法在同时或不同时刻去建立，修改和询问数据； 5）DBMS提供数据定义语言DDL（Data Definition Language）和数据操作语言DML（Data Manipulation Language），供用户定义数据库的模式结构与权限约束，实现对数据的追加、删除等操作； 目前市面上有一种说法，只要做数据处理，软件规模达到一定程度，似乎都称为了数据库。如：HBase/MongoDB等等。而且我们通常听到的这些NOSQL或NEWSQL数据库，通常被认为是分布式数据库，一谈到分布式我们就需要了解CAP理论，也即：</summary></entry><entry><title type="html">子查询逻辑优化</title><link href="https://ftvbftvbq.github.io/%E5%AD%90%E6%9F%A5%E8%AF%A2%E9%80%BB%E8%BE%91%E4%BC%98%E5%8C%96/" rel="alternate" type="text/html" title="子查询逻辑优化" /><published>2018-03-03T00:00:00+00:00</published><updated>2018-03-03T00:00:00+00:00</updated><id>https://ftvbftvbq.github.io/%E5%AD%90%E6%9F%A5%E8%AF%A2%E9%80%BB%E8%BE%91%E4%BC%98%E5%8C%96</id><content type="html" xml:base="https://ftvbftvbq.github.io/%E5%AD%90%E6%9F%A5%E8%AF%A2%E9%80%BB%E8%BE%91%E4%BC%98%E5%8C%96/">&lt;p&gt;一、什么是子查询？&lt;/p&gt;

&lt;p&gt;在百度百科上查询子查询的时候，给出的定义为:&lt;/p&gt;

&lt;p&gt;当一个查询是另一个查询的条件时，称之为子查询。&lt;/p&gt;

&lt;p&gt;这里说的条件，是在是太宽泛，我们在查询的时候，只给出比如一个where条件，那么也成为子查询嘛？显然不正确？所以我们给子查询做出正确的范围，定位应该为：当一个查询是另一个查询的子部分时，称之为子查询（查询语句中嵌套有查询语句）。这里强调子部分，就意味着有主次之分，也意味着在优化的时候主要针对的是主次查询进行的优化。这样的优化才是有实际存在意义的优化。&lt;/p&gt;

&lt;p&gt;二、查询的子部分，包括的情况&lt;/p&gt;

&lt;p&gt;1）目标列位置&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;子查询如果位于目标列，则只能是标量子查询（返回结果是唯一的），否则数据库可能返回类似“错误:  子查询必须只能返回一个字段”的提示。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;2）FROM子句位置&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;相关子查询出现在FROM子句中，数据库可能返回类似“在FROM子句中的子查询无法参考相同查询级别中的关系”的提示，所以相关子查询不能出现在FROM子句中；

非相关子查询出现在FROM子句中，可上拉子查询到父层，在多表连接时统一考虑连接代价然后择优。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3）WHERE子句位置&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; 出现在WHERE子句中的子查询，是一个条件表达式的一部分，而表达式可以分解为操作符和操作数；根据参与运算的不同的数据类型，操作符也不尽相同，如INT型有“&amp;gt;、&amp;lt;、=、&amp;lt;&amp;gt;”等操作，这对子查询均有一定的要求（如INT型的等值操作，要求子查询必须是标量子查询）。另外，子查询出现在WHERE子句中的格式，也有用谓词指定的一些操作，如IN、BETWEEN、EXISTS等。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;4）JOIN/ON子句位置&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;JOIN/ON子句可以拆分为两部分，一是JOIN块类似于FROM子句，二是ON子句块类似于WHERE子句，这两部分都可以出现子查询。子查询的处理方式同FROM子句和WHERE子句。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;5）GROUPBY子句位置&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;目标列必须和GROUPBY关联。可将子查询写在GROUPBY位置处，但子查询用在GROUPBY处没有实用意义。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;6）ORDERBY子句位置&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;可将子查询写在ORDERBY位置处。但ORDERBY操作是作用在整条SQL语句上的，子查询用在ORDERBY处没有实用意义。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里由于代码不好编辑的问题，具体的可以原文链接。&lt;/p&gt;

&lt;p&gt;1.2 子查询的类型&lt;/p&gt;

&lt;p&gt;从特定谓词看：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1）相关子查询：子查询的执行依赖于外层父查询的一些属性值。子查询因依赖于父查询的参数，当父查询的参数改变时，子查询需要根据新参数值重新执行（查询优化器对相关子查询进行优化有一定意义）；

2）非相关子查询：子查询的执行，不依赖于外层父查询的任何属性值。这样子查询具有独立性，可独自求解，形成一个子查询计划先于外层的查询求解。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从对象间的关系看：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1） [NOT] IN/ALL/ANY/SOME子查询：语义相近，表示“[取反] 存在/所有/任何/任何”，左面是操作数，右面是子查询，是最常见的子查询类型之一 ；

2）[NOT] EXISTS子查询：半连接语义，表示“[取反] 存在”，没有左操作数，右面是子查询，也是最常见的子查询类型之一；

3）其他子查询：除了上述两种外的所有子查询。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从语句的构成复杂程度看：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1）SPJ子查询：由选择、连接、投影操作组成的查询；

2）GROUPBY子查询：SPJ子查询加上分组、聚集操作组成的查询；

3）其他子查询：GROUPBY子查询中加上其他子句如Top-N、LIMIT/OFFSET、集合、排序等操作。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;后两种子查询有时合称非SPJ子查询。&lt;/p&gt;

&lt;p&gt;从结果的角度看：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1）标量子查询

子查询返回的结果集类型是一个简单值（return a scalar，a single value）；

2）单行单列子查询

子查询返回的结果集类型是零条或一条单元组（return a zero or single row，but only acolumn）。相似于标量子查询,但可能返回零条元组；

3）多行单列子查询

子查询返回的结果集类型是多条元组但只有一个简单列（return multiple rows，but only a column）；

4）表子查询

子查询返回的结果集类型是一个表（多行多列）（returna table，one or more rows of one ormore columns）。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;为什么数据库需要优化子查询？&lt;/p&gt;

&lt;p&gt;在数据库实现早期，查询优化器对子查询一般采用嵌套执行的方式，即对父查询中的每一行，都执行一次子查询，这样子查询会执行很多次。这种执行方式效率很低。&lt;/p&gt;

&lt;p&gt;而对子查询进行优化，可能带来几个数量级的查询效率的提高。&lt;/p&gt;

&lt;p&gt;子查询转变成为连接操作之后，会得到如下好处：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1）子查询不用执行很多次；

2）优化器可以根据统计信息来选择不同的连接方法和不同的连接顺序。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;子查询中的连接条件、过滤条件分别变成了父查询的连接条件、过滤条件，优化器可以对这些条件进行下推，以提高执行效率。&lt;/p&gt;

&lt;p&gt;怎么进行子查询优化？&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    1）子查询合并（Subquery Coalescing）

      在某些条件下（语义等价：两个查询块产生同样的结果集），多个子查询能够合并成一个子查询（合并后还是子查询，以后可以通过其他技术消除掉子查询）。这样可以把多次表扫描、多次连接减少为单次表扫描和单次连接；

    2）子查询展开（Subquery Unnesting）

      又称子查询反嵌套，又称为子查询上拉。把一些子查询置于外层的父查询中，作为连接关系与外层父查询并列，其实质是把某些子查询重写为等价的多表连接操作（展开后，子查询不存在了，外部查询变成了多表连接）。带来的好处是，有关的访问路径、连接方法和连接顺序可能被有效使用，使得查询语句的层次尽可能的减少。常见的IN/ANY/SOME/ALL/EXISTS依据情况转换为半连接（SEMI JOIN）、普通类型的子查询消除等情况属于此类，如：
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;SELECT * FROM t1, (SELECT * FROM t2 WHERE t2.a2&amp;gt;10) v_t2&lt;/p&gt;

&lt;p&gt;WHERE t1.a1&amp;lt;10 AND v_t2.a2&amp;lt;20;&lt;/p&gt;

&lt;p&gt;可优化为：&lt;/p&gt;

&lt;p&gt;SELECT * FROM t1, t2 WHERE t1.a1&amp;lt;10 ANDt2.a2&amp;lt;20 AND t2.a2 &amp;gt;10;&lt;/p&gt;

&lt;p&gt;/* 子查询变为了t1、t2表的连接操作，相当于把t2表从子查询中上拉了一层 */&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;子查询展开优化的技术手段

展开的条件：

1）如果子查询中出现了聚集、GROUPBY、DISTINCT子句，则子查询只能单独求解，不可以上拉到外层。

2）如果子查询只是一个简单格式的（SPJ格式）查询语句，则可以上拉子查询到外层，这样往往能提高查询效率。子查询上拉，讨论的就是这种格式，这也是子查询展开技术处理的范围。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;把子查询上拉到上层查询，前提是上拉（展开）后的结果不能带来多余的元组，所以子查询展开需要遵循如下规则：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1）如果上层查询的结果没有重复（即SELECT子句中包含主码），则可以展开其子查询。并且展开后的查询的SELECT子句前应加上DISTINCT标志。

2）如果上层查询的SELECT语句中有DISTINCT标志，可以直接进行子查询展开。
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如果内层查询结果没有重复元组，则可以展开。&lt;/p&gt;

&lt;p&gt;子查询展开的具体步骤：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1）将子查询和外层查询的FROM子句连接为同一个FROM子句，并且修改相应的运行参数；

2）将子查询的谓词符号进行相应修改(如：“IN”修改为“=”)；

3）将子查询的WHERE条件作为一个整体与外层查询的WHERE条件合并，并用AND条件连接词连接，从而保证新生成的谓词与原旧谓词的上下文意思相同，且成为一个整体。



3）聚集子查询消除（Aggregate Subquery Elimination）
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通常，一些系统支持的是标量聚集子查询消除。如：&lt;/p&gt;

&lt;p&gt;SELECT * FROM t1 WHERE t1.a1&amp;gt;(SELECT avg(t2.a2)FROM t2);&lt;/p&gt;</content><author><name></name></author><summary type="html">一、什么是子查询？</summary></entry><entry><title type="html">数据库查询优化技术</title><link href="https://ftvbftvbq.github.io/%E6%95%B0%E6%8D%AE%E5%BA%93%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96%E6%8A%80%E6%9C%AF/" rel="alternate" type="text/html" title="数据库查询优化技术" /><published>2018-03-03T00:00:00+00:00</published><updated>2018-03-03T00:00:00+00:00</updated><id>https://ftvbftvbq.github.io/%E6%95%B0%E6%8D%AE%E5%BA%93%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96%E6%8A%80%E6%9C%AF</id><content type="html" xml:base="https://ftvbftvbq.github.io/%E6%95%B0%E6%8D%AE%E5%BA%93%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96%E6%8A%80%E6%9C%AF/">&lt;h2 id=&quot;1-什么是数据库查询优化技术&quot;&gt;1.	什么是数据库查询优化技术&lt;/h2&gt;

&lt;h2 id=&quot;11-查询复用&quot;&gt;1.1	查询复用&lt;/h2&gt;

&lt;p&gt;1.1.1	查询重用是指尽可能利用先前的执行结果，以达到节约查询计算全过程的时间并减少资源消耗的目的。&lt;/p&gt;

&lt;p&gt;1.1.2	目前查询重用技术主要集中在两个方面：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;查询结果的重用。在缓存区中分配一块缓冲块，存放该SQL 语句文本和最后的结果集，当同样的SQL输入时，直接把结果返回。查询结果的重用技术节约了查询计划生成时间，减少了查询执行全过程的资源消耗；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;查询计划的重用。缓存一条查询语句的执行计划及其相应语法树结构。查询计划的重用技术减少了查询计划生成的时间和资源消耗。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;查询重用技术利弊：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;弊端：结果集很大会消耗很大的内存资源，同样的SQL不同用户应该获取的结果集可能不同；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;利端：节约了CPU和IO消耗。在实际使用的过程中，趋利避害，根据实际情况选用；&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;12-查询重写规则&quot;&gt;1.2	查询重写规则&lt;/h2&gt;

&lt;p&gt;1.2.1	查询重写：&lt;/p&gt;

&lt;p&gt;是查询语句的一种等价转换，即对于任何相关模式的任意状态都会产生相同的结果（相同的关系替代两个表达式中相应的关系，所得到的结果是相同的）。&lt;/p&gt;

&lt;p&gt;1.2.2	查询重写的两个目标：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;将查询转换为等价的效率更高的形式，例如将效率低的谓词转换为效率高的谓词、消除重复条件等。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;尽量将查询重写为等价、简单且不受表顺序限制的形式，为物理查询优化阶段提供更多的选择，如视图的重写、子查询的合并转换等。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;1.2.3	查询重写的依据：&lt;/p&gt;

&lt;p&gt;查询重写的依据，是关系代数，请参看：&lt;a href=&quot;http://xn--m8tz91bfdr2pjzo.xyz/MYSQL%E6%9F%A5%E8%AF%A2%E4%BC%98%E5%8C%96%E6%8A%80%E6%9C%AF%E4%B9%8B%E6%95%B0%E6%8D%AE%E5%BA%93%E5%92%8C%E5%85%B3%E7%B3%BB%E4%BB%A3%E6%95%B0/&quot; target=&quot;_blank&quot;&gt;MYSQL查询优化技术之数据库和关系代数&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;1．关系代数的等价变换规则对查询重写提供了理论上的支持。&lt;/p&gt;

&lt;p&gt;2．查询重写后，查询优化器可能生成多个连接路径，可以从候选者中择优。&lt;/p&gt;

&lt;p&gt;1.2.4	查询优化技术类型：&lt;/p&gt;

&lt;p&gt;1．语法级。查询语言层的优化，基于语法进行优化。&lt;/p&gt;

&lt;p&gt;2．代数级。查询使用形式逻辑进行优化，运用关系代数的原理进行优化。&lt;/p&gt;

&lt;p&gt;3．语义级。根据完整性约束，对查询语句进行语义理解，推知一些可优化的操作。&lt;/p&gt;

&lt;p&gt;4．物理级。物理优化技术，基于代价估算模型，比较得出各种执行方式中代价最小的。&lt;/p&gt;

&lt;p&gt;查询重写是基于语法级、代数级、语义级的优化，可以统一归属到逻辑优化的范畴：基于代价估算模型是物理层面的优化，是从连接路径中选择代价最小的路径的过程。&lt;/p&gt;

&lt;p&gt;1.2.5	查询优化重写思路：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;将过程性查询转换为描述性的查询，如视图重写。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;将复杂的查询（如嵌套子查询、外连接消除、嵌套连接消除）尽可能转换为多表连接查询。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;将效率低的谓词转换为等价的效率高的谓词（如等价谓词重写）。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;1.2.6 查询优化重写思路：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;利用等式和不等式的性质，简化WHERE、HAVING和ON条件。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;如何改进现有查询重写规则的效率，如何发现更多更有效的重写规则，是查询优化的研究内容之一。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;常见的查询重写技术类型，每一类都有自己的规则，这些规则没有确定的、统一的规律，但重写的核心一定是“等价转换”，只有等价才能转换，这是需要特别强调的。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;12--查询优化算法&quot;&gt;1.2	 查询优化算法&lt;/h3&gt;

&lt;p&gt;1.2.1 什么是查询优化算法？&lt;/p&gt;

&lt;p&gt;查询优化，求解给定查询语句的高效执行计划的过程。这样的过程，包括了多种子问题求解。不同的子问题，对应了不同的解决方法，即算法。&lt;/p&gt;

&lt;p&gt;1.2.2 什么是查询计划？&lt;/p&gt;

&lt;p&gt;查询计划，也称为查询树，它由一系列内部的操作符组成，这些操作符按一定的运算关系构成查询的一个执行方案。&lt;/p&gt;

&lt;p&gt;简单说，就是表A和表B先连接得到中间结果，然后再和另外的表C连接得到新的中间方式，直至所有表都被连接完毕（连接操作就是操作符，这个示例有两个连接操作符。A连接B连接C、C连接B连接A就是两种不同的执行方案，即是两个不同的执行计划，查询优化要选出最高效的一个执行方案）。&lt;/p&gt;

&lt;p&gt;1.2.3 查询计划的形式：&lt;/p&gt;

&lt;p&gt;查询计划，从形式上看，是一颗二叉树，树叶是每个单表对象；&lt;/p&gt;

&lt;p&gt;两个树叶的父结点是一个连接操作符（如左外连接操作符，A left-out join B）连接后的中间结果（另外还有一些其他结点如排序操作等也可以作为中间结果）这个结果是一个临时“关系”，这样直至根结点。&lt;/p&gt;

&lt;p&gt;1.2.4 查询计划，二叉树上的不同结点：&lt;/p&gt;

&lt;p&gt;1.2.4.1 单表结点:&lt;/p&gt;

&lt;p&gt;考虑单表的数据获取方式，是直接通过IO获得数据，还是通过索引获取数据，或者是通过索引定位数据的位置后再经过IO到数据块中获取数据。&lt;/p&gt;

&lt;p&gt;这是一个从物理存储到内存解析成逻辑字段的过程，即符合冯·诺依曼体系结构的的要求（外存数据读入内存才能被处理）。&lt;/p&gt;

&lt;p&gt;1.2.4.2 两表结点:&lt;/p&gt;

&lt;p&gt;考虑两表以何种方式连接、代价有多大、连接路径有哪些等。表示的是内存中的元组，怎么进行元组间的连接。此时，元组通常已经存在于内存，直接使用即可。这是一个完成用户语义的逻辑操作，但是只是局部操作，只涉及两个具体的关系。完成用户全部语义（用户连接的语义），需要配合多表的连接顺序的操作。&lt;/p&gt;

&lt;p&gt;不同的连接算法导致的连接效率不同，如数据少时可使用Hash连接，数据量大可使用嵌套连接，数据如果有序可使用归并连接或先排序后使用归并连接等。&lt;/p&gt;

&lt;p&gt;1.2.4.3 多表结点:&lt;/p&gt;

&lt;p&gt;考虑多表连接顺序如何构成代价最少的“执行计划”。决定是AB先连接还是BC先连接，这是一个比较花费大小的运算。如果太多的连接方式被判断，也会导致效率问题。多个关系采用不同次序进行连接，花费的CPU资源、内存资源差异可能较大。&lt;/p&gt;

&lt;p&gt;许多数据库采用左深树、右深树、紧密树三种方式或其中一部分对多表进行连接得到多种连接路径。&lt;/p&gt;

&lt;p&gt;1.2.5 生成最优查询计划的策略：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;基于规则优化。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;根据经验或一些已经探知或被证明有效的方式，定义为“规则”（如根据关系代数得知的规则、根据经验得知的规则等），用这些规则化简查询计划生成过程中符合可被化简的操作，使用启发式规则排除一些明显不好的存取路径，这就是基于规则的优化。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;基于代价优化。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;根据一个代价评估模型，在生成查询计划的过程中，计算每条存取路径（存取路径主要包括上述三个“关系结点”）的花费，然后选择代价最小的作为子路径，这样直至所有表连接完毕得到一个完整的路径。&lt;/p&gt;

&lt;p&gt;主流数据库都采用了基于代价策略进行优化的技术。&lt;/p&gt;

&lt;p&gt;主流数据库对于基于规则和基于代价的技术，都在使用！&lt;/p&gt;

&lt;p&gt;基于规则优化具有操作简单且能快速确定连接方式的优点，但这种方法只是排除了一部分不好的可能，所以得到的结果未必是最好的；&lt;/p&gt;

&lt;p&gt;基于代价优化，是对各种可能的情况进行量化比较，从而可以得到花费最小的情况，但如果组合情况比较多则花费的判断时间就会很多；&lt;/p&gt;

&lt;p&gt;查询优化器的实现，多是两种优化策略组合使用，如MySQL和PostgreSQL就采取了基于规则和代价估算的查询优化策略。&lt;/p&gt;

&lt;h3 id=&quot;13-并行查询优化&quot;&gt;1.3	并行查询优化&lt;/h3&gt;
&lt;p&gt;1.3.1 查询优化为什么要并行？&lt;/p&gt;

&lt;p&gt;传统单机数据库系统中，给定一个查询（Query），查询优化算法只需找到查询的一个具有最小执行花费的执行计划，这样的计划必定具有最快的响应时间。&lt;/p&gt;

&lt;p&gt;在并行数据库系统中，查询优化的目标是寻找具有最小响应时间的查询执行计划，这需要把查询工作分解为一些可以并行运行的子工作。一些商业数据库提供了并行查询的功能，用以优化查询执行操作。&lt;/p&gt;

&lt;p&gt;1.3.2 查询优化并行的条件：&lt;/p&gt;

&lt;p&gt;一个查询能否并行执行，取决于多种因素：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;系统中的可用资源（如内存、高速缓存中的数据量等）。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;CPU的数目。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;运算中的特定代数运算符。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如A、B、C、D四个表进行连接，每个表的单表扫描可以并行进行；在生成四个表连接的查询计划过程中，可选择A和B连接的同时C和D进行连接，这样连接操作能并行运行。不同商业数据库，对查询并行的实现也不尽相同。&lt;/p&gt;

&lt;p&gt;1.3.3 查询优化并行的条件：&lt;/p&gt;

&lt;p&gt;在同一个SQL内，查询并行可以分为：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;操作内并行。将同一操作如单表扫描操作、两表连接操作、排序操作等分解成多个独立的子操作，由不同的CPU同时执行。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;操作间并行。一条SQL查询语句可以分解成多个子操作，由多个CPU执行。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;1.3.4 在分布式数据库系统中, 查询策略优化是查询优化的重点。&lt;/p&gt;

&lt;p&gt;主要是数据传输策略，A、B两结点的数据进行连接，是A结点数据传输到B结点或是从B到A或是先各自进行过滤然后再传输等）和局部处理优化（传统的单结点数据库的查询优化技术）。&lt;/p&gt;

&lt;h3 id=&quot;14-分布式查询优化&quot;&gt;1.4	分布式查询优化&lt;/h3&gt;

&lt;p&gt;在查询优化策略中，数据的通信开销是优化算法考虑的主要因素。分布式查询优化以减少传输的次数和数据量作为查询优化的目标。&lt;/p&gt;

&lt;p&gt;分布式数据库系统中的代价估算模型，除了考虑CPU代价和I/O代价外，还要考虑通过网络在结点间传输数据的代价。这是分布式并行查询优化技术与传统单结点数据库系统最大不同之处。&lt;/p&gt;

&lt;p&gt;在分布式数据库系统中，代价估算模型为：&lt;/p&gt;

&lt;p&gt;总代价 = I/O代价 + CPU代价 + 通信代价&lt;/p&gt;

&lt;h2 id=&quot;2-什么是逻辑查询优化&quot;&gt;2. 什么是逻辑查询优化&lt;/h2&gt;

&lt;p&gt;总得来说：关系理论+查询重写+启发式规则=逻辑查询优化&lt;/p&gt;

&lt;h3 id=&quot;21-查询的基本操作&quot;&gt;2.1 查询的基本操作&lt;/h3&gt;

&lt;p&gt;2.1.1 选择操作:&lt;/p&gt;

&lt;p&gt;对应的是限制条件（格式类似“field &lt;op&gt; consant”，field表示列对象，op是操作符如“=”、“&amp;gt;”等）。&lt;/op&gt;&lt;/p&gt;

&lt;p&gt;优化方式是选择操作下推，目的是尽量减少连接操作前的元组数，使得中间临时关系尽量少（元组数少，连接得到的元组数就少），这样可能减少IO和CPU的消耗、节约内存空间。&lt;/p&gt;

&lt;p&gt;2.1.2 投影操作:&lt;/p&gt;

&lt;p&gt;对应的SELECT查询的目的列对象。优化方式是投影操作下推。&lt;/p&gt;

&lt;p&gt;目的是尽量减少连接操作前的列数，使得中间临时关系尽量小（特别注意差别：选择操作是使元组的个数“尽量少”，投影操作是使一条元组“尽量小”），这样虽然不能减少IO（多数数据库存储方式是行存储，元组是读取的最基本单位，所以要想操作列则必须读取一行数据），但可以各减少连接后的中间关系的元组大小，节约内存空间。&lt;/p&gt;

&lt;p&gt;2.1.3 连接操作&lt;/p&gt;

&lt;p&gt;对应的是连接条件（格式类似“field_1 &lt;op&gt; field_2”，field_1和field_2表示不同表上的列对象，op是操作符如“=”、“&amp;gt;”等），表示两个表连接的条件。&lt;/op&gt;&lt;/p&gt;

&lt;p&gt;连接操作涉及两个子问题：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;多表连接中每个表被连接的顺序决定着效率&lt;/p&gt;

    &lt;p&gt;如果一个查询语句只有一个表，则这样的语句很简单；但如果有多个表，则会涉及表之间以什么样的顺序连接最高效（如A、B、C三表连接，如果ABC、ACB、BCA等连接后的结果集一样，则哪种连接次序的效率最高，是需要考虑的问题）。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;多表连接每个表被连接的顺序被用户语义决定&lt;/p&gt;

    &lt;p&gt;查询语句多表连接有着不同的语义（如是笛卡儿集、内连接、还是外连接中的左外连接等），这决定着表之间的前后连接次序是不能随意更换的，否则，结果集中数据是不同的。因此，表的前后连接次序是不能随意交换的。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;2.1.4 查询的2种类型：&lt;/p&gt;

&lt;p&gt;根据SQL语句的形式特点，还可以做如下区分：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;针对SPJ的查询优化&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;基于选择、投影、连接三种基本操作相结合的查询。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;针对非SPJ的查询优化&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在SPJ的基础上存在GROUPBY操作的查询，这是一种较为复杂的查询。所以，针对SPJ和非SPJ的查询优化，其实是对以上多种操作的优化。“选择”和“投影”操作，可以在关系代数规则的指导下进行优化。表连接，需要多表连接的相关算法完成优化。其他操作的优化多是基于索引和代价估算完成的。&lt;/p&gt;

&lt;h3 id=&quot;215-逻辑查询优化包括的技术&quot;&gt;2.1.5 逻辑查询优化包括的技术：&lt;/h3&gt;

&lt;p&gt;1）子查询优化；&lt;/p&gt;

&lt;p&gt;2）视图重写；&lt;/p&gt;

&lt;p&gt;3）等价谓词重写；&lt;/p&gt;

&lt;p&gt;4）条件化简；&lt;/p&gt;

&lt;p&gt;5）外连接消除；&lt;/p&gt;

&lt;p&gt;6）嵌套连接消除；&lt;/p&gt;

&lt;p&gt;7）连接消除；&lt;/p&gt;

&lt;p&gt;8）语义优化；&lt;/p&gt;

&lt;p&gt;9）非SPJ的优化；&lt;/p&gt;

&lt;h2 id=&quot;3-什么是物理查询优化&quot;&gt;3. 什么是物理查询优化&lt;/h2&gt;

&lt;p&gt;总得来说：代价模型+索引/索引的利用+单表扫描算法+两表连接算法+多表连接算法=物理查询优化&lt;/p&gt;

&lt;h2 id=&quot;4-mysql的执行计划&quot;&gt;4. MYSQL的执行计划&lt;/h2&gt;

&lt;p&gt;4.1 语法格式：&lt;/p&gt;

&lt;p&gt;EXPLAIN [explain_type] explainable_stmt&lt;/p&gt;

&lt;p&gt;可选项包括：&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;EXTENDED&lt;/td&gt;
      &lt;td&gt;PARTITIONS&lt;/td&gt;
      &lt;td&gt;FORMAT = format_name&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;format_name:&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;TRADITIONAL&lt;/td&gt;
      &lt;td&gt;JSON&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;说明：
   1．EXPLAIN命令，显示SQL语句的查询执行计划。&lt;/p&gt;

&lt;p&gt;2．EXPLAIN EXTENDED命令，显示SQL语句的详细的查询执行计划；之后可以通过“SHOW WARNINGS”命令查看详细的信息。&lt;/p&gt;

&lt;p&gt;3．EXPLAIN PARTITIONS命令，显示SQL语句的带有分区表信息的查询执行计划。&lt;/p&gt;

&lt;p&gt;4．EXPLAIN命令的输出格式有两种。&lt;/p&gt;

&lt;p&gt;4.1  TRADITIONAL：传统类型；按行隔离，每行标识一个子操作。&lt;/p&gt;

&lt;p&gt;4.2  JSON：JSON格式。&lt;/p&gt;

&lt;p&gt;5．explainable_stmt，可被EXPLAIN执行的SQL语句，包括的类型有：SELECT、INSERT、UPDATE、DELETE。&lt;/p&gt;

&lt;p&gt;执行五表连接的查询语句如下：&lt;/p&gt;

&lt;p&gt;EXPLAIN&lt;/p&gt;

&lt;p&gt;SELECT *&lt;/p&gt;

&lt;p&gt;FROM (t1 LEFT JOIN t2 ON true), (t3 FULL JOIN t4 ON true), t5&lt;/p&gt;

&lt;p&gt;WHERE id1=id2 AND&lt;/p&gt;

&lt;p&gt;id2=id3 AND&lt;/p&gt;

&lt;p&gt;id3=id4 AND&lt;/p&gt;

&lt;p&gt;id4=id5;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://ftvbftvbq.github.io/images/explain.png&quot; alt=&quot;查询执行计划分析图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;说明执行顺序：&lt;/p&gt;

&lt;p&gt;1) 从第1行到第9行，表示了完整的查询执行计划。&lt;/p&gt;

&lt;p&gt;2) 第1行到第3行，表明查询计划的结构；id表示对象被操作的顺序；id值大，先被执行；如果相同，执行顺序从上到下；&lt;/p&gt;

&lt;p&gt; 3) 从第4行起，每一行为一个节点，表示本节点被操作对象的可用信息，如索引等；&lt;/p&gt;

&lt;p&gt;4) 表的连接次序为：t4 t5 t3 t1 t2。这和初始给定的连接次序不同，经过优化，外连接被消除；&lt;/p&gt;

&lt;p&gt;5) t4表的元组数最少，按照MySQL多表连接算法，表经过排序后，顺序为 t4 t5 t2 t3 t1；&lt;/p&gt;

&lt;p&gt;6) 因为t5、t3、t1上有索引可以利用，所以t4上的一条元组确定后，则可以利用索引之间定位t5、t3、t1表上的元组，所以第5、6、7行的key列有索引可用；ref列表明这三个表都是引用了t4表的id4列。&lt;/p&gt;

&lt;p&gt;7) t2表的数据相对较多，且又没有索引，最后被连接，连接使用了Extra列表明的块嵌套循环连接算法，并且使用了连接缓存。&lt;/p&gt;

&lt;p&gt;这里使用到了MYSQL块嵌套循环算法：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://ftvbftvbq.github.io/images/explain_alg.png&quot; alt=&quot;查询执行计划分析图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;结点解析：&lt;/p&gt;

&lt;p&gt;1) id：每个被独立执行的操作的标识，表示对象被操作的顺序；id值大，先被执行；如果相同，执行顺序从上到下；&lt;/p&gt;

&lt;p&gt;2) select_type：查询中每个select子句的类型（读者可以自行百度查询）；&lt;/p&gt;

&lt;p&gt;3) table：名字，被操作的对象名称，通常是表名，但有其他格式。&lt;/p&gt;

&lt;p&gt;4) partitions：匹配的分区信息（对于非分区表值为NULL）。&lt;/p&gt;

&lt;p&gt;5) type：连接操作的类型（读者可以自行百度查询）；&lt;/p&gt;

&lt;p&gt;6) possible_keys：备选的索引（列出可能被使用到的索引）；&lt;/p&gt;

&lt;p&gt;7) key：经优化器选定的索引；常用“ANALYZE TABLE”命令可以使优化器正确地选择索引；&lt;/p&gt;

&lt;p&gt;8) key_len：被优化器选定的索引键的长度，单位是字节；&lt;/p&gt;

&lt;p&gt;9) ref：表示本行被操作的对象的参照对象（被参照的对象可能是一个常量用“const”表示，也可能是其他表的key指向的对象）；&lt;/p&gt;

&lt;p&gt;10) rows：查询执行所扫描的元组个数（对于InnoDB，此值是个估计值）；&lt;/p&gt;

&lt;p&gt;11) filtered：按照条件表上数据被过滤的元组个数的百分比，“rows×filtered/100”可以求出过滤后的元组数即实际的元组数。&lt;/p&gt;</content><author><name></name></author><summary type="html">1. 什么是数据库查询优化技术</summary></entry></feed>