精华内容
下载资源
问答
  • PostgreSQL 优化器入门

    千次阅读 2018-11-06 11:49:11
    优化器经典实现/数据库从业人员核心技能 专家推荐 “这门课从 SQL 示例入手,深入浅出地介绍了 PostgreSQL 优化器技术内幕,让读者能够快速熟悉 PostgreSQL 优化器,对 SQL 优化有非常好的指导作用,值得推荐!” ...

    课程亮点

    • 数据库内核专家通俗解读 PgSQL 优化器
    • 无需阅读分析 PgSQL 源码即可快速掌握
    • 优化器经典实现/数据库从业人员核心技能

    专家推荐

    “这门课从 SQL 示例入手,深入浅出地介绍了 PostgreSQL 优化器技术内幕,让读者能够快速熟悉 PostgreSQL 优化器,对 SQL 优化有非常好的指导作用,值得推荐!”

    ——谭峰,网名 francs,《PostgreSQL 实战》作者之一,《PostgreSQL 9 Administration Cookbook(第2版)》译者之一

    “我相信这一课程能够为对数据库优化器有兴趣却又对其复杂性望而却步的读者指点迷津。”

    ——林文,Pivotal 资深开发工程师

    “国内研究数据库优化器这一领域的人很少,希望本课程能带领广大读者进入这个广阔而精彩的世界。”

    ——李茂增,华为高斯数据库 SQL 优化专家

    “本课程内容用诙谐幽默的语言,将晦涩难懂的数据库查询优化器娓娓道来,是 PostgreSQL 及其他数据库从业者值得一读的好书!”

    ——张文升,PostgreSQL 社区核心成员,《PostgreSQL 实战》作者

    课程背景

    作为数据库的从业者,如果对优化器不够了解,便如同猛虎没有了利爪,苍鹰没有了翅膀,在使用数据库的过程中往往心有余而力不足。PostgreSQL 是世界上最先进的开源关系数据库,而 PgSQL 优化器被广泛认为教科书级实现。

    本课程从数据库一线开发人员的角度出发,通过实打实的案例,结合外在的系统表信息、参数信息、执行计划信息反向把 PgSQL 查询优化器的原理深入浅出、透彻地讲解明白。

    作者介绍

    张树杰,Pivotal 资深开发工程师、数据库内核专家。目前从事 Greenplum 数据库的内核开发工作。拥有超过 13 年的 IT 从业经验,多年从事国产数据库内核开发工作,对数据库内核各个方面均有涉猎,近些年尤其专注于研究对分布式数据库的查询优化、查询执行的改进工作。著有《PostgreSQL 技术内幕:查询优化深度探索》一书。

    课程大纲

    enter image description here

    适宜人群

    • 数据库开发从业人员与学生
    • 数据库优化器研究爱好者
    • 数据库内核研发人员

    课程内容

    开篇词:翻过数据库优化器这座山峰

    大家好,我是张树杰,是一名数据库内核开发者。我在 2018 年 6 月出版了《PostgreSQL 技术内幕:查询优化深度探索》一书,这本书对 PostgreSQL 优化器的源代码进行了详尽的分析,但也有一些朋友向我抱怨:“你只顾自己源码分析得 High,考虑过我们的感受吗?”是的,除了 PostgreSQL 的内核开发者,广阔天地间还有更多 PostgreSQL 的使用者以及其他数据库使用者。如果我们切换一个角度,从使用者的角度出发,是否能够把 PostgreSQL 的优化器解释清楚呢?于是我写了这个课程,相信跟随这个课程,大家可以翻过数据库优化器这座山峰。

    课程背景

    PostgreSQL 号称世界上最先进的开源关系数据库,它的优化器虽然比不上商业数据库的优化器那样复杂,但对于大部分用户来说,已经比较晦涩难懂。如果搞一个投票来评选数据库中最难以理解的模块,那么非优化器莫属。在使用 PostgreSQL 数据库的过程中,你可能会遇到下面这些问题:

    • 在你遇到一个比较糟糕的执行计划时,你是否有能力对其进行改造?
    • 当你遭遇一个莫名的慢查询时,你是否能够通过优化实现方法提升性能?
    • 当你创建的索引不为优化器所用时,你是否清楚地知道优化器的选择习性?
    • 你是否想通过等价改写一个 SQL 语句来改变执行计划,那等价改写 SQL 语句是否隐藏着某些规则?

    优化器是数据库的大脑。作为数据库的从业者,你是否想知道数据库的大脑在思考些什么?反之,如果对优化器不够了解,便如同猛虎没有了利爪、苍鹰没有了翅膀,在使用数据库的过程中往往心有余而力不足。因此今天我们明知山有虎,偏向虎山行,拿出愚公移山的精神,把优化器的知识消化掉。

    针对数据库从业人员的不同,我想对优化器的理解大致可以分成以下 3 个层次。

    • 层次一:粗浅了解,比如知道优化器分为逻辑优化和物理优化,了解一些逻辑优化的方法,知道执行计划的来源,能看懂优化器产生的执行计划。
    • 层次二:详细了解,在粗浅了解的基础上,能够根据自己对优化器的了解,调整出优化器“喜爱”的 SQL 语句,并且对于产生的执行计划的优劣一目了然,知其然更知其所以然。
    • 层次三:深度了解,需要对优化器的每个细节有清楚的认知,在我们写出一个 SQL 语句之后,可以庖丁解牛式地在脑海中浮现出语句在优化器中的优化过程,清楚地知道每个细节的实现过程。

    要想达到第一个层次只需要阅读一些基础理论即可,这种了解对于实际应用的意义不大;而要想达到第三个层次则需要细致地解读 PostgreSQL 优化器的源代码,这个过程又过于“艰辛”。因此,本课程的目标是使大家达到第二个层次:不分析数据库内核的源代码,从数据库使用者的角度出发,结合外在的系统表信息、参数信息、执行计划信息反向把 PostgreSQL 查询优化器的原理讲清楚。

    课程框架

    本课程内容划分为 5 大部分,共计 25 篇,覆盖了 PostgreSQL 优化器的所有重要知识点。我们通过介绍各种系统表信息、参数信息、执行计划信息,从而引出这些信息背后的优化器理论。

    导读部分

    万丈高楼平地起,这部分内容主要介绍了查询优化的一些基本概念。通过小明、大明和牛二哥对话的方式,将查询优化器的基础理论、基本流程、优化规则融入其中。对优化器不甚了解的同学能够快速进入第一个层次,从而为后面的学习打好基础。

    第一部分(第 01 ~ 03 课):准备工作

    工欲善其事、必先利其器。要想知道优化器怎么优化,就需要知道在优化之前,我们给优化器提供了什么。于是,第 01 课通过一个 SQL 示例来分析这个 SQL 语句的执行流程,从而能让读者清楚地知道 SQL 语句的执行过程。另外,查看 SQL 语句的执行计划是数据库从业人员必备的技能之一,我们不但对执行计划的查看进行了说明,还对执行计划背后隐藏的理论进行了说明。这些在第 02 课和第 03 课进行了说明,有了这些知识,就可以很方便地对优化器进行解读了。

    第二部分(第 04 ~ 11 课):逻辑优化部分

    逻辑优化也叫基于规则的优化,它主要优化的方式是检查查询树。如果查询树满足既定的优化规则,那么就按照规则对查询树进行改造。PostgreSQL 的优化规则虽然比较多,但是比较重要的有以下规则:子查询提升、表达式预处理、外连接消除、谓词下推、连接顺序交换和等价类推理等。我们在这一部分对这些规则进行了统一的说明。

    第三部分(第 12 ~ 18 课):物理优化部分

    物理优化中最重要的就是代价计算的部分。为了更好地加深理解代价,我们先是解读了统计信息的内容,根据统计信息可以计算查询数据的选择率,而统计信息和选择率是代价计算的基石。有了这些信息之后,我们尝试对扫描路径、连接路径、Non-SPJ 路径进行代价计算,这样就能让读者了解代价计算的具体过程了。在代价计算之后,我们对路径的搜索算法——动态规划方法和遗传算法进行了说明。

    第四部分(第 19 ~ 22 课):查询执行的部分

    执行计划在生成之后到底是如何执行的?这部分我们列举了一部分执行算子的执行过程中的关键点,这些关键点或者是优化措施,或者是实现的细节。理解这些执行算子的执行过程,有助于去理解执行算子的代价计算的流程。对于 Greenplum 数据库中的分布式执行计划,我们也尝试作出了说明。

    课程特色

    优化器是数据库从业人员必须熟练掌握的内容,而目前单独针对优化器的课程少之又少。在通常情况下,它可能在一本书中只能占到一个很小的章节,这些只能让读者对优化器有一个粗浅的了解。另外,如《PostgreSQL 技术内幕:查询优化深度探索》这样专业剖析 PostgreSQL 优化器源代码的书,对于数据库的“使用者”而言又过于繁琐了。因此本课程致力于不分析 PostgreSQL 的源代码,从一个 SQL 语句的执行开始,逐步分析优化器中涉及的各种优化原则。从参数、系统表、执行计划开始说明,逐步由表及里、由外及内,把 PostgreSQL 优化器背后隐藏的优化思想一一列举出来,最终做到深入浅出解读 PostgreSQL 优化器。

    课程寄语

    本课程的写作目的是让数据库从业人员对数据库的优化器有一个比较详尽的了解。我希望大家在学习的过程中不但能掌握优化器中的各种优化规则,更能举一反三,在工作中,结合自己学到的优化器知识轻松应对各种优化问题。最后祝大家学习愉快,轻松翻过优化器这座山峰!

    点击了解更多《PostgreSQL 优化器入门》

    导读:逻辑优化篇

    这部分课程致力于让读者达到对数据库优化器理解的第二个层次:详细了解

    愿上层楼骋远目,勿在浮沙筑高台,在开始学习第二个层次的内容之前,让我们先来复习一下第一个层次的内容。为了使对优化器分析的过程更为形象生动,接下来我们跟着小明、大明和牛二哥一起来探讨一下 PostgreSQL 查询优化器的一些基础知识。对这块内容已经了如指掌的朋友可以跳过导读,直接开始后面内容的学习。

    查询优化器的基本原理

    小明与大明

    小明考上了北清大学的计算机研究生,今年学校开了数据库原理课。小明对查询优化的内容不是很理解,虽然已经使出了洪荒之力,仍觉得部分原理有些晦涩难懂,于是打算问一下自己的哥哥大明。

    大明是一位资深的数据库内核开发老码农,对 Greenplum/HAWQ 数据库有多年的内核开发经验,眼镜片上的圈圈像年轮一样见证着大明十多年的从业经历。知道小明要来问问题,大明有点紧张,虽然自己做数据库内核好多年了,但是对优化器研究不甚深入,如果被小明这样的小菜鸟问倒就尴尬了。于是大明只好临时抱佛脚,拿出了好多年不看的《数据库系统实现》啃了起来。

    小明的问题

    小明的第一个问题:“为什么数据库要进行查询优化?”

    大明推了推鼻梁上的眼镜,慢条斯理地说:“不止是数据库要进行优化,基本上所有的编程语言在编译的时候都要优化。比如,你在编译 C 语言的时候,可以通过编译选项 -o 来指定进行哪个级别的优化,只是查询数据库的查询优化和 C 语言的优化还有些区别。”

    “有哪些区别呢?” 大明停顿了一下,凝视着小明,仿佛期望小明能给出答案,或是给小明腾挪出足够思考的空间。三、五秒之后,大明自答道:“C 语言是过程化语言,已经指定好了需要执行的每一个步骤;但 SQL 是描述性语言,只指定了 WHAT,而没有指定 HOW。这样它的优化空间就大了,你说是不是?”

    小明点了点头说:“对,也就是说条条大路通罗马,它比过程语言的选择更多,是不是这样?” 大明笑道:“孺子可教也。虽然我们知道它的优化空间大,但具体如何优化呢?”

    说着大明将身子向沙发一靠,翘上二郎腿继续说:“通常来说分成两个层面,一个是基于规则的优化,另一个是基于代价的优化。基于规则的优化也可以叫逻辑优化(或者规则优化),基于代价的优化也可以叫物理优化(或者代价优化)。”

    小明的第二个问题:“为什么要进行这样的区分呢?优化就优化嘛,何必还分什么规则和代价呢?”

    “分层不分层不是重点,有些优化器层次分得清楚些,有些优化器层次分得就不那么清楚,都只是优化手段而已。”大明感到有点心虚,再这么问下去恐怕要被问住,于是试图引开话题:“我们继续说说 SQL 语言吧,我们说它是一种介于关系演算和关系代数之间的语言,关系演算和关系代数你看过吧?”

    小明想了想,好像上课时老师说过关系代数,但没有说关系演算,于是说:“接触过一点,但不是特别明白。”大明得意地说:“关系演算是纯描述性的语言,而关系代数呢,则包含了一些基本的关系操作,SQL 主要借鉴的是关系演算,也包含了关系代数的一部分特点。”

    大明看小明有点懵,顿了一下继续说道:“上课的时候老师有没有说过关系代数的基本操作?”小明想了一下说:“好像说了,有投影、选择、连接、并集、差集这几个。”大明点点头说:“对,还有一个叫重命名的,一共 6 个基本操作。另外,结合实际应用在这些基本操作之上又扩展出了外连接、半连接、聚集操作、分组操作等。”

    大明继续说道:“SQL 语句虽然是描述性的,但是我们可以把它转化成一个关系代数表达式。而关系代数中呢,又有一些等价的规则,这样我们就能结合这些等价规则对关系代数表达式进行等价的转换。”

    小明的第三个问题:“进行等价转换的目的是找到性能更好的代数表达式吧?”

    “对,就是这样。”大明投去赞许的目光。

    那么如何确定等价变换之后的表达式就能变得比之前性能更好呢?或者说为什么要进行这样的等价变换,而不是使用原来的表达式呢?”

    大明愣了一下,仿佛没有想到小明会提出这样的问题,但是基于自己多年的忽悠经验,他定了定神,回答道:“这既有经验的成分,也有量化的考虑。例如,将选择操作下推,就能优先过滤数据,那么表达式的上层计算结点就能降低计算量,因此很容易可以知道是能降低代价的。再例如,我们通常会对相关的子查询进行提升,这是因为如果不提升这种子查询,那么它执行的时候就会产生一个嵌套循环。这种嵌套循环的执行代价是 O(N^2),这种复杂度已经是最坏的情况了,提升上来至少不会比它差,因此提升上来是有价值的。”大明心里对自己的临危不乱暗暗点了个赞。

    大明看小明没有提问,继续说道:“这些基于关系代数等价规则做等价变换的优化,就是基于规则的优化。当然数据库本身也会结合实际的经验,产生一些优化规则,比如外连接消除,因为外连接优化起来不太方便,如果能把它消除掉,我们就有了更大的优化空间,这些统统都是基于规则的优化。同时这些都是建立在逻辑操作符上的优化,这也是为什么基于规则的优化也叫做逻辑优化。”

    小明想了想,自己好像对逻辑操作符不太理解,连忙问第四个问题:“逻辑操作符是啥?既然有物理优化,难道还有物理操作符吗?”

    大明伸了个懒腰继续说:“比如说吧,你在 SQL 语句里写上了两个表要做一个左外连接,那么数据库怎么来做这个左外连接呢?”

    小明一头雾水地摇摇头,向大明投出了期待的眼神。

    大明继续说道:“数据库说‘我也不知道啊,你说的左外连接意思我懂,但我也不知道怎么实现啊?你需要告诉我实现方法啊’。因此优化器还承担了一个任务,就是告诉执行器,怎么来实现一个左外连接。”

    数据库有哪些方法来实现一个左外连接呢?它可以用嵌套循环连接、哈希连接、归并连接等。注意了,重要的事情说三遍,你看内连接、外连接是连接操作,嵌套循环连接、归并连接等也叫连接,但内连接、外连接这些就是逻辑操作符,而嵌套循环连接、归并连接这些就是物理操作符。因此,你说对了,物理优化就是建立在物理操作符上的优化。”

    大明:“从北京去上海,你说你怎么去?”

    小明:“坐高铁啊,又快又方便。”

    大明:“坐高铁先去广州再倒车到上海行不?”

    小明:“有点扎心了,这不是吃饱了撑的吗?”

    大明:“为什么?”

    小明:“很明显,我有直达的高铁,既省时间又省钱,先去广州再倒车?我脑子瓦特了?!”

    大明笑了笑说:“不知不觉之间,你的大脑就建立了一个代价模型,那就是性价比。优化器作为数据库的大脑,也需要建立代价模型,对物理操作符计算代价,然后筛选出最优的物理操作符来。因此,基于代价的优化是建立在物理操作符上的优化,所以也叫物理优化。”

    小明似乎懂了:“公司派我去上海出差就是一个逻辑操作符,它和我们写一个 SQL 语句要求数据库对两个表做左外连接类似;而去上海的实际路径有很多种,这些就像是物理操作符,我们对这些实际的物理路径计算代价之后,就可以选出来最好的路径了。”

    大明掏出手机,分别打开了两个不同的地图 App,输入了北京到上海的信息,然后拿给小明看。小明发现两个 App 给出的最优路径是不一样的。小明若有所思地说:“看来代价模型很重要,代价模型是不是准确决定了最优路径选择得是否准确?”

    大明一拍大腿,笑着说:“太对了,所以我作为一个数据库内核的资深开发人员,需要不断地调整优化器的代价模型,以期望获得一个相对稳定的代价模型,不过仍然是任重道远啊。”

    关于语法树

    听了大明对查询优化器基本原理的讲解,小明在学校的数据库原理课堂上顺风顺水,每天吃饭睡觉打豆豆,日子过得非常悠哉。不过眼看就到了数据库原理实践课,老师给出的题目是分析一个数据库的某一模块的实现。小明千挑万选,终于选定了要分析 PostgreSQL 数据库的查询优化器的实现,据说 PostgreSQL 数据库的查询优化器层(相)次(当)清(复)晰(杂),具有教科书级的示范作用。

    可是当小明下载了 PostgreSQL 数据库的源代码,顿时就懵圈了,虽然平时理论说得天花乱坠,但到了实践的时候却发现,理论和实际对应不上。小明深深陷入代码细节中不可自拔,查阅了好多资料,结果是读破书万卷,下笔如有锤,一点进展都没有。于是小明又想到了与 PostgreSQL 有着不解之缘的大明,想必他一定能站得更高,看得更远,于是小明蹬着自己的宝马向大明驶去。

    大明看着大汗淋漓找上门的小明,意味深长地说:“PostgreSQL 的查询优化器功能比较多,恐怕一次说不完,我们分成几次来说清楚吧。”

    小明说:“的确是,我在看查询优化器代码的时候觉得无从下手。虽然一些理论学过了,但不知道代码和理论如何对应,而且还有一些优化规则好像我们讲数据库原理的时候没有涉及,毕竟理论和实践之间还是有一些差距。”

    PostgreSQL 查询执行的基本流程

    大明打开电脑,调出 PostgreSQL 的代码说:“我们先来看一下 PostgreSQL 一个查询执行的基本流程。”然后调出了一张图。

    7740d080-cdf8-11e8-9819-c5f6b437e972

    “这张图是我自己画的,这种图已经成了优化器培训开篇的必备图了,我们有必要借助这张图来看一下 PostgreSQL 源码的大体结构,了解查询优化器所处的位置。”

    大明一边指点着电脑屏幕,一边继续说:“我们要执行一条 SQL 语句,首先会进行词法分析,也就是说把 SQL 语句做一个分割,分成很多小段段……”小明连忙说:“我们在学编译原理的时候老师说了,分成的小段段可以是关键字、标识符、常量、运算符和边界符,是不是分词之后就会给这些小段段赋予这些语义?”

    “对的!看来你对《编译原理》的第 1 章很熟悉嘛。”大明笑着说。

    “当然,我最擅长写 Hello World。”

    “好吧,Let’s 继续,PostgreSQL 的分词是在 scan.l 文件中完成的。它可能分得更细致一些,比如常量它就分成了 SCONST、FCONST、ICONST 等,不过基本的原理是一样的。进行分词并且给每个单词以语义之后,就可以去匹配 gram.y 里的语法规则了。gram.y 文件里定义了所有的 SQL 语言的语法规则,我们的查询经过匹配之后,最终形成了一颗语法树。”

    “语法树?我还听过什么查询树、计划树,这些树要怎么区分呢?”

    “一个查询语句在不同的阶段,生成的树是不同的,这些树的顺序应该是先生成语法树,然后得到查询树,最终得到计划树,计划树就是我们说的执行计划。”

    “那为什么要做这些转换呢?”小明不解地问。

    “我们通过词法分析、语法分析获得了语法树,但这时的语法树还和 SQL 语句有很紧密的关系。比如我们在语法树中保存的还是一个表的名字,一个列的名字,但实际上在 PostgreSQL 数据库中,有很多系统表,比如 PG_CLASS 用来将表保存成数据库的内部结构。当我们创建一个表的时候,会在 PG_CLASS、PG_ATTRIBUTE 等系统表里增加新的元数据,我们要用这些元数据的信息取代语法树中表的名字、列的名字等。”

    小明想了想,说:“这个取代的过程就是语义分析?这样就把语法树转换成了查询树,而查询树是使用元数据来描述的,所以我们在数据库内核中使用它就更方便了?”

    看着小明迷离的眼神,大明继续说:“我们可以把查询树认为是一个关系代数表达式。”

    小明定了定神,问道:“关系代数表达式?上次我问你查询优化原理的时候你是不是说基于规则的优化就是使用关系代数的等价规则对关系代数表达式进行等价的变换,所以查询优化器的工作就是用这个查询树做等价变换?”

    “恭喜你,答对了。”大明暗暗赞许小明的理解能力和记忆力,继续说:“查询树就是查询优化器的输入,经过逻辑优化和物理优化,最终产生一颗最优的计划树,而我们要做的就是看看查询优化器是如何产生这棵最优的计划树的。”

    逻辑优化示例

    午饭过后,大明惬意地抽起了中华烟,小明看着他好奇地问:“咱爷爷抽的是在农村种的烟叶,自给自足还省钱,你也干脆回农村种烟叶吧。你这中华烟和农村的自己卷的烟叶,能有什么区别?”

    大明看电视剧正看得起劲,心不在焉地说:“自己种的烟叶直接用报纸卷了抽,没有过滤嘴,会吸入有害颗粒物,而且烟叶的味道也不如现在改进的香烟。”说到这里大明好像想到了什么,继续说:“这就像是查询优化器的逻辑优化,查询树输入之后,需要进行持续的改进。无论是自己用报纸卷的烟,还是在超市买的成品烟,都是香烟,但是通过改进之后,香烟的毒害作用更低、香型更丰富了。逻辑优化也是这个道理,通过改进查询树,能够得到一个更‘好’的查询树。”

    “那逻辑优化是如何在已有的查询树上增加香型的呢?”

    大明继续说:“我总结,PostgreSQL 在逻辑优化阶段有这么几个重要的优化——子查询 & 子连接提升、表达式预处理、外连接消除、谓词下推、连接顺序交换、等价类推理。”大明又抽了一口烟,接着说:“从代码逻辑上来看,我们还可以把子查询 & 子连接提升、表达式预处理、外连接消除叫做逻辑重写优化,因为他们主要是对查询树进行改造。而后面的谓词下推、连接顺序交换、等价类推理则可以称为逻辑分解优化,他们已经把查询树蹂躏得不成样子了,已经到了看香烟不是香烟的地步了。”

    “可是我们的数据库原理课上并没有说有逻辑重写优化和逻辑分解优化啊。”

    “嗯,是的,这是我根据 PostgreSQL 源代码的特征自己总结的,不过它能比较形象地将现有的逻辑优化区分开来,这样就能更好地对逻辑优化进行提炼、总结、分析。”大明想了一下觉得如果把所有的逻辑优化规则都说完有点多,于是对小明说:“我们就从中挑选一两个详细说明吧,我们就借用关系代数表达式来说一下谓词下推和等价类推理吧。”

    小明想了想说:“选择下推和等价类是逻辑分解优化中的内容了,可是逻辑重写优化里还有子查询提升、表达式预处理、外连接消除这些大块头你还没有给我讲解过呀。”

    大明说:“这些先留给你自己去理解,如果理解不了再来找我吧。逻辑优化的规则实际上还是比较多的,但可以逐个击破,也就是他们之间通常而言并没有多大的关联,我们不打算在这上面纠缠太多时间,我相信以你自己的能力把他们搞定是没有问题的。” (注意:课程中会对这些内容做介绍。)

    “选择下推是为了尽早地过滤数据,这样就能在上层结点降低计算量,是吧?”

    “是的。”大明点了点头,“还是通过一个关系代数的示例来说明一下吧,顺便我们把等价类推理也说一说。比如说我们想要获得编号为 5 的老师承担的所有课程名字,我们可以给出它的关系代数表达式。”说着在电脑上敲了一个关系代数表达式:

    Πcname,tname (σTEACHER.tno=5∧TEACHER.tno=COURSE.tno (TEACHER×COURSE))

    “小明,你看这个关系代数表达式怎么下推选择操作?”

    小明看着关系代数表达式思考了一会,说:“我看这个 TEACHER.tno = 5 比较可疑。你看这个关系代数表达式,先做了 TEACHER×COURSE,也就是先做了卡氏积,我要是把 TEACHER.tno = 5 放到 TEACHER 上先把一些数据过滤掉,岂不是……完美!”说着小明也在电脑上敲出了把 TEACHER.tno = 5 下推之后的关系代数表达式。

    Πcname,tname (TEACHER.tno=COURSE.tno (σTEACHER.tno=5(TEACHER)×COURSE))

    大明说:“对,你这样下推下来的确能降低计算量,这应用的是关系代数表达式中的分配率 σF(A × B) == σF1(A) × σF2(B),那你看看,既然下推这么好,是不是投影也能下推?”小明看了一下,关系代数表达式中值需要对 cname 进行投影,顿时想到了,COURSE 表虽然有很多个列,但是我们只需要使用 cname 就够了嘛,于是小明在电脑上敲了投影下推的关系代数表达式。

    Πcname,tname (σTEACHER.tno=COURSE.tno (Πcname(σTEACHER.tno=5(TEACHER))×Πcname(COURSE))

    大明拍了小明的头一下说:“笨蛋,你这样下推投影,TEACHER.tno=COURSE.tno 还有办法做吗?”小明顿时领悟了,如果只在 COURSE 上对 cname 做投影是不行的,上层结点所有的表达式都需要考虑到,于是修改了表达式:

    Πcname,tname (σTEACHER.tno=COURSE.tno (Πtname,tno(σTEACHER.tno=5(TEACHER))×Πcname,tno(COURSE)))

    “这还差不多。”大明笑着说:“这是使用的投影的串接率,也是一个非常重要的关系代数等价规则,目前我们对这个表达式的优化主要是使用了选择下推和投影下推,如果用 SQL 语句来表示,就像这样。”大明在电脑的记事本上快速打出了两个 SQL 语句:

    SELECT sname FROM TEACHER t, COURSE c WHERE t.tno = 5 AND t.tno = c.tno;SELECT sname FROM (SELECT * FROM TEACHER WHERE tno = 5) tt, (SELECT cname, tno FROM COURSE) cc WHERE tt.tno = cc.tno;

    “你看这两个语句,就是谓词下推和投影下推前后的对照语句。在做卡氏积之前,先做了过滤,这样笛卡尔积的计算量会变小。”

    小明仔细观察代数表达式和这两个 SQL 语句,发现了一个问题,就是关系代数表达式中有 TEACHER.tno = 5 和 TEACHER.tno = COURSE.tno 这样两个约束条件,这是不是意味着 COURSE.tno 也应该等于 5 呢?小明试着在电脑上写了一个 SQL 语句:

    SELECT sname FROM (SELECT * FROM TEACHER WHERE tno = 5) tt, (SELECT cname, tno FROM COURSE WHERE tno=5) cc WHERE tt.tno = cc.tno;

    然后小明说:“你看,由于有 TEACHER.tno = 5 和 TEACHER.tno = COURSE.tno 两个约束条件,我们是不是可以推理出一个 COURSE.tno = 5 的新约束条件来呢?这样还可以把这个条件下推到 COURSE 表上,也能降低笛卡尔积的计算量。”

    大明说:“是的,这就是等价推理。PostgreSQL 在查询优化的过程中,会将约束条件中等价的部分都记录到等价类中,这样就能根据等价类生成新的约束条件。比如示例的语句中就会产生一个等价类 {TEACHER.tno, COURSE.tno, 5},这是一个含有常量的等价类,是查询优化器比较喜欢的等价类,这种等价类可以得到列属性和常量组合成的约束条件,通常都是能下推的。”

    小明心里很高兴,自己通过仔细观察,得到了等价类的优化,感觉有了学习的动力,心里美滋滋的,然后问大明:“那上面的 SQL 语句还有什么可优化的吗?”

    大明观察了一下这个语句说:“我们已经在 TEACHER 表上进行了 TEACHER.tno = 5 的过滤,在 COURSE 表上也做了 COURSE.tno = 5 的过滤,这就说明在做笛卡尔积时,实际上已确定了 TEACHER.tno = COURSE.tno = 5。也就是说 TEACHER.tno = COURSE.tno 这个约束条件已经隐含成立了,也就没什么用了,我们可以把它去掉,最终形成一个这样的 SQL 语句。”大明敲下了最终的语句:

    SELECT sname FROM (SELECT * FROM TEACHER WHERE tno = 5) tt, (SELECT cname, tno FROM COURSE WHERE tno=5) cc;

    同时也敲出了这个语句对应的关系代数表达式:

    Πcname,tname (Πtname, tno(σTEACHER.tno=5(TEACHER))× Πcname, tno(σCOURSE.tno=5(COURSE)))

    大明说:“经过选择下推、投影下推和等价类推理,我们对这个 SQL 语句或者说关系代数表达式进行了优化,最终降低了计算量。”

    小明感觉对谓词下推已经理解了:“看上去也不复杂嘛,我发现了可以下推的选择我就下推,完全没有问题啊。”大明笑着说:“甚矣,我从未见过如此厚颜无耻之人。我们现在看的这个例子,只不过是最简单的一种情况啊,你就这样大言不惭,你的人生字典里还有羞耻二字吗?”

    小明愤愤地说:“我的人生没有字典……”

    大明问道:“我们这个例子有一个问题,它是内连接,因此我们可以肆意妄为地将选择下推下来,可以没羞没臊地做等价类推理。但如果是外连接,还能这么做吗?”

    小明顿时陷入了苦苦的沉思。

    点击了解更多《PostgreSQL 优化器入门》

    导读:物理优化篇

    通过大明和小明的对话,相信读者朋友对逻辑优化的部分已经有了一个简单的了解,逻辑优化也叫基于规则的优化,这种优化方式比较呆板、不够灵活,就是按照定义好的规则硬性地进行等价变换,于是就催生了新的优化方法——物理优化,物理优化又叫基于代价的优化,今天我们再次跟着小明、大明和牛二哥一起来探讨一下 PostgreSQL 优化器是怎么计算路径代价的,又是怎么筛选路径的。对这些内容已经了如指掌的朋友可以跳过这个导读,直接开始后面的内容学习。

    统计信息和选择率

    “咚咚咚……”门外传来了敲门声,大明打开门一看,原来是同事牛二哥,牛二哥是专门从事数据库查询优化开发的码农,也有十几年从业经验了。大明感到非常 Happy,因为这两天给小明讲查询优化器讲得有些吃力,今天牛二哥来了正好可以帮上忙:“牛二同志,我弟弟小明最近学校要做数据库原理实践,总来问我优化器的问题,可我对优化器也是一知半解,这下你来了可以帮帮忙不?”

    牛二哥痛快地说:“这难不倒我,随时都可以讲。”

    小明对牛二哥早有耳闻,接到大明电话后速速赶到,见面不久便吐起了苦水:“我最近正在查看基于代价的优化,感觉付出了很多代价,但收获甚微,期望今天能得到牛二哥的指导。”

    牛二哥说:“说到代价,我觉得有个东西是绕不过去的,就是统计信息和选择率。PostgreSQL 的物理优化需要计算各种物理路径的代价,而代价估算的过程严重依赖于数据库的统计信息。统计信息是否能准确地描述表中的数据分布情况是决定代价准确性的重要条件之一。”

    小明说:“大明和我说过,数据库有很多物理路径,这些物理路径也叫物理算子。和逻辑算子不同,物理算子是查询执行器的执行方法,我们只需要计算物理算子每个步骤的代价,汇总起来就是路径的代价了,那要统计信息有什么用呢?”

    牛二哥说:“是的,我们就是要计算一个物理算子的代价,但是物理算子的计算量并不是一成不变的。”说着他从旁边的书桌上拿来纸和笔,写了两个 SQL 语句。

    SELECT A+B FROM TEST_A WHERE A > 1;SELECT A+B FROM TEST_A WHERE A > 100000000;

    然后说:“你看,这两个语句可以用同样的物理算子来完成,但是它们的计算量一样吗?”

    小明心想:A > 1 和 A > 1000000000 都是过滤条件,经过过滤之后,它们产生的数据量就不同了,这样投影中的 A + B 的计算次数就不同了,所以它们的代价应该是不同的,那它和统计信息有什么关系呢?小明灵光一闪,马上说:“我知道了,我在计算物理算子的代价的时候,要知道 A > 1 之后还剩下多少数据或者 A > 1000000000 之后还剩下多少数据,如果我们提前对表上的数据内容做了统计,剩下多少数据就不难计算了,所以必须要有统计信息。”

    牛二哥点了点头说:“嗯,通过统计信息,代价估算系统就可以了解一个表有多少行数据、用了多少个数据页面、某个值出现的频率等,然后就能根据这些信息计算出一个约束条件能过滤掉多少数据,这种约束条件过滤出的数据占总数据量的比例称为选择率。”

    统计信息是什么形式

    小明追问道:“那么统计信息是什么形式的呢?”

    牛二哥挠挠头说:“这个还真是有点麻烦,我们说常用的统计信息的形式就是 distinct 率、NULL 值率、高频值、直方图、相关系数这些,它们分别有不同的作用。比如说 distinct 率,你可以获知某一列有多少个独立值,这种信息对于像性别这种列就显得特别有用。NULL 值率呢,在统计的过程中,NULL 值是不好处理的,因此把它独立出来,形成 NULL 值率,这样在高频值、直方图这些形式中就不用考虑 NULL 值的情况了。高频值属于奇异值,顾名思义,就是出现得比较多的一些列值。去掉了 NULL 值,再去掉高频值,剩下的值可以用来做一个等频的直方图。”

    大明看小明有点跟不上,过来说:“统计信息嘛,主要的还是高频值、直方图和相关系数,实际上我建议还是不要纠结于统计信息有哪些形式,只要知道它是用来算代价的就可以了。”

    牛二哥对大明说:“这怎么可以,我还没有说统计信息是如何生成的呢!比如它通过了两阶段采样,然后对样本进行统计时使用的统计方法,哪些值可以作为高频值,直方图有几个桶,相关系数是怎么计算的,相关系数在计算索引扫描路径代价的时候怎么用的……而且我和你说,PostgreSQL 还出了基于多列的扩展统计信息,多列统计信息分成了哪些类型,分别是什么含义,各自是怎么计算的,还有选择率是怎么结合统计信息计算的,这些我还没说呢……”

    大明忍不住说:“像你这样讲优化器,岂不是要出一本书了?”

    牛二哥做痛苦状:“那好吧,统计信息我们就说到这里,但是它确实是代价计算的基石。小明同学,你理解了它的作用就可以了。”

    大明继续神秘地说:“实际上统计信息往往也不准,你想想本来就是采样的结果嘛,样本是否显著压根就不好说,而且随着应用程序对表的更新,统计信息可能更新不及时,那就更会出现偏差。更严重的是,如果我们遇到 a > b 这样的约束条件,使用统计信息计算选择率也很不好计算,即使算出来,也可能不准。”

    牛二哥说:“是的,统计信息确实也有不准确的问题。我听说有个数据库用户,他家后院出了一口泉水,他爸爸觉得是吉兆,去找风水大师看。风水大师掐指一算说:你儿子每次遇到数据库性能慢就知道更新统计信息,可是统计信息太水了,都从你家后院冒出来了。”

    三个人顿时笑做一团。

    关于物理路径

    玩笑过后,小明说:“不如给我说说物理路径吧,代价算来算去,最终还是为了物理路径计算代价嘛。大明和我说过它大体分成扫描路径和连接路径,我查过一些说明,知道扫描路径有顺序扫描路径、索引扫描路径、位图扫描路径等;而连接路径通常有嵌套循环连接路径、哈希连接路径、归并连接路径,另外还有一些其他的路径,比如排序路径、物化路径等。”

    牛二哥说:“是的,我们就来说说这些路径的含义吧。如果要获得一个表中的数据,最基础的方法就是将表中的所有的数据都遍历一遍,从中挑选出符合条件的数据,这种方式就是顺序扫描路径。顺序扫描路径的优点是具有广泛的适用性,各种表都可以用这种方法,缺点自然是代价通常比较高,因为要把所有的数据都遍历一遍。”大明同时在纸上画了个图,说:“这个图大概就是顺序扫描路径。”

    c34383b0-cdd5-11e8-8458-03f9794b87bd

    牛二哥则继续说:“将数据做一些预处理,比如建立一个索引,如果要想获得一个表的数据,可以通过扫描索引获得所需数据的‘地址’,然后通过地址将需要的数据获取出来。尤其是在选择操作带有约束条件的情况下,在索引和约束条件共同的作用下,表中有些数据就不用再遍历了,因为通过索引就很容易知道这些数据是不符合约束条件的。更有甚者,因为索引上也保存了数据,它的数据和关系中的数据是一致的,因此如果索引上的数据就能满足要求,只需要扫描索引就可以获得所需数据了。也就是说在扫描路径中还可以有索引扫描路径和快速索引扫描路径两种方式。”

    大明则继续为牛二哥“捧哏”,在纸上画出了索引扫描和快速索引扫描的图。

    ea0367e0-cdd5-11e8-8458-03f9794b87bd

    索引扫描随机读的问题

    小明看到图上写了“随机读”三个字,问道:“我看这个索引扫描有随机读的问题,这个问题能否解决掉呢?也就是说既利用了索引,还避免了随机读的问题,有这样的办法吗?”

    牛二哥说:“索引扫描路径确实带来随机读的问题,原因是索引中记录的是数据元组的地址,索引扫描是通过扫描索引获得元组地址,然后通过元组地址访问数据,索引中保存的“有序”的地址,到数据中就可能是随机的了。位图扫描就能解决这个问题,它通过位图将地址保存起来,把地址收集起来之后,然后让地址变得有序,这样就通过中间的位图把随机读消解掉了。”大明则继续在纸上画出了位图扫描的示意图。

    164d72f0-cdd6-11e8-8458-03f9794b87bd

    大明补充说道:“扫描过程中还会结合一些特殊的情况,有一些非常高效的扫描路径,比如 TID 扫描路径。TID 实际上是元组在磁盘上的存储地址,我们能够根据 TID 直接就获得元组,这样查询效率就非常高了。”

    牛二哥点了点头继续说:“扫描路径通常是执行计划中的叶子结点,也就是在最底层对表进行扫描的结点。扫描路径就是为连接路径做准备的,扫描出来的数据就可以给连接路径来实现连接操作了。”

    大明一边在纸上画一边说:“要对两个关系做连接,受笛卡尔积的启发,可以用一个算法复杂度是 O(mn) 的方法来实现,我们叫它嵌套循环连接(Nested Loop Join) 方法。这种方法虽然复杂度比较高,但是和顺序扫描一样,胜在具有普适性。”

    牛二哥说:“嵌套循环连接这种方法的复杂度比较高,看上去没什么意义,但是如果嵌套循环连接的内表的路径是一个索引扫描路径,那么算法的复杂度就会降下来。索引扫描的算法复杂度是 O(logn),因此如果嵌套循环连接的内表是一个索引扫描,它整体的算法复杂度就变成了 O(mlogn),看上去这样也是可以接受的。”

    350c3cd0-cdd6-11e8-8458-03f9794b87bd

    哈希连接

    小明点了点头说:“嗯,索引实际上是对数据做了一些预处理,我想如果哈希连接(Hash Join)方法就是将内表做一个哈希表,这样也等于将内表的数据做了预处理,也能方便外表的元组在里面探测吧?”

    牛二哥点了点头说:“假设哈希表有 N 个桶,内表数据均匀地分布在各个桶中,那么哈希连接的时间复杂度就是 O(m * n /N),当然,这里我们没有考虑上建立哈希表的代价。”

    大明则在纸上画出了哈希连接的示意图,并补充道:“哈希连接通常只能用来做等值判断。”

    584cbe40-cdd6-11e8-8458-03f9794b87bd

    归并连接

    牛二哥继续说:“如果将两个表先排序,那么就可以引入第三种连接方式:归并连接(Merge Join)。这种连接方式的代价主要浪费在排序上。如果两个关系的数据量都比较小,那么排序的代价是可控的,归并连接就是适用的。另外如果关系上有有序的索引,那就可以不用单独排序了,这样也比较适用归并连接。你看我画的这个归并连接的示意图,外表是需要排序的,而内表则借用了原有的索引的顺序,消除了排序的时间,降低了物理路径的代价。”

    ed080d70-d027-11e8-a802-f373f137079f

    “这些路径属于 SPJ 路径,在 PostgreSQL 的优化器中,通常会先生成 SPJ 的路径,然后在这基础上再叠加 Non-SPJ 的路径,比如说聚集操作、排序操作、limit 操作、分组操作……”牛二哥继续补充道。

    关于代价的计算

    小明说:“可是算来算去,物理路径的代价还是有选不准的时候啊。”

    牛二哥说:“最优路径选得不准是谁的原因?那就是代价模型不行啊。代价模型不行赖谁?那就是程序员没建好啊,所以要怪就怪到程序员自己头上。”

    小明问道:“可是我看 PostgreSQL 的代价计算已经很复杂了啊。”

    “但数据库的周边环境更复杂啊。你想想,在实际应用中,数据库用户的配置硬件环境千差万别,CPU 的频率、主存的大小和磁盘介质的性质都会影响执行计划在实际执行时的效率。”牛二哥说。

    大明接过来继续说道:“虽然在代价估算的过程中,我们无法获得‘绝对真实’的代价,但是‘绝对真实’的代价也是不必要的。因为我们只是想从多个路径(Path)中找到一个代价最小的路径,只要这些路径的代价是可以‘相互比较’的就可以了。因此可以设定一个‘相对’的代价的单位 1,同一个查询中所有的物理路径都基于这个‘相对’的单位 1 来计算的代价,这样计算出来的代价就是可以比较的,也就能用来对路径进行挑选了。”

    牛二哥接着说:“PostgreSQL 采用顺序读写一个页面的 IO 代价作为单位 1,而把随机 IO 定为了顺序 IO 的 4 倍。”

    小明说:“我知道,这个我查过相关的书。首先,目前的存储介质很大部分仍然是机械硬盘,机械硬盘的磁头在获得数据库的时候需要付出寻道时间。如果要读写的是一串在磁盘上连续的数据,就可以节省寻道时间,提高 IO 性能。而如果随机读写磁盘上任意扇区的数据,那么会有大量的时间浪费在寻道上。其次,大部分磁盘本身带有缓存,这就形成了主存→磁盘缓存→磁盘的三级结构。在将磁盘的内容加载到内存的时候,考虑到磁盘的 IO 性能,磁盘会进行数据的预读,把预读到的数据保存在磁盘的缓存中。也就是说如果用户只打算从磁盘读取 100 个字节的数据,那么磁盘可能会连续地读取磁盘中的 512 字节(不同的磁盘预读的数量可能不同)并将其保存到磁盘缓存。如果下一次是顺序读取 100 个字节之后的内容,那么预读的 512 字节的数据就会发挥作用,性能会大大增加。而如果读取的内容超出了 512 字节的范围,那么预读的数据就没有发挥作用,磁盘的 IO 性能就会下降。”说完小明得意地说:“怎么样,我说得对吧?”

    牛二哥说:“你说得对,目前 PostgreSQL 的查询优化大量考虑了随机 IO 和顺序 IO 所带来的性能差别,在这方面做了不少优化。但是现在的磁盘技术越来越发达了,以后随机 IO 和顺序 IO 是不是还差这么多,就值得商榷了。”

    代价基准单位

    “那到底还有哪些代价基准单位呢?”小明继续问道。

    大明回答:“基于磁盘 IO 的代价单位当然就是和 Page 有关的了,也就是说我们刚才说的顺序 IO 和随机 IO 都属于 IO 方面的基准代价。让牛二哥给你介绍一下 CPU 方面的代价基准单位吧。”

    牛二哥说:“CPU 方面的基准单位有哪些呢?比如说我们通过 IO 把磁盘页面读到了缓存,但我们要处理的是元组啊,所以还需要把元组从页面里解出来,还要处理元组,这部分主要消耗的是 CPU,所以会有一个元组处理的代价基准单位。另外,我们在投影、约束条件里有大量的表达式,这些表达式求解也主要消耗 CPU 资源,所以还有一个表达式代价的基准单位。”

    牛二哥继续说道:“现在 PostgreSQL 增加了很多并行路径,因此它也产生了通信代价,这个也需要计算的。”

    小明听后说:“那我们就能得到一个这样的公式。”说着在纸上写了一个公式:

    总代价 = CPU 代价 + IO 代价 + 通信代价

    牛二哥笑道:“总结得不错,这样就可以计算每种物理路径的代价,就可以对路径进行筛选了,最后挑选出来的路径就是最优路径。”

    关于最优路径

    小明、大明和牛二哥在外卖 App 里搜索附近的饭店,大明突然感叹道:“看,这就是蓝海,我们可以创业搞一个 AI 点评,只推荐最优的饭店,我准确地找到了吃货们的痛点,这里面隐含着很大的商机啊!”

    牛二哥瞥了他一眼说:“AI 推荐当然好,可是要推荐得准才行啊。一个人一个口味,你这个需求太‘智能’了,我估计不好弄。”

    小明突然说:“我最近在算法课上学过一些最优解问题的解决方法,应该能用得上。”

    牛二哥叹口气说:“可是这些方法用到优化器里都不一定够用,何况用到一个更加智能的项目上呢?”

    “嗯?优化器里也用到最优解问题的方法了吗?我们学过动态规划、贪心算法……”小明如数家珍地说起来。

    大明说:“用到了啊, 虽然物理路径看上去也不多,但实际上枚举起来,它的搜索空间也不小。例如,在扫描路径中,我们就可以有顺序扫描、索引扫描和位图扫描。假如一个表上有多个索引,就可能产生多个不同的索引扫描,那么哪个索引扫描路径好呢?还有,索引扫描和顺序扫描、位图扫描相比,哪个好呢?”

    数据库路径的搜索方法

    大明看着小明迷离的眼神后继续说:“数据库路径的搜索方法通常有 3 种类型:自底向上方法、自顶向下方法、随机方法,而 PostgreSQL 采用了其中的两种方法。”

    “采用了哪两种方法?”牛二哥明知故问。

    “采用了自底向上和随机方法,其中自底向上的方法是采用动态规划方法,而随机方法采用的是遗传算法。”

    “那有谁使用了自顶向下的方法呢?”牛二哥继续“捧哏”道。

    “嗯……这个嘛,Pivotal 公司的开源优化器 ORCA 用的就是自顶向下的方法。可以让牛二哥先给你说说怎样用动态规划方法搜索最优物理路径。”

    最优物理路径

    牛二哥拿出纸来画了几个圈,然后说:“这代表四个表,自底向上嘛,所以是从底下向上堆积,这是最底层,我们叫它第一层。”

    bc717e10-cdd6-11e8-8458-03f9794b87bd

    “动态规划方法首先考虑两个表的连接,其中优先考虑有连接关系的表进行连接。两个表的连接可以建立一个新的表,我们把这些新表叫做第二层。”牛二哥通过连线,产生了一些新的“表”。

    f77426c0-cdd6-11e8-8458-03f9794b87bd

    “第二层的表和第一层的表再连接,可以生成基于三个表连接的新的‘表’,这样就又向前推进了一层,产生了第三层。”

    1e911f10-cdd7-11e8-8458-03f9794b87bd

    “然后再用第三层的表和第一层的表进行连接,最终生成整个问题的最优路径。”

    385000b0-cdd7-11e8-8458-03f9794b87bd

    “可是,这不就是穷举吗?”小明问道。

    牛二哥解释说:“动态规划有两个特点,一个是要重复地利用子问题的解,这样能减少计算量,降低复杂度;另外一点就是通过子问题的最优解能够构造出最终的最优解,也就是说需要具有最优子结构的性质,所以动态规划的复杂度和穷举是不一样的。”

    大明继续解释说:“还有,虽然你看图里的连线比较多,但在实际情况里,并不是所有的圈圈之间都能产生连线,连接关系也有个合法性的问题嘛,所以复杂度是可以控制住的。”

    小明感觉好像明白了一点,然后赶紧追问:“那遗传算法呢?”

    大明突然意识到了什么,说:“哎哎哎,我们不是在搜索饭店吗,怎么就说起最优路径了?先点餐吧,再晚饭都没得吃了。”

    于是三个人又热火朝天地搜起饭店来……

    小结

    随着小明、大明和牛二哥的对话结束,我们也要开始进入本次课程的主要内容了......欢迎各位走进 PostgreSQL 优化器的大门,这次我们就没完了。

    点击了解更多《PostgreSQL 优化器入门》

    第01课:SQL 语句的历程
    第02课:解读执行计划
    第03课:调整执行计划
    第04课:谓词下推
    第05课:连接顺序交换规则
    第06课:子连接提升
    第07课:子查询提升
    第08课:消除外连接
    第09课:等价推理
    第10课:表达式的规范化
    第11课:逻辑优化汇总
    第12课:统计信息
    第13课:表达式提取
    第14课:选择率
    第15课:参数化路径
    第16课:选择最优执行计划
    第17课:扫描代价计算
    第18课:连接代价和 Non-SPJ 代价
    第19课:扫描计划路径的执行
    第20课:连接路径的执行说明
    第21课:聚集与分组的执行说明
    第22课:Greenplum 的执行计划

    阅读全文: http://gitbook.cn/gitchat/column/5bbee9ed2409541174645a2d

    展开全文
  • Nginx核心配置深入理解及优化

    万次阅读 2016-07-10 22:58:52
    Nginx的配置是以模块为单位来组织的,每一个模块包含一个或多个指令,指令是配置文件中的最小配置单元,一切配置项皆为指令。如http核心模块中的include、default_type、sendfile指令,都属于http模块。nginx所有...

    原文链接:http://blog.csdn.net/xyang81/article/details/51814787

    Nginx的配置是以模块为单位来组织的,每一个模块包含一个或多个指令,指令是配置文件中的最小配置单元,一切配置项皆为指令。如http核心模块中的include、default_type、sendfile指令,都属于http模块。nginx所有模块中的指令见官方文档说明:http://nginx.org/en/docs/dirindex.html

    注意:以下配置中的“上下文”表示指令可以配置在哪些模块中。
    main:顶层配置,约束服务器的行为
    

    1、服务器级别核心配置

    指令上下文语法默认值功能描述
    usermainuser nobody nobyd;nobody以哪个用户权限运行工作线程
    daemonmaindaemon yes;yesnginx是否以守护进程运行
    worker_processesmainworker_processes number;1配置工作进程数。传统的web服务器(如apache)都是同步阻塞模型,一请求一进(线)程模式,当进(线)程数增达到一定程度后,更多CPU时间浪费在线程和进程切换当中,性能急剧下降,所以负载率不高。Nginx是基于事件的非阻塞多路复用(epoll或kquene)模型,一个进程在短时间内就可以响应大量的请求。建议将该值设置<=cpu核心数量,一般高于cpu核心数量不会带来好处,反而可能会有进程切换开销的负面影响。
    worker_connectionseventsworker_connections number;1024并发响应能力的关键配置值,表示每个进程允许的最大同时连接数。maxConnection = work_connections * worker_processes;一般一个浏览器会同时开两条连接,如果是反向代理,nginx到后服务器的连接数量也要占用2条连接数,所以,做静态服务器,一般maxConnection = work_connections * worker_processes / 2; 做反代理服务器时maxConnection = work_connections * worker_processes / 4;
    useeventsuse epoll;根据不同的平台,选择最高效的连接处理方法指定处理连接请求的方法。linux内核2.6以上默认使用epoll方法,其它平台请参考:http://nginx.org/en/docs/events.html 备注:要达到超高负载下最好的网络响应能力,还有必要优化与网络相关的linux内核参数
    worker_cpu_affinitymainworker_cpu_affinity cpumask …;将工作进程绑定到特定的CPU上,减少CPU在进程之间切换的开销。用二进制bit位表示进程绑定在哪个CPU内核。如8内核4进程时的设置方法:worker_cpu_affinity 00000001 00000010 00000100 10000000
    worker_rlimit_nofilemainworker_rlimit_core size;受linux内核文件描述符数量限制设置nginx最大能打开的文件描述符数量。因为Linux对每个进程所能打开的文件描述数量是有限制的,默认一般是1024个,可通过ulimit -n FILECNT或/etc/securit/limits.conf配置修改linux默认能打开的文件句柄数限制。建议值为:系统最大数量/进程数。但进程间工作量并不是平均分配的,所以可以设置在大一些。推荐值为:655350
    error_logmain, http, mail, stream, server, locationerror_log 日志文件路径 日志级别;error_log logs/error.log error;配置错误日志文件的路径和日志级别。日志级别有debug, info, notice, warn, error, crit, alert和emerg几种。nginx的日志使用syslog输出,所以输出的日志格式是有规律的,系统运维人员可以根据日志规则进行查错或统计分析。更多说明请参考官方文档:http://nginx.org/en/docs/ngx_core_module.html#error_log
    pidmainpid 守护进程socket文件路径;pid logs/nginx.pid配置nginx守护进程ID存储文件路径(不是工作进程)

    以上是nginx的顶层配置,管理服务器级别的行为。更多配置请参考官方文档:http://nginx.org/en/docs/ngx_core_module.html#working_directory

    2、HTTP模块核心配置

    nginx做为一个HTTP反向代理服务器,平时接触得最多的应该是针对http请求的相关配置了,和http模块有关的所有配置都放在http { ... }配置中。

    指令上下文语法功能描述
    typeshttp, server, locationtypes { mime类型 文件后缀;};配置能处理的文件类型。如:text/html html htm shtml;
    includeanyinclude 文件路径;将外部文件的内容做为配置拷贝到nginx.conf文件中。如:include mime.type; 将当前目录下的mime.type配置文件拷贝到nginx配置文件中。文件路径可以是相对路径或绝对路径。文件名可以用*来表示通配符。
    default_typehttp, server, locationdefault_type mime类型;文件名到后缀的映射关系。配置默认的mime类型,当在types指令中找不到请求的文件类型时,就使用default_type指定的类型。默认为text/plain类型。
    access_loghttp, server, location, if in location, limit_exceptaccess_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
    access_log off;
    关闭或开启访问日志。默认配置为:access_log logs/access.log combined; 表示根据combined定义的日志格式,写入logs/access.log文件中,combined是http模块默认格式。如果定义了buffer和gzip其中一个参数,日志默认会先写入缓存中,当缓存满了之后,通过gzip压缩缓存中的日志并写入文件,启用了gzip压缩必须保证nginx安装的时候添加了gzip模块。缓存大小默认为64K。可以配置gzip的1~9的压缩级别,级别越高压缩效率越大,日志文件占用的空间越小,但要求系统性能也越高。默认值是1。http://nginx.org/en/docs/http/ngx_http_log_module.html#access_log
    log_formathttplog_format 格式名称 日志格式;定义http访问日志的格式,在日志格式中可以访问http模块的内嵌变量,如果变存在的话,会做为日志输出。如: remoteaddr request等,更多变量请参考:http://nginx.org/en/docs/http/ngx_http_core_module.html#variables
    sendfilehttp, server, location, if in locationsendfile on | off;启用内核复制模式。作为静态服务器可以提高最大的IO访问速度。传统的文件读写采用read和write方式,流程为:硬盘 >> kernel buffer >> user buffer>> kernel socket buffer >>协议栈,采用sendfile文件读写的流程为:硬盘 >> kernel buffer (快速拷贝到kernelsocket buffer) >>协议栈,很明显sendfile这个系统调用减少了内核到用户模式之间的切换和数据拷贝次数,直接从内核缓存的数据拷贝到协议栈,提高了很大的效率。这篇文章介绍比较详细:http://xiaorui.cc/?p=1673
    tcp_nodelayhttp, server, locationoff|on;
    tcp_nopushhttp, server, locationoff|on;tcp_nodelay和tcp_nopush这两个参数是配合使用的,启动这两项配置,会在数据包达到一定大小后再发送数据。这样会减少网络通信次数,降低阻塞概率,但也会影响响应及时性。比较适合于文件下载这类的大数据通信场景。
    keepalive_timeouthttp, server, locationkeepalive_time 65;客户端到服务器建立连接的超时时长,超过指定的时间服务器就会断开连接。默认为75秒。降低每个连接的alive时间可在一定程度上提高可响应连接数量,所以一般可适当降低此值
    gziphttp, server, location, if in locationgzip on | off;开启内容压缩,可以有效降低客户端的访问流量和网络带宽
    gzip_min_lengthhttp, server, locationgzip_min_length length;单位为k,默认为20k。内容超过最少长度后才开启压缩,因为太短的内容压缩效果不佳,且压缩过程还会浪费系统资源。这个压缩长度会作为http响应头Content-Length字段返回给客户端。 建议值:1000
    gzip_comp_levelhttp, server, locationgzip_comp_level 1~9;压缩级别,默认值为1。范围为1~9级,压缩级别越高压缩率越高,但对系统性能要求越高。建议值:4
    gzip_typeshttp, server, locationgzip_types mime-type …;压缩内容类型,默认为text/html;。只压缩html文本,一般我们都会压缩js、css、json之类的,可以把这些常见的文本数据都配上。如:text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    open_file_cachehttp, server, locationopen_file_cache off; open_file_cache max=N [inactive=time];默认值为off; 设置最大缓存数量,及缓存文件未使用的存活期。建议值:max=655350(和worker_rlimit_nofile参数一致) inactive=20s;
    open_file_
    cache_min_uses
    http, server, locationopen_file_cache_min_uses number;默认为1,有效期内文件最少使有的次数。建议值:2
    open_file
    _cache_valid
    http, server, locationopen_file_cache_valid time;默认为60s,验证缓存有效期时间间隔。 表示每隔60s检查一下缓存的文件当中,有哪些文件在20s以内没有使用超过2次的,就从缓存中删除。采用lru算法。
    serverserver { … }httpHTTP服务器的核心配置,用于配置HTTP服务器的虚拟主机,可以配置多个
    listenlisten ip[:端口]server配置虚拟主机监听的IP地址和端口,默认监听本机IP地址和80或8000端口。如果只设置了IP没设端口,默认使用80端口。如果只设置了端口,没设置IP默认使用本机IP。详细配置请参考:http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
    server_nameserver_name domain_name …;server配置虚拟主机的域名,可以指定多个,用空格分隔。默认为空
    charsethttp, server, location, if in locationcharset charset | off;设置请求编码,和url参数乱码问题有关。
    locationserver, locationlocation [ = | ~ | ~* | ^~ ] uri { … }
    location @name { … }
    http请求中的一个重要配置项,用于配置客户端请求服务器url地址的匹配规则。可以配置多个匹配规则

    3、核心配置优化

    
    # nginx不同于apache服务器,当进行了大量优化设置后会魔术般的明显性能提升效果
    # nginx在安装完成后,大部分参数就已经是最优化了,我们需要管理的东西并不多
    
    #user  nobody;
    
    #阻塞和非阻塞网络模型:
    #同步阻塞模型,一请求一进(线)程,当进(线)程增加到一定程度后
    #更多CPU时间浪费到切换一,性能急剧下降,所以负载率不高
    #Nginx基于事件的非阻塞多路复用(epoll或kquene)模型
    #一个进程在短时间内可以响应大量的请求
    #建议值 <= cpu核心数量,一般高于cpu数量不会带好处,也许还有进程切换开销的负面影响
    worker_processes 4;
    
    #将work process绑定到特定cpu上,避免进程在cpu间切换的开销
    worker_cpu_affinity 0001 0010 0100 1000 
    #8内核4进程时的设置方法 worker_cpu_affinity 00000001 00000010 00000100 10000000
    
    # 每进程最大可打开文件描述符数量(linux上文件描述符比较广义,网络端口、设备、磁盘文件都是)
    # 文件描述符用完了,新的连接会被拒绝,产生502类错误
    # linux最大可打开文件数可通过ulimit -n FILECNT或 /etc/security/limits.conf配置
    # 理论值 系统最大数量 / 进程数。但进程间工作量并不是平均分配的,所以可以设置的大一些
    worker_rlimit_nofile 65535; 
    
    #error_log  logs/error.log;
    #error_log  logs/error.log  notice;
    #error_log  logs/error.log  info;
    #pid        logs/nginx.pid;
    
    
    events {
        # 并发响应能力的关键配置值
        # 每个进程允许的最大同时连接数,work_connectins * worker_processes = maxConnection;
        # 要注意maxConnections不等同于可响应的用户数量,
        # 因为一般一个浏览器会同时开两条连接,如果反向代理,nginx到后端服务器的连接也要占用连接数
        # 所以,做静态服务器时,一般 maxClient = work_connectins * worker_processes / 2
        # 做反向代理服务器时 maxClient = work_connectins * worker_processes / 4
    
        # 这个值理论上越大越好,但最多可承受多少请求与配件和网络相关,也可最大可打开文件,最大可用sockets数量(约64K)有关
        worker_connections  65535;
    
        # 指明使用epoll 或 kquene (*BSD)
        use epoll;
    
        # 备注:要达到超高负载下最好的网络响应能力,还有必要优化与网络相关的linux内核参数
    }
    
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
    
        #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
        #                  '$status $body_bytes_sent "$http_referer" '
        #                  '"$http_user_agent" "$http_x_forwarded_for"';
    
        # 关闭此项可减少IO开销,但也无法记录访问信息,不利用业务分析,一般运维情况不建议使用
        access_log off
        # 只记录更为严重的错误日志,可减少IO压力
        error_log logs/error.log crit;
        #access_log  logs/access.log  main;
    
        # 启用内核复制模式,应该保持开启达到最快IO效率
        sendfile        on;
    
        # 简单说,启动如下两项配置,会在数据包达到一定大小后再发送数据
        # 这样会减少网络通信次数,降低阻塞概率,但也会影响响应及时性
        # 比较适合于文件下载这类的大数据包通信场景
        #tcp_nopush     on; 在 
        #tcp_nodelay on|off on禁用Nagle算法 
    
        #keepalive_timeout  0;
    
        # HTTP1.1支持持久连接alive
        # 降低每个连接的alive时间可在一定程度上提高可响应连接数量,所以一般可适当降低此值
        keepalive_timeout  30s;
    
        # 启动内容压缩,有效降低网络流量
        gzip on;    
        # 过短的内容压缩效果不佳,压缩过程还会浪费系统资源
        gzip_min_length 1000;
        # 可选值1~9,压缩级别越高压缩率越高,但对系统性能要求越高
        gzip_comp_level 4;
        # 压缩的内容类别
        gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
    
        # 静态文件缓存
        # 最大缓存数量,文件未使用存活期
        open_file_cache max=65535 inactive=20s;
        # 验证缓存有效期时间间隔
        open_file_cache_valid 30s;
        # 有效期内文件最少使用次数
        open_file_cache_min_uses 2;
    
        server {
            listen       80;
            server_name  localhost;
    
            charset utf-8;
    
            #access_log  logs/host.access.log  main;
    
            location / {
                root   html;
                index  index.html index.htm;
            }
    
            #error_page  404              /404.html;
    
            # redirect server error pages to the static page /50x.html
            #
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
    
            ...
        }
    
        ...
    
    }
    展开全文
  • 单元测试是黑盒测试的基础,基本的准入测试,既能验证逻辑的准确,又能给后续的接口重构提供基础。总之就是『单元测试很重要』,在敏捷迭代开发过程中,开发人员往往对单元测试不够重视,主要原因还是排期紧,比如...
    大部分程序员有两个特点:一不愿意写文档和注释,二不愿意写单测。单元测试是黑盒测试的基础,基本的准入测试,既能验证逻辑的准确性,又能给后续的接口重构提供基础。总之就是『单元测试很重要』,在敏捷迭代开发过程中,开发人员往往对单元测试不够重视,主要原因还是排期紧,比如我们团队初期对单测的要求是所有的 dao 层都要进行单测覆盖,到后来时间充裕才在 service 层进行单元测试的补充。controller 层的单元测试由于经常采用 mock 对象,很多人嫌繁琐就索性直接跟前端联调,不去管了。那为什么要写单元测试?单元测试能以更细的粒度,构造更多的业务场景,模拟更多的输入行为,在此过程中保证程序行为如程序员所想。单元测试可以驱动重构,通过重构进而优化代码本身,为什么这么说呢?因为单元测试对于被测试代码是有要求的,那什么是好的代码呢?援引一个我认识的架构师的观点,好的代码有以下特点:可读性。代码整洁有序;命名规范,见名知意(get/load, create/build), 在方法没有好到见名知意或者有特殊业务逻辑时,加必要注释。扩展性,易维护性。合理规划变和不变(核心业务逻辑不变);抽取模块、服务、方法;NO 复制粘贴效率。避免一个请求对远程服务/数据库重复请求可测性。避免大而全的方法,一个方法只做一件事(大方法抽取出多个可测小方法);相似的事情尽量合并成一个方法(比如 dao,sql 不要写死,尽量一个 sql 可以做多件事情)其中可测性是单元测
    展开全文
  • nginx的优化1. gzip压缩优化 2. expires缓存有还 3. 网络IO事件模型优化 4. 隐藏软件名称和版本号 5. 防盗链优化 6. 禁止恶意域名解析 7. 禁止通过IP地址访问网站 8. HTTP请求方法优化 9. 防DOS攻击单IP...

    基于网上的技术文章思路,自己加以整理,有常用的和不常用的策略,供参考。

    nginx的优化

    复制代码
    1.    gzip压缩优化
    2.    expires缓存有还
    3.    网络IO事件模型优化
    4.    隐藏软件名称和版本号
    5.    防盗链优化
    6.    禁止恶意域名解析
    7.    禁止通过IP地址访问网站
    8.    HTTP请求方法优化
    9.    防DOS攻击单IP并发连接的控制,与连接速率控制
    10.    严格设置web站点目录的权限
    11.    将nginx进程以及站点运行于监牢模式
    12.    通过robot协议以及HTTP_USER_AGENT防爬虫优化
    13.    配置错误页面根据错误码指定网页反馈给用户
    14.    nginx日志相关优化访问日志切割轮询,不记录指定元素日志、最小化日志目录权限
    15.    限制上传到资源目录的程序被访问,防止木马入侵系统破坏文件
    16.    FastCGI参数buffer和cache配置文件的优化
    17.    php.ini和php-fpm.conf配置文件的优化
    18.    有关web服务的Linux内核方面深度优化(网络连接、IO、内存等)
    19.    nginx加密传输优化(SSL)
    20.    web服务器磁盘挂载及网络文件系统的优化
    21.    使用nginx cache
    复制代码

    1、基本安全优化

    1.1  隐藏版本信息

        一般来说,软件的漏洞都和版本相关,所以我们要隐藏或消除web服务对访问用户显示的各种敏感信息。

    复制代码
     1 [root@db01 rpm]# curl -I 10.0.0.8
     2 HTTP/1.1 401 Unauthorized
     3 Server: nginx                                          #隐藏版本号
     4 Date: Thu, 21 Jul 2016 03:23:38 GMT
     5 Content-Type: text/html
     6 Content-Length: 188
     7 Connection: keep-alive
     8 WWW-Authenticate: Basic realm="oldboy training"
     9 过程:
    10 vim /application/nginx/conf/nginx.conf
    11 在http模块下加入:
    12 server_tokens off;
    13 /application/nginx/sbin/nginx -t
    14 /application/nginx/sbin/nginx -s reload
    复制代码

    1.2  隐藏nginx要修改源代码

    要修改内容的路径:

    第一路径:

    1 /home/oldboy/tools/nginx-1.6.3/src/core/nginx.h 第14,16行
    2 #define NGINX_VERSION  "1.6.2" 修改为想要的版本号如2.4.3
    3 #define NGINX_VER "nginx/" NGINX_VERSION 将nginx修改为想要修改的软件名称,如Apache。

    第二路径

    1 /home/oldboy/tools/nginx-1.6.3/src/http/ngx_http_header_filter_module.c第49行
    2 grep 'Server:nginx' ngx_http_header_filter_module.cstatic
    3 sed -i 's#Server:nginx#Server:Apache#g' ngx_http_header_filter_module.c

    第三路径

    /home/oldboy/tools/nginx-1.6.3/src/http/ngx_http_special_response.c第21,30行
    "<hr><center>"NGINX_VER "(http://oldboy.blog.51cto.com)</center>" CRLF
    "<hr><center>OWS</center>" CRLF

    然后重新编译

    1.3  更改nginx服务的默认用户

    第一种方法:

        直接更改配置文件nginx.conf.default参数,将默认的#user nobody;改为user nginx.nginx;

    第二种方法:

      直接在编译nginx的时候指定用户和用户组命令如下:

      ./configure --prefix=/application/nginx-1.6.3 --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module

    1.4  降权启动nginx

    复制代码
    1 useradd inca
    2 cd /home/inca/
    3 mkdir conf logs www
    4 echo inca >www/index.html
    5 chown -R inca.inca *
    6 ln -s /application/nginx/conf/mime.types conf/mime.types  #mime.types媒体类型文件
    复制代码

    egrep -v "#|^$" /application/nginx/conf/nginx.conf.default >conf/nginx.conf

    nginx.conf配置文件

    复制代码
    worker_processes  1;
    error_log  /home/inca/logs/error.log;
    pid /home/inca/logs/nginx.pid;
    events {
        worker_connections  1024;
    }
    http {
        include       mime.types;
        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                                     '$status $body_bytes_sent "$http_referer" '
                                     '"$http_user_agent" "$http_x_forwarded_for"';
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout  65;
        server {
            listen       8080;
            server_name  localhost;
            location / {
                root   /home/inca/www;
                index  index.html index.htm;
                }
                access_log /home/inca/logs/access.log main;
    }
    }
    复制代码

      su - inca -c "/application/nginx/sbin/nginx -c /home/inca/conf/nginx.conf"   #启动nginx服务

     重点强调:

      1.nginx.conf里面的相关路径都要更改

      2.普通用户的端口问题

    2、  根据参数优化nginx服务性能

    2.1  优化nginx进程个数的策略

      在高并发、高访问量的web服务场景,需要事先启动好更多的nginx进程,以保证快速响应并处理大量并发用户的请求。

      worker_processes  1;一般调整到与CPU的核数相同(如,2个四核的cpu计为8)

      (1)查看LInux可查看CPU个数及总核数

    grep processor /proc/cpuinfo|wc -l

      (2)查看CPU总颗数

    grep 'physical id' /proc/cpuinfo|sort|uniq|wc -l

      (3)通过执行top命令,然后按数字1,即可显示所有的CPU核数

    top  按1键就会显示第一个的信息
    
    Cpu0  :  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0

    2.2   优化绑定不同的nginx进程到不同的CPU上

         默认情况下,nginx的进程跑在某一个CPU或CPU的某一个核上,导致nginx进程使用硬件的资源不均,本节的优化是不同的nginx进程给不同的CPU处理,充分有效的利用有效的硬件资源

      四核cpu配置

    worker_processes    4;
    worker_cpu_affinity 0001 0010 0100 1000;

      双核配置

    worker_processes    2;
    worker_cpu_affinity 0101 1010;

      还有一个命令taskset -c用来分配服务给CPU

    2.3 nginx事件处理模型优化

      nginx的连接处理机制在于不同的操作系统会采用不同的I/O模型,Linux下,nginx使用epoll的I/O多路复用模型,在freebsd使用kqueue的IO多路复用模型,在solaris使用/dev/pool方式的IO多路复用模型,在windows使用的icop等等。 
      要根据系统类型不同选择不同的事务处理模型,选择有“use [ kqueue | rtsig |epool |dev/pool |select |pllo ];”我们使用的是Centos6.5的linux,因此将nginx的事件处理模型调整为epool模型。

    events {
    worker_connections  1024;
    use epoll;
    }

    官方说明:在不指定事件处理模型时,nginx默认会自动的选择最佳的事件处理模型服务

    2.4 调整nginx单个进程允许的客户端最大连接数

    参数语法:worker_connections number 
    默认配置:worker_connections 512 
    放置位置:events 标签  

    events {
        worker_connections  1024;    #一个worker进程的并发
    }

    总并发= worker_processes* worker_connections

    2.5 配置nginx worker进程最大打开文件数

    参数语法:worker_rlimit_nofile number 
    放置位置:主标签段 
    说明:作用是改变worker processes能打开的最大文件数

    worker_rlimit_nofile 65535;

    这各参数受系统文件的最大打开数限制,解决方法:

    [root@admin nginx]# cat /proc/sys/fs/file-max
    8192

    文件系统最大可打开文件数

    [root@admin nginx]# ulimit -n
    1024

    程序限制只能打开1024个文件

    使用# ulimit -n 8192调整一下

    或者永久调整打开文件数 可在启动文件/etc/rc.d/rc.local末尾添加(在/etc/sysctl.conf末尾添加fs.file-max=xxx无效)

    2.6 开启高效文件传输模式

    设置参数 sendfile on;

    sendfile参数用于开启文件的高效传输模式。同时将tcp_nopush和tcp_nodelay两个指令设置为on,可防止网络及磁盘i/o阻塞,提升nginx工作效率。

    http {
      sendfile        on;     #放在http,server,location都可以
    }

    设置参数tcp_nopush;

    激活tcp_nopush参数可以允许把httpresponse header和文件的开始放在一个文件里发布,积极的作用是减少网络报文段的数量(只有sendfile on开启才生效)

    复制代码
    例:
        sendfile   on;
        tcp_nopush on;
        tcp_nodelay on;
        server_tokens off;
        server_names_hash_bucket_size 128;
        server_names_hash_max_size 512;
        keepalive_timeout  65;
        client_header_timeout 15s;
        client_body_timeout 15s;
        send_timeout 60s;
    复制代码

    2.7 FastCGI相关参数调优

        fastcgi参数是配合nginx向后请求PHP动态引擎服务的相关参数。

     

     

     

    复制代码
    fastcgi_connect_timeout 240;       
    fastcgi_send_timeout 240;
    fastcgi_read_timeout 240;
    fastcgi_buffer_size 64k;
    fastcgi_buffers 4 64k;
    fastcgi_busy_buffers_size 128k;
    fastcgi_temp_file_write_size 128k;
    #fastcgi_temp_path /data/ngx_fcgi_tmp;
    fastcgi_cache_path /data/ngx_fcgi_cache levels=2:2 keys_zone=ngx_fcgi_cache:512m inactive=1d max_size=40g;
    复制代码

     

     

     

    2.8 配置nginx gzip压缩实现性能优化

      nginx压缩功能的介绍:

      nginx gzip压缩模块提供了压缩文件内容的功能,用户请求的内容在发送给客户端之前,nginx服务器会根据一些具体的策略实施压缩,以节省网站出口带宽,同时加快了数据传输效率,提升了用户的访问体验。

    2.8.1 压缩的优点:

      提升网站用户体验:由于发给用户的内容小了,所以用户访问单位大小的页面就快了,用户体验就提升了

      节约网站带宽成本:由于数据时压缩传输的,因此,会消耗一些cpu资源

    2.8.2 压缩的对象:

      纯文本内容压缩比很高,因此,纯文本的内容最好要压缩

      被压缩的纯文本文件必须要大于1KB,由于压缩算法的特殊原因,极小的文件压缩反而变大

      图片、视频(流媒体)等文件尽量不要压缩,因为这些文件大多都是经过压缩的,如果再压缩很可能不会减小或减小很少,或者有可能增大,而在压缩时还会消耗大量的CPU、内存资源

    2.8.3 参数介绍及配置说明:

    gzip on;                               #表示开启压缩功能

    gzip_min_length  1k;                 #表示允许压缩的页面最小字节数,页面字节数从header头的Content-Length中获取。默认值是0,表示不管页面多大都进行压缩,建议设置成大于1K。如果小于1K可能会越压越大

    gzip_buffers     432k;                   #压缩缓存区大小

    gzip_http_version 1.1;                  #压缩版本

    gzip_comp_level 9;                    #压缩比率

    gzip_types  text/css text/xml application/javascript;  #指定压缩的类型

    gzip_vary on;                       #vary header支持

    完美配置:

    复制代码
    nginx.conf  http模块
      gzip on;
      gzip_min_length  1k;
      gzip_buffers     4 32k;
      gzip_http_version 1.1;
      gzip_comp_level 9;
      gzip_types  text/css text/xml application/javascript;
      gzip_vary on;
    复制代码

    2.9 nginx expires功能

            为用户访问网站的内容设定一个过期时间,当用户第一次访问到这些内容时,会把这些内容存储在用户浏览器本地,这样用户第二次及之后继续访问该网站,浏览器就会检查已经缓存在用户浏览器本地的内容,就不会去浏览器下载了,直到缓存的内容过期或者被清除为止。

     

     

    2.9.1 expires作用和优点:

       expires可以降低网站的带宽,节约成本

      加快用户访问网站的速度,提升了用户访问体验

      服务器访问量降低,服务器压力就减轻了,服务器的成本也会降低,甚至可以节约人力成本

      几乎对于所有web服务来说,这是非常重要的功能之一,apache服务也有此功能。

    2.9.2 nginx expires 配置详解:

    ## Add expires header according to URI(path or dir).
    
    location ~ ^/(images|javascript|js|css|flash|media|static)/ {
        expires 360d;
    }

    2.9.3 expire缺点及解决办法:

      当网站被缓存的页面或数据更新了,此时用户端看到的可能还是旧的已经缓存的内容,这样就会影响用户体验,那么如何解决这个问题呢?

      第一:对于经常需要的变动图片等文件,可以缩短对象缓存时间,例如,百度、谷歌等网站的首页图片经常会换成一些节日的图,这里可以将缓存期修改为1天

      第二:当网站改版或更新内容时,可以在服务器将缓存的对象改名(网站代码程序)

      对于网站的图片、附件,一般不会被用户直接修改,用户层面上的修改图片,实际上是重新传到服务器,虽然内容一样但是一个新的图片名了

      网站改版升级会修改JS、CSS元素,若改版的时候对这些元素改了名,会使得前端的CDN以及用户端需要重新缓存内容

    3.   nginx日志的优化

    3.1 编写脚本实现日志轮询

      编写脚本实现nginx access日志轮询

      用户咋请求一个软件时,绝大多数软件都会记录用户的访问情况,nginx软件目前没有类似apache通过cronolog或者rotatelog对日志分隔处理的功能,但是,运维人员可以通过利用脚本开发、nginx的信号控制功能或reload重新加载,来实现日志的自动切割、轮询。

      操作步骤:

        写一个定时任务

    mv www_access.log www_access_$(date +F -d -1day).log
    /usr/local/nginx/sbin/nginx -s reload

    3.2 不记录不需要的日志

         在实际工作中,对于负载均衡器健康检查节点或某些特定的文件(图片、JS、CSS)的日志,一般不需要记录下来,因为在统计PV时是按照页面计算的,而且日志写入的太频繁会消耗磁盘i/o,降低服务的性能

    具体配制方法:
    
    location ~ .*\.(js|jpg|JPG|jpeg|JPEG|css|bmp|gif|GIF)$ {
        access_log off;
    }

    3.3访问日志的权限设置

      假如日志目录为/app/logs,则授权方法为:

      chown -R root.root /usr/local/nginx/logs
      chmod -R 600 /usr/local/nginx/logs

      不需要在日志目录上给nginx用户读写或者读写许可,很多人都没有注意这个问题,这就称为了安全隐患。

    4.   nginx站点目录及文件URL访问控制

    4.1 根据扩展名限制程序和文件访问

      web2.0时代,绝大多数网站都是以用户为中心的,这些产品有一些共同点,就是不允许用户发布内容到服务器,还允许用户发图片甚至附件上传到服务器上,给用户开启了上传的功能。带来了很大的安全隐患。

      下面将利用nginx配置禁止访问上传资源目录下的PHP,SHELL,PERL,PYTHON程序文件,这样就算是用户上传了木马文件也没办法执行

    复制代码
     1        location ~ ^/images/.*\.(php|php5|.sh|.pl|.py)$
     2                {
     3                          deny all;
     4                }
     5        location ~ ^/static/.*\.(php|php5|.sh|.pl|.py)$
     6                {
     7                     deny all;
     8                }
     9        location ~* ^/data/(attachment|avatar)/.*\.(php|php5)$
    10            {
    11                    deny all;
    12            }    
    复制代码

      对于上述目录的限制必须写在nginx处理PHP服务配置的前面

    4.2 禁止访问指定目录下的所有文件和目录

    配置禁止访问指定的单个或多个目录

    复制代码
    location ~ ^/(static)/ {
            deny all;
    }
    
     
    
    location ~ ^/static {
            deny all;
    }
    复制代码

    禁止访问目录并且返回代码404

    复制代码
    server {
    
            listen       80;
    
            server_name  www.etiantian.org etiantian.org;
    
                root   /data0/www/www;
    
                index  index.html index.htm;
    
                access_log  /app/logs/www_access.log  commonlog;
    
                location /admin/ { return 404; }
    
                location /templates/ { return 403; }
    
    }
    复制代码

    4.3 限制网站来源的IP访问

    使用ngx_http_access_module限制网站来源IP访问。

    范例1:禁止外界访问,但允许某个IP访问该目录

    复制代码
    location ~ ^/oldboy/ {
    
           allow 202.111.12.211;
    
           deny all;
    
    }
    复制代码

    范例2:限制及指定IP或IP段访问。 

    复制代码
    location / {
    
           deny 192.168.1.1;
    
           allow 192.168.1.0/24;
    
           allow 10.1.1.0/16;
    
           deny all;
    
    }
    复制代码

    4.4 配置nginx禁止非法域名解析访问企业网站

    问题:nginx如何防止用户IP访问网站(恶意域名解析,相当于直接使用IP访问网站)

    方法1:直接报错,用户体验不好

    复制代码
    server {
    
    listen 80 default_server;
    
    server_name _;
    
    return 501;
    
    }
    复制代码

    方法2:通过301跳转到主页

    复制代码
    server {
    
    listen 80 default_server;
    
    server_name _;
    
    rewrite ^(.*) http//:blog.etiantian.org/$1 permanent;
    
    }
    复制代码

     

    5.   nginx图片防盗链解决方案。

    简单的说,没有经过你的允许在自己网站嵌入你的图片。

     

      

    5.1 常见的防盗链解决方案的基本原理

    1. 根据HTTP referer实现防盗链
    2. 根据cookie防盗链

     

    5.2 防盗链实战

    被盗链的网站配置

    复制代码
     1 #Preventing hot linking of images and other file types
     2 
     3 location ~* ^.+\.(jpg|png|swf|flv|rar|zip)$ {
     4 
     5     valid_referers none blocked *.etiantian.org etiantian.org;
     6 
     7     if ($invalid_referer) {
     8 
     9         rewrite ^/ http://bbs.etiantian.org/img/nolink.gif;
    10 
    11     }
    12 
    13     root html/www;
    14 
    15 }

    6.   nginx错误页面的优雅显示

    范例:当出现403错误会跳转到403.html页面

    error_page  403  /403.html;

    7.   nginx站点目录文件及目录权限优化

     

     

     

    8.  部署网站程序权限设置

    (1)wordpress站点目录权限设置

    方案1:推荐方案

    目录:755

    文件:644

    所有者:root

    图片及上传目录设置所有者为www

    复制代码
    cd /application/apache/html/
    
    chown -R root.root blog
    
    find ./blog/ -type f|xargs chmod 644
    
    find ./blog/ -type d|xargs chmod 755
    复制代码

    9.   nginx防爬虫优化

    配置

    复制代码
    if  ($http_user_agent   ~*  LWP:Simple|BBBike|wget)  {
    
    return  403 ;
    
    rewrite ^(.*) http://blog.etiantian.org/$1 permanent;
    
    }
    复制代码

     

    10.   利用nginx限制HTTP的请求方法

    配置:

    if ($request_method  !~  ^(GET|HEAD|POST)$ ) {
    
    return  501;
    
    }

    配置上传服务器限制HTTP的GET的配置

    复制代码
    #Only allow these request methods ##
    
    if ($request_method ~*(GET)$ ) {
    
       return 501;
    
    }
    复制代码 

     

    11.   使用CDN做网站内容加速

    cdn特点

      本地Cache加速 提高了企业站点(尤其含有大量图片和静态页面站点)的访问速度,并大大提高以上性质站点的稳定性 

      镜像服务 消除了不同运营商之间互联的瓶颈造成的影响,实现了跨运营商的网络加速,保证不同网络中的用户都能得到良好的访问质量。 

      远程加速 远程访问用户根据DNS负载均衡技术智能自动选择Cache服务器,选择最快的Cache服务器,加快远程访问的速度 

      带宽优化 自动生成服务器的远程Mirror(镜像)cache服务器,远程用户访问时从cache服务器上读取数据,减少远程访问的带宽、分担网络流量、减轻原站点WEB服务器负载等功能。 

      集群抗攻击 广泛分布的CDN节点加上节点之间的智能冗余机制,可以有效地预防黑客入侵以及降低各种D.D.o.S攻击对网站的影响,同时保证较好的服务质量 。

    12.  使用普通用户启动Nginx(监牢模式)

    解决方案 

      给Nginx服务降权,用普通用户跑Nginx服务,给开发及运维设置普通账号 

      开发人员使用普通账号即可管理nginx服务及站点下的程序和日志 

      责任划分:网络问题:运维责任,网站打不开开发责任。(共同承担)

    实战配置:

    复制代码
    useradd inca
    
    cd /home/inca
    
    mkdir conf www log
    
    echo inca >www/index.html
    
    复制代码

    修改配置文件

    error_log /home/inca/log/error.log
    
    pid /home/inca/log/nginx.pid

    13. 控制Nginx并发连接数量

      ngx_http_limit_conn_module这个模块用于限制每个定义key值得连接数,特别是单个TP的连接数。 
      不是所有的连接数都会被计算。一个符合计数要求的连接是整个请求头已经被读取的连接。

      控制Nginx并发连接数量参数的说明 
      1)limit_conn_zone参数: 
        语法:limit_conn_zone key zone=name:size; 
        上下文:http 
        用于设置共享内存区域,key可以是字符串、Nginx自带变量或前两个组合。name为内存区域的名称,size为内存区域的大小。

      2)limit_conn参数 
        语法:limit_conn zone number; 
        上下文:http、server、location 
        用于指定key设置最大连接数。当超时最大连接数时,服务器会返回503报错。

    14. 控制客户端请求Nginx的速率

      ngx_http_limit_req_module模块用于限制每个IP访问每个定义key的请求速率。

      limit_req_zone参数说明如下。 
        语法:limit_req_zone key zone=name:size rate=rate; 
        上下文:http 
        用于设置共享内存区域,key可以是字符串,Nginx自带变量或前两个组合。name为内存区域的名称,size为内存区域的大小,rate为速率,单位为r/s,每秒一个请求。 
      limit_req参数说明如下: 
        语法:limit_req zone=name [burst-number] [nobelay] 
        上下文:http、server、location 
        这里运用了令牌桶原理,burst=num,一个有num快令牌,令牌发完后,多出来的那些请求就会返回503。 
      nodelay默认在不超过burst值得前提下会排队等待处理,如果使用此参数,就会处理完num+1次请求,剩余的请求为超时,返回503。

    再加一个linux的系统内核和内存的优化

    1、关于内核参数的优化:

    net.ipv4.tcp_max_tw_buckets = 6000

    timewait 的数量,默认是180000。

    net.ipv4.ip_local_port_range = 1024 65000

    允许系统打开的端口范围。

    net.ipv4.tcp_tw_recycle = 1

    启用timewait 快速回收。

    net.ipv4.tcp_tw_reuse = 1

    开启重用。允许将TIME-WAIT sockets 重新用于新的TCP 连接。

    net.ipv4.tcp_syncookies = 1

    开启SYN Cookies,当出现SYN 等待队列溢出时,启用cookies 来处理。

    net.core.somaxconn = 262144

    web 应用中listen 函数的backlog 默认会给我们内核参数的net.core.somaxconn 限制到128,而nginx 定义的NGX_LISTEN_BACKLOG 默认为511,所以有必要调整这个值。

    net.core.netdev_max_backlog = 262144

    每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。

    net.ipv4.tcp_max_orphans = 262144

    系统中最多有多少个TCP 套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,孤儿连接将即刻被复位并打印出警告信息。这个限制仅仅是为了防止简单的DoS 攻击,不能过分依靠它或者人为地减小这个值,更应该增加这个值(如果增加了内存之后)。

    net.ipv4.tcp_max_syn_backlog = 262144

    记录的那些尚未收到客户端确认信息的连接请求的最大值。对于有128M 内存的系统而言,缺省值是1024,小内存的系统则是128。

    net.ipv4.tcp_timestamps = 0

    时间戳可以避免序列号的卷绕。一个1Gbps 的链路肯定会遇到以前用过的序列号。时间戳能够让内核接受这种“异常”的数据包。这里需要将其关掉。

    net.ipv4.tcp_synack_retries = 1

    为了打开对端的连接,内核需要发送一个SYN 并附带一个回应前面一个SYN 的ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发送SYN+ACK 包的数量。

    net.ipv4.tcp_syn_retries = 1

    在内核放弃建立连接之前发送SYN 包的数量。

    net.ipv4.tcp_fin_timeout = 1

    如 果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2 状态的时间。对端可以出错并永远不关闭连接,甚至意外当机。缺省值是60 秒。2.2 内核的通常值是180 秒,3你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB 服务器,也有因为大量的死套接字而内存溢出的风险,FIN- WAIT-2 的危险性比FIN-WAIT-1 要小,因为它最多只能吃掉1.5K 内存,但是它们的生存期长些。

    net.ipv4.tcp_keepalive_time = 30

    当keepalive 起用的时候,TCP 发送keepalive 消息的频度。缺省是2 小时。


    2、下面是关于系统连接数的优化

    linux 默认值 open files 和 max user processes 为 1024

    #ulimit -n

    1024

    #ulimit Cu

    1024

    问题描述: 说明 server 只允许同时打开 1024 个文件,处理 1024 个用户进程

    使用ulimit -a 可以查看当前系统的所有限制值,使用ulimit -n 可以查看当前的最大打开文件数。

    新装的linux 默认只有1024 ,当作负载较大的服务器时,很容易遇到error: too many open files 。因此,需要将其改大。

    解决方法:

    使用 ulimit Cn 65535 可即时修改,但重启后就无效了。(注ulimit -SHn 65535 等效 ulimit -n 65535 ,-S 指soft ,-H 指hard)

    有如下三种修改方式:

    1. 在/etc/rc.local 中增加一行 ulimit -SHn 65535
    2. 在/etc/profile 中增加一行 ulimit -SHn 65535
    3. 在/etc/security/limits.conf 最后增加:

    * soft nofile 65535
    * hard nofile 65535
    * soft nproc 65535
    * hard nproc 65535

    具体使用哪种,在 CentOS 中使用第1 种方式无效果,使用第3 种方式有效果,而在Debian 中使用第2 种有效果

    # ulimit -n

    65535

    # ulimit -u

    65535

    备注:ulimit 命令本身就有分软硬设置,加-H 就是硬,加-S 就是软默认显示的是软限制

    soft 限制指的是当前系统生效的设置值。 hard 限制值可以被普通用户降低。但是不能增加。 soft 限制不能设置的比 hard 限制更高。 只有 root 用户才能够增加 hard 限制值。


    展开全文
  • 数据库优化 - 实例优化

    千次阅读 2019-10-25 10:30:00
    就算有也都是基于某个特定数据库的实例优化,本文涵盖目前市面上所有主流数据库的实例优化(Oralce、MySQL、POSTGRES、达梦),按照文章的配置能够将你数据库性能用到80%或以上。 数据库优化方法论 这部分为理论...
  • app开发人员配置【职责】

    千次阅读 2018-08-22 09:16:15
    app开发人员配置组成 作为一个独立APP开发项目的开发项目,人员必须包括:产品经理,程序开发人员(ios开发工程师,Android开发工程师,服务端开发工程师),UI设计,测试工程师,运营团队。各职位要求如下: 1.app...
  • 一个问题请大家思考一下,如果线上的服务器全部掉电后以短信方式通知值班人员,那么线上一台机器的根分区打满,也通过短信来通知是否有必要。 上述的问题在日常工作也屡屡发生,对于问题、异常和故障,我们采取了...
  • Web服务器及性能优化

    万次阅读 2018-08-09 07:03:34
    二、浏览器端,关于浏览器端优化 2.1 压缩源码和图片 2.2 选择合适的图片格式 2.3 合并静态资源 2.4 开启服务器端的Gzip压缩 2.5 使用CDN 2.6 延长静态资源缓存时间 2.7 把CSS放在页面头部,把JavaScript放在页面...
  • 黑帽SEO大牛告诉你:想学网站SEO优化的百种法则你必须了解,这些SEO规则就像是交通指示灯一样,按照这些SEO规则走的,排名肯定会好,不按套路走的,除了快排只有死路一条。本文章句句精炼,不用深入剖析,如果想...
  • 数据库sql优化

    千次阅读 多人点赞 2018-07-06 15:55:30
    网上关于SQL优化的教程很多,但是比较杂乱。近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充。这篇文章我花费了大量的时间查找资料、修改、排版,希望大家阅读之后,感觉好的...
  • SQL优化方案

    万次阅读 2019-05-08 15:49:10
    转载至:http://blog.itpub.net/31555484/viewspace-2565387/ 作者1:惨绿少年 ... 作者2:喜欢拿铁的人 ... 在进行MySQL的优化之前,必须要了解的就是MySQL的查询过程,很多查询优化...
  • iptables高性能前端优化-无压力配置1w+条规则

    万次阅读 多人点赞 2017-08-27 20:36:19
    我本来是想特别感谢一下这个台风的,然而它已经造成了重大的人员伤亡,无论如何,我都不能再对它多说一句褒义的话,我是一个喜欢风雨的人,仅诠释我个人。  在台风“天鸽”将至的那晚,我把一个在暴热和
  • 16.1 规划验证的必要性 16.2 规划验证的方法 16.3 规划验证的实例 参考文献 第3篇 CDMA无线网络优化方法 第17章 优化方法概述 17.1 网络优化目标 17.2 网络优化内容 17.3 网络优化流程 参考文献 第18章 数据...
  • Redis Cluster架构优化

    万次阅读 热门讨论 2015-09-25 15:01:09
    Redis Cluster架构优化在《全面剖析Redis Cluster原理和应用》中,我们已经详细剖析了现阶段Redis Cluster的缺点: 无中心化架构 Gossip消息的开销 不停机升级困难 无法根据统计区分冷热数据 客户端的挑战 Cluster...
  • iOS之性能优化·优化App的启动速度

    万次阅读 2020-11-11 04:16:35
    苹果是一家特别注重用户体验的公司,过去几年一直在优化 App 的启动时间,特别是去年的 WWDC 2019 keynote [1] 上提到,在过去一年苹果开发团队对启动时间提升了 200%; 虽然说是提升了 200%,但是有些问题还是没有...
  • MySQL 性能优化实战

    千次阅读 2018-07-03 09:05:17
    MySQL 知识点及性能优化的知识,其实性能优化的目标是针对后端研发人员及一些资深的前端人员,可能会从如下大的知识点讲解。一、安装说明首先学习数据库,当然是安装软件。千里之行,始于足下,如果...
  • 原标题:DBA的五款最佳SQL查询优化工具,收藏了一般来说,SQL查询优化器分析给定查询...这些工具极大地简化了开发人员和数据库管理员的工作,因为他们提供了正确的查询调优建议和索引建议。现在我们已经知道了SQL查...
  • MyEclipse x.x各版本终极优化配置指南

    千次阅读 2013-07-11 10:48:50
    先说优化:随着myeclipse版本不断更新,其功能不断强大,更加智能及人性化,为开发人员提供了很多便利、提高了开发速度,但是也牺牲了性能,让很多机器配置稍差的开发人员头疼不已。其实我们平时常用的功能只用20%,...
  • Android App性能优化总结

    千次阅读 2020-02-04 23:34:15
    一、稳定 Android 应用的稳定定义很宽泛,影响稳定的原因很多,比如内存使用不合理(内存泄漏、内存溢出)、代码异常场景考虑不周全、代码逻辑不合理等,都会对应用的稳定造成影响。其中代码逻辑导致的Crash情况...
  • 金蝶K/3产品性能稳定性优化指导手册  2011-08-15 11:43:05| 分类: ERP应用|字号 订阅  金蝶K/3产品性能稳定性优化指导手册(常见问题)(V3.0) ?金蝶软件(中国)有限公司研发中心K/3产品...
  • MySQL数据库访问性能优化

    万次阅读 多人点赞 2018-03-01 09:07:50
    MYSQL应该是最流行的WEB后端数据库。... MYSQL如此方便和稳定,以至于我们在开发 WEB 程序...即使想到优化也是程序级别的,比方不要写过于消耗资源的SQL语句。可是除此之外,在整个系统上仍然有非常多能够优化的地方。...
  • 上一篇文章主要介绍了HBase读性能优化的基本套路,本篇文章来说道说道如何诊断HBase写数据的异常问题以及优化写性能。和读相比,HBase写数据流程倒是显得很简单:数据先顺序写入HLog,再写入对应的缓存Memstore,当...
  • 开源软件修改的必要性

    千次阅读 2017-02-07 09:48:32
    使用开源软件了解其内部的机制和原理是有必要的,但是成为一名不在开发组名单中的开发人员必要吗?而且,你所谓的性能优化或者功能添加怎么不知道是别人在深思熟虑后切割的bug或者鸡肋呢? 伴随着技
  • Android应用开发性能优化完全分析

    万次阅读 多人点赞 2015-10-11 02:20:58
    不想写的原因是随便上网一搜一堆关于性能的建议,感觉大家你一总结、我一总结的都说到了很多优化注意事项,但是看过这些文章后大多数存在一个问题就是只给出啥啥啥不能用,啥啥啥该咋用等,却很少有较为系统的进行...
  • 数据库优化方案整理

    万次阅读 多人点赞 2018-08-29 16:05:16
    一:优化说明 A:有数据表明,用户可以承受的最大等待时间为8秒。数据库优化策略有很多,设计初期,建立好的数据结构对于后期性能优化至关重要。因为数据库结构是系统的基石,基础打不好,使用各种优化策略,也不能...
  • 数据库性能优化经验总结

    千次阅读 2018-08-20 13:18:58
    要正确的优化SQL,我们需要快速定位能的瓶颈点,也就是说快速找到我们SQL主要的开销在哪里?而大多数情况性能最慢的设备会是瓶颈点,如下载时网络速度可能会是瓶颈点,本地复制文件时硬盘可能会是瓶颈点,为什么...
  • 必要的系统调整 4. 手动优化 5. 使用工具优化与清理6. 常规软件安装7. 封装8. 测试效果 Windows 10更新的太频繁了,WIN10 1809 第二版还没用舒服,第三版都已经出来了,追新版永远追不完,教程还是按第二版来...
  • 数据库SQL优化大总结之 百万级数据库优化方案

    万次阅读 多人点赞 2016-06-23 09:43:50
    网上关于SQL优化的教程很多,但是比较杂乱。近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充。 这篇文章我花费了大量的时间查找资料、修改、排版,希望大家阅读之后,感觉好的...
  • 前端性能优化方法总结

    万次阅读 多人点赞 2018-06-29 10:51:49
    前端优化是复杂的,针对方方面面的资源都有不同的方式。那么,前端优化的目的是什么 ? 1. 从用户角度而言,优化能够让页面加载得更快、对用户的操作响应得更及时,能够给用户提供更为友好的体验。 2. 从服务商角度...
  • DBA的五款优秀SQL查询优化工具

    千次阅读 2019-07-30 11:00:33
    一般来说,SQL查询优化器分析给定查询的许多选项,预估每个选项的成本,最后选择成本最低的选项。如果查询优化器选择了错误的计划,则性能差异可能从几毫秒到几分钟。幸运的是,现在有许多第...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 69,280
精华内容 27,712
关键字:

优化人员配置必要性