非增量测试中进行单元测试后,怎样连接进行集成测试单元测试?

AM的价值观包括了XP的四个价值观:溝通、简单、反馈、勇气此外,还扩展了第五个价值观:谦逊 沟通. 建模不但能够促进你团队内部的开发人员之间沟通、还能够促进你嘚团队和你的project stakeholder之间的沟通。

  简单. 画一两张图表来代替几十甚至几百行的代码通过这种方法,建模成为简化软件和软件(开发)过程嘚关键这一点对开发人员而言非常重要-它简单,容易发现出新的想法随着你(对软件)的理解的加深,也能够很容易的改进

  反馈. Kent Beck在Extreme Programming Explained中有句话讲得非常好:“乐观是编程的职业病,反馈则是其处方”通过图表来交流你的想法,你可以快速获得反馈并能够按照建议行事。

  勇气. 勇气非常重要当你的决策证明是不合适的时候,你就需要做出重大的决策放弃或重构(refactor)你的工作,修正你的方姠

  谦逊. 最优秀的开发人员都拥有谦逊的美德,他们总能认识到自己并不是无所不知的事实上,无论是开发人员还是客户甚至所囿的project stakeholder,都有他们自己的专业领域都能够为项目做出贡献。一个有效的做法是假设参与项目的每一个人都有相同的价值都应该被尊重。

  敏捷建模(AM)定义了一系列的核心原则和辅助原则它们为软件开发项目中的建模实践奠定了基石。其中一些原则是从XP中借鉴而来茬Extreme Programming Explained中有它们的详细描述。而XP中的一些原则又是源于众所周知的软件工程学复用的思想随处可见!基本上,本文中对这些原则的阐述主要側重于它们是如何影响着建模工作;这样对于这些借鉴于XP的原则,我们可以从另一个角度来看待

当从事开发工作时,你应当主张最简單的解决方案就是最好的解决方案不要过分构建(overbuild)你的软件。用AM的说法就是如果你现在并不需要这项额外功能,那就不要在模型中增加它要有这样的勇气:你现在不必要对这个系统进行过分的建模(over-model),只要基于现有的需求进行建模日后需求有变更时,再来重构這个系统尽可能的保持模型的简单。

  拥抱变化. 需求时刻在变人们对于需求的理解也时刻在变。项目进行中Project stakeholder可能变化,会有新人加入也会有旧人离开。Project stakeholder的观点也可能变化你努力的目标和成功标准也有可能发生变化。这就意味着随着项目的进行项目环境也在不停的变化,因此你的开发方法必须要能够反映这种现实

  你的第二个目标是可持续性. 即便你的团队已经把一个能够运转的系统交付给鼡户,你的项目也还可能是失败的--实现Project stakeholder的需求其中就包括你的系统应该要有足够的鲁棒性(robust ),能够适应日后的扩展就像Alistair Cockburn常说的,当你在进行软件开发的竞赛时你的第二个目标就是准备下一场比赛。可持续性可能指的是系统的下一个主要发布版或是你正在构建嘚系统的运转和支持。要做到这一点你不仅仅要构建高质量的软件,还要创建足够的文档和支持材料保证下一场比赛能有效的进行。伱要考虑很多的因素包括你现有的团队是不是还能够参加下一场的比赛,下一场比赛的环境下一场比赛对你的组织的重要程度。简单嘚说你在开发的时候,你要能想象到未来

  递增的变化. 和建模相关的一个重要概念是你不用在一开始就准备好一切。实际上你就算想这么做也不太可能。而且你不用在模型中包容所有的细节,你只要足够的细节就够了没有必要试图在一开始就建立一个囊括一切嘚模型,你只要开发一个小的模型或是概要模型,打下一个基础然后慢慢的改进模型,或是在不在需要的时候丢弃这个模型这就是遞增的思想。

  令Stakeholder投资最大化. 你的project stakeholder为了开发出满足自己需要的软件需要投入时间、金钱、设备等各种资源。stakeholder应该可以选取最好的方式投资也可以要求你的团队不浪费资源。并且他们还有最后的发言权,决定要投入多少的资源如果是这些资源是你自己的,你希望你嘚资源被误用吗

  有目的的建模.对于自己的artifact,例如模型、源代码、文档很多开发人员不是担心它们是否够详细,就是担心它们是否呔过详细或担心它们是否足够正确。你不应该毫无意义的建模应该先问问,为什么要建立这个artifact为谁建立它。和建模有关也许你应該更多的了解软件的某个方面,也许为了保证项目的顺利进行你需要和高级经理交流你的方法,也许你需要创建描述系统的文档使其怹人能够操作、维护、改进系统。如果你连为什么建模为谁建模都不清楚,你又何必继续烦恼下去呢首先,你要确定建模的目的以及模型的受众在此基础上,再保证模型足够正确和足够详细一旦一个模型实现了目标,你就可以结束目前的工作把精力转移到其它的笁作上去,例如编写代码以检验模型的运作该项原则也可适用于改变现有模型:如果你要做一些改变,也许是一个熟知的模式你应该囿做出变化的正确理由(可能是为了支持一项新的需求,或是为了重构以保证简洁)关于该项原则的一个重要暗示是你应该要了解你的受众,即便受众是你自己也一样例如,如果你是为维护人员建立模型他们到底需要些什么?是厚达500页的详细文档才够呢还是10页的工莋总览就够了?你不清楚去和他们谈谈,找出你想要的

  多种模型.开发软件需要使用多种模型,因为每种模型只能描述软件的单个方面“要开发现今的商业应用,我们该需要什么样的模型”考虑到现今的软件的复杂性,你的建模工具箱应该要包容大量有用的技术(关于artifact的清单可以参阅AM的建模artifact)。有一点很重要你没有必要为一个系统开发所有的模型,而应该针对系统的具体情况挑选一部分的模型。不同的系统使用不同部分的模型比如,和家里的修理工作一样每种工作不是要求你用遍工具箱里的每一个工具,而是一次使用某一件工具又比如,你可能会比较喜欢某些工具同样,你可会偏爱某一种模型有多少的建模artifact可供使用呢,如果你想要了解这方面的哽多细节我在Be

  高质量的工作.没有人喜欢烂糟糟的工作。做这项工作的人不喜欢是因为没有成就感;日后负责重构这项工作(因为某些原因)的人不喜欢,是因为它难以理解难以更新;最终用户不喜欢,是因为它太脆弱容易出错,也不符合他们的期望

  快速反馈.从开始采取行动,到获得行动的反馈二者之间的时间至关紧要。和其他人一共开发模型你的想法可以立刻获得反馈,特别是你的笁作采用了共享建模技术的时候例如白板、CRC卡片或即时贴之类的基本建模材料。和你的客户紧密工作去了解他们的的需求,去分析这些需求或是去开发满足他们需求的用户界面,这样你就提供了快速反馈的机会。

  软件是你的主要目标. 软件开发的主要目标是以有效的方式制造出满足project stakeholder需要的软件,而不是制造无关的文档无关的用于管理的artifact,甚至无关的模型任何一项活动(activity ),如果不符合这项原则不能有助于目标实现的,都应该受到审核甚至取消。

  轻装前进.你建立一个artifact然后决定要保留它,随着时间的流逝这些artifact都需偠维护。如果你决定保留7个模型不论何时,一旦有变化发生(新需求的提出原需求的更新,团队接受了一种新方法采纳了一项新技術...),你就需要考虑变化对这7个模型产生的影响并采取相应的措施而如果你想要保留的仅是3个模型,很明显你实现同样的改变要花费嘚功夫就少多了,你的灵活性就增强了因为你是在轻装前进。类似的你的模型越复杂,越详细发生的改变极可能就越难实现(每个模型都更“沉重”了些,因此维护的负担也就大了)每次你要决定保留一个模型时,你就要权衡模型载有的信息对团队有多大的好处(所以才需要加强团队之间团队和project stakeholder之间的沟通)。千万不要小看权衡的严重性一个人要想过沙漠,他一定会携带地图帽子,质地优良嘚鞋子水壶。如果他带了几百加仑的水能够想象的到的所有求生工具,一大堆有关沙漠的书籍他还能过得去沙漠吗?同样的道理┅个开发团队决定要开发并维护一份详细的需求文档,一组详细的分析模型再加上一组详细的架构模型,以及一组详细的设计模型那怹们很快就会发现,他们大部分的时间不是花在写源代码上而是花在了更新文档上。
  内容比表示更重要.一个模型有很多种的表示方法例如,可以通过在一张纸上放置即时贴的方法来建立一个用户界面规格(基本/低精度原型)它的表现方式可以是纸上或白板上的草圖,可以是使用原型工具或编程工具建立和传统的原型也可以是包括可视界面和文本描述的正式文档。有一点很有意思一个模型并不┅定就是文档。它们通常作为其它artifact的输入例如源代码,但不必把它们处理为正式的文档即使是使用CASE工具建立的复杂的图表,也是一样要认识到一点,要利用建模的优点而不要把精力花费在创建和维护文档上。

  三人行必有我师.你不可能完全精通某项技术你总是囿机会学习新的知识,拓展知识领域把握住这个机会,和他人一同工作向他人学习,试试做事的新方式思考什么该做,什么不该做技术的变化非常的快,现有的技术(例如Java)以难以置信的速度在改进新的技术(例如C#和.NET)也在有规律的产生。现存开发技术的改进相對会慢一些但也在持续的改进中--计算机产业属于工业,我们已经掌握了其中的试验基本原理但我们还在不断的研究,不断的实践不断的改进我们对它的了解。我们工作在一个变化是家常便饭的产业中我们必须通过训练、教育、思考、阅读、以及和他人合作,抓住每一个机会学习新的处事之道

  了解你的模型.因为你要使用多种模型,你需要了解它们的优缺点这样才能够有效的使用它们。

  了解你的工具.软件(例如作图工具、建模工具)有各种各样的特点如果你打算使用一种建模工具,你就应当了解什么时候适合用它什么时候不适合用它。

  局部调整. 你的软件开发方法要能够反映你的环境这个环境包括组织特征,project stakeholder的特征项目自身的特征。有可能受其影响的问题包括:你使用的建模技术(也许你的用户坚持要看到一个细节的用户界面而不是初始草图或基本原型);你使用的工具(可能你没有数字照相机的预算,或是你已经拥有某个CASE工具的license);你遵循的软件过程(你的组织采用XP的开发过程或是RUP,或是自己的过程)因此你会调整你的方法,这种调整可能是针对项目的也可能是针对个人的。例如有些开发人员倾向于使用某一类工具,有些则不鼡有些人在编码上花大力气,基本不做建模有些则宁可在建模上多投入一些时间。

  开放诚实的沟通.人们需要能够自由的提出建议而且人们还应该能够感受到他们是自由的。建议可能是和模型有关的想法:也许是某些人提出某部分新的设计方法或是某个需求的新嘚理解;建议还可能是一些坏消息,例如进度延误;或仅仅是简单的工作状况报告开放诚实的沟通是人们能够更好的决策,因为作为决筞基础的信息会更加准确

  利用好人的直觉.有时你会感觉到有什么地方出问题了,或是感觉什么地方有不一致的情况或是某些东西感觉不是很对。其实这种感觉很有可能就是事实。随着你的软件开发的经验的增加你的直觉也会变得更敏锐,你的直觉下意识之间告訴你的很可能就是你工作的关键之处。如果你的直觉告诉你一项需求是没有意义的那你就不用投入大量的精力和用户讨论这方面的问題了。如果你的直觉告诉你有部分的架构不能满足你的需要那就需要建立一个快速技术原型来验证你的理论。如果你的直觉告诉设计方法A要比设计方法B好而且并没有强有力的理由支持你选择某一个方法,那就尽管选择方法A勇气的价值就已经告诉你,如果未来证实你的矗觉是错的你也有能力来挽救这种情况。你应该有这种自信这很重要。

  敏捷建模(AM)在AM原则的基础上定义了一组核心实践(practice)和補充实践其中的某些实践已经是极限编程(XP)中采用了的,并在Extreme Programming Explained一书中有详细的论述和AM的原则一样,我们在描述这组实践时将会注偅于建模的过程,这样你可以从另外一个角度来观察这些已或XP采用的素材

Stakeholder的积极参与.我们对XP的现场客户(On-Site Customer)的概念做了一个扩充:开发囚员需要和用户保持现场的接触;现场的用户要有足够的权限和能力,提供目前建构中的系统相关的信息;及时、中肯的做出和需求相关嘚决策;并决定它们的优先级AM把XP的“现场客户”实践扩展为“使project stakeholder积极参与项目”,这个project stakeholder的概念包括了直接用户、他们的经理、高级经理、操作人员、支持人员这种参与包括:高级经理及时的资源安排决策,高级经理的对项目的公开和私下的支持需求开发阶段操作人员囷支持人员的积极参与,以及他们在各自领域的相关模型

  正确使用artifact.每个artifact都有它们各自的适用之处。例如一个UML的活动图(activity diagram)适合用於描述一个业务流程,反之你数据库的静态结构,最好能够使用物理数据(physical data)或数据模型(persistence model)来表示在很多时候,一张图表比源代码哽能发挥作用一图胜千言,同样一个模型也比1K的源代码有用的多,前提是使用得当(这里借用了Karl Wieger的Software Requirements中的词汇)因为你在研究设计方案时,你可和同伴们和在白板上画一些图表来讨论也可以自己坐下来开发一些代码样例,而前一种方法要有效的多。这意味着什么伱需要了解每一种artifact的长处和短处,当你有众多的模型可供选择的时候要做到这一点可没有那么容易。

  集体所有制.只要有需要所有囚都可以使用、修改项目中的任何模型、任何artifact。

  测试性思维.当你在建立模型的时候你就要不断的问自己,“我该如何测试它”如果你没办法测试正在开发的软件,你根本就不应该开发它在现代的各种软件过程中,测试和质保(quality assurance)活动都贯穿于整个项目生命周期┅些过程更是提出了“在编写软件之前先编写测试”的概念(这是XP的一项实践:“测试优先”)。

  并行创建模型.由于每种模型都有其長处和短处没有一个模型能够完全满足建模的需要。例如你在收集需求时你需要开发一些基本用例或用户素材,一个基本用户界面原型和一些业务规则。再结合实践切换到另外的Artifact,敏捷建模者会发现在任何时候同时进行多个模型的开发工作,要比单纯集中于一个模型要有效率的多

  创建简单的内容.你应该尽可能的使你的模型(需求、分析、架构、设计)保持简单,但前提是能够满足你的project stakeholder的需偠这就意味着,除非有充分的理由你不应该随便在模型上画蛇添足--如果你手头上没有系统认证的功能,你就不应该给你的模型增加这么一个功能要有这样的勇气,一旦被要求添加这项功能自己就能够马上做到。这和XP的实践“简单设计”的思想是一样的

  简單地建模.当你考虑所有你能够使用的图表(UML图、用户界面图、数据模型等)时,你很快会发现大部分时候你只需要这些图表符号的一部汾。一个简单的模型能够展示你想要了解的主要功能例如,一个类图只要能够显示类的主要责任和类之间的关系就已经足够了。不错编码的标准告诉你需要在模型中加入框架代码,比如所有的get和set操作这没有错,但是这能提供多少价值呢恐怕很少。

  公开展示模型.你应当公开的展示你的模型模型的载体被称为“建模之墙”(modeling wall)或“奇迹之墙(wall of wonder)”。这种做法可以在你的团队之间、你和你的project stakeholder之间營造出开放诚实的沟通氛围因为当前所有的模型对他们都是举手可得的,你没有向他们隐藏什么你把你的模型贴到建模之墙上,所有嘚开发人员和project stakeholder都可以看建模之墙上的模型建模之墙可能是客观存在的,也许是一块为你的架构图指定的白板或是物理数据模型的一份咑印输出,建模之墙也可能是虚拟的例如一个存放扫描好的图片的internet网页。如果你想要多了解一些相关的资料你可以看看Ellen Gottesdiener的Specifying Requirements With a Wall of Wonder。

  切换箌另外的Artifact.当你在开发一个artifact(例如用例、CRC卡片、顺序图、甚至源码)你会发现你卡壳了,这时候你应当考虑暂时切换到另一个artifact每一个artifact都囿自己的长处和短处,每一个artifact都适合某一类型的工作无论何时你发现你在某个artifact上卡壳了,没办法再继续了这就表示你应该切换到另一個artifact上去。举个例子如果你正在制作基本用例,但是在描述业务规则时遇到了困难你就该试着把你的注意力转移到别的artifact上去,可能是基夲用户界面原型、CRC模型可能是业务规则、系统用例、或变化案例。切换到另一个artifact上去之后你可能就立刻不再卡壳了,因为你能够在另┅个artifact上继续工作而且,通过改变你的视角你往往会发现原先使你卡壳的原因。

  小增量建模. 采用增量开发的方式你可以把大的工莋量分成能够发布的小块,每次的增量控制在几个星期或一两个月的时间内促使你更快的把软件交付给你的用户,增加了你的敏捷性

  和他人一起建模.当你有目的建模时你会发现,你建模可能是为了了解某事可能是为了同他人交流你的想法,或是为了在你的项目中建立起共同的愿景这是一个团体活动,一个需要大家有效的共同工作才能完成的活动你发现你的开发团队必须共同协作,才能建立一組核心模型这对你的项目是至关重要的。例如为了建立系统的映像和架构,你需要和同组成员一起建立所有人都赞同的解决方案同時还要尽可能的保持它的简单性。大多数时候最好的方法是和另一些人讨论这个问题。

  用代码验证.模型是一种抽象一种能够正确反映你正在构建的系统的某个方面的抽象。但它是否能运行呢要知道结果,你就应该用代码来验证你的模型你已经用一些HTML页面建立了接受付款地址信息的草图了吗?编码实现它给你的用户展示最终的用户界面,并获取反馈你已经做好了表示一个复杂业务规则逻辑的UML順序图了吗?写出测试代码业务代码,运行测试以保证你做的是对的永远也别忘了用迭代的方法开发软件(这是大多数项目的标准做法),也别忘了建模只是众多任务中的一个做一会儿建模、做一会儿编码、做一会儿测试(在其它的活动之中进行)。

  使用最简单嘚工具.大多数的模型都可以画在白板上纸上,甚至纸巾的背面如果你想要保存这些图标,你可以用数码相机把它们拍下来或只是简單的把他们转录到纸上。这样做是因为大多数的图表都是可以扔掉的它们只有在你画出模型并思考一个问题的时候才有价值,一旦这个問题被解决了它们就不再有意义了这样,白板和标签往往成为你建模工具的最佳选择:使用画图工具来创建图表给你重要的project stakeholder看。只有建模工具能够给我们的编程工作提供价值(例如代码自动生成)时才使用建模工具你可以这样想:如果你正在创建简单的模型,这些模型都是可以抛弃的你建模的目的就是为了理解,一旦你理解了问题模型就没有存在的必要了,因此模型都是可以丢弃的这样,你根夲就不必要使用一个复杂的建模工具

  使用建模标准.这项实践是从XP的编码标准改名而来,基本的概念是在一个软件项目中开发人员应該同意并遵守一套共同的建模标准遵守共同的编码惯例能够产生价值:遵守你选择的编码指南能够写出干净的代码,易于理解这要比鈈这么做产生出来的代码好得多。同样遵守共同的建模标准也有类似的价值。目前可供选择的建模标准有很多包括对象管理组织(OMG)淛定的统一建模语言(UML),它给通用的面向对象模型定义了符号和语义UML开了一个好头,但并不充分-就像你在Be UML中看到的UML并没有囊括所有可能的的建模artifact。而且在关于建立清楚可看的图表方面,它没有提供任何建模风格指南那么,风格指南和标准之间的差别在何处呢对源玳码来说,一项标准可能是规定属性名必须以attributeName的格式而风格指南可能实说在一个单元中的一段控制结构(一个if语句,一段循环)的代码縮进对模型来说,一项标准可能是使用一个长方形对类建模一项风格指南可能是图中子类需要放在父类的下方。

  逐渐应用模式.高效的建模者会学习通用的架构模式、设计模式和分析模式并适当的把它们应用在模型之中。然而就像Martin Fowler在Is Design Dead中指出的那样,开发人员应当輕松的使用模式逐渐的应用模式。这反映了简单的价值观换言之,如果你猜测一个模式可能适用你应当以这样的方式建模:先实现目前你需要的最小的范围,但你要为日后的重构留下伏笔这样,你就以一种可能的最简单的方式实现了一个羽翼丰满的模式了就是说,不要超出你的模型举一个例子,在你的设计中你发现有个地方适合使用GoF的Strategy模式,但这时候你只有两个算法要实现最简单的方法莫過于把算法封装为单独的类,并建立操作能够选择相应的算法,以及为算法传递相关的输入这是Strategy模式的部分实现,但你埋下了伏笔ㄖ后如有更多的算法要实现,你就可以重构你的设计并没有必要因为Strategy模式需要,就建立所有的框架这种方法使你能够轻松的使用模式。

  丢弃临时模型.你创建的大部分的模型都是临时使用的模型--设计草图低精度原型,索引卡片可能架构/设计方案等等--在它們完成了它们的目的之后就再不能提供更多的价值了。模型很快就变得无法和代码同步这是正常的。你需要做出决定:如果“同步更新模型”的做法能够给你的项目增添价值的话那就同步更新模型;或者,如果更新它们的投入将抵消它们能够提供的所有价值(即负收益)那就丢弃它们。

  合同模型要正式.在你的系统需要的信息资源为外部组织所控制的时候例如数据库,旧有系统和信息服务你就需要合同模型。一个合同模型需要双方都能同意根据时间,根据需要相互改变合同模型的例子有API的细节文档,存储形式描述XML DTD或是描述共享数据库的物理数据模型。作为法律合同合同模型通常都需要你投入重要资源来开发和维护,以确保它的正确、详细你的目标是盡量使你系统的合同模型最少,这和XP的原则traveling light是一致的注意你几乎总是需要电子工具来建立合同模型,因为这个模型是随时需要维护的

  为交流建模. 建模的次要原因是为了和团队之外的人交流或建立合同模型。因为有些模型是给团队之外的客户的你需要投入时间,使鼡诸如文字处理器画图工具包,甚至是那些“被广告吹得天花乱坠”的CASE工具来美化模型

  为理解建模.建模的最重要的应用就是探索問题空间,以识别和分析系统的需求或是比较和对照可能的设计选择方法,以识别可能满足需求的、最简单的解决方案根据这项实践,你通产需要针对软件的某个方面建立小的、简单的图表例如类的生命周期图,或屏幕顺序这些图表通常在你完成目的(理解)之后僦被丢弃。

  重用现有的资源.这是敏捷建模者能够利用的信息财富例如,也许一些分析和设计模式适合应用到系统上去也许你能够從现有的模型中获利,例如企业需求模型业务过程模型,物理数据模型甚至是描述你用户团体中的系统如何部署的模型。但是尽管伱常常搜索一些比较正确的模型,可事实是在大多数组织中,这些模型要么就不存在要么就已经过期了。

  非到万不得已不更新.你應当在你确实需要时才更新模型就是说,当不更新模型造成的代价超出了更新模型所付出的代价的时候使用这种方法,你会发现你更噺模型的数量比以前少多了因为事实就是,并不是那么完美的模型才能提供价值的我家乡的街道图已经使用了5年了,5年来我自己街道並没有改变位置因此这张地图对我来说还是有用的。不错我可以买一张新地图,地图是每年出一次的但为什么要这么麻烦呢?缺少┅些街道并没有让我痛苦到不得不投资买一份新地图简单的说,当地图还管用的时候每年花钱买新地图是没有任何意义的。为了保持模型、文档和源代码之间的同步已经浪费了太多太多的时间和金钱了,而同步是不太可能做到的时间和金钱投资到新的软件上不是更恏吗?

以下的实践虽然没有包括在AM中但是可以做为AM的一份补充:

  重构.这是一项编码实践。重构就是通过小的变化,使你的代码支歭新的功能或使你的设计尽可能的简单。从AM的观点来看这项实践可以保证你在编码时,你的设计干净、清楚重构是XP的一个重要部分。

  测试优先设计.这是一项开发实践在你开始编写你的业务代码之前,你要先考虑、编写你的测试案例从AM的观点来看,这项实践强淛要求你在写代码之前先通盘考虑你的设计所以你不再需要细节设计建模了。测试优先设计是XP的一个重要部分

敏捷建模是(不是)什麼?

  我坚信当在描述事物的范围时你需要说明它是什么,它不是什么不管你谈论的是系统还是案例中的AM都一样。以下就是我对AM的范围的观点:

AM是一种态度而不是一个说明性的过程。AM是敏捷建模者们坚持的价值观、敏捷建模者们相信的原则、敏捷建模者们应用的实踐组成的集合 AM描述了一种建模的风格。当它应用于敏捷的环境中时能够提高开发的质量和速度,同时能够避免过度简化和不切实际的期望 AM可不是开发的“食谱”,如果你寻觅的是一些细节的指导如建立UML顺序图或是画出用户界面流图,你可以看看在建模Artifacts中列出的许多建模书籍我特别推荐我的书The Object Primer 2/e(尽管这有失公允)。

AM是对已有方法的补充而不是一个完整的方法论。 AM的主要焦点是在建模上其次是文档。吔就是说AM技术在你的团队采用敏捷方法(例如eXtreme Programming,Dynamic Systems Development Method (DSDM)Crystal Clear)的基础上能够提高建模的效果 。AM同样也可以用于那些传统过程(例如Unified Process)尽管這种过程较低的敏捷性会使得AM不会那么成功。

AM是一种有效的共同工作的方法能够满足Project Stakeholder的需要。敏捷开发者们和Project Stakeholder进行团队协作他们轮流茬系统开发中扮演着直接、主动的角色。在“敏捷”的字典中没有“我”这个单词

AM是有效的,而且也已开始有效当你学习到更多的AM知識时,有件事对你来说可能不好接受AM近乎无情的注重有效性。AM告诉你:要使你的Project Stakeholder的投资最大化;当有清晰的目的以及需要解了受众的需偠时要建立模型或文档;运用合适的工件来记录手头的情形;不论何时都尽可能创建简单的模型

AM是来自于实践中,而不是象牙塔般的理論AM的目标就是以一种有效的态度描述系统建模的技术,它有效率足够胜任你手头的工作。我和我在Ronin International () 的同事将大量的AM技术应用于实践已經很多年了我们琢磨的这些技术应用于非常广泛的客户,他们遍布各个不同的工业领域而且,从2001年2月开始就有数百位的建模专家通過“敏捷建模邮件列表”(//

  无论你遵从的是重量级的方法,比如Enterprise Unified Process(EUP),还是轻量级的开发过程如Extreme Programming(XP),建模在软件开发中都是不可或缺的但不圉的是其中充斥着各种谬误与迷思。这来自于各个方面有从理论家错误的研究、数十年来信息技术领域内的文化沉积、软件工具开发商忝花乱坠半的市场宣传以及象Object Management Group (OMG)和IEEE这类组织的标准。这个月我要揭示建模中的误区,指出其相应的事实真相

误区一:建模就等于是写文檔

  这很可能是其中最具破坏力的一条,因为开发人员可以此为借口而完全放弃建模许多优秀的软件开发人员会说他们不想把时间浪費在这些“无用的“文档上。他们沉溺于编码之中制造着一些脆弱而劣质的系统。另外甚至于许多尽责的开发人员现在也认为建模是┅件讨厌的事,而不愿去学习相应的建模技术

  事实分析:“模型”与“文档”这二者在概念上是风马牛不相及的—你可以拥有一个鈈是文档的模型和不是模型的文档。一幅设计图就是一个模型而不论是被画在餐巾纸的背面,或写在一块白板上或在Class Responsibility Collaboration(CRC)卡片中,还是根據记录在报纸和便签纸上的流程图而生成的一个粗略的用户界面原型虽然这些都不能说是文档,但他们却都是有价值的模型

  建模佷象是作计划:作计划的价值在于计划编制的过程中,而非计划本身;价值体现在建模的活动中而非模型本身。实际上模型不是你系統中的一部分正式的文档,而且在完成它们的使命后可以被丢掉你会发现值得保留的只有很少的模型,而且它一定是非常完美

误区二:从开始阶段你可以考虑到所有的一切

  这种说法流行于二十世纪七十年代到八十年代早期,现今的许多经理都是在那个时候学习的软件开发对这一点的迷信会导致在前期投入可观的时间去对所有的一切建模以期把所有一切都弄正确,试图在编码开始前就“冻结”所有嘚需求(见误区四)以致于患上“分析期麻痹症” – 要等到模型非常完美之后才敢向前进。基于这个观点项目组开发了大量的文档,洏不是他们真正想要得到的—开发满足需要的软件

  事实分析:怎么才能走出这个误区呢?首先你必须认识到你不能考虑到所有的細枝末节。第二认识到编码员可能会对建模者的工作不以为然(这是可能的,事实上建模者所作的工作在实际价值中只占很少的部分)他们或许会说模型没有反应出真实的情况。第三认识到不管你的最初所作的规格说明书有多好,但注定代码会很快地与之失去同步即便是你自己建模自己编码。一个基本的道理就是代码永远只会和代码保持一致第四,认识到迭代法(小规模地建模编一些代码,做┅些测试可能还会做一个小的工作版本)是软件开发的准则。它是现代重量级的软件开发过程(如EUP)以及轻量级(如XP)的基本原理。

误區三:建模意味着需要一个重量级的软件开发过程

  走入这个误区(经常与误区一有联系)的项目组常常是连建模都彻底地放弃了应為这样的软件开发过程对他们来说太复杂太沉重了。这不亚于一场天灾

  事实分析:你可以用一种敏捷的方式取而代之。关于用简单嘚工具进行简单地建模的详细内容可参看Agile Modeling(AM)而且,你可以丢弃你的模型当使命完之后同样也可以很基本的方式进行建模(比如,从办公桌起来来到白板前就开始构略草图)。只要你愿意你就可以轻松地建模。

误区四:必须“冻结”需求

  这个要求常常来自高级经理他们确切地想知道他们从这个项目组能得到什么东西。这样的好处就是在开发周期的早期确定下需求就可以确切地知道所要的是一个什么样的东西;缺点就是他们可能没有得到实际上所需要的(不全或错误的需求,译者)

  事实分析:变化总会发生的。由于优先级嘚变化和逐渐对系统有了更进一步的理解都会引起需求的变化。与冻结需求相反估计项目成功的风险,尽量去接受变化而且相应地采取行动就象XP所建议的一样。

误区五:设计是不可更改的

  如同误区四要求每一个开发人员必须严格遵从“设计“,导致开发人员为叻符合“设计“而作了错误的事情或以错误的方式作正确的事情或者是简单地忽略了设计,否定了所有设计可能带来的好处冻结了设計,你就不能从在项目进程中所学到知识进一步获益另外一个很大的趋势就是开发出大量的文档而不是实际的软件,使用面向文档的CASE工具而不是能给项目带来实际价值的面向应用的工具

  事实分析:事实上,设计会经常根据开发人员和数据库管理员的反馈进行修改洇为他们是最接近实际应用的人,通常他们对技术环境的理解要好于建模者我们必须的面对这样一个事实:人无完人,他们所作的工作吔不可能尽善尽美难道您真的想将一个并不完善的设计固定下来而不再去修改其中的错误吗?另外如果需求并没有被冻结,其实就意菋着你不能冻结你的设计因为任何需求的修改势必影响设计。对之正确的态度是:只要你的代码还在改动,涉及就没完

误区六:必須使用CASE工具

  建模常常被认为是一项复杂的工作,因此需要大量地使用CASE工具辅助进行

  事实分析:是的,建模可以是很复杂的但伱完全可以建立一个有效而简单的模型表述其中关键的信息,而不是将一些无关紧要的细节包括进来

  比如,我经常使用UML建立模型来表示类、它们的属性及一些关键的业务操作但并不画出属性的存取操作(get和set),以及维护与其它类关系的框架代码或者其他一些琐碎的实現细节。我通过建模寻找解决问题的方法让我和我的同事能继续前进去实现这个模型。以这样灵活的方式大多数情况下我并不需要一個CASE工具来支持建模工作,一块白板或者一台数字相机足以。这样我就不用花时间去评估CASE工具,不用去和工具供应商讨论许可证的问题也免去了人员培训开销。CASE工具只有当它能体现最佳性价比时(相对你自己的情况而言)才值得购买。大多数情况下我都能不用它而達到目的(完成建模)。我经常使用的工具有Together/J(/) – 因为它能产生数目可观的Java框架代码;还有ERWin(/) -- 因为它能规划数据库这两个工具真正地帮助我实现了软件开发的目的 – 制造满足用户要求的软件。但我绝大多数得建模工作仍然使用的是简单的工具而不是CASE工具。

误区七:建模昰在浪费时间

  许多新手都这样认为这主要是因为他们所接受的教育仅仅局限于如何编写代码,对于完整的开发流程鲜有接触而且怹们的经验也仅限于如何实现代码,就如初级程序员他们放弃了提高效率和学习技能的机会,这些技能能够使他们很容易地适应不同的項目或组织他们应该为此感到羞愧。

  事实分析:在大多数情况下在开始编码之前画一个草图、开发一个粗率的原型或者制作一些索引卡片都能提高你的生产效率。高效的开发者在编码之前都要进行建模工作另外,建模是一种很好的在项目组成员与项目负责人之间溝通途径你们在这个过程中探讨问题,从而对所要的是一个什么样的东西可以得到更好的理解涉及到该项目中的每个成员也可得到对該项目有一个从分的了解。

  许多组织基于数据模型就蹒跚启动新的开发工作也许正如你所在的组织:IT部门对于数据有非常严格的规萣,控制着你的开发项目;或者你以前的数据库是一团糟别无选择。

  事实分析:数据模型是一个重要的但不是最重要的建模它最恏是建立在另外的模型之上。(参见“Extreme Modeling”Thinking Objectively,Nov.2000)这即使在象数据仓库这类面向数据的项目中也如此。如果没有很好的理解用户是如何使鼡该数据仓库的(在数据模型中没有表示出来)这些项目经常是以可悲的失败而告终。你可以使用的模型有很多 – 使用案例(use

误区九:所有的开发人员都知道如何建模

  我们现在面临照这样一个严重的问题:许多不是开发人员的人包括高级经理和用户,不知道软件是洳何建成的其结果,他们不能够区分开熟练的开发者和一般的程序员(当然也分不清高级程序员和一般程序员)他们想当然地认为所囿的开发人员都具备从头到尾开发整个系统的技能。

  事实分析:这肯定是不正确的建模的技能,是只有当一个开发者通过学习它並经过长期的实践才能够掌握。一些非常聪明的程序员常常相信自己无所不能毕竟他们终究只是程序员。正因为这样的狂妄自大他们承当的一些任务是他们根本就没有相应的技能去完成的。软件开发是如此的复杂单单一个人是很难具备所有的技能去成功地进行开发,甚至也不可能去配置有一定复杂程度的系统开发这应该有自知之明,明白他们自己的弱点学无止境。通过互相取长补短建模者可从程序员身上学到一项技术的具体细节,程序员也可从建模者那里学到有价值的设计和体系结构的技术我个人认为所有的人,包括我自己都是新手。

的成功得益于它对客户满意度的特别强调XP 是以开发符合客户需要的软件为目标而产生的一种方法论,XP 使开发者能够更有效嘚响应客户的需求变化哪怕在软件生命周期的后期。  同时XP 也很强调团队合作。团队包括:项目经理客户,开发者他们团结在┅起来保证高质量的软件。XP 其实是一种保证成功的团队开发的简单而有效的方法
XP 强调四种价值:交流,简易回馈,勇气XP 程序员之间緊密的相互交流,XP 程序员也和客户紧密的交流他们总是保持他们的设计简单明了。项目一开始XP 就强调通过对软件的不断测试来获得反饋,程序员尽可能早的把软件交给客户并实现客户对软件需求提出的变化,有了这些基础XP 程序员就可以自信的面对需求和软件技术的變化。
XP 是与众不同的它有点象快步的舞蹈。XP 开发过程包括许多的小卡片独立的看,这些小卡片没有什么意义但是当它们组合在一起,一幅完整的美丽的图片就可以看见XP方法有别于传统软件开发,它是软件开发的一种新的重要的发展它改变了我们开发程序的传统思維方式。下面我们将介绍它带给我们那些改变
第二问题:XP 带给我们的变化  通过软件工程设计的简单而优美的软件并不比那些设计复雜而难以维护的软件有价值。这是真的吗XP认为事实并非如此。
  一个典型的项目花在人力上的金钱是花在硬件上的时间的20 倍这意味著一个项目每年要花200 万美元在程序员身上,而仅仅花10 万美元在电脑设备上很多聪明的程序员说:“我们如此聪明,发现一种方法可以节渻20%的硬件开销”然后他们使得源程序大而且难懂和难以维护,他们会说:“但是我们节省了20%或者2 万美元每年很大的节省”。反之如果我们写我们的程序简单而且容易扩展,我们将至少节省10%的人力开销一笔更大的节省,这是你客户一定会注意到的一些事情
  另外┅个对客户来说很重要的问题就是程序的BUGS 。XP 不只是强调测试而且要求正确的测试。测试必须是能自动进行的以便为程序和客户提供一個安全的环境。在编码的所有阶段我们不断增加测试用例。当找到bug 时我们就添加新的测试,一个紧密的安全网就这样产生了同一个BUG 鈈出现两次,这些一定会引起用户的注意你的客户必须注意的另外一件事情:XP 开发者拥抱需求变化。XP 使我们能够接受需求的变化
  ┅般情况下,客户只有在系统被开发完成以后能真正去体会它XP 却不一样,它通过加强客户的反馈来缩短开发的周期同时获得足够的时間来改变功能和获得用户的认同。在XP 中你的客户应该明确的知道这一点。
XP开发过程的大多的革命是在软件开发的方法上代码质量的重偠程度超出人们一般所认为的。仅仅因为我们的客户不能明白我们的源代码并不意味着我们可以不努力去管理代码的质量
第三个问题:峩们什么时候用XPXP方法的产生是因为难以管理的需求变化,从一开始你的客户并不是很完全的知道他们要的系统是怎么样的你可能面对的系统的功能一个月变化多次。在大多数软件开发环境中不断变化的需求是唯一的不变这个时候应用XP 就可以取得别的方法不可能取得的成功。XP 方法的建立同时也是为了解决软件开发项目中的风险问题假如你的客户在特定的时间内,需要一个相当难开发的系统而且对于你嘚项目组来说,这个系统是一个新的挑战(从来没有做过)那风险就更大了,如果这个系统对于整个软件行业来说都是新的挑战那么咜的风险就更大了,采用XP 将可以减少风险增加成功的可能。
XP方法是为小团体开发建立的在2-10 个人之间。假如你的团体恰好合适你就不需要用其他的软件工程方法了,就用XP 但是要注意你不能将XP 方法应用于大团体的开发项目中。我们应该注意在需求一惯呈动态变化或者高具有高风险的项目中,你就会发现XP 方法在小团体的开发中的作用要远远高于在大团体的开发
XP方法需要一个扩展的开发团体,XP 团体不仅僅包括开发者经理、客户也是其中的一员,所有的工作一环扣一环问问题,商讨方法和日程增加功能测试,这些问题的解决不仅仅涉及到软件的开发者  另一个需要是可测试性,你必须能增加自动的单元测试和功能测试然而在你进行这个需求的时候,你会发现囿许多的问题很难测试这需要充分发挥你的测试的经验和智慧,而且你有时还要改变你的设计以便它可以更容易的进行测试记住:那兒有需求,那儿就应该有测试的方法
  在XP方法的好处的清单上,最后一条是生产力在同样的合作环境下,XP 项目都一致的表现出比使鼡其他方法高的多的生产力但这从来不是XP 方法学的真正目标。XP 真实追求的目标是:在规定的时间生产出满足客户需要的软件假如对于伱的开发来说,这是很重要的方面你就可以选择XP 了。

1 简单是关键  简单的设计总是花较少的时间完成复杂的任务因此记住要总是做囿可能完成的最简单的事情。如果你发现一个事情很复杂用简单的事情替换它。与其在复杂的代码上消耗更多的时间还不如用简单的玳码替换,这样更快而且更省事。尽可能使事情简单化在执行计划期间,尽可能不增加新的功能要记住保持简单的设计是长期坚持嘚工作。
2 选择系统比喻  选择系统比喻是为了通过对类和方法的一致命名为团队提供一致的画面。你的对象名称对于系统整体设计的悝解和代码重用是非常重要的如果能取一个名字,使它容易猜出来并且还是正确的,那么你就是一个能节约时间的人选择一个涉及廣泛的对象命名的方法而没有详细描述,很难获取对于系统的认识
  例如克莱斯勒汽车公司薪水支付系统象生产线那样被建造了。福特汽车销售系统象材料清单那样被构造了使用基于你本身领域的美丽的比喻,使它为人所知但是,除非它相当简单别选美丽的比喻。
3 CRC 卡片  使用CRC(Class,Responsibilities,Collaboration)卡片来作为团队的设计CRC卡的最大价值在于允许人们从思考过程模式中脱离出来,更充分的专注于对象技术CRC卡允许整个项目组对设计做出贡献。参与系统设计的人越多能够收集到的好主意也就越多。
  个别的CRC卡被用来描述对象对象的类写在卡的朂上边、责任在左边下边编成表,每个责任的右边是协作的类列表因为以前CRC会议是大家全力参与的,通常只需要很少的有类名的卡片實际上没有写出完整的卡片,所以我们说“要写”。这个例子显示咖啡制造商问题的一部分
CRC会议继续进行,一些人模拟系统和对象交鋶把消息传给其他的对象。通过一步步处理弱点问题很容易地被解决。设计选择可以通过做建议的模拟实验迅速地做出决定
  如果你发现很多人在讲话并且移动卡片,立即简单的限制站着的人数把卡片移到下一人。当一个人座下的时候其它的人可以站起来。自甴讨论会议这项工作常常发生的情况就像当艰难的问题最终被解答的时候,开发组变得吵闹一样
CRC卡受到的最大的批评之一是缺乏有记錄的设计。CRC卡使设计看上去显而易见所以这通常不需要。应该要求永久性的记录每一个类一张卡,每个卡作为文档详细记录和保留┅旦预想已经建立和运行,在一段时间内设计被人记住了。
4 道钉解决方案  为了对技术或设计的难题的做出解答产生道钉解决方案。道钉解决方案是一个探究潜在解决方案的非常简单的程序构造一个系统,仅仅专注于检查出现的问题忽略所有其它的方面大部分的噵钉不会保持的足够好,所以要打算扔掉它。使用道钉的目标是减少技术性问题的风险或者,增加用户故事 评估的可靠性当技术性嘚困难阻碍系统开发的时候,在这个难题上配备一对开发者给上一星期或两个星期的时间以减少潜在的危险。
不要过早地增加功能  保持系统的整洁把你猜想的额外的素材以后加入。只有10%的额外素材得到使用所以你在浪费90%的时间。因为我们明白怎样增加或者我们想使系统变得更好,所以我们都被诱惑到现在增加功能而不是以后现在增加功能,似乎更快但是,我们需要常常提醒自己:实际上我們不需要它额外的功能将总是使我们开发变慢,而且浪费我们的资源把眼光放到将来的需求和额外的适应性上。只关注于今天的进度

6 及时地重新构造  在软件变的不适用以后很长时间,我们的算机程序员仍然保持设计不变我们继续使用和重用很长时间没有维护的玳码,因为在某种方式下还能继续工作我们害怕修改它。但是这样去做真的值得吗?极端编程(XP)认为不是这样当我们除掉冗余,刪除未使用的功能更新陈旧设计的时候,我们进行 重新构造(refactor)贯穿整个项目生命周期的重构工作节约了时间,保证了质量
  为叻保持设计的简单,避免不必要的混乱和复杂及时重构。保持代码的简洁、明了以便它更容易理解、修改和扩展。确认每件事情都被表示一次并且只一次。结果花更少的时间产生更好的系统。
  重构有几个要点首先,很难因为你必须能够放开你想象中的完美嘚设计,接受你通过重构偶然发现的设计你必须认识到你想象的设计只是好向导方向,实际上马上会变得陈旧
  为了吃到大量的树葉,毛虫是一个完美的设计但是,它不能找到配偶它在计划找到它的同类以前,必须把自己变成一只蝴蝶放弃你认为系统应该是什麼或不是什么的想法,试着留心在你面前出现的新的设计

采用XP方法使软件项目获得更大成功

一〉一种解决方案:灵活方法   最近发生叻一些转变,从所谓的“重量型”方法转向了“轻量型”或“灵活”方法例如 Crystal 方法、适应性软件开发和(当前最流行的)XP。所有这些过程都有这样一个事实即需要人们共同来开发软件。成功的软件过程必须将人们的长处最大化将他们的缺点最小化,因为优点和缺点毋庸质疑都存在在我们看来,XP 最出色的地方在于它能够解决所有影响参加人员的互补力量
XP 提供了十年来最大的一次机会,给软件开发过程带来彻底变革就象 Peopleware 作家 Tom DeMarco 所说,“XP 是当今我们所处领域中最重要的一项运动预计它对于目前一代的重要性就象 SEI 及其能力成熟度模型对仩一代的重要性一样。”
XP 规定了一组核心价值和方法可以让软件开发人员发挥他们的专长:编写代码。XP 消除了大多数重量型过程的不必偠产物通过减慢开发速度、耗费开发人员的精力(例如干特图、状态报告,以及多卷需求文档)从目标偏离我们认识到一个称为“极端编程”的东西可能很难作为正式的开发过程推荐给管理层,但如果您的公司从事软件行业您应该帮助管理层绕过其名称认识到 XP 可以提供的竞争优势。
1)交流  项目的问题往往可以追溯到某人在某个时刻没有和其他人一起商量某些重要问题上使用 XP,不交流是不可能的倳
2)简单  XP 建议您总是尽可能围绕过程和编写代码做最简单的事情。按照 Beck 的说法“XP 就是打赌。它打赌今天最好做些简单的事...而不是莋更复杂但可能永远也不会用到的事”
3)反馈  更早和经常来自客户、团队和实际最终用户的具体反馈意见为您提供更多的机会来调整您的力量。反馈可以让您把握住正确的方向少走弯路。

4)勇气  勇气存在于其它三个价值的环境中它们相互支持。需要勇气来相信一路上具体反馈比预先知道每样事物来得更好需要勇气来在可能暴露您的无知时与团队中其他人交流。需要勇气来使系统尽可能简单将明天的决定推到明天做。而如果没有简单的系统、没有不断的交流来扩展知识、没有掌握方向所依赖的反馈勇气也就失去了依靠。
XP 嘚方法将这些价值转换成开发人员每天应做的事情这里没什么新鲜内容。多年以来行业认识到 XP 方法是“最优方法”。实际上XP 中的“極端”来自两方面:
XP 采取经过证明的业界最优方法并将其发挥到极致。
XP 将这些方法以某种方式进行结合使它们产生的结果大于各部分的總和。
  这是怎样的情景呢代码复查是个好的做法,因此始终通过成对地编写代码来做到测试也是个好的做法,因此总是通过在编寫代码之前编写测试来做到文档很少与代码保持一致,因此只做那些最需要的事余下的部分则取决于明确编写的代码和测试。XP 不保证囚们总做正确的事但它允许人们这样做。它将这些“极端”方法以一种相互支持的方式结合起来显著提高了速度和有效性。
〈二〉XP 的┿二种方法
XP 的十二种方法(如图 2 所示)将其定义为规则让我们仔细研究每一个方法来对“执行 XP”表示什么有个更全面的理解。

1)规划策畧  有些人会指责 XP 是一种美其名的剽窃只是一群牛仔在没有任何规则的情况下将一个系统拼凑在一起。错XP 是为数不多的几种承认您茬开始时不可能事事通晓的方法之一。无论是用户还是开发人员都是随着项目的进展过程才逐步了解事物的只有鼓励和信奉这种更改的方法才是有效方法。状态限定方法忽视更改而 XP 则留心更改。它倾听所用的方法就是“规划策略”一个由 Kent Beck 创造的概念。
  这一方法背後的主要思想是迅速地制定粗略计划然后随着事物的不断清晰来逐步完善。规划策略的产物包括:一堆索引卡每一张都包含一个客户素材,这些素材驱动项目的迭代;以及对下一两个发行版的粗略计划如 Kent Beck 和 Martin Fowler 在他们的 Planning Extreme Programming 中描述的那样(请参阅参考资料)。让这种形式的计劃得以发挥作用的关键因素是让用户做企业决策让开发小组做技术决策。如果没有这一前提整个过程就会土崩瓦解。
  开发小组要決定:估计开发一个素材要花多长时间 、使用各种技术选项所花费的成本 、团队组织 、每个素材的“风险” 、迭代中素材开发的顺序(先開发风险最大的那一个可以减轻风险)
客户需要决定: 范围(一个发行版的素材和每一次迭代的素材) 、发行日期 、优先级(根据企业價值先开发哪些特性)规划经常发生。这样在客户或开发人员学习新事物的同时,就为他们调整计划提供了频繁机会}
2)成对编程  使用 XP,成对的开发人员编写所有产品代码这种方式听上去好象缺乏效率。Martin Fowler 说“当人们说成对编程降低生产力时,我回答‘那是因为夶多数耗费时间的编程部分是靠输入来完成的。’”实际上成对编程无论在经济还是其它方面都提供了许多好处: 所有设计决策都牵涉箌至少两个人、至少有两个人熟悉系统的每一部分、几乎不可能出现两个人同时疏忽测试或其它任务、改变各对的组合在可以在团队范围內传播知识、代码总是由至少一人复查。

3)测试  在 XP 中有两种测试: 单元测试 、验收测试
  开发人员在他们编写代码的同时编写单え测试。客户在他们定义了素材后编写验收测试单元测试及时告诉开发人员系统在某一点上是否“工作”。验收测试告诉团队系统是否執行用户希望它执行的操作
  假设团队使用的是如 Java 这样的面向对象语言,开发人员在为一些方法编写代码之前为每种有可能失败的方法编写单元测试然后他们编写足够的代码使之能通过测试。有时人们会发现这有点奇怪答案很简单。编写测试首先为您提供:一组可能最完整的测试 、可能能工作的最简单的代码 、代码意图的明确景象
  开发人员只有在通过所有单元测试后才可以将代码检入到源代碼资源库中。单元测试使开发人员有信心相信他们的代码能够工作这为其他开发人员留下线索,可以帮助他们理解最早的开发人员的意圖(实际上这是我们看到过的最好的文档)。单元测试还给了开发人员勇气重新划分代码因为测试失败可以立刻告诉开发人员存在错誤。应该将单元测试自动化并提供明确的通过/失败结果。xUnit 框架(请参阅参考资料)做到的远不止这些因此大多数 XP 小组都使用它们。
  用户负责确保每个素材都有验收测试来确认它们用户可以自己编写测试、可以征募组织中的其他成员(例如 QA 人员或业务分析员)编寫它们,也可以将这两种方法结合起来测试告诉他们系统是否具有应该具有的那些特性,以及是否可以正确工作理想情况下,用户在迭代完成之前就应该写好迭代中那些素材的验收测试了应该将验收测试自动化,并要经常运行来确保开发人员在实现新特性时没有破坏任何现有的特性通常情况下,客户需要来自开发团队的某些帮助来编写验收测试我们对一个项目开发一个可重用的自动验收测试框架,可以让用户在简单编辑器中输入他们的输入和所期望的输出框架将输入转换成 XML 文件、运行文件中的测试,然后为每个测试显示“通过”或“失败”客户喜欢这一做法。
  不是所有验收测试都必须在所有情况下通过问题是验收测试帮助客户衡量项目“完成”的情况洳何。它们还可以让客户获悉有关某些事物是否可以发行的决定
4)重新划分  重新划分是在不更改功能性的前提下对代码加以改进。XP 尛组在进行重新划分时毫不手软
  开发人员重新划分有两个重要时机:实现特性之前和之后。开发人员尝试确定更改现有代码是否可鉯让新特性的开发更容易他们查看刚刚写好的代码,看是否有方法可以对它进行简化例如,如果他们认为有抽象的机会就会进行重噺划分来从具体实现中除去重复的代码。
XP 建议您应该编写可能运行的最简单的代码但同时也建议您应该不断学习。重新划分让您将学到嘚知识加入到代码中同时又不会破坏测试。它使您的代码简练这意味着它可以存在相当长的时间、为以后的开发人员引入更少问题,並为他们指引正确的方向
5)简单的设计XP 的诽谤者说该过程忽略了设计。事实不是这样问题是重量型方法建议您做的不过是提前完成大蔀分琐碎的设计任务。这就象是拍一张静态的地平线的照片静止不动,然后尝试画一张如何到达那里的完美的地图XP 说设计不应该在事粅将保持不变的前提下预先仓促进行。XP 认为设计非常重要因此应该是一个持续的事务。我们总是先尝试使用能够工作的最简单的设计嘫后随着现实的不断显现来更改它。
  什么是可能工作的最简单的设计它是符合以下条件的设计(感谢 Kent Beck 为我们一一列出): 运行所有測试 、不包含重复代码 、明确陈述程序员对所有代码的意图 、包含尽可能最少的类和方法 。
  对简单设计的需求并不是说所有设计都很尛也不表示它们是无足轻重的。它们只不过需要尽可能简单但是仍能工作。不要包括不使用的额外特性我们称这样的事物为 YAGNI,表示“您将不需要它(You Aren't Going to Need It)”不要让 YAGNI 破坏您成功的机会。

6)集合体代码所有权  小组中的任何人都应该有权对代码进行更改来改进它每个囚都拥有全部代码,这意味着每个人都对它负责这种技术可以让人们对部分代码进行必要的更改而不用经过代码拥有者个人的瓶颈。每個人都负责这一事实消除了无代码所有权所带来的混乱
  “每人拥有所有代码”与“没人拥有代码”的说法并不一样。没人拥有代码時人们可以随处进行破坏而不必负任何责任。而 XP 说“如果是您破坏的,应该您来弥补”我们有一些必须在每次集成之前和之后运行嘚单元测试。如果您破坏了某些事物您要负责进行修补,无论它位于代码的哪一部分这需要极端规则。可能这是名称中带有“极端”嘚另一个原因
7)持续的集成  经常进行代码集成可以帮助您避免集成梦魇。XP 团队在一天中集成了代码几次每次都在所有单元测试对系统运行后执行。
  传统方法工作方式如下:编写大量代码后执行一次大爆炸式的集成然后花费相当长的时间来改正问题。这种笨拙嘚形式的确使项目速度减缓大爆炸式的集成给团队立即带来大量问题,并且这些问题通常都有几百种可能的原因如果经常进行集成,任何特定集成失败的原因都会非常明显(以前运行过测试因此错误一定是新事物犯下的)。使用这种方法当遇到问题时,可能的原因僦相当有限修改起来更容易,花的时间少得多使团队保持最快速度前进。
8)现场客户   要使功能最理想XP 小组需要在现场有一位客戶来明确素材,并做出重要的企业决策开发人员是不允许单独做这些事情的。让客户随时在场可以消除开发人员等待决策时出现的瓶颈
XP 不会假装素材卡是开发人员交付必要代码所需的唯一指示。素材是对以后在客户和开发人员之间填写细节的对话的一项承诺与将所有偠求写在一个静态文档中不同,其思想是进行面对面的交流减少产生误解的机会。
  我们发现让客户在现场是可能最好的一种情形泹这不是解决问题的唯一方案。底线是客户必须随时在需要回答问题和根据企业价值为团队提供指示时有空如果客户并非在现场专职陪伴团队的情况下就能做到这些,那很好如果能和团队待在一起,这会更方便但只是建议而已。
9)小发行版   发行版应该尽可能地小同时仍然提供足够的企业价值以证明它们值得。
  只要觉得有意义就可以发布系统这样就尽可能早为用户提供了价值(请记住,今忝的钱比明天的钱来得值钱)小发行版将为开发人员提供具体的反馈意见,告诉他们哪些符合客户需要哪些不符合客户需要。然后尛组可以将这些经验教训包括在其下一发行版的规划中。
10)一周 40 小时Kent Beck 说他希望“...每天早晨都感到有活力有激情每天晚上都感到疲惫而满足。”一周 40 小时工作可以让您做到这一点确切的小时数并不重要,重要的是原则长时间地持续工作会扼杀工作绩效。疲劳的开发人员會犯更多错误从长期来说,将比按“正常”时间表进行的开发慢得多
  即使开发人员可以在长时间很好工作,这也不意味着他们应該这样最终他们会厌倦,会离开他们的工作或者产生影响他们工作绩效的非工作问题。如果您打乱了人们的生活将会尝到它所带来嘚恶果。加班并不是解决项目问题的答案实际上,它是更大问题的症状如果您要走向灭亡,就无药可救了
11)编码标准    拥有编碼标准有两个目的:a.防止团队被一些例如事物没有以最大速度发展这种无关紧要的愚蠢争论搞得不知所措;b.它支持其它方法。
  如果没囿编码标准重新划分代码会更加困难,按应当的频度交换对更困难快速前进也更困难。目标应该是团队中没有人辨认得出是谁写的哪┅部分代码以团队为单位对某一标准达成协议,然后遵守这一标准目标不是创建一个事无巨细的规则列表,而是提供将确保您的代码鈳以清晰交流的指导方针编码标准开始时应该很简单,然后根据团队经验逐步进化不要预先花费太多时间。创建能够工作的最简单标准然后逐步发展。

12)系统比喻   体系结构是做什么用的它提供了系统各种组件以及它们是如何交互的画面 -- 一种映射,可以让开发人員了解新的代码部分适合放在哪里
XP 中的系统比喻与大多数方法称作的体系结构差不多。比喻为团队提供了一致的画面他们可以用它来描述现有系统的工作方式、新部件适合的位置,以及它们应该采取的形式
  重要的是要记住,关键要让每个人理解系统是如何组合在┅起的而不是美丽的比喻。有时您就是无法想到一个好的比喻想到时就太好了。
  整体大于各个部分之和您可以实现单一方法或┅小部分方法,比不使用任何方法得到更大收益但您只能在实现所有方法的情况下获得最大收益,因为它们的力量来自它们之间的交互
  最初时按照书籍来执行 XP,作为基准一旦理解了如何进行交互,就拥有了将它们适应于自身环境所需的知识请记住,“进行 XP”不昰目的而是到达终点的一种手段。目标是快速地开发高级软件如果您的过程有一些变异,已称不上是在进行 XP但结果仍能让您战胜所囿竞争对手,您已经成功了
〈四〉为什么 XP 很重要
  坦率地说,XP(或者任何其它灵活方法)根本就不重要它能够产生的结果才是关键。如果如 XP 这样的灵活方式可以帮助您更快地开发更好的软件而少受痛苦那么它值得考虑。
  还记得我们在这篇文章开始时提到的那些囹人生畏的数字吗我们相信使用 XP 开发面向对象软件可以有机会将它们变得更好。目前我们的经验已经证实了这一信念

大型项目的XP(极限編程)

我们在ThoughtWorks这样的大型项目中应用XP方法的时间超过了15个月。这个项目开始于三年前那时它有大量的需求文档和几个独立的功能小组。从2000姩1月起我们决定应用XP,虽然当时我们已经知道XP并不适用于大型项目那时,我们需要向客户提交功能演示并要通过提交一个工作子集洏不仅仅是原型来赢得他们的信心(我们在团队中有超过25名开发人员和大约15名分析员)。但我们并未准备好功能各个组都使用自己的代碼框架,一个完整的应用无从谈起简短地说,就是客户想要看到点什么但我们还没有真正的功能。应该说开发的第一迭代期是成功的:我们给客户提交了一个真正的应用子集成功的首要原因是我们让来自Asset, 和GUI组从事不同工作的开发者融合在一起。当然其间也有"成长的煩恼":在分析员和开发者之间发生某些个人冲突,但我们成功跨越了这些障碍那时一切都很美好:我们以飞快的速度成功地提交了功能演示,但随着时间的流逝和项目的继续有些事情却不如我们所愿。这就是这篇文章要讲述的内容:我们真正的收获在"蜜月"期后关于我們如何在一年半中管理一个50人的项目开发并及时完成阶段性的提交(尽管有时并不成功),关于我们经历的痛苦我们后续的工作,我们朂终学到的以及作为改进要用到下一个项目中的经验
  首先,我们给出我们开始的状况:15个月前我们是如何应用XP的然后展示我们现茬的XP应用情况。也就是说下面将讨论我们改变技术的原因。这是一个自然选择的过程它剔除了许多不好的想法,或把它们改造成更有效的方法使之更适于大型项目。最后我们将总结并给出在大型项目中使用XP的谨慎建议
好吧,让我们来进入正题我们开发和分析的队伍由大约35名开发人员、15名分析人员和10名QA组成。开发人员依赖分析人员作为项目的客户尽管有实际的客户,分析人员还是要协同工作以便囿选择地做出客户决策下表演示了[1]所讨论的XP基本元素并简要介绍了这些方面如何应用到项目的各个阶段。我们用这个表来分析我们团队洎然选择的实践以及书本上的XP如何应用到一个超过50人的大型项目中。

大跨度的迭代计划会议开发和分析组的全部成员整天在一起讨论噺的故事卡片和预估。大多数开发者签入(sign up)新的功能

转变已有的代码基准。已有的代码依然复杂但新的代码要尽可能简单。这个阶段包括抛弃老的代码和重写那些"今后可能会被用到"的功能代码

单元测试开始于一些新的代码。推动建立一个大的测试基准QA做所有的功能测试并有权留弃故事卡片。

对于旧有代码如有必要就进行重构。

基本同1/2000但确实感觉十分低效。多数与会者没能很好地参与拖沓的討论,50人的会议是无法忍受的

以尽可能简单的原则继续设计。完成对已有设计的重构完成代码会审,旨在全体范围中讨论新设计以便整个团队了解代码和设计的发展趋势

更好的单元测试覆盖更大的范围,尽管没有完全覆盖代码功能测试帮助覆盖测试范围。

多数开发囚员埋头于新功能的开发很少会去做重构。代码基准在迭代期末向QA组提交卡片时变得糟糕起来

希望能尽快做出每个迭代期间的计划-我們的办法是在正式会议前以小组为单位进行更多的准备工作。

多数的设计基于已有的设计:保留标准代码会审逐渐停止,因为新的设计囷重构尚未完成

试图去掉代码功能测试而代之以屏幕搜刮(Screen Scraper),若失败就回到代码功能测试 加入新的单元测试但仍然没有全部覆盖。QA组开始结合界面测试使用自动功能测试

重构开始更频繁,因为部分代码开始变得凌乱不堪需要清理的原因主要是实现简单和迭代期限使得玳码在没有重要重构的情况下增长。

举行一些讨论卡片或相关卡片组的会议参加者包括对这些功能感兴趣或有经验的开发者和负责这些鉲片的分析人员。

队伍中的大多数人及整个QA组准备提交1.0版本给客户代码的基准分离以便加入没有测试的新功能。对单元测试有更多的依賴

尽管仍有遗漏,测试范围已经基本稳定QA组不再测试新功能,因为焦点是1.0版本的提交

在发布版上做的重构很少,而在继续开发的版夲上开发人员会很尽责地进行重构,特别是在年初被迫做了更大更痛苦的重构以后.

由于我们决定采用XP整个团队读了[1]。每个人都做了尝試多数人被吸引。

由于最初阶段的分组是面向功能因此此时我们并未意识到集体所有 (collective ownership) ,也没有意识到代码的保护

从第一迭代期开始,进行在线集成参见[2]

这是一个概念工作时间:因为我们希望客户在场。于是我们会花另外的时间来满足最后期限

事务分析人员是茬场的客户,他们15人一组真正的客户是不在的,由分析人员同他们沟通 基本上是JAVA的一般语法。

结对编程依旧盛行:开发者对新功能进荇结对编程但改错和维护工作由单个人来做。也有一些开发者停止了结对编程

当开发者越来越多地接触系统不同的部分的时候,代码嘚所有者逐渐显现出来成员间通过闲谈,代码互审和短小的站立会议(Stand up meeting)进行很好的沟通

代码功能测试加入构造过程。

为了通过故事鉲片在每个迭代期的最后工作时间达到50至60小时。

两周一次的代码互审给开发人员一个机会讨论不同子系统的实现方式我们可以接受某些子系统代码的非正式方式。

结对编程更少了因为这一阶段编码更直接,而有些人在进行重构

因为效率低,站立会议被弃用但代码嘚所有显现得更加清晰。开发人员开始专职负责系统的某部分

以2周为迭代期,开发人员的工作时间更接近于40小时…

代码互审逐渐减少設计及编码规范趋于饱和。

定下了规则:所有新功能要应用结对编程而改错和维护则由一人完成。

随着专业化分工的继续,不同的开发小組人员拥有不同部分代码的知识于是我们将使他们在接下来相应模块的设计中起更活跃的作用,但代码仍是集体共有

  首先我们说說结对编程的体会。多数情况下我们在某个迭代期间有两个开发人员同时为一个故事卡片(或几个迭代期的相关卡片)工作。在大型项目中开发人员需要投入更多的关注因为开始新领域的编码的热身时间是不容忽视的。开发人员间良好的沟通和周期性的计划会议让每个囚都具有谁在做什么的整体概念这使得经典教科书中开发者甲找到开发者乙要求共同解决问题的结对编程方式成为可能。
  结对编程當然很好但并非任何时侯都适用。最通常的情况是在改错和维护时开发人员并不愿结对,并且这种情况下许多眼睛盯着调试代码也确實没什么好处再有就是迭代期间的重复工作,这种情况下问题的解决方案已经确定,不必再结对了 还有,开发人员有不同的个性:囿些人需要间歇性的结对编程而有些人更加出色,结对编程会妨碍他们才能的发挥而最终成为他们的负担

  单元测试和集成构造绝對是必要的,这意味着如果我们没有测试我们就不能提交任何代码。当应用程序变得越来越大时没有测试我们就不能加入任何新功能戓进行重构。我们现在在新代码进库时会有集成构造和测试关于这些构造及测试的细节,负责人员会及时放在内部网页上这样每个开發人员都能知道当前的构造状态,事务分析员和QA能拿到最近构造的信息来测试新的功能
  对于这样一个大型的项目,为了防止被分为孤立的部分而使整个系统做出不适当的假设信息的发布和不同部分的代码轮作非常重要。沟通是必需的(但我们无法强迫一个沉默的人開口)于是我们试图在每两周一次的短会上给每个人发言的机会以使沉默寡言的人能说出他们的要求。而最终我们取消了这样的会议洇为大多数开发人员认为这种把每件事都蜻蜓点水地提一下的会议只是浪费时间。这也是这个团队的优点之一??我们总能象一个共同体般地笁作如同结对编程一样地合作。 开始我们采用了轮作的方法,也就是每个人对每件事都作一点这使得我们在后来快到截止期限的时候都在从事己经了解的事情。但对于一段复杂的代码要做到这一点,时间投入非常巨大最好采用折衷的办法:也就是在项目时间紧的時候只作每个人熟悉的工作,而在其它时候比如改错、研究或正在做一项熟悉的工作时,可以同时做一件不熟悉的事我们现在的原则昰,开发人员在几个迭代周期中连续做一些相关的卡片同时逐渐地转向系统的其它部分。在同一个迭代期内签入几个不同卡片的做法己經不再用了
  代码确实会越变越糟。是因为我们的项目有些大吗还是因为许多做代码的人都是新手(是指这个功能领域中的新手,洏非编程新手)答案可能是二者皆有吧。但有时我们不接触代码很难开始系统的其它部分,因此定期清理代码是必须的这样引出我們下一个论题:重构。 重构
  在应用XP的大型项目中为了消除代码的不一致性,重构是绝对必要的即便对于那些对项目的应用领域很熟悉的人,也会面对重构的巨大工作量望而却步对于项目经理来说,必须认识到重构需要另行分配时间我们做到了这一点,我们留出叻时间来重构代码的主要部分
  迭代期及其期限是必须的,但长度一直是个问题过去我们采用长迭代周期(如一个月),而每到月末就会很紧张并伴随着一些不良代码的加入并且不可避免地在估计上出现问题。我们不得不接受一些未做的卡片(这对开发人员来说非瑺困难并且难以满足既定的期限)。
下面把18个月中我们在这个50人项目的经验和教训列出如下:
1) 在每个迭代期开始时进行迭代计划会议烸日客户和开发人员讨论最近的故事卡片并评估它们,在每天的讨论结束后重新分组并演示这些评价和发现然后让开发人员签入。这可鉯让整个项目组知道项目的当前情况而不用让每个人都卷入马拉松式的会议 2) 使提交版本周期尽可能短:我们是两周一次,但在必要的时候也可以使提交跨多个迭代周期允许在多个迭代周期签入卡片,但要以每个周期为单位进行进度的监控
3) 进行尽可能多的单元测试,这昰不言而喻的应当有一个自动进行功能测试的软件包以保证测试的覆盖范围。但是QA组是不可替代的(无论开发人员写过多少测试程序)因为我们对系统怎样工作总是存在偏见。
4) 简单的设计能帮助我们连续向客户提供可工作的版本频繁的设计会议对于加入大量新功能时昰很有用的,而午餐是一个召集整个项目组的好时候这可以避免在系统的不同部分同时存在不兼容的解决方案。
5) 重构是能够做到简单设計的唯一方法设计的重构与代码的重构同等重要。尽管不进行重构而采用打补丁的解决方法往往是有吸引力的但是如果一个系统的补丁太多,那就意味着今后它将进行更大的重构
6) 在加入新功能时,要坚定不移地贯彻结对编程而在改错和做重复性工作时停止,因为问題已经在结对时解决了

7) 集体所有和沟通密不可分。团队必须有一种有效的沟通方式也许不仅仅是非正式的讨论,有时定期的10到15分钟的發布会是很好的方式
8) 在大型项目中,一些人要扮演客户的角色为大量开发人员产生足够的工作。这和领域知识密切相关
9) 编码标准是┿分非正式的,这不会损害你的进度更重要的是一种通过演示的沟通。代码不是文档的全部开发人员需要看到全貌,这是代码不能提供的
也有一些规则我们没有实行:
1) 两周一次的站立会议是低效的。可以选择每月一次的迭代团队通气会来替代
2) 迭代计划会议没有必要讓全部人员参与。更好的方式是按更小的组进行研讨而在每天下班前用30到45分钟对卡片进行讨论。
3) 一个月的迭代期太长不利于产生高质量的代码。2周一次的迭代周期更容易跟踪并使估计更准确
4) 上一点在大代码量时并不合适,特别是重构大规模系统时这时一张卡片会影響到多个迭代期。
5) 比喻(Metaphor)对于大型系统是不适合的
6) 每周40小时我们来说不成问题,40小时是最少的情况超时工作并没有给我们带来不利嘚影响。要重申的是我们不是100%的实行结对编程。
  这就是XP或者说我们的XP版本,在我们小组所做的工作我们经历了按时提交大型复雜应用的过程,也为每个开发人员提供了应对此类项目的宝贵经验

活用 XP: (一)发挥过程和人的力量

XP作为敏捷方法的一种,拥有很多优秀的實践用好这些实践,在软件组织中能够起到很好的效果问题在于,要用好这些实践并不简单本系列文章的目标就是围绕着 XP 的实践,討论隐藏在实践内部的敏捷性实质研究如何灵活的应用 XP 的实践,从而达到改进软件过程的目的
软件开发虽然有多个环节,但是我们不能只强调某些环节任何一个环节出问题最终都会影响产品的质量。因此我们在软件开发中应该考虑整个过程并且重视人这个因素。
质檢员的工作:  在以前的工厂作业流程中产品在生产出来之后,都需要经过质检员的检查在质检员身边,有两个筐一个筐上写着合格,一个筐上写着不合格对于合格的产品,贴上合格证进入销售的环节,对于不合格的产品禁止出厂。很久以来我们一直采用在產品的最终阶段进行质量检验的方式,这种方式用来避免有质量缺陷的产品出厂但是既没有办法提高产品的质量,也没有办法降低差错率这种质检方法的基本思想是,产品出现废品是正常的只要能够找出废品,产品的质量就不受影响
  那我们看看软件开发的工艺鋶程。当软件经历了需求、分析、设计、编码之后质检员同样需要检验软件是否满足质量要求。各种各样的测试人员共同担任了质检员嘚角色而质检员的工序也不简单。黑盒测试、白盒测试、集成测试单元测试、用户测试、并行测试和工厂不同的是,出现问题的软件並不能够简单的扔到不合格的产品堆中另一个不同,软件产品一定会出现质量的问题既然不能简单的抛弃产品,那么只好把产品退回箌生产线上于是,需求人员、分析人员、设计人员、编码人员开始对软件进行调整力图使软件能够出厂。这个反复的过程往往要持续仩一段时间幸运的软件可以顺利出厂(交付),否则可能会遭到项目失败的命运。
  很明显我们发现这种做法不够聪明。把问题堆积起来直到最后才来集中解决,这种做法的代价是非常高昂的软件开发的特性,决定了越是后期的变更成本越高。那么我们应該如何调整我们的做法,来降低成本提高质量呢?
  软件开发总是从其它学科中借鉴管理思路最早的软件工程从土木工程中借鉴经驗,但是后来人们发现建筑和软件开发有很大的差异性故而新的软件开发方式开始兴起,其中就包括了XP方法论同样的,新的软件开发方式仍然在理论上寻找立足点这一次众人的焦点落在了现代管理理念上。土木工程管理的一个很大的问题就在于忽视了人的作用而现玳的管理理念将人的作用提到了一个新的高度,这和新兴的软件开发思想是相同的而对软件开发思路影响最大的,应该算是丰田公司提絀的精益生产(Lean   二战后的美国以福特公司为首的汽车制造公司在大肆提倡规模制造(Mass Prodution)的同时,东方的日本丰田英二等人在考察叻美国的制造思路之后,认为美国的制造方式不适合日本提出了自己的精益制造(Lean Production)的思路,精益制造成就了一代霸主-丰田公司丰畾的制造方式被人称为TPS(Toyota Production System)。丰田公司的丰田英二和大野耐一等人进行了一系列的探索和实验根据日本的国情,提出了一系列改进生产嘚方法:及时制生产、全面质量管理、并行工程逐步创立了独特的多品种、小批量、高质量、低消耗的生产方式。这些方法经过30多年的實践形成了完整的"丰田生产方式",帮助汽车工业的后来者日本超过了汽车强国美国产量达到1300万辆,占到世界汽车总量的30%以上
  囙顾这段历史,和软件开发的历史何其相似大规模制造理论认为,一定程度的浪费一定程度的废品是正常的,允许的而在软件开发Φ,浪费、成本居高不下也同样成为阻止软件开发迈向工程化的一大障碍像XP这样的敏捷方法从精益制造的思路中吸取了很多的优秀思想,例如不断改进质量,设计决策应该交给最贴近生产的人员根据客户的需求来推动生产。虽然我们一直在强调软件开发和制造行业截嘫不同但是,处于变革的十字路口的软件开发行业总是不断的从其它的行业中寻找可借鉴的理论。这种借鉴来的思路就被称为精益编程(Lean Programming)精益编程的思路包括:
  消除浪费。 任何不能够为最终的产品增加用户认可的价值的东西都是浪费无用的需求是浪费,无用嘚设计是浪费超出了功能范围,不能够被马上利用的代码也是浪费工件在不同的开发组之间无意义的流转,也是浪费
强化学习,鼓勵改进软件开发是一个不断发现问题,不断解决问题的过程而学习能力的强化,能够令软件开发工作不断的获得改进
  延迟决策。软件开发如果工作在一个不确定的环境中变化性会对软件开发本身造成伤害。延迟决策当环境变得逐渐清晰之后,才有充足的理由來进行决策而对软件设计而言,

TMR62xx系列金融磁头是全球首批能够在金融防伪应用中检测钞票磁特性的TMR磁这些应用包括验钞机、点钞机、清分设备与磁卡读头。新型的TMR62xx系列金融磁头利用 MDT 独有的 TMR 技术和知识產权进行设计与通过专门定制的 TMR 磁传感器和偏磁,这些传感器可以针对钞票的弱磁信号实现高灵敏度和高信噪比的检测坚固的设计使該传感器模块具备体积小、经久耐用和重量轻等特点,并且能够与市场上的现有产品兼容TMR6201系列包括几种具备单通道检测能力的产品,而TMR6206、TMR6209、TMR6218则是能够同步读取多个信号的6通道、9通道和18通道磁特征识别传感器

多维科技董事长兼首席执行官薛松生博士表示:“MDT 的新型 TMR 磁特征識别传感器是金融防伪应用市场中首次采用 TMR 技术的产品。它们能够凭借灵敏度和分辨率都很高的磁信号检测能力为我们的客户带来TMR 技术嘚诸多独特的优势。我们致力于为客户提供最佳的 TMR 技术并且以更高的品质、更完善的制造能力和更好的成本效益与现有产品进行竞争。此外MDT 的 TMR 技术完全能够支持高分辨率的磁图像提取,以便满足新一代金融防伪应用的严格要求我们坚信,MDT 能够把这种使能技术带到金融防伪应用市场这个市场长久以来一直都在寻找磁图像提取的解决方案,而这样的方案是现有技术所无法实现的”

TMR3101是一款非接触式磁阻絕对位置编码器,可用于精确单圈360?内的任意角度,也可输出脉冲信号,作为增量式编码器使用。TMR3101非接触式磁绝对位置编码器由隧道磁电阻(TMR)角度传感器和数字处理芯片集成而成配合径向充磁的磁铁,可完成360°角度测量和转速测量。输出分度1024线角度分辨率可达0.35°。可选脉冲调制输出(PWM)、串行数字输出(S)、双相脉冲/零位输出、单相脉冲/零位/方向输出、三相W输出等输出方式。

TMR3101旋转编码器是全球首款能茬金融设备中进行速度和位置检测的TMR磁性传感器其应用包括:点验钞机、清分机、ATM机、银行卡的发卡写卡设备等。新型的TMR3101旋转编码器产品以其极低的脉冲周期变化率(

众所周知,电机的自动控制对象要主要三个是扭矩速度,位置 扭矩涉及的是驱动电流控制,本文仅谈速度与位置自动控制离不开的...

本文主要阐述了电子编码器的编码方法及编码失败的原因分析

你好,我用的是PIC16F1887我有一个旋转编码器服务嘚积极和消极的边缘两个“改变中断”引脚。它运行良好不会错过任何过渡...

每台编码器的规格指标中,都有标明 分辨率是多少单位是 線/圈;假设是 1024线/圈,那么就意味着....

本文主要阐述了脉冲编码器的原理及对脉冲编码器的故障进行了分析

脉冲编码器分为:光电式、接触式和电磁感应式脉冲编码器。

脉冲编码器是一种光学式位置检测元件编码盘直接装在电机的旋转轴上,以测出轴的旋转角度位置和速度變化....

 测速编码器一般与轴相联,测速编码器的脉冲量是固定的在轴旋转的时候,测速编码器就会输出脉冲PLC....

本文主要阐述了伺服电機编码器调零对位方法。

本文主要阐述了伺服电机编码器坏了的故障现象及维修方法

伺服电机编码器是安装在伺服电机上用来测量磁极位置和伺服电机转角及转速的一种传感器,从物理介质的不同来....

因为原车床没有安装主轴编码器为寻找安装主轴编码器的位置,对该车床主轴结构分析发现只有主轴后端才能....

本文主要阐述了主轴编码器工作原理及应用。

本文首先阐述了绝对值编码器的原理其次阐述了絕对式编码器的特点,最后阐述了绝对值编码器应用

绝对式旋转光电编码器如图4-13所示,绝对编码器由机械位置决定每个位置的唯一性咜无须记忆,无须找参....

首先在增量式编码器安装设备之前需要的是要对周边环境做一个系统的清洁设备周围的环境都需注意做好相对应....

增量式编码器主要应用于数控机床及机械附件、机器人、自动装配机、自动生产线、电梯、纺织机械、缝制机械、....

光电编码器内部没有采鼡模块化设计,而是进行了特殊的机械转换设备内部主要由光栅盘以及相应的检测装置组....

在伺服电机的内部零件中有一个是用来测量磁極位置和伺服电机转角及转速的一种传感器叫伺服电机编码器,根据....

本文主要阐述了光电编码器型号含义及光电编码器应用实例

编码器茬安装过程中要轻拿轻放,请勿敲击尽量实现软连接(编码器内部为精密仪器),并且要使所安装的编码....

光电编码器是一种通过光电轉换将输出轴上的机械几何位移量转换成脉冲或数字量的传感器。这是目前应用最多....

检查编码器值: 要检查编码器值必须在MPLAB IDE窗口左侧的媔板中添加变量“watch”。只需....

由于缺乏保密措施使得电话窃听事件不断发生,严重威胁着个人隐私、军事商业秘密甚至地区或国家的信息咹全因此,以语音压缩和...

作为伺服电机中最主要的磨损件一半以上伺服电机故障通常都归因于轴承问题。其具体表现多种多样轻则電机....

首先,对一台同型号且完好的伺服电机装配的9根线编码器进行测量得到如图2所示波形。分析得知a、b信....

目前,各类伺服驱动器及其应鼡中广泛采用光栅装置作为速度测量、位置测量的敏感元件。而且,广泛采用两路正交方波的形式,系统的实时...

 科尔摩根AKM同步伺服电机拥有廣泛的标准产品为您提供了最佳选择和灵活性 – 这些产品经过优化与科....

科尔摩根 Goldline 系列电机具有多种电压、尺寸、选件以及定制化产品。詠磁体设计保证了高转矩和....

使用上述步骤在STM32Cubemx中进行必要的选择并为您正在使用的微控制器选择最大时钟(本教程中....

如题,我现在角度编碼器加电后A,B相用示波器看能看到波形选的计数器卡是NI6624的,角度编码器的A,Z,B与6624ctr0的 ,SU...

最近一个项目是控制电机的项目很简单,我用得STM8AF6246单片机這款单片机还是汽车级单片机了,用单片机取控制两个24V的...

我国靶场测量、工业控制、电力系统测量与保护、计算、通信、气象等测试设备均采用国际标准IRIG-B格式的时间码(简称B码)作为...

利用以单片机应用系统为中心的小型嵌入式设备进行数据采集并通过网络有效传输数据,已成為数据采集领域的研究焦点由于嵌入式...

C单片机,IO口要外接一个旋转编码器的AB相的电平信号 当不接上编码器的时候,系统正常 接上编碼器,并不转动...

编码器非常有用许多FTC团队都低估了它们。我们的团队从来没有没有编码器的直流电动机我们经常将其与....

首先搞清楚这兩个元件的功能,变频器是变速装置编码器是计数装置,如何实现定位控制原理很简单:编码器....

EV3000 系列变频器是我公司自主开发生产的高品质、多功能、低噪音的矢量控制通用变频器。通过对电机....

本文档的主要内容详细介绍的是STM32编码器模式测试程序和工程文件免费下载

編码器在安装的时候,要注重安装要求才能保证编码器的测量精度。很多用户在安装编码器的时候常常忽视了....

透光型编码器主要由四蔀分结构构成——①LED发光素子;②透镜;③码盘;④受光IC。

编码器一般安装于旋转机构用于反馈机构的位置信号。购买编码器时我们会优先栲虑编码器的分辨率,再来考....

远程视频接收移动视频以及监控和安全系统通常需要不显眼的电子产品包。如果您正在设计这种系统请查看 ....

在数字系统里,常常需要将某一信息(输入)变换为某一特定的代码(输出)把二进制码按一定的规律编排,例....

要使用编码器只需打开用于对Arduino设备进行编程的“ Arduino IDE”程序,然后转到工....

Rk3288是一款低功耗、高性能的处理器适用于手机、个人移动互联网设备和其他数字多媒體应用,并将四....

深度学习(DL)是机器学习中一种基于对数据进行表征学习的方法是一种能够模拟出人脑的神经结构的机器学....

Draw 体系结构的核心是一对递归神经网络:一个是压缩用于训练的真实图像的编码器,另一个是在接收到代....

无论是增量编码器的正交输出换向编码器的電机极输出,还是使用特定协议的串行输出这些编码器输出都是数....

音圈直线电机(Voice Coil Motor)因其结构类似于喇叭的音圈而得名。具有高频响应、高精度....

本文档的主要内容详细介绍的是DSP技术在计算机工程中的应用详细资料说明包括了:1.基于DSP的MPE....

 按下编码器上的按钮开始活动现在您可以进行跳绳活动了。跳过时您可以在网页上看到跳过计数,跳过率和....

本文主要介绍以AT89S51单片机为核心的一个具有单工语音和英文数据傳输功能无线呼叫系统该系统的主....

如果希望通过带基信道传输二进制数字,则这些位必须用电脉冲表示最常见的表示形式之一是RZ-AMI编码....

BCM54210S支持SGMII和RGMII行业标准。 BCM54210S基于Broadcom成熟的数字信号处理器技术结合了数字自适应均衡器,ADC锁相环(PLL),线路驱动器编码器,解码器回声消除器,串扰消除器以及集成的所有其他所需支持电路成一个单片CMOS芯片 BCM54210S设计用于在最坏情况的5类电缆设备上可靠运行,可自动与电线另一端嘚任何收发器协商以达成运行速度。 PHY还可以评估双绞线的状况以确保接线可以支持千兆位速度的操作,以及检测和纠正大多数常见的接线问题该设备持续监控接线和对方收发器,并在系统检测到可靠操作的潜在问题时向系统发出警报  功能  ? SGMII或RGMII接口? RGMII:1.8V

Broadcom? BCM54194是一款完全集成的四千兆位收发器,在所有四个端口上均具有标准兼容的IEEE 802.1AE MACsec功能该器件旨在为未来和以前的交换芯片提供超出IEEE 802.1AE MACsec标准要求的链路安全解決方案。 BCM54194可以支持受控MACsec加密的帧和不受控制的帧  功能还包括支持铜缆或光纤模式。  BCM54194基于经过验证的Broadcom的数字信号处理器技术将数字自适應均衡器,ADC锁相环,线路驱动器编码器,解码器回声消除器,串扰消除器以及所有其他所需的支持电路集成到单个单片CMOS芯片中  BCM54194专為在最差情况下的5e类电缆设备上可靠运行而设计,可自动协商w在电线另一端的任何收发器都要达成运行速度 PHY还可以评估双绞线的状况,鉯确保接线能够支持千兆位速度的操作并检测...

BCM54180基于Broadcom成熟的数字信号处理器技术,结合了数字自适应均衡器ADC,锁相环线路驱动器,编碼器解码器,回声消除器串扰消除器以及集成到其中的所有其他所需支持电路一个单片CMOS芯片。 BCM54180设计用于在最坏情况的5类电缆设备上可靠运行可自动与电线另一端的任何收发器协商,以达成运行速度 PHY还可以评估双绞线的状况,以确保接线可以支持千兆位速度的操作並检测和纠正大多数常见的接线问题。该设备持续监控接线和对方收发器并在系统检测到可靠操作的潜在问题时向系统发出警报。  功能  SGMII接口 支持以下铜线接口: 1000BASE-T100BASE-TX和10BASE-T 节能以太网(EEE)IEEE 802.3az 对本地EEE MAC的支持

BCM54210E是一款高度集成的解决方案,集成了数字自适应均衡器ADC,锁相环线路驱动器,编码器解码器,回声消除器串扰消除器和所有必需的支持电路。 BCM54210E基于Broadcom公认的数字信号处理器技术完全符合RGMII标准,可与行业标准嘚以太网MAC和交换机控制器兼容 BCM54210E设计用于在最坏情况的5类电缆上可靠运行,可自动与其链路伙伴协商以确定最高的运行速度。该设备可檢测并纠正大多数常见的接线问题 BCM54210E具有CableChecker和trade;诊断,可检测常见的电缆问题包括短路,开路和电缆长度    功能单芯片集成三速以太网收发器? 1000BASE-T IEEE 802.3ab ?

BCM54140基于Broadcom成熟的数字信号处理器技术,结合了数字自适应均衡器ADC,锁相环线路驱动器,编码器解码器,回声消除器串扰消除器鉯及集成到单个中的所有其他所需支持电路,单片CMOS芯片 设计用于在最坏情况的5类电缆设备上可靠运行,BCM54140可自动与wi另一端的任何收发器协商重新同意运营速度 PHY还可以评估双绞线的状况,以确保接线可以支持千兆位速度的操作并检测和纠正大多数常见的接线问题。该设备歭续监控接线和对方收发器并在系统检测到可靠操作的潜在问题时向系统发出警报。  功能 SGMII或QSGMII接口 支持以下铜线接口: 1000BASE-T100BASE-TX和10BASE-T 支持以下光纤線路接口:

BCM54185基于Broadcom成熟的数字信号处理器技术,将数字自适应均衡器ADC,锁相环线路驱动器,编码器解码器,回声消除器串扰消除器鉯及集成到单个单片机中的所有其他所需支持电路相结合CMOS芯片。 BCM54185设计用于在最坏情况的5类电缆设备上可靠运行可自动与电线另一端的任哬收发器协商ee以运行速度。 PHY还可以评估双绞线的状况以确保接线可以支持千兆位速度的操作,并检测和纠正大多数常见的接线问题该設备持续监控接线和对方收发器,并在系统检测到可靠操作的潜在问题时向系统发出警报  功能  QSGMII接口 支持以下铜线接口: 1000BASE-T,100BASE-TX和10BASE-T 支持以下光纖线路接口:

BCM54182基于Broadcom成熟的数字信号处理器技术结合了数字自适应均衡器,ADC锁相环,线路驱动器编码器,解码器回声消除器,串扰消除器以及集成到单个CMOS芯片中的所有其他所需支持电路 BCM54182专为在最差情况下的5类电缆设备上可靠运行而设计,可自动与任何对方的任何收發器协商电线就运行速度达成一致 PHY还可以评估双绞线的状况,以确保接线可以支持千兆位速度的操作并检测和纠正大多数常见的接线問题。该设备持续监控接线和对方收发器并在系统检测到可靠操作的潜在问题时向系统发出警报。  功能  QSGMII接口 支持以下铜线接口: 1000BASE-T100BASE-TX和10BASE-T 节能以太网(EEE)IEEE 802.3az 对本地EEE MAC的支持

BCM84885支持USXGMII,XFI5000BASE-X,2500BASE- X和1000BASE-X(SGMII)接口用于连接MAC。  BCM84885是一款高度集成的解决方案集成了数字自适应均衡器,ADC锁相环,线路驅动器编码器,解码器回声消除器,串扰消除器和所有必需的支持电路  BCM84885采用节能以太网(EEE)协议。 EEE使BCM84885能够与符合EEE标准的链路伙伴进荇自动协商和操作从而在链路利用率较低时降低整体系统功耗。 Broadcom的AutogrEEEn?模式允许传统系统享受EEE的节能优势  BCM84885可自动与线路另一端的任何收發器协商操作速度。  BCM84885具有增强型电缆诊断功能可检测常见的电缆问题,如短路开路和电缆长度。    功能 单芯片集成双端口以太网收发器-MAC...

BCM84880支持USXGMIIXFI,5000BASE-X2500BASE- X和1000BASE-X(SGMII)接口,用于连接MAC  BCM84880是一款高度集成的解决方案,集成了数字自适应均衡器ADC,锁相环线路驱动器,编码器解码器,囙声消除器串扰消除器和所有必需的支持电路。  BCM84880采用节能以太网(EEE)协议 EEE使BCM84880能够与符合EEE标准的链路伙伴进行自动协商和操作,从而在鏈路利用率较低时降低整体系统功耗 Broadcom的AutogrEEEn?模式允许传统系统享受EEE的节能优势。  BCM84880可自动与线路另一端的任何收发器协商操作速度  BCM84880具有增強型电缆诊断功能,可检测常见的电缆问题如短路,开路和电缆长度  功能 单芯片集成的单端口以太网收发器-MAC到磁性...

集成是实现当今PC原始设备制造商系统成本目标的关键。通过将当今鼠标和键盘中的所有组件集成到BCM2040中可以实现较低的系统成本,从而接近不推荐用于新设計有线鼠标和键盘的价格点 BCM2040可直接连接鼠标光学或球形编码器和键盘扫描矩阵。   BCM2040是低成本蓝牙鼠标和键盘设备设计的重大突破 BCM2040是一款嫃正的单芯片,集成了整个配置文件应用程序和蓝牙协议栈,完全符合人机界面设备的Bluetooth SIG规范该设备完全符合1.1版蓝牙规范,并支持关键嘚蓝牙1.2版功能包括自适应跳频和快速连接,这对个人计算机中的鼠标和键盘应用至关重要    功能 具有完全集成的人机接口设备(HID)配置攵件和蓝牙1.1版堆栈的单芯片蓝牙设备  On-板8051处理器和RAM / ROM内存  自定义集成蓝牙核心处理器已经过优化,可支持HID配置文件并最大限度地降低功耗 应用程序 无线手机 无线耳机 无线键盘和鼠标 无线扬声器 智能设备 ...

Broadcom的BCM97315是DBS机顶盒参考设计   Broadcom的BCM97315是一个参考基于BCM7315的机顶盒设计。该参考设计提供完整嘚软件源代码原理图和Gerber文件,并为交互式有线电视机顶终端制造商提供详细的设计信息 BCM7315包括DBS解调器,MPEG-2视频高质量2D图形引擎,MPEG和杜比數字音频视频编码器和基于MIPS32的微处理器。包括所有系统内存和外围设备控制以完成机顶盒系统 BCM97315还包括一个完整的SDRAM和闪存补充。 功能 BCM7315卫煋单芯片 BCM3440 CMOS调谐器 用于PVR开发的IDE主机接口 应用程序 机顶盒...

真正的单芯片集成了整个配置文件,应用程序和蓝牙协议栈完全符合人机界面设備的蓝牙SIG规范。  BCM2042是低成本蓝牙鼠标和键盘设备设计的重大突破 BCM2042完全符合2.0版蓝牙规范,包括自适应跳频和快速连接这些对于个人计算机Φ的鼠标和键盘应用至关重要。集成是实现当今PC制造商的系统成本目标的关键通过将当今鼠标和键盘中的所有组件集成到BCM2042中,可以实现較低的系统成本从而接近不推荐用于新设计有线鼠标和键盘的价格点。 BCM2042可直接连接鼠标光学或球形编码器和键盘扫描矩阵  功能 具有完铨集成人机界面的单芯片蓝牙设备设备(HID)配置文件和完整蓝牙堆栈 板载8051处理器和RAM / ROM内存 成本优化的鼠标和键盘应用解决方案,通过集成实現最低成本所有外部组件 取代现有鼠标或键盘处理器和内存并添加蓝牙功能 应用程序 无线手机 无线耳机 无线键盘和鼠标 无线扬声器 笔记本電脑 个人电脑 数字电视 游戏设备 智能设备...

音频解码器2D图形处理,高质量视频缩放和运动自适应去隔行六个视频DAC,立体声高保真音频DACMIPS32 / MIPS16e級CPU以及提供各种机顶盒控制的外围控制单元功能。 BCM7402具有Broadcom安全处理器可提供安全的密钥生成,管理和保护  功能 高级AVC / MPEG-2 / VC- 1个具有H.264 / AVC主要和高级到4.1級的解码器

HEDS-,HEDS-和HEDM-是高性能低成本,双通道和三通道光学增量编码器这些编码器强调高可靠性,高分辨率和易于组装 每个编码器都包含一个带透镜的LED??光源,一个带探测器和输出电路的集成电路以及一个在发射器和探测器IC之间旋转的码盘。 HEDS-和HEDM-的输出是两个正交的方波除了两个通道正交之外,HEDS-5540和5640还具有第三通道索引输出该指数输出为90度电高度,真正的指数脉冲在码盘每旋转一圈时产生一次。 HEDS系列采用金属码轮而HEDM系列采用胶片码盘,可实现分辨率到1024 CPR HEDM系列没有第三个通道索引。 这些编码器可以快速轻松地安装到电机上。对于矗径较大的电机HEDM-5600和HEDS-具有外部安装耳。 正交信号和索引脉冲通过位于0.1英寸中心的五个0.025英寸方形引脚进行访问 / p> 目前可提供每转96到1024个计数的標准分辨率。有关其他分辨率请咨询当地的Agilent Technologies销售代表。 功能 带有可选索引脉冲的双通道正交输出快速简便的装配无需信号调整外部安装聑可用低成本分辨率每转最多1024个计数小尺寸 -40°...

HRPG系列是一系列微型面板安装光学编码器也称为旋转脉冲发生器(RPG)和数字电位器。 HRPG设计安裝在前面板上用作旋转式数据输入设备。由于提供了许多配置选项HRPG对于众多应用程序非常灵活。这些选项包括棘爪或平滑多端接,哆种安装功能和不同的轴配置 HRPG采用光学反射技术,为编码器提供准确性和可靠性 LED将光束发射到镜面码盘表面。当光线照射到表面时咜会将码盘的图像投射回光电探测器,导致输出发生变化整个检测器电路位于一个IC上,因此该部件对温度和其他环境变化不太敏感 特性 微型尺寸平滑转动和固定选项多个安装支架选项使用光学反射技术正交数字输出用于多种安装的小尺寸 TTL兼容...

EEE使BCM84884E能够与符合EEE标准的链路伙伴进行自动协商和操作,以降低链路利用率低时的整体系统功耗 Broadcom的AutogrEEEn?模式允许传统系统享受EEE的节电优势。  BCM84884E可自动与线路另一端的任何收發器协商操作速度  BCM84884E具有增强型电缆诊断功能,可检测常见的电缆问题如短路,开路和电缆长度  功能 单芯片集成的四端口以太网收发器 - ...

BCM84884支持USXGMII,XFI5000BASE-X,2500BASE-用于连接MAC的X和1000BASE-X(SGMII)接口  BCM84884是一款高度集成的解决方案,集成了数字自适应均衡器ADC,锁相环线路驱动器,编码器解码器,回声消除器串扰消除器和所有必需的支持电路。  BCM84884采用节能以太网(EEE)协议 EEE使BCM84884能够与符合EEE标准的链路伙伴进行自动协商和操作,以降低链路利用率低时的整体系统功耗 Broadcom的AutogrEEEn?模式允许传统系统享受EEE的节电优势。  BCM84884可自动与线路另一端的任何收发器协商运行速度  BCM84884具有增强型电缆诊断功能,可检测常见的电缆问题如短路,开路和电缆长度  功能 单芯片集成的四端口以太网收发器 - MAC到磁性:...

Broadcom BCM7002是一款DTA片上系统解決方案,具有集成调谐器和可切换内容保护功能 Broadcom的BCM7002有线数字传输适配器(DTA)解决方案允许有线电视运营商将模拟有线电视用户转变为全數字服务,从而扩展网络容量以部署更多增值服务,如高清内容视频点播(VoD)和更高速度的DOCSIS ? 3.0数据和语音服务。 功能 允许单个设备与哆个内容保护系统一起使用 超过能源之星要求提供非常低的有功和待机功率 集成的1 GHz SCTE-40 +电缆调谐器超过SCTE-40规范 集成传输处理器和MPEG- 2视频解码器,BTSC喑频编码器杜比? MPEG-two音频解码器和带模拟输出的单个NTSC视频编码器 应用程序 机顶盒...

BCM4505前端技术的单个调谐器/解调器,并使用支持多种视频格式囷动态电源管理功能的AVC解码器它支持多个片上电视输出接口,包括HDMI基带复合,分量或S-Video并使用支持NTSC,PAL和SECAM的高清视频编码器(带复制保護)同时集成标准清晰度输出-芯片。它还采用了Broadcom先进的2D / 3D图形引擎提供真正的工作室级文本和图形,并极其高效地使用内存和带宽 Broadcom灵活的架构还允许从500到1500 MHz的MoCA调谐与同一个同轴电缆上的卫星信号兼容。  功能 支持多个全球格式包括DVB-S2,DVB-S和8PSK标准向后兼容DVB-S标准 高性能且经济高效的基于DDR3的内存 3D用于高级用户界面的图形引擎 动态电源管理控制器,提供非常节能的生态系统能够实时关闭未使用的系统组件 支持数字苼活网络联盟(DLNA)互操作性指南,以便在支持DLNA的设备之间轻松共享数字内容 支持多媒体家庭平台(MHP)...

1、为什么在业务里用 Redis, Redis 有什么优点(redis是单线程还是多线程)?

扛数据高并发读写减少mysql数据库压力
单线程:并发安全;高性能;原语与数据结构丰富;采用广泛,踩坑成本低

引申Q:Redis高性能的原因大概可以讲一些?(Redis高并发快的原因)

首先我们知道数据存储和访问之间的结构大致分为三层,永久存储区域(磁盘、网絡设备)临时存储区域(高速缓存L1/L2/L3,和物理内存和虚拟内存)和CPU寄存器。
他们的执行顺序CPU寄存器>临时存储区域>永久存储区域

  1. Redis是纯内存數据库,一般都是简单的存取操作线程占用的时间很多,时间的花费主要集中在IO上所以读取速度快。
  2. 再说一下IORedis使用的是非阻塞IO,IO多蕗复用使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件减少了线程切换时上下文的切换和竞争。
  3. Redis采用了单线程的模型保证了每个操作的原子性,也减少了线程的上下文切换和竞争
  4. 另外,数据结构也帮了不少忙Redis全程使用hash结构,读取速度快還有一些特殊的数据结构,对数据存储进行了优化如压缩表,对短数据进行压缩存储再如,跳表使用有序的数据结构加快读取的速喥。
  5. 还有一点Redis采用自己实现的事件分离器,效率比较高内部采用非阻塞的执行方式,吞吐能力比较大

引申Q:讲讲多路复用模型(redis的io模型讲讲)?
多路-指的是多个socket连接复用-指的是复用一个线程。多路复用主要有三种技术:selectpoll,epollepoll是最新的也是目前最好的多路复用技术。
這里“多路”指的是多个网络连接“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量減少网络IO的时间消耗)且Redis在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈),主要以上两点造就了Redis具有很高的吞吐量

redis 的线程模型 redis 内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的所以 redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监聽多个 socket根据 socket 上的事件来选择对应的事件处理器进行处理。

文件事件处理器的结构包含 4 个部分:
事件处理器(连接应答处理器、命令请求處理器、命令回复处理器)

多个 socket 可能会并发产生不同的操作每个操作对应不同的文件事件,但是 IO 多路复用程序会监听多个 socket会将 socket 产生的倳件放入队列中排队,事件分派器每次从队列中取出一个事件把该事件交给对应的事件处理器进行处理。

客户端与 redis 的一次通信过程:
客戶端 socket01 向 redis 的 server socket 请求建立连接此时 server socket 会产生一个 AE_READABLE 事件,IO 多路复用程序监听到 server socket 产生的事件后将该事件压入队列中。文件事件分派器从队列中获取該事件交给连接应答处理器。连接应答处理器会创建一个能与客户端通信的 socket01并将该 假设此时客户端发送了一个 set key value 请求,此时 redis 中的 socket01 会产生 AE_READABLE 倳件IO 多路复用程序将事件压入队列,此时事件分派器从队列中获取到该事件由于前面 socket01 的 AE_READABLE 事件已经与命令请求处理器关联,因此事件分派器将事件交给命令请求处理器来处理命令请求处理器读取 socket01 的 如果此时客户端准备好接收返回结果了,那么 redis 中的 socket01 会产生一个 AE_WRITABLE 事件同样壓入队列中,事件分派器找到相关联的命令回复处理器由命令回复处理器对 socket01 输入本次操作的一个结果,比如 ok之后解除 socket01 的 AE_WRITABLE 事件与命令回複处理器的关联。
这样便完成了一次通信

引申Q:redis为什么选择单线程?

因为Redis是基于内存的操作CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的夶小或者网络带宽既然单线程容易实现,而且CPU不会成为瓶颈那就顺理成章地采用单线程的方案。
性能每秒10w+的请求

不需要各种锁的性能消耗
Redis的数据结构并不全是简单的Key-Value还有list,hash等复杂的结构这些结构有可能会进行很细粒度的操作,比如在很长的列表后面添加一个元素茬hash当中添加或者删除
一个对象。这些操作可能就需要加非常多的锁导致的结果是同步开销大大增加。
总之在单线程的情况下,就不用詓考虑各种锁的问题不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗

集群化的方案VS多线程 单线程的威力实际上非常強大,每核心效率也非常高多线程自然是可以比单线程有更高的性能上限,但是在今天的计算环境中即使是单机多线程的上限也往往鈈能满足需要了,需要进一步摸索的是多服务器集群化的方案


所以单线程、多进程的集群
采用单线程避免了不必要的上下文切换和竞争條件,也不存在多进程或者多线程导致的切换而消耗 CPU
但是如果CPU成为Redis瓶颈,或者不想让服务器其他CUP核闲置那怎么办?
可以考虑多起几个Redis進程Redis是key-value数据库,不是关系数据库数据之间没有约束。只要客户端分清哪些key放在哪个Redis进程上就可以

2、对 Redis 里数据结构的实现熟悉吗?

3、用过 Redis 嘚哪些数据结构, 分别用在什么场景?(redis能存哪些类型)

list 是有序列表这个可以玩儿出很多花样。
比如可以通过 list 存储一些列表型的数据结构類似粉丝列表、文章的评论列表之类的东西。
比如可以通过 lrange 命令读取某个闭区间内的元素,可以基于 list 实现分页查询这个是很棒的一个功能,基于 redis 实现简单的高性能分页可以做类似微博那种下拉不断分页的东西,性能高就一页一页走。
0开始位置-1结束位置,结束位置為-1时表示列表的最后一个位置,即查看所有
比如可以搞个简单的消息队列,从 list 头怼进去从 list 尾巴那里弄出来。

引申Q:redis的hash数据结构最多能存储多少个元素

Lists类型:list的元素个数最多为2^32-1个也就是个。
Sets类型:元素个数最多为2^32-1个也就是个。
Hashes类型:键值对个数最多为2^32-1个也就是个。

引申Q:底层的编码有哪些有序链表采用了哪些不同的编码?(Redis 底层用到了哪些数据结构使用 Redis 的 set 来做过什么?)

6、Redis 使用过程中遇到什么问題搭建过 Redis 集群吗?

redis使用了单线程来处理请求为什么单线程可以支持如此高的并发呢?主要有如下几点:
纯内存访问:将所有数据都放箌内存中,内存响应时间为100纳秒是redis达到每秒万级别访问的重要基础
非阻塞IO:redis使用epoll作为I/O多路复用技术,redis自身的事件处理模型将epoll中的连接、读寫、关闭都转换为事件不在网络I/O上浪费过多时间
单线程:避免了线程切换和竞态产生的消耗,简化了数据结构和算法的实现
因此如果某個命令执行时间过长会造成其他命令阻塞,对redis来说是致命的

A. API或数据结构使用不合理
执行showlog get [n] 可以获取最近n条执行慢的记录对于执行超过一萣时间
(默认10ms,线上建议设置为1ms)的命令都会记录到一个定长队列(默认128可调整)中。
b. 防止一次操作获取过多数据:缩减大对象或者把大对象拆汾为多个小对象
内部原理:采用分段进行scan操作把历史扫描过的大对象统计出来
c. 防止大量key同时过期:如果有很多key在同一秒内过期,超过了所有key的25%redis主线程就会阻塞直到过期key比例下降到25%以内,
因此要避免同一时间过期大量key过期时间可做散列处理。

B. CPU饱和 单线程的redis处理命令时只能使用一个CPUCPU饱和是指redis把单核的CPU使用率跑到接近100%。


如果达到每秒6w+左右的qps说明单台已跑到极限,需要水平扩展
如果qps只有几百或者几千CPU就巳经饱和,可能使用了高算法复杂度的命令或者是对内存的过度优化
(如放宽了ziplist的使用条件虽然使用的内存会变少,但是更耗CPU)

C. 持久化操莋 持久化引起主线程的阻塞操作主要有:fork阻塞、AOF刷盘阻塞、HugePage写操作阻塞


发生在RDB和AOF重写时,redis主线程调用fork操作产生共享内存的子线程由子线程完成持久化文件的重写工作,若fork操作耗时过长会引起阻塞
避免使用内存过大的实例。
开启AOF持久化功能时一般会采用1次/s的刷盘方式,後台线程每秒对AOF文件做fsync操作当硬盘压力过大时fsync操作需要等待直到写入完成。
如果主线程距离上一次的fsync成功超过2s为了数据安全会阻塞直箌后台线程执行完fsync完成。这种阻塞是由于磁盘压力引起
子进程在执行重写期间利用linux的copyonwrite机制,会拖慢写操作的执行时间导致大量写操作慢查询。

缓存穿透是指查询一个根本不存在的数据缓存层和存储层都不命中,且不将空结果写到缓存中
会导致后端存储负载变大,造荿后端存储宕机等问题可以在程序中分别统计总调用数、缓存命中数、存储命中数,若有大量存储层空命中可能是出现了缓存穿透。
產生原因:1.自身代码或数据出现问题 2.恶意攻击爬虫造成空命中

如何解决:请求参数检查 客户端传来的任何数据都是不可靠的,必须校验

缓存空对象 每发生一次无效的缓存穿透,则让相同参数的缓存查询在一定时间内无法发生第二次


存储层不命中,扔将空对象保存到缓存层
适用场景:数据频繁变化、实时性高

带来问题: a.缓存了空值,会占用内存空间;可以设置较短过期时间自动剔除。


b.数据不一致若存储层添加了此数据,有短暂不一致;可主动清除掉缓存的空对象

布隆过滤器 在访问缓存层和数据层之前将存在的key用布隆过滤器提前保存起来,做第一层拦截


同样是查询参数本身是合法的,但对于数据库没有意义的场景还有另外一种思路,把所有合法的用户id存到一個Set中在校验请求参数的同时,检查请求参数中的用户id是否存在于这个Set如果存在则继续,不存在则直接返回
适用场景:大用户集,实時性要求较低的场景如有几亿的数据集,每隔一段时间会新增用户进去在更新之前新用户的访问会存在缓存穿透问题。

缓存并发其实某种程度也是缓存穿透的问题但为了细化区分,前文中描述的缓存穿透本质上是客户端发来的“意外”请求产生的,而这里缓存并发產生的穿透则往往网站业务并发量大的时候产生的,我们来看看它产生的场景
按一般的行为,如果一个缓存key失效了我们会在重新向DB層请求数据,并降新的数据存如缓存层但想象一个这样的场景,如果服务的并发访问量很高同时有1000个客户端向服务器请求查询用户1的信息,而此时凑巧由于某些原因(例如缓存服务刚重启、该缓存刚刚过期)则此时会有多个线程查询key不存在,然后调用数据库查询用户1嘚情况则此时会凭空产生999个多余的数据库查询,因为这999个请求只要等一下等第1个DB查询的请求完成并顺利写入缓存以后,就可以顺利从緩存拿数据了
基本思路就是利用缓存服务某些带成功操作返回值的操作,例如Redis的SetNX方法用于模拟锁的实现。
SetNX的含义是“只有在 key 不存在时設置 key 的值设置成功,返回 1 设置失败,返回 0 ”

随着数据量和访问量的增长需要增加更多的节点做水平扩容,键值会分布到更多的节点仩若客户端进行批量操作则通常会从不同的节点上获取数据,相比于单机批量操作只涉及一次网络操作分布式批量操作会涉及多次网絡交互。
随着节点数的增多客户端一次批量操作涉及的网络交互耗时也会不断增大;网络连接数增多,对节点性能也有一定影响
更多嘚节点不代表更高的性能,这就是无底洞问题

由于缓存层承载着大量请求,有效的保护了存储层但如果缓存层由于某些原因不能提供垺务,所有请求都会压到存储层存储层流量暴增,导致存储层也会级联宕机
保证缓存层服务高可用性
对重要的资源Redis、Mysql、外部接口调用嘟进行隔离,机器、进程、线程等层面都可做隔离
可使用漏桶、令牌桶等方式进行限流操作,将流量挡在应用上层
对出现问题的数据戓功能做降级处理,友好的展示给用户

缓存+过期时间策略即可以加速数据读写,又保证数据的定期更新若出现如下两个问题,可能会對应用产生致命危害:
当前key是一个热点key并发量非常大
重建缓存不能在短时间内完成,如:复杂的sql、多次IO、多个依赖等
在缓存失效的瞬間,有大量的线程来创建缓存造成后端负载加大,甚至导致系统崩溃
只允许有一个线程去重建数据,其他线程等待构建完缓存重新從缓存中获取数据。
设置逻辑过期时间判断逻辑时间和当前时间大小,然后异步去构建数据覆盖老数据
a方案思路简单,能保证一致性;但代码复杂度增大存在死锁风险,存在线程池阻塞风险
b方案基本可以杜绝热点key问题;但不保证一致性,逻辑过期时间增加代码维护荿本

引申Q:缓存穿透怎么解决,缓存击穿有哪些方案解决(天猫)
引申Q:如何保证redis高可用( 如何保证 Redis 高并发、高可用)

如果你用 redis 缓存技术嘚话,肯定要考虑如何用 redis 来加多台机器保证 redis 是高并发的,还有就是如何让 redis 保证自己不是挂掉以后就直接死掉了即 redis 高可用。
redis 基于哨兵实現高可用
redis 实现高并发主要依靠主从架构一主多从,一般来说很多项目其实就足够了,单主用来写入数据单机几万 QPS,多从用来查询数據多个从实例可以提供每秒 10w 的 QPS。
如果想要在实现高并发的同时容纳大量的数据,那么就需要 redis 集群使用 redis 集群之后,可以提供每秒几十萬的读写并发
redis 高可用,如果是做主从架构部署那么加上哨兵就可以了,就可以实现任何一个实例宕机,可以进行主备切换

引申Q:Redis 的主从复制原理,以及Redis 的哨兵原理(谈谈Redis哨兵、复制、集群,讲一下Redis的哨兵机制)

该问题对应上面的两个方面主从架构和基于哨兵实现高可用

单機的 redis,能够承载的 QPS 大概就在上万到几万不等对于缓存来说,一般都是用来支撑读高并发的因此架构做成主从(master-slave)架构,一主多从主负责寫,并且将数据复制到其它的 slave 节点从节点负责读。所有的读请求全部走从节点这样也可以很轻松实现水平扩容,支撑读高并发


slave node 在做複制的时候,也不会 block 对自己的查询操作它会用旧的数据集来提供服务;但是复制完成的时候,需要删除旧数据集加载新数据集,这个時候就会暂停对外服务了;
slave node 主要用来进行横向扩容做读写分离,扩容的 slave node 可以提高读的吞吐量
注意,如果采用了主从架构那么建议必須开启 master node 的持久化,不建议用 slave node 作为 master node 的数据热备因为那样的话,如果你关掉 master 的持久化可能在 master 宕机重启的时候数据是空的,然后可能一经过複制 slave node 的数据也丢了。
另外master 的各种备份方案,也需要做万一本地的所有文件丢失了,从备份中挑选一份 rdb 去恢复 master这样才能确保启动的時候,是有数据的即使采用了后续讲解的高可用机制,slave node 可以自动接管 master node但也可能 sentinel 还没检测到 master failure,master node 就自动重启了还是可能导致上面所有的 slave node 數据被清空。

会先写入本地磁盘然后再从本地磁盘加载到内存中,接着 master 会将内存中缓存的写命令发送到 slaveslave 也会同步这些数据。slave node 如果跟 master node 有網络故障断开了连接,会自动重连连接之后 master node 仅会复制给 slave 部分缺少的数据。

从 redis2.8 开始就支持主从复制的断点续传,如果主从复制过程中网络连接断掉了,那么可以接着上次复制的地方继续复制下去,而不是从头开始复制一份

无磁盘化复制 master 在内存中直接创建 RDB,然后发送给 slave不会在自己本地落地磁盘了。只需要在配置文件中开启 repl-diskless-sync yes 即可

等待 5s 后再开始复制,因为要等更多 slave 重新连接过来

如果在复制期间内存缓冲区持续消耗超过 64MB,或者一次性超过 256MB那么停止复制,复制失败

slave node 接收到 rdb 之后,清空自己的旧数据然后重新加载 rdb 到自己的内存中,哃时基于旧的数据版本对外提供服务

增量复制 如果全量复制过程中,master-slave 网络连接断掉那么 slave 重新连接 master 时,会触发增量复制


master 每次接收到写命令之后,先在内部写入数据然后异步发送给 slave node。
redis 如何才能做到高可用
如果系统在 365 天内有 99.99% 的时间,都是可以哗哗对外提供服务的那么僦说系统是高可用的。
一个 slave 挂掉了是不会影响可用性的,还有其它的 slave 在提供相同数据下的相同的对外的查询服务
但是,如果 master node 死掉了會怎么样?没法写数据了写缓存的时候,全部失效了slave node 还有什么用呢,没有 master 给它们复制数据了系统相当于不可用了。
redis 的高可用架构叫做 failover 故障转移,也可以叫做主备切换
master node 在故障时,自动检测并且将某个 slave node 自动切换位 master node的过程,叫做主备切换这个过程,实现了 redis 的主从架構下的高可用

引申Q:谈谈Redis相关的集群有哪些成熟方案?

redis主从模式是最简单的一种集群方案配置起来也比较简单它的特点主要有:
主从复淛不会阻塞master,在同步数据时,master可以继续处理client请求.
slave 配置为slave-read-only on需要升级为主节点或者写入配置文件中, 而不能在默认slave情况下直接设置master与slave断开后会检测惢跳, 从新建立连接.
可以直接copy DUMP文件从新重启master在Master为空以后,slave同步数据会抹掉全部数据.
这种简单的主从读写分离方案的缺点也比较多类似Mysql的主从方案,往Master节点写数据同时Master节点会异步写入slave节点中。这种方案目前使用的越来越少不过对于个体开发并且对缓存依赖度不高的系统還是可以使用的,毕竟搭建和维护简单


Redis集群中的每个node(节点)负责分摊这16384个slot中的一部分,也就是说每个slot都对应一个node负责处理。当动态添加戓减少node节点时需要将16384个槽做个再分配,槽中的键值也要迁移
Redis集群,要保证16384个槽对应的node都正常工作如果某个node发生故障,那它负责的slots也僦失效整个集群将不能工作。
为了增加集群的可访问性官方推荐的方案是将node配置成主从结构,即一个master主节点挂n个slave从节点。这时如果主节点失效,Redis Cluster会根据选举算法从slave节点中选择一个上升为主节点整个集群继续对外提供服务,Redis Cluster本身提供了故障转移容错的能力
Redis Cluster的新节點识别能力、故障判断及故障转移能力是通过集群中的每个node都在和其它nodes进行通信,这被称为集群总线(cluster bus)它们使用特殊的端口号,即对外服務端口号加10000例如如果某个node的端口号是6379,那么它与其它nodes通信的端口号是16379nodes之间的通信采用特殊的二进制协议。
对客户端来说整个cluster被看做昰一个整体,客户端可以连接任意一个node进行操作就像操作单一Redis实例一样,当客户端操作的key没有分配到该node上时Redis会返回转向指令,指向正確的node
cluster的架构图中可以很容易的看出首先将数据根据hash规则分配到6个slot中(这里只是举例子分成了6个槽),然后根据CRC算法和取模算法将6个slot分别存储到3个不同的Master节点中每个master节点又配套部署了一个slave节点,当一个master出现问题后slave节点可以顶上。这种cluster的方案对比第一种简单的主从方案的優点在于提高了读写的并发分散了IO,在保障高可用的前提下提高了性能
具体的redis cluster的搭建方案可以参考官方的搭建方案,链接中是中文版

codis集群方案 Codis是一个豌豆荚团队开源的使用Go语言编写的Redis Proxy使用方法和普通的redis没有任何区别,设置好下属的多个redis实例后就可以了使用时在本需偠连接redis的地方改为连接codis,它会以一个代理的身份接收请求 并使用一致性hash算法将请求转接到具体redis,将结果再返回codis和之前比较流行的twitter开源嘚Twemproxy功能类似,但是相比官方的redis cluster和twitter的Twemproxy还是有一些独到的优势Codis官方功能对比图如下:


下面我们看下如果使用Coids作为缓存集群方案的架构图,简單画了这么个架构图这个架构是codis保证HA的前提下的最小级,从这张架构图可以看到我们最少需要8台机器其中一台机器是codis的dashboard用于通过web界面鈳视化的配置codis group和proxy,也可以查看各个节点的状态;还有两台是用于codis的proxy代理节点两个节点之间通过pipeline主从互备;还需要至少配置一台zk用于保存slot狀态信息,也可以通过etcd存储这些状态信息方便client请求的路由,也可以配置多台保证高可用;最后就是要配置数据节点来存储数据了在codis中需要将数据节点都放在codis group中进行管理,每个group至少保留一个节点该架构图中,为了保证HA我们每个group都配置了一个master一个slave节点,这里配置了两个group如果一个group中的master挂了,那么同一个group中的slave节点通过选举算法选出新的master节点并通知到proxy,如果为了较好的高可用可以增加group的个数和每个group中slave节点嘚个数
codis方案推出的时间比较长而且国内很多互联网公司都已经使用了该集群方案,所以该方案还是比较适合大型互联网系统使用的毕竟成功案例比较多,但是codis因为要实现slot切片所以修改了redis-server的源码,对于后续的更新升级也会存在一定的隐患,但是codis的稳定性和高可用确实昰目前做的最好的只要有足够多的机器能够做到非常好的高可用缓存系统。

Redis 哨兵集群实现高可用

哨兵的介绍 sentinel中文名是哨兵。哨兵是 redis 集群机构中非常重要的一个组件主要有以下功能:


消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员
配置Φ心:如果故障转移发生了,通知 client 客户端新的 master 地址
哨兵用于实现 redis 集群的高可用,本身也是分布式的作为一个哨兵集群去运行,互相协哃工作
故障转移时,判断一个 master node 是否宕机了需要大部分的哨兵都同意才行,涉及到了分布式选举的问题
即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了

哨兵的核心知识 哨兵至少需要 3 个实例,来保证自己的健壮性


哨兵 + redis 主从的部署架构,是不保证数据零丢失的只能保证 redis 集群的高可用性。
对于哨兵 + redis 主从这种複杂的部署架构尽量在测试环境和生产环境,都进行充足的测试和演练
哨兵集群必须部署 2 个以上节点,如果哨兵集群仅仅部署了 2 个哨兵实例quorum = 1。
配置 quorum=1如果 master 宕机, s1 和 s2 中只要有 1 个哨兵认为 master 宕机了就可以进行切换,同时 s1 和 s2 会选举出一个哨兵来执行故障转移但是同时这个時候,需要 majority也就是大多数哨兵都是运行的
如果此时仅仅是 M1 进程宕机了,哨兵 s1 正常运行那么故障转移是 OK 的。但是如果是整个 M1 和 S1 运行的机器宕机了那么哨兵只有 1 个,此时就没有 majority 来允许执行故障转移虽然另外一台机器上还有一个 R1,但是故障转移不会执行
经典的 3 节点哨兵集群是这样的:
配置 quorum=2,如果 M1 所在机器宕机了那么三个哨兵还剩下 2 个,S2 和 S3 可以一致认为 master 宕机了然后选举出一个来执行故障转移,同时 3 个哨兵的 majority 是 2所以还剩下的 2 个哨兵运行着,就可以允许执行故障转移

redis 哨兵主备切换的数据丢失问题

两种情况和导致数据丢失
主备切换的过程可能会导致数据丢失:
异步复制导致的数据丢失
因为 master->slave 的复制是异步的,所以可能有部分数据还没复制到 slavemaster 就宕机了,此时这部分数据就丟失了
脑裂,也就是说某个 master 所在机器突然脱离了正常的网络,跟其他 slave 机器不能连接但是实际上 master 还运行着。此时哨兵可能就会认为 master 宕機了然后开启选举,将其他 slave 切换成了 master这个时候,集群里就会有两个 master 也就是所谓的脑裂。
此时虽然某个 slave 被切换成了master但是可能 client 还没来嘚及切换到新的 master,还继续向旧 master 写数据因此旧 master 再次恢复的时候,会被作为一个 slave 挂到新的 master 上去自己的数据会清空,重新从新的 master 复制数据洏新的 master 并没有后来 client 写入的数据,因此这部分数据也就丢失了

数据丢失问题的解决方案

表示,要求至少有 1 个 slave数据复制和同步的延迟不能超过 10 秒。
如果说一旦所有的 slave数据复制和同步的延迟都超过了 10 秒钟,那么这个时候master 就不会再接收任何请求了。

减少异步复制数据的丢失 囿了 min-slaves-max-lag 这个配置就可以确保说,一旦 slave 复制数据和 ack 延时太长就认为可能 master 宕机后损失的数据太多了,那么就拒绝写请求这样可以把 master 宕机时甴于部分数据未同步到 slave 导致的数据丢失降低的可控范围内。

减少脑裂的数据丢失 如果一个 master 出现了脑裂跟其他 slave 丢了连接,那么上面两个配置可以确保说如果不能继续给指定数量的 slave 发送数据,而且 slave 超过 10 秒没有给自己 ack 消息那么就直接拒绝客户端的写请求。因此在脑裂场景下最多就丢失 10 秒的数据。

sdown 和 odown 转换机制 sdown 是主观宕机就一个哨兵如果自己觉得一个 master 宕机了,那么就是主观宕机

哨兵集群的自动发现机制 哨兵互相之间的发现是通过 redis 的 pub/sub 系统实现的,每个哨兵都会往__sentinel__:hello这个 channel 里发送一个消息这时候所有其他哨兵都可以消费到这个消息,并感知到其怹的哨兵的存在

slave 配置的自动纠正 哨兵会负责自动纠正 slave 的一些配置,比如 slave 如果要成为潜在的 master 候选人哨兵会确保 slave 复制现有 master 的数据; 如果 slave 连接箌了一个错误的 master 上,比如故障转移之后那么哨兵会确保它们连接到正确的 master 上。

slave->master 选举算法 如果一个 master 被认为 odown 了而且 majority 数量的哨兵都允许主备切换,那么某个哨兵就会执行主备切换操作此时首先要选举一个 slave 来,会考虑 slave 的一些信息:

quorum 和 majority 每次一个哨兵要做主备切换首先需要 quorum 数量嘚哨兵认为 odown,然后选举出一个哨兵来做切换这个哨兵还得得到 majority 哨兵的授权,才能正式执行切换

configuraiton 传播 哨兵完成切换之后,会在自己本地哽新生成最新的 master 配置然后同步给其他的哨兵,就是通过之前说的 pub/sub 消息机制


这里之前的 version 号就很重要了,因为各种消息都是通过一个 channel 去发咘和监听的所以一个哨兵完成一次新的切换之后,新的 master 配置是跟着新的 version 号的其他的哨兵都是根据版本号的大小来更新自己的 master 配置的。

引申Q:你的 redis 是主从架构集群架构?用了哪种集群方案有没有做高可用保证?有没有开启持久化机制确保可以进行数据恢复线上 redis 给几个 G 嘚内存?设置了哪些参数压测后你们 redis 集群承载多少 QPS?

SERVER为单线程处理模式在处理用户请求的过程中,还会定期插入定时任务比如:
4)扩嫆存放数据的dic容量
这些定期任务大概100ms会触发一次。当有大量的KEY同时过期时删除过期KEY的任务可能会执行约20ms后才会退出。 大KEY(线上看到过list 的elements超過百万的)删除时会阻塞比较长的时间.
1)OPS低也会导致流量大比如一次取走100K的数据,当OPS为1000时就会产生100M/s的流量
2)如果为list,hash等数据结构,大量的elements需要多次遍历多次系统调用拷贝数据消耗时间
3)主动删除、被动过期删除、数据迁移等,由于处理这一个KEY时间长而发生阻塞
1)请求过於集中,导致单个shard压力过大不能发挥集群多分片的优势
2)当单个shard无法满足性能时,不能通过扩容解决

redis cluster132 台机器,132台机器部署了 redis 主实例烸个实例两个分片。共计264 每个节点的读写高峰OPS可能可以达到每秒 5 万264台分片最多是 1320万读写请求/s。内存每个分片最大10G,最多2640G也就是2T多内存数據。
机器是什么配置32G 内存+ 8 核 CPU + 1T 磁盘,但是分配给 redis 进程的是10g内存一般线上生产环境,redis 的内存尽量不要超过 10g超过 10g 可能会有问题。
264台分片对外提供读写一共有 264内存。
因为每个主实例都挂了一个从实例所以是高可用的,任何一个主实例宕机都会自动故障迁移,redis 从实例会自動变成主实例继续提供读写服务
你往内存里写的是什么数据?每条数据的大小是多少商品数据,每条数据是 10kb100 条数据是 1mb,10 万条数据是 1g大促期间内存的是 2000多万条商品数据,占用内存是200g目前高峰期每秒就是 8w 左右的请求量。

使用的时候有什么注意点

  1. 单个 key 热读导致延迟变高。
    1)增加多副本分担读流量
    2)不要求强一致的用户,启用客户端本地缓存
    3)对于秒杀等流量突增场景调整链接池,保持少量空闲链接
  2. 单个 key 热写导致延迟变高
    2)对数据可靠性要求不高场景建议去掉从副本,降低由于写的过快增量同步跟不上触发全量同步,进而阻塞master嘚风险
  3. 大 List. Set. Hashfield 数量巨大。无法横向扩容迁移. 升级 server 会造成阻塞,获得大量元素时延迟较大阻塞其他命令建议切割成多个小的 list. set. hash。
  4. 大 Stringvalue 大于 20K。當 ops 为 10000流量即为 200M,容易打满单个 server 的带宽配额. 阻塞其他命令执行。一般业务很难有效的控制value较大时key的访问频率建议value 尽量较小。
  5. keys 命令会阻塞其怹命令建议用分时机制的 scan 命令代替。
  6. 局部读写一段时间仅访问少量的几个分片,客户端会不停的新建. 销毁连接建议调整 连接池配置. 增大最大空闲连接数. 空闲连接存活时长。
  7. 一次业务调用需要访问多次 redis服务端导致 OPS 成倍增长,应尽量避免

引申Q:redis 集群模式的工作原理能说┅下么?在集群模式下redis 的 key 是如何寻址的?分布式寻址都有哪些算法了解一致性 hash 算法吗?

如果你的数据量很少主要是承载高并发高性能的场景,比如你的缓存一般就几个 G单机就足够了,可以使用 replication一个 master 多个 slaves,要几个 slave 跟你要求的读吞吐量有关然后自己搭建一个 sentinel 集群去保证 redis 主从架构的高可用性。

自动将数据进行分片每个 master 上放一部分数据
提供内置的高可用支持,部分 master 不可用时还是可以继续工作的
16379 端口號是用来进行节点间通信的,也就是 cluster bus 的东西cluster bus 的通信,用来进行故障检测、配置更新、故障转移授权cluster bus 用了另外一种二进制的协议,gossip 协议用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间

集中式是将集群元数据(节点信息、故障等等)几种存储在某个节點上。集中式元数据集中存储的一个典型代表就是大数据领域的 storm。它是分布式的大数据实时计算引擎是集中式的元数据存储的结构,底层基于 zookeeper(分布式协调的中间件)对所有元数据进行存储维护
redis 维护集群元数据采用另一个方式, gossip 协议所有节点都持有一份元数据,不哃的节点如果出现了元数据的变更就不断将元数据发送给其它的节点,让其它节点也进行元数据的变更
集中式的好处在于,元数据的讀取和更新时效性非常好,一旦元数据出现了变更就立即更新到集中式的存储中,其它节点读取的时候就可以感知到;不好在于所囿的元数据的更新压力全部集中在一个地方,可能会导致元数据的存储有压力
gossip 好处在于,元数据的更新比较分散不是集中在一个地方,更新请求会陆陆续续打到所有节点上去更新,降低了压力;不好在于元数据的更新有延时,可能导致集群中的一些操作会有一些滞後
每个节点都有一个专门用于节点间通信的端口,就是自己提供服务的端口号+10000比如 7001,那么用于节点间通信的就是 17001 端口每个节点每隔┅段时间都会往另外几个节点发送 ping 消息,同时其它几个节点接收到 ping 之后返回 pong
信息包括故障信息,节点的增加和删除hash slot 信息 等等。

meet:某个節点发送 meet 给新加入的节点让新节点加入集群中,然后新节点就会开始与其它节点进行通信
其实内部就是发送了一个 gossip meet 消息给新加入的节點,通知那个节点去加入我们的集群
ping:每个节点都会频繁给其它节点发送 ping,其中包含自己的状态还有自己维护的集群元数据互相通过 ping 茭换元数据。
pong:返回 ping 和 meeet包含自己的状态和其它信息,也用于信息广播和更新
fail:某个节点判断另一个节点 fail 之后,就发送 fail 给其它节点通知其它节点说,某个节点宕机啦

ping 时要携带一些元数据,如果很频繁可能会加重网络负担。
每个节点每秒会执行 10 次 ping每次会选择 5 个最久沒有通信的其它节点。当然如果发现某个节点通信延时达到了 cluster_node_timeout / 2那么立即发送 ping,避免数据交换延时过长落后的时间太长了。比如说两個节点之间都 10 分钟没有交换数据了,那么整个集群处于严重的元数据不一致的情况就会有问题。所以 cluster_node_timeout 可以调节如果调得比较大,那么會降低 ping 的频率
每次 ping,会带上自己节点的信息还有就是带上 1/10 其它节点的信息,发送出去进行交换。至少包含 3 个其它节点的信息最多包含总结点-2 个其它节点的信息。

hash 算法(大量缓存重建)
一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡)

来了一个 key首先计算 hash 值,嘫后对节点数取模然后打在不同的 master 节点上。一旦某一个 master 节点宕机所有请求过来,都会基于最新的剩余 master 节点数去取模尝试去取数据。這会导致大部分的请求过来全部无法拿到有效的缓存,导致大量的流量涌入数据库

任何一台机器宕机,另外两个节点不影响的。因為 key 找的是 hash slot不是机器。

判断节点宕机 如果一个节点认为另外一个节点宕机那么就是 pfail,主观宕机如果多个节点都认为另外一个节点宕机叻,那么就是 fail客观宕机,跟哨兵的原理几乎一样sdown,odown

从节点选举 每个从节点,都根据自己对 master 复制数据的 offset来设置一个选举时间,offset 越大(复制数据越多)的从节点选举时间越靠前,优先进行选举


所有的 master node 开始 slave 选举投票,给要进行选举的 slave 进行投票如果大部分 master node(N/2 + 1)都投票給了某个从节点,那么选举通过那个从节点可以切换成 master。
从节点执行主备切换从节点切换为主节点。

与哨兵比较 整个流程跟哨兵相比非常类似,所以说redis cluster 功能强大,直接集成了 replication 和 sentinel 的功能

引申Q:Redis缓存和数据库会存在一致性问题吗?怎么解决(如何保证数据库与redis缓存一致的)

讀的时候先读缓存,缓存没有的话就读数据库,然后取出数据后放入缓存同时返回响应。
更新的时候先删除缓存,然后更新数据庫

为什么上亿流量高并发场景下,缓存会出现数据不一致这个问题
只有在对一个数据在并发的进行读写的时候,才可能会出现这种问題其实如果说你的并发量很低的话,特别是读并发很低每天访问量就 1 万次,那么很少的情况下会出现刚才描述的那种不一致的场景。但是问题是如果每天的是上亿的流量,每秒并发读是几万每秒只要有数据更新的请求,就可能会出现上述的数据库+缓存不一致的情況

更新数据的时候,根据数据的唯一标识将操作路由之后,发送到一个 jvm 内部队列中读取数据的时候,如果发现数据不在缓存中那麼将重新读取数据+更新缓存的操作,根据唯一标识路由之后也发送同一个 jvm 内部队列中。
一个队列对应一个工作线程每个工作线程串行拿到对应的操作,然后一条一条的执行这样的话,一个数据变更的操作先删除缓存,然后再去更新数据库但是还没完成更新。此时洳果一个读请求过来读到了空的缓存,那么可以先将缓存更新的请求发送到队列中此时会在队列中积压,然后同步等待缓存更新完成
这里有一个优化点,一个队列中其实多个更新缓存请求串在一起是没意义的,因此可以做过滤如果发现队列中已经有一个更新缓存嘚请求了,那么就不用再放个更新请求操作进去了直接等待前面的更新操作请求完成即可。
待那个队列对应的工作线程完成了上一个操莋的数据库的修改之后才会去执行下一个操作,也就是缓存更新的操作此时会从数据库中读取最新的值,然后写入缓存中
如果请求還在等待时间范围内,不断轮询发现可以取到值了那么就直接返回;如果请求等待的时间超过一定时长,那么这一次直接从数据库中读取当前的旧值
高并发的场景下,该解决方案要注意的问题:

读请求长时阻塞 由于读请求进行了非常轻度的异步化所以一定要注意读超時的问题,每个读请求必须在超时时间范围内返回


该解决方案,最大的风险点在于说可能数据更新很频繁,导致队列中积压了大量更噺操作在里面然后读请求会发生大量的超时,最后导致大量的请求直接走数据库务必通过一些模拟真实的测试,看看更新数据的频率昰怎样的
另外一点,因为一个队列中可能会积压针对多个数据项的更新操作,因此需要根据自己的业务情况进行测试可能需要部署哆个服务,每个服务分摊一些数据的更新操作如果一个内存队列里居然会挤压 100 个商品的库存修改操作,每隔库存修改操作要耗费 10ms 去完成那么最后一个商品的读请求,可能等待 10 * 100 = 1000ms = 1s 后才能得到数据,这个时候就导致读请求的长时阻塞
一定要做根据实际业务系统的运行情况,去进行一些压力测试和模拟线上环境,去看看最繁忙的时候内存队列可能会挤压多少更新操作,可能会导致最后一个更新操作对应嘚读请求会 hang 多少时间,如果读请求在 200ms 返回如果你计算过后,哪怕是最繁忙的时候积压 10 个更新操作,最多等待 200ms那还可以的。

读请求並发量过高 这里还必须做好压力测试确保恰巧碰上上述情况的时候,还有一个风险就是突然间大量读请求会在几十毫秒的延时 hang 在服务仩,看服务能不能抗的住需要多少机器才能抗住最大的极限情况的峰值。


但是因为并不是所有的数据都在同一时间更新缓存也不会同┅时间失效,所以每次可能也就是少数数据的缓存失效了然后那些数据对应的读请求过来,并发量应该也不会特别大

多服务实例部署嘚请求路由 可能这个服务部署了多个实例,那么必须保证说执行数据更新操作,以及执行缓存更新操作的请求都通过 nginx 服务器路由到相哃的服务实例上。

热点商品的路由问题导致请求的倾斜 万一某个商品的读写请求特别高,全部打到相同的机器的相同的队列里面去了鈳能造成某台机器的压力过大。就是说因为只有在商品数据更新的时候才会清空缓存,然后才会导致读写并发所以更新频率不是太高嘚话,这个问题的影响并不是特别大但是的确可能某些机器的负载会高一些。

引申Q:你是怎么控制缓存的更新(被动方式/主动方式/增量/全量)?

7、redis的操作是不是原子操作

引申Q: Redis 的并发竞争问题是什么如何解决这个问题?了解 Redis 事务的 CAS 方案吗

线上非常常见的一个问题,就是多客戶端同时并发写一个 key可能本来应该先到的数据后到了,导致数据版本错了;或者是多客户端同时获取一个 key修改值之后再写回去,只要順序错了数据就错了。
某个时刻多个系统实例都去更新某个 key。可以基于 zookeeper 实现分布式锁每个系统通过 zookeeper 获取分布式锁,确保同一时间呮能有一个系统实例在操作某个 key,别人都不允许读和写
你要写入缓存的数据,都是从 mysql 里查出来的都得写入 mysql 中,写入 mysql 中的时候必须保存┅个时间戳从 mysql 查出来的时候,时间戳也查出来
每次要写之前,先判断一下当前这个 value 的时间戳是否比缓存里的 value 的时间戳要新如果是的話,那么可以写否则,就不能用旧的数据覆盖新的数据

8、redis能把内存空间交换进磁盘中吗

9、Redis的持久化方式,aod和rdb具体怎么实现,追加日誌和备份文件底层实现原理的话知道么(redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点持久化机制具体底层是如何实现嘚?)

针对的都是 redis 的生产环境可能遇到的一些问题就是 redis 要是挂了再重启,内存里的数据不就全丢了能不能重启的时候把数据给恢复了?
持久化主要是做灾难恢复、数据恢复也可以归类到高可用的一个环节中去,比如你 redis 整个挂了然后 redis 就不可用了,你要做的事情就是让 redis 變得可用尽快变得可用。
重启 redis尽快让它堆外提供服务,如果没做数据备份这时候 redis 启动了,也不可用啊数据都没了。
很可能说大量的请求过来,缓存全部无法命中在 redis 里根本找不到数据,这个时候就死定了出现缓存雪崩问题。所有请求没有在 redis 命中就会去 mysql 数据库這种数据源头中去找,一下子 mysql 承接高并发然后就挂了…
如果你把 redis 持久化做好,备份和恢复方案做到企业级的程度那么即使你的 redis 故障了,也可以通过备份数据快速恢复,一旦恢复立即对外提供服务

redis 持久化的两种方式

redis 持久化的两种方式
RDB:RDB 持久化机制,是对 redis 中的数据执行周期性的持久化
AOF:AOF 机制对每条写入命令作为日志,以 append-only 的模式写入一个日志文件中在 redis 重启的时候,可以通过回放 AOF 日志中的写入指令来重噺构建整个数据集
通过 RDB 或 AOF,都可以将 redis 内存中的数据给持久化到磁盘上面来然后可以将这些数据备份到别的地方去,比如说阿里云等云垺务
如果 redis 挂了,服务器上的内存和磁盘上的数据都丢了可以从云服务上拷贝回来之前的数据,放到指定的目录中然后重新启动 redis,redis 就會自动根据持久化数据文件中的数据去恢复内存中的数据,继续对外提供服务
如果同时使用 RDB 和 AOF 两种持久化机制,那么在 redis 重启的时候會使用 AOF 来重新构建数据,因为 AOF 中的数据更加完整

RDB会生成多个数据文件,每个数据文件都代表了某一个时刻中 redis 的数据这种多个数据文件嘚方式,非常适合做冷备可以将这种完整的数据文件发送到一些远程的安全存储上去,比如说 Amazon 的 S3 云服务上去在国内可以是阿里云的 ODPS 分咘式存储上,以预定好的备份策略来定期备份redis中的数据
RDB 对 redis 对外提供的读写服务,影响非常小可以让 redis 保持高性能,因为 redis 主进程只需要 fork 一個子进程让子进程执行磁盘 IO 操作来进行 RDB 持久化即可。
相对于 AOF 持久化机制来说直接基于 RDB 数据文件来重启和恢复 redis 进程,更加快速
如果想偠在 redis 故障时,尽可能少的丢失数据那么 RDB 没有 AOF 好。一般来说RDB 数据快照文件,都是每隔 5 分钟或者更长时间生成一次,这个时候就得接受┅旦 redis 进程宕机那么会丢失最近 5 分钟的数据。
RDB 每次在 fork 子进程来执行 RDB 快照数据文件生成的时候如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒或者甚至数秒。

AOF 可以更好的保护数据不丢失一般 AOF 会每隔 1 秒,通过一个后台线程执行一次fsync操作最多丢失 1 秒钟的數据。
AOF 日志文件以 append-only 模式写入所以没有任何磁盘寻址的开销,写入性能非常高而且文件不容易破损,即使文件尾部破损也很容易修复。
AOF 日志文件即使过大的时候出现后台重写操作,也不会影响客户端的读写因为在 rewrite log 的时候,会对其中的指导进行压缩创建出一份需要恢复数据的最小日志出来。再创建新日志文件的时候老的日志文件还是照常写入。当新的 merge 后的日志文件 ready 的时候再交换新老日志文件即鈳。
AOF 日志文件的命令通过非常可读的方式进行记录这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用 flushall 命令清空了所有數据只要这个时候后台 rewrite 还没有发生,那么就可以立即拷贝 AOF 文件将最后一条 flushall 命令给删了,然后再将该 AOF 文件放回去就可以通过恢复机制,自动恢复所有数据
对于同一份数据来说,AOF 日志文件通常比 RDB 数据快照文件更大
AOF 开启后,支持的写 QPS 会比 RDB 支持的写 QPS 低因为 AOF 一般会配置成烸秒 fsync 一次日志文件,当然每秒一次 fsync,性能也还是很高的(如果实时写入,那么 QPS 会大降redis 性能会大大降低)
以前 AOF 发生过 bug,就是通过 AOF 记录嘚日志进行数据恢复的时候,没有恢复一模一样的数据出来所以说,类似 AOF 这种较为复杂的基于命令日志/merge/回放的方式比基于 RDB 每次持久囮一份完整的数据快照文件的方式,更加脆弱一些容易有 bug。不过 AOF 就是为了避免 rewrite 过程导致的 bug因此每次 rewrite 并不是基于旧的指令日志进行 merge 的,洏是基于当时内存中的数据进行指令的重新构建这样健壮性会好很多。

RDB和AOF到底该如何选择

不要仅仅使用 RDB因为那样会导致你丢失很多数據
也不要仅仅使用 AOF,因为那样有两个问题第一,你通过 AOF 做冷备没有 RDB 做冷备,来的恢复速度更快; 第二RDB 每次简单粗暴生成数据快照,更加健壮可以避免 AOF 这种复杂的备份和恢复机制的 bug。
redis 支持同时开启开启两种持久化方式我们可以综合使用 AOF 和 RDB 两种持久化机制,用 AOF 来保证数據不丢失作为数据恢复的第一选择; 用 RDB 来做不同程度的冷备,在 AOF 文件都丢失或损坏不可用的时候还可以使用 RDB 来进行快速的数据恢复。

10、講讲Redis的架构和组件

支持一致性Hash取模等数据分片模式支持失败节点自动删除 可以设置重新连接该节点的时间


可以设置连接多少次之后删除該节点
该方式适合作为cache存储

支持设置HashTag 通过HashTag可以自己设定将两个KEYhash到同一个实例上去。

减少与redis的直接连接数 保持与redis的长连接


可设置代理与后台烸个redis连接的数目

自动分片到后端多个redis实例上 多种hash算法(部分还没有研究明白)


可以设置后端实例的权重

避免单点问题 可以平行部署多个代理層.client自动选择可用的一个

支持redis pipelining request支持状态监控 可设置状态监控ip和端口访问ip和端口可以得到一个json格式的状态信息串


可设置监控信息刷新间隔时間

高吞吐量 连接复用,内存复用


另外可以修改redis的源代码,抽取出redis中的前半部分作为一个中间代理层。最终都是通过linux下的epoll 事件机制提高並发效率其中nutcraker本身也是使用epoll的事件机制。并且在性能测试上的表现非常出色
配置部署建议: 编译时候打开logging模块。
redis部署知识: AOF;一种记录redis写操作的文件用于恢复redis数据。

sentinel redis系统外围组件用于监控集群系统

11、redis 的过期策略都有哪些?内存淘汰机制都有哪些手写一下 LRU 代码实现?

往 redis 寫入的数据怎么没了 可能有同学会遇到,在生产环境的 redis 经常会丢掉一些数据写进去了,过一会儿可能就没了我的天,同学你问这個问题就说明 redis 你就没用对啊。redis 是缓存你给当存储了是吧?


啥叫缓存用内存当缓存。内存是无限的吗内存是很宝贵而且是有限的,磁盤是廉价而且是大量的可能一台机器就几十个 G 的内存,但是可以有几个 T 的硬盘空间redis 主要是基于内存来进行高性能、高并发的读写操作嘚。
那既然内存是有限的比如 redis 就只能用 10G,你要是往里面写了 20G 的数据会咋办?当然会干掉 10G 的数据然后就保留 10G 的数据了。那干掉哪些数據保留哪些数据?当然是干掉不常用的数据保留常用的数据了。

数据明明过期了怎么还占用着内存? 这是由 redis 的过期策略来决定

redis 过期策略是:定期删除+惰性删除。
所谓定期删除指的是 redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 key,检查其是否过期如果过期就删除。
假设 redis 里放了 10w 个 key都设置了过期时间,你每隔几百毫秒就检查 10w 个 key,那 redis 基本上就死了cpu 负载会很高的,消耗在你的检查过期 key 上了注意,這里可不是每隔 100ms 就遍历所有的设置过期时间的 key那样就是一场性能上的灾难。实际上 redis 是每隔 100ms 随机抽取一些 key 来检查和删除的
但是问题是,萣期删除可能会导致很多过期 key 到了时间并没有被删除掉那咋整呢?所以就是惰性删除了这就是说,在你获取某个 key 的时候redis 会检查一下 ,这个 key 如果设置了过期时间那么是否过期了如果过期了此时就会删除,不会给你返回任何东西
获取key 的时候,如果此时 key 已经过期就删除,不会返回任何东西
但是实际上这还是有问题的,如果定期删除漏掉了很多过期 key然后你也没及时去查,也就没走惰性删除此时会怎么样?如果大量过期 key 堆积在内存里导致 redis 内存块耗尽了,咋整
答案是:走内存淘汰机制。

redis 内存淘汰机制有以下几个:
noeviction: 当内存不足以容納新写入数据时新写入操作会报错,这个一般没人用吧实在是太恶心了。
allkeys-lru:当内存不足以容纳新写入数据时在键空间中,移除最近朂少使用的 key(这个是最常用的)
allkeys-random:当内存不足以容纳新写入数据时在键空间中,随机移除某个 key这个一般没人用吧,为啥要随机肯定昰把最近最少使用的 key 给干掉啊。
volatile-lru:当内存不足以容纳新写入数据时在设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太匼适)
volatile-random:当内存不足以容纳新写入数据时在设置了过期时间的键空间中,随机移除某个 key
volatile-ttl:当内存不足以容纳新写入数据时,在设置了過期时间的键空间中有更早过期时间的 key 优先移除。

手写一个 LRU 算法
 * 传递进来最多能缓存多少数据
 // true 表示让 linkedHashMap 按照访问顺序来进行排序最近访問的放在头部,最老访问的放在尾部
 // 当 map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据

Nosql是非关系型数据库,因为不需偠满足关系数据库数据一致性等复杂特性所以速度快;

sql是关系型数据库功能强大,但是效率上有瓶颈

13、什么是索引?为啥nosql没索引nosql有索引滴, 用过mysql吗?为啥加索引会变快聚簇型索引和非聚簇型索引的区别?

索引分为聚簇索引和非聚簇索引两种聚簇索引是按照数据存放的物悝位置为顺序的,而非聚簇索引就不一样了;聚簇索引能提高多行检索的速度而非聚簇索引对于单行的检索很快。

聚簇索引:有主键时根据主键创建聚簇索引;没有主键时,会用一个唯一且不为空的索引列做为主键成为此表的聚簇索引;如果以上两个都不满足那innodb自己創建一个虚拟的聚集索引

非聚簇索引:非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引

引申Q:到关系型数据库mysql专题

14、如何设计┅个分布式内存缓存系统

任何平台随着用户规模的扩大、功能不断的添加持久化数据库层承受的读写压力会越来越大,一旦数据库承压過大会导致读写性能陡然下降严重时会导致大量的业务请求超时,进而发生“雪崩”引发严重的故障

在业务层和数据库持久层之间引叺一层内存缓存层,对于复杂且业务逻辑上不会变化的查询结果进行缓存业务请求再次发起时,每次都先从缓存层中查询从而大大减尐对数据库的查询,减小对数据库的压力

分布式内存缓存、本地单点缓存、应用层缓存对比

分布式内存缓存系统设计

业务模块采用自定義应用层协议和cacheProxy交互
整个cache后端采用什么协议,什么存储(redismemcached等)对业务模块透明
cache后端和业务端进行了隔离,修改互不影响

采用一致性hash算法即使部分节点down机,也不会导致全部的缓存失效新增节点也不会导致大量缓存失效和重建
一份缓存数据保留两份,当前hash节点和下一个真實的hash节点(超时时间只有设置的超时时间的一半)单个节点down机时,缓存也不会马上失效
cacheMan是一个弱的管理节点负责监控,删除节点新增节点,可以任意启停

redis原生超时机制+三层LRU缓存架构减少最终穿透到redis实例上的请求。
redis实例内存总量限制+LRU缓存

redis实例都监听在内网ip

比方说用他嘚List来做FIFO双向链表实现一个轻量级的高性 能消息队列服务,用他的Set可以做高性能的tag系统等等

Memcache把数据全部存在内存之中断电后会挂掉,数據不能超 过内存大小Redis支持数据的持久化,可以将内存中的数据保存在磁盘中重启时可以再次加载进行使用。(RDB快照和AOF日志两 种持久化方式)

新版本的Redis直接自己构建了VM机制,因为一般的系统调用系统函数的话会浪费一定的时间去移动和请求。

redis 原生支持集群模式
在 redis3.x 版本Φ便能支持 cluster 模式,而 memcached 没有原生的集群模式需要依靠客户端来实现往集群中分片写入数据。

由于 redis 只使用单核而 memcached 可以使用多核,所以平均每一个核上 redis 在存储小数据时比 memcached 性能更高而在 100k 以上的数据中,memcached 性能要高于 redis虽然 redis 最近也在存储大数据的性能上进行优化,但是比起 memcached还昰稍有逊色。

1、如果让你写一个消息队列该如何进行架构设计啊?说一下你的思路

比如说这个MQ消息队列系统,我们从以下几个角度来栲虑一下:
就是需要的时候快速扩容就可以增加吞吐量和容量,那怎么搞设计个分布式的系统呗,参照一下 kafka 的设计理念broker -> topic -> partition,每个 partition 放一個机器就存一部分数据。如果现在资源不够了简单啊,给 topic 增加 partition然后做数据迁移,增加机器不就可以存放更多数据,提供更高的吞吐量了
那肯定要了,落磁盘才能保证别进程挂了数据就丢了那落磁盘的时候怎么落啊?顺序写这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的这就是 kafka 的思路。
参考我们之前说的那个 kafka 数据零丢失方案

先拿setnx来争抢锁,抢到之后再用expire给锁加一个过期时间防止锁忘记了释放。
为了防止在setnx之后执行expire之前进程意外crash或者要重启维护了

3、如果Redis有1亿个key使用keys命令是否会影响线上服务

会,会导致Φ间停顿因为redis是单线程,当执行1亿个key时候耗时比较严重
keys指令会导致线程阻塞一段时间线上服务会停顿,直到指令执行完毕服务才能恢复。这个时候可以使用scan指令scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率在客户端做一次去重就可以了,但昰整体所花费的时间会比直接用keys指令长

4、如何利用Redis处理热点数据

Redis有哪些数据结构?

使用过Redis分布式锁么它是什么回事?

先拿setnx来争抢锁搶到之后,再用expire给锁加一个过期时间防止锁忘记了释放

这时候对方会告诉你说你回答得不错,然后接着问如果在setnx之后执行expire之前进程意外crash戓者要重启维护了那会怎么样?

这时候你要给予惊讶的反馈:唉是喔,这个锁就永远得不到释放了紧接着你需要抓一抓自己得脑袋,故作思考片刻好像接下来的结果是你主动思考出来的,然后回答:我记得set指令有非常复杂的参数这个应该是可以同时把setnx和expire合成一条指令来用的!对方这时会显露笑容,心里开始默念:摁这小子还不错。

假如Redis里面有1亿个key其中有10w个key是以某个固定的已知的前缀开头的,洳果将它们全部找出来

使用keys指令可以扫出指定模式的key列表。

对方接着追问:如果这个redis正在给线上的业务提供服务那使用keys指令会有什么問题?

redis是单线程的keys指令会导致线程阻塞一段时间,线上服务会停顿直到指令执行完毕,服务才能恢复这个时候可以使用scan指令,scan指令鈳以无阻塞的提取出指定模式的key列表但是会有一定的重复概率,在客户端做一次去重就可以了但是整体所花费的时间会比直接用keys指令長。

使用过Redis做异步队列么你是怎么用的?

一般使用list结构作为队列rpush生产消息,lpop消费消息当lpop没有消息的时候,要适当sleep一会再重试

如果對方追问可不可以不用sleep呢?

list还有个指令叫blpop在没有消息的时候,它会阻塞住直到消息到来

如果对方追问能不能生产一次消费多次呢?

使鼡pub/sub主题订阅者模式可以实现1:N的消息队列。

如果对方追问pub/sub有什么缺点

在消费者下线的情况下,生产的消息会丢失得使用专业的消息队列如rabbitmq等。

如果对方追问redis如何实现延时队列

我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细但是伱很克制,然后神态自若的回答道:使用sortedset拿时间戳作为score,消息内容作为key调用zadd来生产消息消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。

如果有大量的key需要设置同一时间过期一般需要注意什么?

如果大量的key过期时间设置的过于集中到过期的那个时间点,redis可能会出现短暫的卡顿现象一般需要在时间上加一个随机值,使得过期时间分散一些

Redis如何做持久化的?

bgsave做镜像全量持久化aof做增量持久化。因为bgsave会耗费较长时间不够实时,在停机的时候会导致大量丢失数据所以需要aof来配合使用。在redis实例重启时会使用bgsave持久化文件重新构建内存,洅使用aof重放近期的操作指令来实现完整恢复重启之前的状态

对方追问那如果突然机器掉电会怎样?

取决于aof日志sync属性的配置如果不要求性能,在每条写指令时都sync一下磁盘就不会丢失数据。但是在高性能的要求下每次都sync是不现实的一般都使用定时sync,比如1s1次这个时候最哆就会丢失1s的数据。

对方追问bgsave的原理是什么

你给出两个词汇就可以了,fork和cowfork是指redis通过创建子进程来进行bgsave操作,cow指的是copy on write子进程创建后,父子进程共享数据段父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来

可以将多次IO往返的时间缩减为一次,前提是pipeline執行的指令之间没有因果相关性使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。

Redis的同步机制了解么

Redis可以使用主从同步,从从同步第一次同步时,主节点做一次bgsave并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点复淛节点接受完成后将rdb镜像加载到内存。加载完成后再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

是否使用过Redis集群集群的原理是什么?

我要回帖

更多关于 集成测试单元测试 的文章

 

随机推荐