精华内容
下载资源
问答
  • 2021-06-30 21:21:03

    优化算法综述

    依据分类具体算法
    1全局优化 遗传算法(GA)、帝国竞争算法(ICA)、 粒子群优化(PSO)
    局部优化 模拟退火(SA)、贪婪算法(Greedy)、 邻域搜索(NS)
    2精确算法线性规划(LP)、分支定界法(BB)
    模拟进化算法v
    群体仿生类算法、又称为群体智能优化算法GA、PSO
    数学规划方法: 动态规划(DP)、线性规划、整数规划、 混合整数规划、 分支定界法、 割平面法
    v
    v
    启发式算法(Heuristic Algorithms)v
    元启发式算法(Meta-Heuristic Algorithms)v

    数学规划法

    数学规划法:通常将多目标问题转化为单目标问题来解决。

    精确算法(exact algorithm)

    精确算法:通常将待解决的优化问题转换为数学规划问题,进行精确求解。如:分支定界法BB)。

    • 优点:在问题规模较小时,精确算法能在合理的时间内找到问题的最优解
    • 缺点:但当问题规模较大时(是NP-Hard问题),精确算法的计算复杂度高,求解时间呈指数级增长,不能容忍(指数爆炸)。

    启发式 VS. 元启发式

    问题描述:现实中很多问题的优化都可以建模为基于序列的组合优化,如旅行商问题(TSP)、排产问题、各类资源分配问题等。寻找最优序列的问题是NP难问题(NP-Hard问题)(其解空间为n!)。

    解决这类问题常用的方法有两种:

    • (1)一种方法是启发式算法,启发式算法是基于问题本身的规则得到较好的可行解,本质是贪心算法(贪婪算法,greedy)。这种方法速度较快,但因与问题本身联系紧密(problem specific, problem dependent),导致其通用性较差。
    • (2)另一种方法是元启发式算法,例如遗传算法、禁忌搜索算法、蚁群算法、模拟退火算法等都是元启发式算法。这类方法从生物进化、物理、化学等过程中受到启发,得到一种解空间的搜索策略,因其搜索策略独立于问题本身(problem independent),因此通用性强。元启发式这类算法有两个最本质的操作:①选择操作(从当前种群中选出优秀的个体,选择的策略有精英保留、轮盘赌、锦标赛等);②改变操作(如交叉、变异、灾变操作等,它们本质上都是从一个可行解改变为另一个可行解,用来扩大搜索范围,避免陷入局部最优)。(详见:元启发式算法常用操作详解:https://bbs.huaweicloud.com/blogs/195717)

    启发式算法

    启发式策略(heuristic)是一类在求解某个具体问题时,在可以接受的时间和空间内能给出其可行解(或近优解),但又不保证求得最优解(以及可行解与最优解的偏离)的策略的总称。
    许多启发式算法是相当特殊的,它依赖于某个特定问题。启发式策略在一个寻求最优解的过程中能够根据个体或者全局的经验来改变其搜索路径,当寻求问题的最优解变得不可能或者很难完成时(e.g. NP-C问题),启发式策略就是一个高效的获得可行解(即近优解)的办法。

    启发式算法包括:包括构造型方法、局部搜索算法、松弛方法、解空间缩减算法、贪婪策略、随机化贪婪策略、近邻策略、最大饱和度策略等。

    元启发式算法

    元启发式策略(Meta-heuristic)则不同,元启发式策略通常是一个通用的启发式策略,它们通常不借助于某种问题的特有条件,从而能够运用于更广泛的方面元启发式是启发式策略的增强改进版,它是随机算法与局部搜索算法相结合的产物。“元”可以理解为一个哲学概念,大概是“事物的本原”。

    元启发式策略通常会对搜索过程提出一些要求,然后按照这些要求实现的启发式算法便被称为元启发式算法。许多元启发式算法都从自然界的一些随机现象取得灵感(e.g. 模拟退火、遗传算法、粒子群算法)。现在元启发式算法的重要研究方向在于防止搜索过早得陷入局部最优,已经有很多人做了相应的工作,例如禁忌搜索(tabu)和非改进转移(模拟退火)。

    元启发式算法是相对于最优化算法提出来的,一个问题的最优化算法可以求得该问题的最优解,而元启发式算法是一个基于直观或经验构造的算法,它可以在可接受的花费(指计算时间和空间)下给出问题的一个可行解(近优解),并且该可行解与最优解的偏离程度不一定可以事先预计。

    元启发式算法(Meta-heuristic)是基于计算智能的机制求解复杂优化问题最优解满意解(近优解)的方法,有时也被称为智能优化算法(Intelligent optimization algorithm),智能优化通过对生物、物理、化学、社会、艺术等系统或领域中相关行为、功能、经验、规则、作用机理的认识,揭示优化算法的设计原理,在特定问题特征的引导下提炼相应的特征模型,设计智能化的迭代搜索型优化算法。

    常见的Meta-Heuristic Algorithms(有基于个体基于群体两类):
    一、基于个体(Single solution-based heuristics)
    1、模拟退火(Simulated Annealing,SA)
    2、禁忌搜索(Tabu Search,TS)
    3、变邻域搜索(Variable Neighborhood Search)
    4、自适应大规模领域搜索(Adaptive Large Neighborhood Search)
    二、基于群体(Population-based heuristics)
    1、遗传算法(Genetic Algorithm,GA)
    2、蚁群优化算法(Ant Colony Optimization,ACO)
    3、粒子群优化算法(Particle Swarm Optimization,PSO)
    4、差分进化算法(Differential Evolution, DE)
    4、人工蜂群算法(ABC)、人工鱼群算法、狼群算法等
    5、人工神经网络算法(ANN)

    另外还有:免疫算法、蛙跳算法、帝国竞争算法(Imperialist Competitive Algorithm,ICA)、和声搜索算法、分布估计算法、Memetic算法、文化算法、灰狼优化算法、人工免疫算法、进化规划、进化策略、候鸟优化算法、布谷鸟搜索算法、花朵授粉算法、引力搜索算法、化学反应优化算法、头脑风暴优化算法(Brain Storm Optimization Algorithm,BSO)等等。

    附:元启发式算法时间表(部分)
    在这里插入图片描述

    What is the difference between heuristics and meta-heuristics?

    • 启发式算法和元启发式算法,一般都是用来解决NP-H问题,目的是求得组合优化问题的近优解可行解满意解),但不一定是最优解。【它们和最优化算法相对应,最优化算法是用来求得问题的最优解的。】
    • 启发式算法是 problem dependent(依赖于问题的,与问题有关)或者叫 problem specific(面向特定问题的,与特定问题有关),故适应面窄; 而元启发式算法是problem independent(与问题无关的),元启发法是与问题无关的技术,可以应用于广泛的问题适用范围广
    • 启发式方法利用问题相关信息来找到特定问题的"足够好"的解决方案,根据给定问题制定启发式规则,故与该问题特性有关;而元启发式方法则像设计模式一样,是一般的算法思想,可以应用于各种各样的问题,与问题无关
    • 复杂性方面,启发式算法简单,就是用简单策略(如:贪婪策略、随机化贪婪策略、近邻策略、最大饱和度策略)构造可行集的过程。而元启发式则是一种高层次的多个算法模块的聚合算法。

    有段英文可以读读,理解一下:
    A locally optimal solution is better than all neighbouring solutions. A globally optimal solution is better than all solutions in the search space. A neighbourhood is a set of solutions that can be reached from the current solution by a simple operator. A neighbourhood is a subset of the search space. (局部最优解、全局最优解与邻域的概念)
    A heuristic is a rule of thumb method derived from human intuitions. For example, we can use the nearest neighbour heuristic to solve the TSP problem and use the maximal saturation degree heuristic to solve the graph colouring problem.(其次,我们可以利用启发式算法搜索并得到一个局部最优解(不保证是全局最优解))
    Meta-heuristics are methods that orchestrate an interaction between local improvement procedures and higher-level strategies to create a process capable of escaping from the local optima and performing a robust search in the solution space. Single-point meta-heuristics include Simulated Annealing, Tabu Search, and Variable Neighbourhood Search. Population-based meta-heuristics include Genetic Algorithm, Ant Colony Optimization, and Particle Swarm Optimization.(最后,我们可以利用元启发式算法更好地探索解空间,从而避免算法陷入局部最优)

    多目标智能优化算法

    多目标进化算法(Multi-Objective Evolutionary Algorithm,MOEA),如:增强多目标灰狼优化算法、多目标粒子群优化算法多目标遗传算法NSGA-Ⅱ算法(即带有精英保留策略的快速非支配多目标优化算法,是一种基于Pareto最优解的多目标优化算法,是多目标遗传算法的一种)。

    多目标优化算法,它主要针对同时优化多个目标(两个及两个以上)的优化问题,这方面比较经典的算法有NSGAII算法、MOEA/D算法以及人工免疫算法等。这部分内容的介绍已经在博客《[Evolutionary Algorithm] 进化算法简介》进行了概要式的介绍,有兴趣的博友可以进行参考(2015.12.13)-Poll的笔记。

    模拟进化算法与传统的精确算法(确定性算法)的区别

    1. 模拟进化算法的作用对象是由多个可行解组成的集合(种群),而非单个可行解
    2. 模拟进化算法只利用目标函数(或适应度函数)的适应值信息(fitness value),而无需使用梯度等其它辅助信息;
    3. 模拟进化算法利用概率机制非确定性迭代过程描述。
    4. 正是这些有别于确定型方法的特征,决定了模拟进化算法应用的广泛性、描述的简单性、本质上的并行性及良好的鲁棒性。

    优化算法分类

    1. 启发式优化算法:一个基于直观或经验构造的算法,在可接受的花费(指计算时间和空间)下给出待解决组合优化问题每一个实例的一个可行解,该可行解与最优解的偏离程度不一定事先可以预计.意思就是说,启发式算法是根据经验或者某些规则来解决问题,它求得的问题的解不一定是最优解,很有可能是近似解(或近优解)。这个解与最优解近似到什么程度,是不能确定的。相对于启发式算法,最优化算法或者精确算法(比如说分支定界法、动态规划法等则能求得最优解)。而元启发式优化算法(Meta-heuristic Algo.)是启发式算法中比较通用的一种高级一点的算法,主要有遗传算法、禁忌搜索算法、模拟退火算法、蚁群算法、粒子群算法、变邻域搜索算法、人工神经网络、人工免疫算法、差分进化算法等。这些算法可以在合理的计算资源条件下给出较高质量的解(最优解或近优解)。
    2. 仿生算法:基于仿生学的算法,是一类模拟自然生物进化或者群体社会行为的随机搜索方法的统称.由于这些算法求解时不依赖于梯度信息,故其应用范围较广,特别适用于传统方法难以解决的大规模复杂优化问题.主要有:遗传算法、人工神经网络、蚁群算法、蛙跳算法、粒子群优化算法等.这些算法均是模仿生物进化、神经网络系统、蚂蚁寻路、鸟群觅食等生物行为,故叫仿生算法
    3. 智能计算:也称为计算智能,群体智能算法。包括遗传算法、模拟退火算法、禁忌搜索算法、进化算法、蚁群算法、人工鱼群算法,粒子群算法、混合智能算法、免疫算法、神经网络、机器学习、生物计算、DNA计算、量子计算、模糊逻辑、模式识别、知识发现、数据挖掘等.智能计算是以数据为基础,通过训练建立联系,然后进行问题求解.

    所以说,你接触的很多算法,既是仿生算法,又是启发式算法,又是智能算法,这都对!只是分类方法不同而已。

    算法介绍

    帝国竞争算法(Imperialist Competitive Algorithm,ICA)

    帝国竞争算法(ICA)是Atashpaz-Gargari和Lucas于2007年提出的一种基于帝国主义殖民竞争机制的进化算法,属于社会启发的随机优化搜索方法。目前,ICA已被成功应用于多种优化问题中,如调度问题、分类问题和机械设计问题等。

    帝国主义竞争算法,借鉴了人类历史上政治社会殖民阶段帝国主义国家之间的竞争、占领、吞并殖民殖民地国家从而成为帝国国家的演化,是一种全局性的优化算法。该算法把所有初始化的个体都称作国家,按照国家势力分成帝国主义国家及殖民地两种,前者优势大于后者。

    其实,从另一个角度来看,ICA可以被认为是遗传算法(GA)的社会对应物。ICA是基于人类社会进化的过程,而GA是基于物种的生物进化过程。二者其实有异曲同工之妙。

    不过话说回来,大多数群体仿生类算法都有异曲同工之妙。

    分支定界法(Branch and Bound, BB)

    For instance, since the space of possible solutions is still too vast, a branch and bound type algorithm is proposed to further decimate the number of potential solutions to evaluate.
    例如,由于可行解的参数空间很大,一种分支限界算法被用来减少需要考察的可行解的数目。

    Branch and bound (BB or B&B) is an algorithm design paradigm for discrete and combinatorial optimization problems, as well as general real valued problems. A branch-and-bound algorithm consists of a systematic enumeration of candidate solutions by means of state space search: the set of candidate solutions is thought of as forming a rooted tree with the full set at the root. The algorithm explores branches of this tree, which represent subsets of the solution set. Before enumerating the candidate solutions of a branch, the branch is checked against upper and lower estimated bounds on the optimal solution, and is discarded if it cannot produce a better solution than the best one found so far by the algorithm.

    NSGA-Ⅱ算法

    NSGA(Non-dominated Sorting Genetic Algorithm,非支配排序遗传算法)、NSGA-II(带精英策略的快速非支配排序遗传算法),都是基于遗传算法的多目标优化算法,是基于pareto最优解讨论的多目标优化。

    NSGA-Ⅱ算法,即带有精英保留策略的快速非支配多目标优化算法,是一种基于Pareto最优解的多目标优化算法。
    NSGA-Ⅱ算法是进化算法中的一种,进化算法是在遗传算法的基础上改进而来的,所以,你得先弄懂遗传算法是什么。

    NSGA-Ⅱ是最流行的多目标遗传算法之一,它降低了非劣排序遗传算法的复杂性,具有运行速度快,解集的收敛性好的优点,成为其他多目标优化算法性能的基准。

    NSGA-Ⅱ算法是 Srinivas 和 Deb 于 2000 年在 NSGA 的基础上提出的,它比 NSGA算法更加优越:它采用了快速非支配排序算法,计算复杂度比 NSGA 大大的降低;采用了拥挤度和拥挤度比较算子,代替了需要指定的共享半径 shareQ,并在快速排序后的同级比较中作为胜出标准,使准 Pareto 域中的个体能扩展到整个 Pareto 域,并均匀分布,保持了种群的多样性;引入了精英策略,扩大了采样空间,防止最佳个体的丢失,提高了算法的运算速度和鲁棒性。

    NSGA-Ⅱ就是在第一代非支配排序遗传算法的基础上改进而来,其改进主要是针对如上所述的三个方面:

    1. 提出了快速非支配排序算法,一方面降低了计算的复杂度,另一方面它将父代种群跟子代种群进行合并,使得下一代的种群从双倍的空间中进行选取,从而保留了最为优秀的所有个体;
    2. 引进精英策略,保证某些优良的种群个体在进化过程中不会被丢弃,从而提高了优化结果的精度;
    3. 采用拥挤度和拥挤度比较算子,不但克服了NSGA中需要人为指定共享参数的缺陷,而且将其作为种群中个体间的比较标准,使得准Pareto域中的个体能均匀地扩展到整个Pareto域,保证了种群的多样性。

    这个算法是本人接触科研学习实现的第一个算法,因此想在这里和大家分享一下心得。 讲解的很详细,读个大概,有个思路和印象即可,不必深究。
    site:https://blog.csdn.net/qq_40434430/article/details/82876572/

    遗传算法(Genetic Algorithm, GA)

    遗传算法(Genetic Algorithm,简称GA)是一种最基本的进化算法,它是模拟达尔文生物进化理论的一种优化模型,最早由J.Holland教授于1975年提出。

    遗传算法中种群分每个个体都是解空间上的一个可行解,通过模拟生物的进化过程,从而在解空间内搜索最优解。

    禁忌搜索算法(Tabu Search,TS)

    遗传算法是具有全局搜索能力的算法,但是传统遗传算法求解调度问题并不是很成功,主要原因在于它的局部搜索能力较差,且容易早熟收敛。而禁忌搜索(Tabu Search, TS)是一种优秀的局部搜索算法。因此,可以结合遗传算法(GA)和禁忌搜索算法(TS)两者的优点,将“适者生存”的遗传准则嵌入到多起点的禁忌搜索算法中,构成混合遗传禁忌搜索算法(GATS)。

    由于遗传算法和禁忌搜索算法具有互补的特性,因此混合遗传禁忌搜索算法GATS)在性能上能够超越它们单独使用时的性能。

    文化基因算法(Memetic Algorithm,MA)

    文化基因(Meme)的概念是由Hawkins于1976年提出的,Pablo Moscato于1989年提出了Memetic Algorithm。Memetic Algorithm,是基于群体的计算智能方法与局部搜索相结合的一类算法的总称。文化基因算法的简单介绍。

    在这里插入图片描述

    文化基因算法(Memetic Algorithm,简称MA),也被称为是“密母算法”(Meme),它是由Moscato在1989年提出的。文化基因算法MA是一种基于种群的全局搜索和基于个体的局部启发式搜索的结合体,它的本质可以理解为:

    MA= GA + Local Search

    即MA算法实质上是为遗传算法(全局搜索算法)加上一个局部搜索(Local Search)算子。局部搜索算子可以根据不同的策略进行设计,比如常用的爬山机制、模拟退火、贪婪机制、禁忌搜索等。

    在这里插入图片描述
    在这里插入图片描述
    Pablo Moscato认为:在遗传算法(GA)中,变异操作可以看作是含有一定噪声的爬山搜索,实际上模拟遗传编码和自然选择的过程不应包含变异操作,因为在文化进化的过程中,在众多的随机变化步骤中得到一个正确的、可提高整体性能的一步进展是非常困难的,只有此领域的拥有足够的专业知识的精通者们,才有可能创造新的进展,并且这样的事情发生的频率是很低的。 因此,文化基因的传播过程应是严格复制的,若要发生变异,那么每一步的变异都需要有大量的专业知识支撑,而每一步的变异都应带来进展而不是混乱,这就是为什么我们观察到的文化进化速度要比生物进化速度快得多的原因。 对应于模拟生物进化过程的遗传算法,Moscato提出了模拟文化进化过程的文化基因算法,文化基因算法用局部启发式搜索来模拟由大量专业知识支撑的变异过程。因此说,文化基因算法是一种基于种群的全局搜索和基于个体的局部启发式搜索的结合体

    文化基因算法的这种全局搜索和局部搜索的结合机制,使其搜索效率在某些问题领域比传统遗传算法快几个数量级,可应用于广泛的问题领域并得到满意的结果。 很多人将文化基因算法看作混合遗传算法、 遗传局部搜索或是拉马克式进化算法。实际上,文化基因算法提出的只是一种框架、 是一个概念在这个框架下,采用不同的搜索策略可以构成不同的文化基因算法。如全局搜索策略可以采用遗传算法、 进化策略、 进化规划等,局部搜索策略可以采用爬山搜索、模拟退火、贪婪算法、禁忌搜索、导引式局部搜索等。 这种全局与局部的混合搜索机制显然要比单纯在普通个体间搜索的进化效率高得多

    机器学习中的最优化模型

    我们每个人都会在我们的生活或者工作中遇到各种各样的最优化问题,比如每个企业和个人都要考虑的一个问题“在一定成本下,如何使利润最大化”等。最优化方法是一种数学方法,它是研究在给定约束之下如何寻求某些因素(的量),以使某一(或某些)指标达到最优的一些学科的总称。随着学习的深入,博主越来越发现最优化方法的重要性,学习和工作中遇到的大多问题都可以建模成一种最优化模型进行求解,比如我们现在学习的机器学习算法,大部分的机器学习算法的本质都是建立优化模型,通过最优化方法对目标函数(或损失函数)进行优化,从而训练出最好的模型。常见的最优化方法有梯度下降法牛顿法和拟牛顿法共轭梯度法最速下降法等等。

    梯度下降法(Gradient Descent)

    牛顿法和拟牛顿法(Newton’s method & Quasi-Newton Methods)

    共轭梯度法(Conjugate Gradient)

    拉格朗日乘数法(Lagrange Multiplier Method)

    详见:[Math & Algorithm] 拉格朗日乘数法

    更多相关内容
  • MySQL版SQL优化

    千人学习 2019-07-06 23:26:29
    本课程通过Centos版的MySQL讲解了SQL优化的一些常见手段。 其中讲解了MySQL的分层、存储引擎等底层逻辑,并讲解了常见的索引优化手段。在讲解时,先通过理论对先关的优化知识进行了铺垫,然后使用实际的案例详细的...
  • matlab环境下使用PSO算法对SVM多分类器的参数进行优化的案例,代码有详细的注释,另有一篇博客对算法的大致过程有介绍.
  • 用粒子群算法优化支持向量机的matlab程序,用于对股价、经济的预测作用,优化后预测精确
  • 比较新的优化算法,用差分进化(DE)改进原始的灰狼优化(GWO)得到的HGWO(DE-GWO)算法,以优化SVR参数为例,matlab源码有详细中文注释,便于使用,可以根据需要自己修改,是很好地学习材料。
  • oracle数据库的性能优化直接关系到系统的运行效率,而影响数据库性能的一个重要因素就是sql性能问题。本书是作者十年磨一剑的成果之一,深入分析与解剖oracle sql优化与调优技术,主要内容包括: 第一篇“执行计划...
  • 深入浅出MySQL数据库开发、优化与管理维护

    千次下载 热门讨论 2014-01-21 15:48:01
     18.1.4 确定问题并采取相应的优化措施   18.2 索引问题   18.2.1 索引的存储分类   18.2.2 MySQL如何使用索引   18.2.3 查看索引使用情况   18.3 两个简单实用的优化方法   18.3.1 定期分析表...
  • MySQL优化

    千次阅读 2022-03-18 20:28:08
    MySQL优化方向: 在设计上:字段类型,存储引擎,范式 在功能上:索引,缓存,分库分表 在架构上:集群,主从复制,负载均衡,读写分离 SQL优化 1.插入优化 大量数据采用批量插入形式 事务设置手动提交,...

    MySQL优化方向:

    在设计上:字段类型,存储引擎,范式

    在功能上:索引,缓存,分库分表

    在架构上:集群,主从复制,负载均衡,读写分离

    SQL优化

    1.插入优化

    • 大量数据采用批量插入形式
    • 事务设置手动提交,MySQL默认是自动提交,意味着每写一个SQL事务就自动提交,可能会频繁的涉及事务开始和提交,所以建议手动提交

    2.order by优化

    • Using filesort:通过表的索引或者全表扫描,读取到满足条件的数据行,然后在排序缓冲区 sort buffer 中完成排序,所以返回的数据不是通过索引直接返回的,这样的排序形式就叫filesort
    • Using index:通过有序索引顺序扫描直接返回有序数据,这种情况不需要额外的排序,所以效率比较高
    • 根据多字段排序时,遵循左前缀原则

    3.group by优化

    group by进行分组

    在分组操作时,可以通过索引来提高效率,索引使用也要满足左前缀原则

    4.limit优化

    limit用来限制检索数据范围,一般用于分页功能

    比如:检索数据为limit 2000000,10。此时需要MySQL排序前2000010记录仅仅返回2000000-2000010间的数据,其他数据丢弃,查询排序的代价比太大

    优化思路:一般分页查询时,通过创建覆盖索引能够比较好的提高性能,可以通过创建覆盖索引加子查询的形式进行优化

    select * from student s,(select SID from student order by SID limit 2000000,10) s1
    where s.SID=s1.SID;

    覆盖索引:

    查询列要被所有的索引覆盖到,就是select的数据列只用从索引(只查询一个索引树)中就能够获取到

    select SID from student where Sname="18";//覆盖索引
    select Ssex from student where Sname="18";//非覆盖索引

    5.count优化

    myisam存储引擎是把表的总行数存储在磁盘上,因此count(*)直接获取结果,效率比较高

    innodb存储引擎执行count(*),需要将数据一行一行读取出来,然后累计结果,效率较低

    count()的几种用法:

    count(*),count(1),count(主键),count(字段)

    • count(*):innodb引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加
    • count(1):innodb引擎遍历整张表,但不取值,服务层对于返回的每一行,放一个数字“1”进去,直接按行进行累加
    • count(主键):innodb引擎遍历整张表,把每一行的主键都取出来,返回给服务层,服务层拿到主键直接按行进行累加(主键不可能为空)
    • count(字段):没有not null约束,innodb引擎遍历整张表,把每一行的字段都取出来,判断是否为null,若不为null,按行进行累加

    扩展:explain 中Extra字段说明

    using index:SQL所需要的返回值所有列数据均在一颗索引树上,即无需访问实际的行记录

    using where:SQL使用了where过滤条件

    注:使用了where条件的SQL,并不代表不需要优化,需要进一步判断explain执行计划中type的类型,如果type为all,表示进行了全表扫描,则需要优化,否则不需要优化

    using index condition:说明检索确实命中索引,但不是所有的列都在索引树上,还有需要访问实际的行记录,这个SQL语句性能也很高,但不如using index

    using filesort:得到的所有的结果集,需要进行文件排序,SQL语句性能很差,需要优化

    索引优化

    explain分析查询SQL

    索引设计需要遵循的原则:

    1. 给区分度比较高的字段创建索引,比如:学号,身份证号
    2. 给经常需要排序、分组和多表联合操作的字段创建索引
    3. 给经常作为查询条件的字段创建索引
    4. 索引的数据不宜过多
    5. 对于多列索引,优先指定最左侧的列
    6. 删除不使用或者很少使用的索引
    7. 索引失效的场景:not in,like“%li”,<>等

    大表拆分优化

    分库分表:

    分库:将以前存在于一个数据库实例中的数据拆分成多个数据库实例,部署在不同的服务器上

    分表:将以前存在于一张表上的数据拆分成多个表

    分库是为了解决服务器资源受单机限制,顶不住高并发的问题,把请求分发到多台服务器降低服务器的压力

    分表是为了解决单张表数据量过大,查询效率慢的问题

    如何分库

    分库一般按照业务划分,比如订单库,用户库

     分库会带来问题:

    事务问题:无法保证事务完整性,采用分布式事务

    连接join问题:跨库无法join:1.在业务代码上进行关联 2.适当冗余字段

    如何分表

    垂直分表:把一些不常用的大字段剥离出去

    水平分表:则是因为一张表内的数据太多了,上文提到数据越多B+树就越高,访问的性能越差,所以进行水平拆分

    分表带来问题:

    排序、count、分页问题:在业务代码上将数据进行排序、count、分页处理

    路由问题:分表路由问题:hash路由、范围路由

    集群架构主从复制

    通常采用数据库集群方案来解决高并发问题,也满足高可用,在多个数据库上一旦一个数据库宕机后,可以将请求分发到其他服务器上,可以持续提供服务

    MySQL通过主从复制来实现读写分离,负载均衡等功能

     binlog二进制日志

    undo log 、redo log

    MySQL集群中主从复制通过binlog将数据从主库同步到从库

    binlog默认是不开启的

    通过命令查看

    show variables like "log_%";

     开启binlog日志,通过修改配置文件,MySQL server启动时会自动加载配置文件,Windows下my.ini文件,Linux下是my.conf文件,在打开的文件中在[mysql]上添加配置,保存并重启MySQL server服务器,默认就采用给定的日志文件

    binglog_format=ROW

    配置文件写完需要重启服务器:systrmctl restart mysql

    通过show variables like "log_%"命令,如果log_bin为ON则表示开启二进制日志

    主从复制原理

     MySQL主从复制需要三个线程,master(log dump thread),slave(I/O thread,SQL thread)

    master

    • log dump thread:当主库中有数据更新时,主库就会根据按照设置的binlog格式,将此次更新的事件类型写入到主库的binlog文件中,此时主库会创建log dump线程通知slave有数据更新,当I/O线程请求日志内容时,会将此时的binlog名称和当前更新的位置同时传给slave的I/O线程

    slave

    • I/O线程:该线程会连接到master,向log dump线程请求一份指定binlog文件位置的副本,并将请求回来的binlog存到本地的relay log中,relay log 和binlog日志一样是记录了数据更新的事件,他也是按照递增后缀名的方式,产生多个relay log(host_name_relay_bin.000001)文件,slave会使用一个index文件(host_name_relay_bin.index)来追踪当前正在使用的relay log文件。
    • SQL线程:该线程检测到relay log有更新后,会读取并在本地做redo操作,把发生在主库的事件在本地重新执行一遍,来保证主从数据同步

    此外,如果有一个relay log文件中的全部事件都执行完毕,那么SQL线程会自动将该relay log文件删除掉

    集群成熟方案

    数据库中间件:常用的有MySQL Proxy、MyCat以及ShardingSphere等等

    • MySQL Proxy:是官方提供的MySQL中间件产品可以实现负载均衡,读写分离等,是一个基于服务器端的代理
    • MyCat:是一款基于阿里开源产品Cobar而研发的,基于java语言编写的开源数据库插件
    • ShardingSphere:是一套开源的分布式数据库中间件解决方案,它由ShardingJDBC、Sharding-Proxy、Sharding-Sidecar(计划中)这三款相互独立的产品组成

    展开全文
  • 其实不仅是启动优化,整个性能优化领域都是围绕着“攻”和“守”来展开的,“攻”即为前述的分析与优化,而“守”则是防止劣化,在防劣化方面大家往往不会像优化的方面那么重视,但实际上能防止劣化是可持续取得优化...

    动手点关注 干货不迷路 👆

    启动性能是 APP 使用体验的门面,启动过程耗时较长很可能导致用户使用 APP 的兴趣骤减,抖音通过对启动性能做劣化的 AB 实验也验证了其对于业务指标有影响显著。抖音拥有数亿的用户,启动耗时几百毫秒的增长就可能带来成千上万用户的留存缩减,因此,启动性能的优化成为了抖音 Android 基础技术团队在体验优化方向上的重中之重。

    本文基于过往对抖音 Android 客户端做启动性能优化的实战经验总结提炼出普适性的方法论,并将该过程中沉淀的工具加以分享,希望能给大家带来一些新的思考。

    抖音 Android 性能优化系列往期文章回顾:新一代全能型性能分析工具 Rhea

    带着问题出发

    4b38baa002d812fbdf3cb17b7b574f6b.png

    假如你要负责优化抖音的启动性能,你会怎样去规划整体的优化方案?你可能会一下子想到很多方面的细节点,比如:要优化主线程耗时、要减少布局层级、要对某些启动任务做按需加载或预加载、要避免主线程 IO、要对线程使用进行优化、还要有分析工具帮助定位性能问题等……

    然而,该如何系统性地把这些细碎点组织起来并按照一定的章法来落地启动优化呢?此时,需要我们在具体细节点之上有进一步的问题分解与深入思考,最终形成一套完整的方法论,不仅能覆盖所有细节点,还能切实指导在实战中达成启动优化的效果。切实有效的方法论必然是从实战中经过千锤百炼才能形成的,而抖音庞大的用户基数又进一步保障了方法论的可行性与普适性。那么接下来让我们带着前述问题来看抖音的启动优化方法论是怎样的又是如何应用于实战之中的。

    启动优化方法论

    抖音的启动性能优化方法论分为五部分,分别是:理论分析、现状分析、启动性能优化、线上验证与防劣化。

    abcf33a4d64fc3868df13faee1d1835a.png

    这五部分间存在明显的先后顺序,又能闭环达成可持续的启动性能优化,下面将对这五部分做详细阐述:

    理论分析

    理论分析放在最先是为了从一开始就避免让视野受到限制,很多同学往往一开始接手启动优化就容易陷入对各种现状细节的分析,拘泥于片面的潜在可优化点,这样就难以做到对全局和优先级的把控,所以,我们应该首先跳出现状,从更加全局的视角来思考整体优化的目标和策略。这里可以利用特斯拉创始人——埃隆·马斯克所推崇的“第一性原理”思考法:

    “通过第一性原理,把事情升华到最根本的真理,然后从最核心处开始推理。”

    基于此,我们在做启动优化的理论分析时可以从更本源的角度出发做到全局思考,比如抖音会做从进程创建到页面展示的全启动路径分阶段耗时分析、还会按照消耗的系统资源类型做耗时成因分析,通过这种极致的耗时分析可以带来极致的优化策略,此外,从全路径出发还能够发现容易忽视的问题、探索优化的极限。

    现状分析

    在完成理论分析后,我们基本具备了全局的视角,并且也大致清楚了整体的优化目标和策略,接下来就要基于此来做现状分析从而明晰实现目标的具体路径:

    • 首先使用 profile 工具对可优化点进行摸底:其实不合理的高耗时点就是潜在的优化点,并能按照前述的理论分析归入一个或多个耗时成因中;

    • 然后结合线上的指标数据确定最终优化方向:线下摸底的潜在优化点要结合其线上打点确认是否为普遍耗时,再根据耗时成因明确大致的优化思路、实施成本和预估收益。

    在这部分需要尤其注意三点:优质的 profile 工具(这里推荐使用同样来自基础技术团队的“新一代全能型性能分析工具”)、线下 trace 结合线上监控综合分析根据投入产出比评估实施优先级,这三点是保障切实有效取得启动优化收益的关键。

    启动优化

    在完成了理论和现状分析后,就可以根据规划的路径来实施具体的启动优化项了。在实施过程中,主要考虑主线程优化、后台线程优化和全局优化三个维度:

    1. 主线程耗时优化需要在启动全路径各阶段中细化具体的耗时成因,如:CPU Time、CPU Schedule、IO wait、Lock wait 等,完成耗时归因后可以使用逐步升级的优化策略来逐个击破:对于首屏所必须的耗时逻辑做正面优化(可使用缩减耗时逻辑、异步并发、延迟加载等手段)、对于非首屏必须的耗时逻辑做按需加载(需要架构优化的基础)、对于优化后仍存在耗时的逻辑尝试做业务降级(大都有损需评估全局收益);

    2. 后台线程优化策略与主线程类似,在此基础上还可以实施后台任务缩减、线程收敛、开启多进程等优化措施;此外,主线程和后台线程均存在较多启动任务且彼此间可能存在关联,因此,可以对全局的启动任务做依赖关系梳理并实施精细化的任务重排,旨在减少依赖任务间的等待耗时;

    3. 全局优化主要是指业务无关的通用的全局优化策略,如虚拟机层面或 IO 层面的优化等。

    线上验证

    在完成了具体的优化项施工后,就来到了线上验证大盘收益的阶段。这个阶段有三点需要注意:

    1. 线下的优化一定要有线上的指标反馈,线下的优化项因为设备或操作习惯差异往往难以评估是否具备普遍影响,只有当相应的线上指标取得正面反馈后才能验证拿到了有效的优化收益;

    2. 线上指标需要结合均值与分位值综合来评估,只关注启动耗时的均值往往会掩盖低分位设备的现状,这部分设备可能占比不高,对均值影响有限,但抖音庞大的用户基数乘以该比例仍旧是不小的数量,为了保障该部分用户的启动性能体验,抖音一般会分 50%、70%、90%三个分位值来评估指标;

    3. 在验证收益时通过 AB 实验达成,这样做不仅能控制变量确保优化项的严格有效,还能借此来观察性能优化所带来的业务指标收益,这些都可以作为规划后续启动优化方向的参考指导。

    防劣化

    在线上验证优化措施取得切实收益后,并不是万事大吉了,持续保持住优化效果才算完整达成了启动性能优化的目的。其实不仅是启动优化,整个性能优化领域都是围绕着“”和“”来展开的,“攻”即为前述的分析与优化,而“守”则是防止劣化,在防劣化方面大家往往不会像优化的方面那么重视,但实际上能防止劣化是可持续取得优化效果的前提(否则新的优化效果会用于弥补劣化甚至入不敷出),并且防劣化相比于优化是更能持久有益的。

    抖音启动性能防劣化的进程分为了三个时期,不同时期有不同的表现与应对手段,这很可能是大多数 APP 优化启动性能都要经历的,这里提炼出来以供参考:

    1. 快速下降期:此时一般位于启动优化的初始阶段,优化空间很大,伴随有小幅度的劣化但往往都能被更大幅度的优化抵消且还仍有收益,这时应该抓大放小,按照更高投入产出比的策略重点推进优化,同时也抽出少部分精力治理修复成本低的劣化。

    2. 瓶颈期:到了该时期绝大部分优化收益已经拿到,想进一步做到优化往往需要投入更多成本,且优化幅度有限,整体的投入产出比不高,同期还会伴随有中小幅的劣化,此时需要建立完善的线上线下监控体系,及时发现并修复劣化,此外还要通过架构改造从源头上限制劣化的发生,综合保障优化的收益不会被劣化抵消。

    3. 劣化期:这个时期往往出现在年关或重要节日期间,这类时间点往往有重要且紧急的活动项目上线,众多关联方面均要为其开绿灯,启动性能指标也不例外,为了保障活动效果可能要加入若干耗时的主线程启动任务,所带来的的劣化幅度往往比较大,此时需要对齐预期并在活动结束后及时修复。

    启动优化方法论的应用实践

    古人云“纸上得来终觉浅,绝知此事要躬行”,前述的方法论讲得再详细再透彻也会与实际的落地存在隔阂,为了做到真正的学以致用,下文将细致讲解如何将启动优化方法论应用于实践之中。

    理论分析的实践

    抖音在理论分析部分会对启动流程分别作全路径分析和耗时成因分析,前者用于发现全路径各个阶段的潜在耗时点避免疏漏,后者用于系统性地将各个耗时点归因从而引导我们找寻优化思路,关于这两部分的具体实践如下:

    启动性能全路径分析:抖音的启动路径和大多数 APP 类似,整体分为两大阶段和两个间隙,它们按时间顺序排布为:Application 阶段、handle message 间隙、Activity 阶段和数据加载间隙,全路径各部分细分涵盖的内容如下图所示:

    ed06d66dd6c04b2d7a209a9f2b1ecb7f.png

    APP 进程由 zygote 进程 fork 出来后会执行 ActivityThread 的 main 方法,该方法最终触发执行bindApplication,这也是 Application 阶段的起点;然后是我们在应用中能触达到的attachBaseContext阶段,4.x 的机型在该阶段具有较长的 MultiDex 耗时可以做针对性优化(可参考“抖音 BoostMultiDex 优化实践”),本阶段也是最早的预加载时机;接下来是installProvider阶段,很多三方 sdk 借助该时机来做初始化操作,很可能导致启动耗时的不可控情形,需要按具体 case 优化;此后就到了 Application 的onCreate阶段,这里有很多三方库和业务的初始化操作,是通过异步、按需、预加载等手段做优化的主要时机,它也是 Application 阶段的末尾。

    6405ae840721b14157cc51f795e9533e.png

    Application 阶段和 Activity 阶段之间往往会不可避免地被插入很多 post 到主线程的消息及相应待执行任务,这是拉长启动耗时的另一不可控问题点,需要加以监控治理或通过消息调度优化来尽量减小此间隙。

    960a986ef16c4bc479b55778006e6663.png

    来到 Activity 阶段后,首先经历的是其onCreate生命周期,这里涵盖了首屏业务优化的主要场景也是开启异步并发的主要时机,在其中有个重要的 setContentView 方法会触发 DecorView 的 install,可尝试对 DecorView 的构建进行预加载;后续自然来到View 构建的阶段,该阶段在抖音上相当耗时,可采用异步 Inflate 配合 X2C(编译期将 xml 布局转代码)并提升相应异步线程优先级的方法综合优化;再来到View 的整体渲染阶段,涵盖 measure、layout、draw 三部分,这里可尝试从层级、布局、渲染上取得优化收益。

    a1d09a63c8a214eab98e3871e500c3e6.png

    最后是首屏数据加载阶段,这部分涵盖非常多数据相关的操作,也需要综合性优化,可尝试预加载、缓存或网络优先级调度等手段。

    5da4dccad026560328e2d41dfe002c3a.png

    此外,针对全路径所有阶段还可以实施通用性的优化项,如:启动任务调度框架、类重排、IO 预加载、全局通用性框架优化等。

    a6a58e77b87577b34e678d578c92d6c1.png

    启动耗时成因分析:所有的耗时均因代码运行时不合理地消耗系统资源产生,而不合理的耗时点正是需要做归因分析之处。抖音按照不合理耗时点消耗的主要系统资源类型划分出五大成因,分别是:CPU Time、CPU Schedule、IO Wait、Lock Wait 和 IPC,下面分别对各成因进行剖析:

    1. CPU Time 指占用 CPU 进行计算所花费的时间绝对值,中断、挂起、休眠等行为是不会增加 CPU Time 的,所以因 CPU Time 开销占比高导致的不合理耗时点往往是逻辑本身复杂冗长需要消耗较多 cpu 时间片才能处理完。比较常见的高 CPU 占用是循环,比如抖音启动时遇到过一个 so 加载耗时,最后定位原因是在解压 so 的时候,遍历 ZipEntry 的次数过多导致,一个可行的优化策略就是可以把 so 所在的 ZipEntry 提前,遍历完 so 的 ZipEntry 之后可以提前中止遍历,而不需要遍历剩下的无效 ZipEntry。除循环之外,反射也是导致 CPU Time 的重要原因,像在序列化/反序列化、View Inflate 时,都有大量的反射操作,反射的耗时主要是字符串去查找 Method 或者 Field,这个优化策略也可以考虑提前查找 Method 和 Field 缓存起来,或者是通过内联来降低 Field 数量等。另外一个常见的 CPU 耗时是类加载,类的加载过程包括:Load,从 Dex 文件里读取类的信息,可通过类重排优化;Verify,验证指令是否合法等,通过关掉 Class Verify 可以优化该过程,同时高版本的 vdex 也是为了优化 verify 过程而设计,在 dex2oat 的时候做 verify,verify 之后的结果保存成 vdex,后续只需要加载 vdex;Link,给 Field、Method 分配内存,按照名字排序以方便后续反射的时候查找 Field、Method 等,这个过程的优化,art 虚拟机采用了 ImageSpace 的方案进行了优化,将 Link 后的内存保存为 image 文件,后续可以直接 load 这个 image 文件,省去了 Link 过程;Init,类的初始化。

    2. CPU Schedule 在分析时主要针对主线程,是指主线程处于可执行状态但获取不到 cpu 时间片,这类耗时可能和线程调度等有关,最终导致分配给主线程的 cpu 时间片不足以及时处理完其内任务。由于主线程的线程优先级比其他线程的优先级要高很多,通常影响并不大,事实上抖音做了线上用户的启动耗时统计,这部分的耗时占比也是不大的。不过有一个场景需要关注,就是渲染,渲染是需要 RenderThread 提交 GPU 的渲染命令,而 RenderThread 并没有主线程那么高的优先级,因此比较容易受 CPU 的负载的影响,导致渲染耗时,这个对于启动来说影响并不算大,启动只有一次首页的渲染,占整体时间的比例不算大,但对于流畅度的影响就会比较大。这类耗时的优化主要还是从降低 CPU 的负载的角度考虑,比如业务降级、业务打散等手段。抖音还通过对 RenderThread 优先级的提升优化,拿到了不错的收益。

    3. IO Wait 指发生了 IO 操作需要等待 IO 返回结果,这类耗时可能发生在读取资源和文件,类加载,甚至在内存不足时的 PageFault 都会导致 IO Wait。Resources 的相关的操作耗时,主要是需要从 apk 里读取资源文件,优化策略可以有预加载、资源重排、资源异步加载等。类加载的 IO Wait 和 Resources 类似,也可以通过类的重排、预加载等优化方案。文件读写导致的 IO Wait 又分为业务文件和系统文件,业务文件指业务逻辑的读写文件,一般都可以通过异步来解决,而系统文件的例子是 dex 的读写,抖音的 IO Wait 很大一块是它贡献的,目前的思路还是做 dex 的重排和 IO 的预读来尝试优化。

    4. Lock Wait 也是主要针对主线程,指其处于等锁状态,等待被其他线程唤醒或自己超时唤醒,导致这类耗时的问题种类多样,大体也是可以分为业务锁和系统锁,业务锁主要是被主线程等待的业务逻辑未能及时处理完,优化思路一般是移除主线程的锁等待逻辑或者加快被等待的业务逻辑的执行速度。系统锁主要有:String InternTable Lock,ClassLinker Lock,GC Wait Lock 等,目前抖音正在尝试优化这几类的锁耗时。

    5. IPC 指进程间通信,操作系统大都含有相应的机制,Android 中所特有的 IPC 机制是 Binder,由于进行 IPC 调用往往需要等待通信结果本质上这也算是一种 Lock Wait,但 Android 特有 Binder 机制所以单独列出,这类耗时可采用减少或替代 Binder 调用等手段来优化。

    综合前述的五大耗时成因,这里举一个分析启动阶段 UI 耗时成因的例子作为实践参考,根据 UI 界面的生命周期(一般划分)——UI 构建、数据绑定、View 显示三个阶段分别进行分析:

    b81d75705a355f092189fe0054447ae7.png
    • UI 构建阶段中首先要对界面布局的 xml 文件进行解析,这会导致 IO Wait 耗时,在接下来要解析 xml 文件中的 TagName 从而获取对应 View 的 class 会用到反射、创建各子 View 实例并生成 View 树又会用到循环递归,两部分都会增加 CPU Time 的开销。

    • 然后是数据绑定阶段,该阶段主要分两部分,一部分是对数据做请求、解析、适配,另一是部分是将适配好的数据填充进 UI 中,前一部分往往会涉及到 Json 解析成 Data Class 实例,这里就可能涉及反射、循环遍历嵌套的数据类结构等增加 CPU Time 的操作。

    • 最后是View 显示阶段,常见的 measure、layout、draw 三大渲染 View 的步骤就在其中,它们同样会产生递归遍历父子 View 的耗时,此外这里还涉及将应用层计算好的渲染 View 的数据传递给系统层做最终的像素点排布,那么必然又会产生 IPC 耗时。

    从这个例子可见即使再复杂的场景只要我们进行细粒度的分析,都能将耗时点归入前述某一成因中。

    现状分析的实践

    如前文方法论所述,现状分析包括线下 Profile 数据与线上监控数据的对照分析,综合这两部分可以明确切实影响大盘启动性能的普遍耗时点,从而确保要做的优化项是行之有效的。下面分别讲述这两部分数据的分析实践:

    线下 Profile 数据分析:Profile 主要是指使用性能探测工具抓取应用启动路径各阶段的耗时和系统资源消耗情况,常见的开源 Profile 工具有 TraceView、Systrace、Android Profiler 等,这些工具各有优势但均不能完全满足抖音做线下 Profile 的需求(详见后文“启动性能优化工具”部分的讲解),为此,抖音自研了“新一代全能型性能分析工具 RheaTrace”满足了需求。通过该工具我们可以在线下抓取整个启动路径的 Trace 文件,其整体样式与 Systrace 一致,但是涵盖了更多的信息点,一个样例 Trace 文件如下图所示:

    49b37a2183f42137fdfb109694d1b4e5.png

    这里需要注意抓取 Trace 一定要基于 release 包,debug 包中往往涵盖诸多调试逻辑可能影响启动性能,导致 profile 数据与实际使用情形存在偏差。在查看 profile 数据时,首要观察主线程,寻找其中不符合预期的耗时方法,抖音将主线程耗时在 5ms 以上的方法均认定为不符合预期;然后在所有不符合预期的方法中寻找 Top n 的耗时点,逐个分析耗时原因、寻找突破口;耗时原因需要结合方法实现逻辑以及诸多运行时信息综合分析(这里可以参考 Google 官方文档“浏览 Systrace 报告”),需要关注的运行时信息有方法执行时段对应的 CPU 负载、线程状态的颜色标识值、锁信息、IO 耗时、Binder 调用耗时等,根据这些信息判定引起方法耗时的主要原因,再结合理论分析中不同阶段、不同系统资源类型探寻优化手段。

    线上监控数据分析:这部分数据的分析主要是用作参照和补充,参照是指线下 Profile 数据分析出的耗时点要对照线上数据确认其在大盘中存在普遍耗时,补充是指线下 Profile 数据未能复现的耗时点可能存在于线上大盘中,这部分漏掉的耗时点需要在线下尝试复现、归因后实施优化。这里有个很重要的点是:该如何对线上的启动性能指标做监控,这是保障线上数据能真实反映用户体验并且与 QA(做竞品测试等)和业务方(判断业务需求是否影响启动性能等)达成一致的前提,下面将对这部分做详细阐述,分为启动性能指标的定义、统计和校准三部分:

    • 启动性能指标定义:启动指标定义主要在于如何确定启动路径的起点与终点。起点的备选项有下图中的三个点以及 Application 的 attachBaseContext 方法:

      • 下图中“点击图标”后的/proc/self/stats starttime 是内核中记录的 App 启动时间点,该数据的 Android 版本兼容性良好也比较贴合真实情况,可以作为备选;

      • 接下来的 Process.getStartElapsedRealTime 是 Framework 中记录的 APP 进程创建起点,该 API 是 Android N 起才提供的兼容性较差;

      • 再往后是 Application 的构造函数,按照 Android 官方生命周期式的开发模式通常不会往 Application 的构造函数中加逻辑,所以不建议在这里记录起点;

      • 最后是大家熟悉的 attachBaseContext 方法也是 Application 生命周期中非常早的一个点,可以在这里记录启动点,虽然可能和真实情况有小幅差距,但能够起到基本的对照效应并且处于 APP 逻辑可以干预的范围内,抖音的启动路径起点选定的正是此处,此外也可以结合前述的内核中启动时刻综合观测。

    af6a44018eadd937671896e0215a34e5.png
    • 而关于终点的定义同样有几个备选项:Activity-onResume、Activity-onWindowFocusChanged、View-dispatchDraw 和 DecorView-post:

      • 少数 APP 在选定冷启阶段终点时可能会选择首页 Activity 的 onResume 时机,但此时整个 Activity 还不是完全可见,与用户感受到的冷启阶段结束时刻有一定的差距;

      • 基于上述原因,更多 APP 会选择首页 Activity 的 onWindowFocusChanged 时机,抖音也是选择的此时机作为冷启过程的终点,此时首页 Activity 已经可见但其内部的 view 还不可见,对于用户侧已经可以看见首页背景,即认为冷启阶段到此结束,后续的首页内 View 绘制归入首刷过程中;

      • dispatchDraw 从 View 可见这个角度讲应该是比较接近用户感受的,但其受业务改动影响较大,不利于把控冷启时间及维护;

      • 最后是通过 DecorView 在 attachToWindow 前 post 一个 runnable 来打点的方式,该方式可以保障在业务 View 完成渲染后做打点,但该方式可能会受业务同学做懒加载在打点前插入逻辑的影响,因此抖音的冷启终点也未选用该时机。

    • 启动性能指标统计:在统计性能指标时有个关键点往往被大家忽略,就是分位值的概念,由于平均值相对更通俗易懂且对大盘突发问题敏感往往作为首要统计指标被关注,但其存在波动较大不利于大盘监控以及难以体现不同分位机型启动性能差异的问题,而分位值更有利于全面监控且各分位波动相对较小,此外对于低端机的性能问题能够更好地显现出来,有助于做专项优化,在优化抖音的启动性能时我们会重点关注 50 分位和 90 分的性能指标,不过分位值也存在一些缺点,比如:概念理解起来相对复杂、个别 bad case 分散到各分位不容易体现出来等,因此比较好的实践是:日常优化主要统计分位值,平均值作为辅助完善监控体系。

    • 启动性能指标校准:由于启动路径往往比较复杂,因此添加了启动性能埋点后还需要额外的校准,总的原则是需要保障指标数据能切实反映大盘用户情形。在添加客户端埋点时最好是先梳理再分主路径和重点 case 分别打点,此外还要对若干异常 case 的数据进行剔除或分类避免污染打点数据,比如抖音在添加启动时间打点时就会对开屏广告、保活进程、push 拉起、deeplink 拉起、启动期间退后台、新用户启动等场景进行过滤或分开统计。

    启动优化的实践

    在做完理论分析与现状分析后,我们基本对全局待优化点及其大致优化方向会产生整体的认知,在开始落地各个优化措施之前还有很重要但往往会被忽略的一步——按优先级排布优化项、制定整体优化方案,这一步在很大程度上制约着后续启动优化的收益预期与进展把控,这两点对于按时达成启动优化的终极目标都至关重要。前述中提及了对“优先级”的把控,这点是制定整体优化方案的重中之重。

    从抖音启动优化实践总结来看比较好的优先级策略是按照“投入产出比”来排布优化项,顾名思义:投入人力越少但优化幅度越大的优化项越应该排在前期,因为所有的性能优化历程都势必会经历从高收益到低收益的变化,那么相应的在排布优化项的前后顺序时也需顺应此规律,最终呈现的态势即为:前期以小成本快速降低大盘启动耗时,后期逐步提高投入突破各个瓶颈型耗时点(更后期大规模重构仅能减少几十毫秒启动时间的情形也应在预期之内),全过程同期加强防劣化机制,最终做到可持续优化。

    d7a131a1789e136ec8ea5178386a6a51.png

    在完成前述的全局优先级排布及方案制定后,才算真正来到了实施优化的阶段,在这个阶段所要用到的各类优化策略及配合方法在前文方法论部分已有详细讲述,在实战部分首先要补充一下前述几类优化策略按照“性能无损”、“业务无损”的区别划分,整体如上图所示,此外,我们会结合抖音启动优化实战经验列举各优化策略下可实施的优化项,以供参考:

    • 正面优化:删减非必要的启动逻辑、开屏页与首页 Activity 合并、获取进程名从 IPC 转反射方式等;

    • 按需优化:ContentProvider 中过早初始化逻辑转为使用时初始化、多进程由启动时加载转为使用时或特定场景触发加载等;

    • 延迟优化:4.x 机型延迟执行 Multidex.install 中的 Odex 操作、主线程消息队列中非启动必要消息延迟执行、启动路径非高优业务逻辑延迟初始化等;

    • 运行时优化:CPU 提频、语言层面优化(内联、替换反射、避免用 Kotlin 的 Range 循环)、关闭 Verify Class、4.x 机型抑制 GC、主动触发 AOT 编译、资源重排、类重排、dex 重排等;

    • 异步优化:异步预加载(ShardPreference、实例化对象)、异步 inflate view、线程收敛等;

    • 降级优化:极速版、组件化降级、非必要耗时逻辑按人群/地区降级等;

    • 综合优化:启动任务调度框架、启动路径重构、前后台启动任务精细化重排、后台负载优化等,这些优化项属于前述优化思想的综合应用,一般不局限于单方面的优化。

    通过上述列举的各策略优化项你可能会发现,这其中有的优化项其实会对个别业务性能或功能有损,但最终对于启动性能是有显著提升的,那么此时需要按照“全局收益最大”的策略来综合评估这些优化项的可落地性,并不是只看单点的得失,这种全局性的思维在性能优化中非常重要。

    线上验证的实践

    这部分在前述的方法论中已针对三个关键点阐述得比较细致,这里仅针对三个关键点在落地时的技巧或注意事项加以补充:

    • 线下的优化一定要有线上的指标反馈:由于线上设备的固有硬件性能各异,所以需要有足够量级的用户启动打点数据才能相对准确地判定线下的优化是否在线上产生了效果,这个量级从抖音启动优化中摸索的经验来看一般达到 100 万即可;此外,观测启动性能数据的时间点也需要把控好,这是由于每次发布升级版 APP 后,大都是性能相对好的手机会先升级,这个现象会等导致发版初期的启动性能数据整体偏好,不能反映真实大盘情形,因此,抖音一般会选取每个版本发版后 4-5 天(可能随 APP 升级覆盖安装的速度不同而不同)的数据判定大盘情形。

    • 线上指标需要结合均值与分位值综合来评估:在抖音启动优化实践中,启动耗时均值会更多用于大盘情形评估或线上监控中,而作为性能优化的同学最主要关注的是 50 分位机型的数据,这是能代表过半数用户启动性能水准的指标,此外 90 分位以上的机型也需要我们额外关注,这类机型非常容易放大启动性能问题,从实际来看,90 以上相对 50 的绝对分位数差了不到一倍,但冷启耗时却可能差到 2 倍左右(如下图所示抖音在某段时期的各分位冷启耗时情形),这说明低端机的用户启动体验是明显可感知的差,基于我们曾经做过的劣化实验结果来看,这些机型的启动性能如果不能有效提升,将有很大概率减少其留存。

    7538c3fd0d42bd0835e9875e78dca516.png
    • 在验证收益时通过 AB 实验达成:AB 实验相对于观测不同版本的大盘数据来看更具有严谨性,因此在产出实验结论前同样需要保障数据量和时间跨度,抖音在开启性能的 AB 实验后,一般会让对照组及实验组进组用户各达到 100 万并保持至少 5 天后才进行实验的数据分析并产出结论,这样可以基本保障所有相关指标的稳定及置信。

    防劣化的实践

    防劣化的体系建设是个比较复杂的工程,要做好是有非常大的挑战的。抖音从最早的线下手动的分版本测试开始,经过了逐步的摸索优化,演变到当前涵盖了代码提交时静态检测、线下自动化劣化测试和归因、灰度劣化发现和归因、线上常态化的劣化监控和归因。防劣化是一个漏斗,从代码提交阶段线下测试阶段,再到灰度发布阶段,再到线上版本发布阶段,我们希望劣化能够更前置的发现,每个环节都尽可能的发现解决更多的劣化,保证更少的劣化被带到线上。

    防劣化的有几个难点:一是劣化检测的准确率和召回率,为了更多更准确的发现劣化;二是劣化的准确归因,发现劣化之后,如果不能精准的指出劣化的原因,需要投入比较多的人力资源和时间定位劣化原因,影响劣化解决的效率;三是劣化的修复,如果是比较严重的劣化,可以采用阻塞发版限期解决的方式,是比较容易推进解决的。但是从抖音的实践来看,当启动优化到了深水区之后,优化的速度已经比较缓慢,需要关注几十毫秒级别的劣化了,假设我们解决了一二两个难点,发现了这些轻微的劣化,但是如何推进业务去解决这些小劣化也同样是一个难题。我们需要能够量化出这些劣化对业务的影响,针对不同的劣化量级,和业务对齐优先级,确定标准的劣化修复流程,才能够保证劣化不会被带到线上影响大盘用户。

    防劣化是一个长期的工作,抖音投入已经有一年多了,目前整体效果还不错,在这个过程中也积累了比较多的经验,之后会专门写一个抖音的防劣化系列文章来给大家介绍我们的技术成果。

    启动优化工具

    古人云“工欲善其事必先利其器”,在启动性能优化领域也是一样,我们不仅需要趁手的工具来定位优化耗时问题,还需要尽量自动化的工具来持续发现劣化问题,也就是说整个启动优化在“攻”和“守”的两大方面均需要工具的辅助。那么下面将针对这两部分的工具分别进行介绍及分享抖音在启动优化工具方面的探索:

    线下分析工具

    这部分主要针对业界常见的 APP 性能探测工具进行基本原理解析及优缺点对比,具体包含的工具有:TraceView、CPU Profiler、Systrace,此外还将提及抖音自研的“新一代全能型性能分析工具 RheaTrace”:

    • TraceView:Instrumentation 模式下采用 AddListener 的方式注册 MethodError、MethodExited、MethodUnwind 的回调来采集方法起止时间;Sampling 模式下使用一个 SamplingThread 定时主权线程堆栈,通过对此的堆栈对比近似确定函数的进入和退出时间;虽然是官方提供的工具,但两种模式本身都存在比较大的性能损耗,可能带偏优化方向;

    • CPU Profiler:整体通过 JVM Agent 实现,具有完成方法调用栈输出,且支持 Java、C/C++方法的耗时检测,上手比较简单,但其同样存在性能损耗较大的问题,且一般仅用于 debug 包,release 包需要额外添加 debuggable 的配置;

    • Systrace:基于 Android 系统层的 Atrace 实现,Atrace 又基于 Linux kernal 层的 ftrace 实现,ftrace 在内核中通过函数插桩获取耗时;其自身性能损耗比较低、数据源丰富且具有较好的可视化页面,但其默认监控点较少,在 APP 自有代码中的监控点需要手动加入,比较麻烦;

    • RheaTrace:这是抖音基于字节码插桩结合 Systrace 及 Atrace 自研的工具,其具有自动加入监控点、各类耗时信息全面、性能损耗低等特点,是抖音日常在线下实施性能优化时首选的工具,其细节详见前述公众号文章,这里不再赘述。

    RheaTrace 目前是抖音性能优化同学的主要工具,它不仅仅是一个工具,也是一个平台。除了 Systrace 自带的性能数据之外,我们增加了业务的函数耗时插桩的数据,可以更全面地对耗时进行分析。但是这些数据还不够,我们支持以插件的形式,增加自己定制的数据,比如为了优化 IO 的耗时,我们通过 hook 增加了更精细化的 IO 的信息,辅助定位 IO 的耗时问题;抖音的类加载耗时也是有些严重,我们也 hook 了类加载,增加了类加载的性能数据。我们要极致地优化抖音启动时间,以上这些数据是不够的,还有锁、View 耗时信息等相关数据补充,给性能优化的同学提供全方位的性能分析工具。

    除了 RheaTrace 之外,还有一些特定场景的小工具,比如线程分析工具、内存分析工具、高频函数分析等。由于篇幅有限,就不在这里一一介绍,后面会有专门的系列文章来介绍。

    线上监控工具

    上面介绍启动优化方法论的时候我们提到了,不能只是看线下的性能分析,线下的分析结果并不能完全代表线上大盘用户的情况。我们分析线上的性能数据,一方面能够验证我们的线上优化效果,另一方面能够从线上多个维度的数据里指导后续的优化方向。

    线上监控工具和线下的差异点主要在低性能损耗兼容性,我们将 RheaTrace 做了改造,使其能够满足线上的监控要求。性能损耗上,我们将监控的性能损耗控制在 1%以内,包大小控制在 200KB 以内,基本实现了线上全量用户的启动耗时监控。通过启动路径的全量插桩,可以针对启动路径的各个阶段进行监控,一是可以发现线上用户哪些任务比较耗时,可以针对性的优化,让更多用户受益;二是可以监控线上的启动任务,如果发生了耗时增加,那么说明有劣化,这比监控到启动时间的劣化,要更容易定位到原因。除了线上的全量慢函数监控之外,我们的线上启动监控还会细化IO、锁、GC等多种维度的耗时数据,帮助定位线上为什么耗时慢,提供新的优化方向。

    总结一下线上启动监控工具的思路就是:将线下的性能分析数据,低损耗的移植到线上,观察线上用户的性能数据,线上线下相结合的分析启动耗时,为启动优化提供优化方向指导。

    启动性能优化之路去向何方

    看了上文关于启动性能优化如此多的理论与实践,想必你已经意识到启动优化之路注定是不会平凡的,抖音在这条路上探索了 2 年之久且仍未到达尽头。在这条路上势必会经历前期的坦途、中期的迷茫与后期的瓶颈,但无论如何都要一直坚定地走下去,因为只要业务还有一天在迭代那么启动性能就有一天存在挑战的可能,所以启动优化之路的未来必然是无尽头的

    既然如此,那么我们的重点就应该从何时才能走完这条路转移到如何走得更精彩之上,甚至到最后能够做到把控这条路的走向,这或许也能算作另一种意义上的走完启动优化之路,那么什么才算走得更精彩以及把控路的走向呢?

    迷茫时慢下步子再分析全局的耗时点寻找到新的优化策略、遇到瓶颈时先暂时放缓追赶指标尝试从代码重构上挖掘深层的收益、不断开拓跨领域(如端上智能降级)结合的优化方向……这些或许都能称作是一种精彩,并且会因人而异,最终,当这种精彩累计得足够多之时我们很可能会发现启动优化之路上已知的所有岔路口全被走了个遍,同期 APP 的启动性能也很可能已经达到了再优化也没什么明显业务收益的地步,并且出现的任何劣化点都能及时被解决掉,那么这时不出意外的话,启动优化之路走向的把控权已经尽在你手中了。

    加入我们

    抖音 Android 基础技术团队是一个深度追求极致的团队,我们专注于性能、架构、包大小、稳定性、基础库、编译构建等方向的深耕,保障超大规模团队的研发效率和数亿用户的使用体验。目前北京、上海、杭州、深圳都有大量人才需要,欢迎有志之士与我们共同建设亿级用户全球化 APP!

    可以点击阅读原文,进入字节跳动招聘官网查询「抖音基础技术 Android」相关职位,也可以邮件联系:fengda.1@bytedance.com 咨询相关信息或者直接发送简历内推!

    展开全文
  • Oracle数据库性能优化实践指南

    热门讨论 2015-11-08 18:05:05
    《Oracle数据库性能优化实践指南》涉及了Oracle优化的所有方面,系统地讲述了Oracle优化的相关内容。《Oracle数据库性能优化实践指南》分为5个部分。第1部分总体介绍了性能计划、实例优化和SQL优化,也简单提及了...
  • mysql数据库优化方案

    2018-04-16 17:09:34
    简单描述数据库优化方案,以及数据库一些常用的操作,包括一些简单的查询语句,函数使用等等
  • MySql基础知识总结(SQL优化篇)

    万次阅读 多人点赞 2021-07-10 12:26:26
    2、添加索引 3、更改索引顺序 4、去掉in 5、小结 四、双表sql优化 1、建表语句 2、左连接查询 3、小结 五、避免索引失效的一些原则 六、一些其他的优化方法 1、exist和in 2、order by 优化 七、sql顺序 -> 慢日志...

    🍅 作者简介:CSDN2021博客之星亚军🏆、新星计划导师✌、博客专家💪

    🍅 哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师

    🍅 关注公众号【哪吒编程】,回复1024,获取Java学习路线思维导图、大厂面试真题、加入万粉计划交流群、一起学习进步

    目录

    一、explain返回列简介

    1、type常用关键字

    2、Extra常用关键字

    二、触发索引代码实例

    1、建表语句 + 联合索引

    2、使用主键查询

    3、使用联合索引查询

    4、联合索引,但与索引顺序不一致

    5、联合索引,但其中一个条件是 >

    6、联合索引,order by

    三、单表sql优化

    1、删除student表中的联合索引。

    2、添加索引

    3、更改索引顺序

    4、去掉in

    5、小结

    四、双表sql优化

    1、建表语句

    2、左连接查询 

    3、小结

    五、避免索引失效的一些原则

    六、一些其他的优化方法

    1、exist和in

    2、order by 优化

    七、sql顺序 -> 慢日志查询

    1、慢查询日志

    2、阈值

    八、慢查询日志 --> mysqldumpslow工具

    1、mysqldumpslow工具

    2、查询不同条件下的慢sql

    九、分析海量数据

    1、show profiles

    2、精确分析,sql诊断

    3、全局查询日志

    十、锁机制详解

    1、操作分类

    2、操作范围

    3、加读锁,代码实例

    4、加写锁

    5、MyISAM表级锁的锁模式

    6、MyISAM分析表锁定

    7、InnoDB分析表锁定

    8、加行锁代码实例

    9、行锁的注意事项


    本篇是MySQL知识体系总结系列的第二篇,该篇的主要内容是通过explain逐步分析sql,并通过修改sql语句与建立索引的方式对sql语句进行调优,也可以通过查看日志的方式,了解sql的执行情况,还介绍了MySQL数据库的行锁和表锁。

    一、explain返回列简介

    1、type常用关键字

    system > const > eq_ref > ref > range > index > all。

    1. system:表仅有一行,基本用不到;
    2. const:表最多一行数据配合,主键查询时触发较多;
    3. eq_ref:对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了const类型;
    4. ref:对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取;
    5. range:只检索给定范围的行,使用一个索引来选择行。当使用=、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN或者IN操作符,用常量比较关键字列时,可以使用range;
    6. index:该联接类型与ALL相同,除了只有索引树被扫描。这通常比ALL快,因为索引文件通常比数据文件小;
    7. all:全表扫描;

    实际sql优化中,最后达到ref或range级别。

    2、Extra常用关键字

    Using index:只从索引树中获取信息,而不需要回表查询;

    Using where:WHERE子句用于限制哪一个行匹配下一个表或发送到客户。除非你专门从表中索取或检查所有行,如果Extra值不为Using where并且表联接类型为ALL或index,查询可能会有一些错误。需要回表查询。

    Using temporary:mysql常建一个临时表来容纳结果,典型情况如查询包含可以按不同情况列出列的GROUP BY和ORDER BY子句时;

    索引原理及explain用法请参照前一篇:MySQL索引原理,explain详解

    二、触发索引代码实例

    1、建表语句 + 联合索引

    CREATE TABLE `student` (
      `id` int(10) NOT NULL,
      `name` varchar(20) NOT NULL,
      `age` int(10) NOT NULL,
      `sex` int(11) DEFAULT NULL,
      `address` varchar(100) DEFAULT NULL,
      `phone` varchar(100) DEFAULT NULL,
      `create_time` timestamp NULL DEFAULT NULL,
      `update_time` timestamp NULL DEFAULT NULL,
      `deleted` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `student_union_index` (`name`,`age`,`sex`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    2、使用主键查询

    3、使用联合索引查询

    4、联合索引,但与索引顺序不一致

    备注:因为mysql优化器的缘故,与索引顺序不一致,也会触发索引,但实际项目中尽量顺序一致。

    5、联合索引,但其中一个条件是 >

    6、联合索引,order by

    where和order by一起使用时,不要跨索引列使用。

    三、单表sql优化

    1、删除student表中的联合索引。

    2、添加索引

    alter table student add index student_union_index(name,age,sex);

    优化一点,但效果不是很好,因为type是index类型,extra中依然存在using where。

    3、更改索引顺序

    因为sql的编写过程

    select distinct ... from ... join ... on ... where ... group by ... having ... order by ... limit ...

    解析过程

    from ... on ... join ... where ... group by ... having ... select distinct ... order by ... limit ...

     因此我怀疑是联合索引建的顺序问题,导致触发索引的效果不好。are you sure?试一下就知道了。

    alter table student add index student_union_index2(age,sex,name);

    删除旧的不用的索引:

    drop index student_union_index on student

    索引改名

    ALTER TABLE student RENAME INDEX student_union_index2 TO student_union_index

    更改索引顺序之后,发现type级别发生了变化,由index变为了range。

    range:只检索给定范围的行,使用一个索引来选择行。

    备注:in会导致索引失效,所以触发using where,进而导致回表查询。

    4、去掉in

    ref:对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取;

    index 提升为ref了,优化到此结束。

    5、小结

    1. 保持索引的定义和使用顺序一致性;
    2. 索引需要逐步优化,不要总想着一口吃成胖子;
    3. 将含in的范围查询,放到where条件的最后,防止索引失效;

    四、双表sql优化

    1、建表语句

    CREATE TABLE `student` (
      `id` int(10) NOT NULL,
      `name` varchar(20) NOT NULL,
      `age` int(10) NOT NULL,
      `sex` int(11) DEFAULT NULL,
      `address` varchar(100) DEFAULT NULL,
      `phone` varchar(100) DEFAULT NULL,
      `create_time` timestamp NULL DEFAULT NULL,
      `update_time` timestamp NULL DEFAULT NULL,
      `deleted` int(11) DEFAULT NULL,
      `teacher_id` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    CREATE TABLE `teacher` (
      `id` int(11) DEFAULT NULL,
      `name` varchar(100) DEFAULT NULL,
      `course` varchar(100) DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    2、左连接查询 

    explain select s.name,t.name from student s left join teacher t on s.teacher_id = t.id where t.course = '数学'

    上一篇介绍过,联合查询时,小表驱动大表。小表也称为驱动表。其实就相当于双重for循环,小表就是外循环,第二张表(大表)就是内循环。

    虽然最终的循环结果都是一样的,都是循环一样的次数,但是对于双重循环来说,一般建议将数据量小的循环放外层,数据量大的放内层,这是编程语言的优化原则。

    再次代码测试:

    student数据:四条

    teacher数据:三条

    按照理论分析,teacher应该为驱动表。

    sql语句应该改为:

    explain select teacher.name,student.name from teacher left join student on teacher.id = student.id  where teacher.course = '数学'

    优化一般是需要索引的,那么此时,索引应该怎么加呢?往哪个表上加索引?

    索引的基本理念是:索引要建在经常使用的字段上。

    on teacher.id = student.id可知,teacher表的id字段使用较为频繁。

    left join on,一般给左表加索引;因为是驱动表嘛。

    alter table teacher add index teacher_index(id);
    alter table teacher add index teacher_course(course);

    备注:如果extra中出现using join buffer,表明mysql底层觉得sql写的太差了,mysql加了个缓存,进行优化了。

    3、小结

    1. 小表驱动大表
    2. 索引建立在经常查询的字段上
    3. sql优化,是一种概率层面的优化,是否实际使用了我们的优化,需要通过explain推测。

    五、避免索引失效的一些原则

    1、复合索引,不要跨列或无序使用(最佳左前缀);

    2、符合索引,尽量使用全索引匹配;

    3、不要在索引上进行任何操作,例如对索引进行(计算、函数、类型转换),索引失效;

    4、复合索引不能使用不等于(!=或<>)或 is null(is not null),否则索引失效;

    5、尽量使用覆盖索引(using index);

    6、like尽量以常量开头,不要以%开头,否则索引失效;如果必须使用%name%进行查询,可以使用覆盖索引挽救,不用回表查询时可以触发索引;

    7、尽量不要使用类型转换,否则索引失效;

    8、尽量不要使用or,否则索引失效;

    六、一些其他的优化方法

    1、exist和in

    select name,age from student exist/in (子查询);

    如果主查询的数据集大,则使用in;

    如果子查询的数据集大,则使用exist;

    2、order by 优化

    using filesort有两种算法:双路排序、双路排序(根据IO的次数)

    MySQL4.1之前,默认使用双路排序;双路:扫描两次磁盘(①从磁盘读取排序字段,对排序字段进行排序;②获取其它字段)。

    MySQL4.1之后,默认使用单路排序;单路:只读取一次(全部字段),在buffer中进行排序。但单路排序会有一定的隐患(不一定真的是只有一次IO,有可能多次IO)。

    注意:单路排序会比双路排序占用更多的buffer。

    单路排序时,如果数据量较大,可以调大buffer的容量大小。

    set max_length_for_sort_data = 1024;单位是字节byte。

    如果max_length_for_sort_data值太低,MySQL底层会自动将单路切换到双路。

    太低指的是列的总大小超过了max_length_for_sort_data定义的字节数。

    提高order by查询的策略:

    1. 选择使用单路或双路,调整buffer的容量大小;
    2. 避免select * from student;(① MySQL底层需要对*进行翻译,消耗性能;② *永远不会触发索引覆盖 using index);
    3. 符合索引不要跨列使用,避免using filesort;
    4. 保证全部的排序字段,排序的一致性(都是升序或降序);

    七、sql顺序 -> 慢日志查询

    慢查询日志就是MySQL提供的一种日志记录,用于记录MySQL响应时间超过阈值的SQL语句(long_query_time,默认10秒) ;

    慢日志默认是关闭的,开发调优时打开,最终部署时关闭。

    1、慢查询日志

    (1)检查是否开启了慢查询日志:

    show variables like '%slow_query_log%'

    (2)临时开启:

    set global slow_query_log = 1;

    (3)重启MySQL: 

    service mysql restart;

    (4)永久开启:

    /etc/my.cnf中追加配置:

    放到[mysqld]下:

    slow_query_log=1
    
    slow_query_log_file=/var/lib/mysql/localhost-slow.log

    2、阈值

    (1)查看默认阈值:

    show variables like '%long_query_time%'

    (2)临时修改默认阈值:

    set global long_query_time = 5;

    (3)永久修改默认阈值:

    /etc/my.cnf中追加配置:

    放到[mysqld]下:

    long_query_time = 5;

    (4)MySQL中的sleep:

    select sleep(5);

    (5)查看执行时间超过阈值的sql:

    show global status like '%slow_queries%';

    八、慢查询日志 --> mysqldumpslow工具

    1、mysqldumpslow工具

    慢查询的sql被记录在日志中,可以通过日志查看具体的慢sql。

    cat /var/lib/mysql/localhost-slow.log

    通过mysqldumpslow工具查看慢sql,可以通过一些过滤条件,快速查出需要定位的慢sql。

    mysqldumpslow --help

    参数简要介绍:

    s:排序方式

    r:逆序

    l:锁定时间

    g:正则匹配模式

    2、查询不同条件下的慢sql

    (1)返回记录最多的3个SQL

    mysqldumpslow -s r -t 3 /var/lib/mysql/localhost-slow.log

    (2)获取访问次数最多的3个SQL

    mysqldumpslow -s c -t 3 /var/lib/mysql/localhost-slow.log

    (3)按时间排序,前10条包含left join查询语句的SQL

    mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/localhost-slow.log

    九、分析海量数据

    1、show profiles

    打开此功能:set profiling = on;

    show profiles会记录所有profileing打来之后,全部SQL查询语句所花费的时间。

    缺点是不够精确,确定不了是执行哪部分所消耗的时间,比如CPU、IO。

    2、精确分析,sql诊断

    show profile all for query  上一步查询到的query_id。

    3、全局查询日志

    show variables like '%general_log%'

    开启全局日志:

    set global general_log = 1;

    set global log_output = table;

    十、锁机制详解

    1、操作分类

    读写:对同一个数据,多个读操作可以同时进行,互不干扰。

    写锁:如果当前写操作没有完毕,则无法进行其它的读写操作。

    2、操作范围

    表锁:一次性对一张表整体加锁。

    如MyISAM存储引擎使用表锁,开销小、加锁快、无死锁;但锁的范围大,容易发生冲突、并发度低。

    行锁:一次性对一条数据加锁。

    如InnoDB存储引擎使用的就是行锁,开销大、加锁慢、容易出现死锁;锁的范围较小,不易发生锁冲突,并发度高(很小概率发生高并发问题:脏读、幻读、不可重复读)

    lock table 表1 read/write,表2 read/write,...

    查看加锁的表:

    show open tables;

    3、加读锁,代码实例

    会话0:
    lock table student read;
    select * from student; --查,可以
    delete from student where id = 1;--增删改,不可以
    
    select * from user; --查,不可以
    delete from user where id = 1;--增删改,不可以
    

    如果某一个会话对A表加了read锁,则该会话可以对A表进行读操作、不能进行写操作。即如果给A表加了读锁,则当前会话只能对A表进行读操作,其它表都不能操作

    会话1:
    select * from student; --查,可以
    delete from student where id = 1;--增删改,会“等待”会话0将锁释放
    
    会话1:
    select * from user; --查,可以
    delete from user where id = 1;--增删改,可以
    
    

    会话0给A表加了锁,其它会话的操作①可以对其它表进行读写操作②对A表:读可以,写需要等待释放锁。

    4、加写锁

    会话0:
    lock table student write;

    当前会话可以对加了写锁的表,可以进行任何增删改查操作;但是不能操作其它表;

    其它会话:

    对会话0中对加写锁的表,可以进行增删改查的前提是:等待会话0释放写锁。

    5、MyISAM表级锁的锁模式

    MyISAM在执行查询语句前,会自动给涉及的所有表加读锁,在执行增删改前,会自动给涉及的表加写锁。

    所以对MyISAM表进行操作,会有如下情况发生:

    (1)对MyISAM表的读操作(加读锁),不会阻塞其它会话(进程)对同一表的读请求。但会阻塞对同一表的写操作。只有当读锁释放后,才会执行其它进程的写操作。

    (2)对MyISAM表的写操作(加写锁),会阻塞其它会话(进程)对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作。

    6、MyISAM分析表锁定

    查看哪些表加了锁:

    show open tables;1代表被加了锁

    分析表锁定的严重程度:

    show status like 'table%';

    Table_locks_immediate:可能获取到的锁数

    Table_locks_waited:需要等待的表锁数(该值越大,说明存在越大的锁竞争)

    一般建议:Table_locks_immediate/Table_locks_waited > 5000,建议采用InnoDB引擎,否则采用MyISAM引擎。

    7、InnoDB分析表锁定

    为了研究行锁,暂时将自动commit关闭,set autocommit = 0;

    show status like '%innodb_row_lock%';

    Innodb_row_lock_current_waits:当前正在等待锁的数量
    Innodb_row_lock_time:等待总时长。从系统启动到现在一共等待的时间
    Innodb_row_lock_time_avg:平均等待时长。从系统启动到现在一共等待的时间
    Innodb_row_lock_time_max:最大等待时长。从系统启动到现在一共等待的时间
    Innodb_row_lock_waits:等待次数。从系统启动到现在一共等待的时间

    8、加行锁代码实例

    (1)查询student

    select id,name,age from student

    (2)更新student 

    update student set age = 18 where id = 1

    (3)加行锁 

    通过select id,name,age from student for update;给查询加行锁。

    依旧修改成功,原因是MySQL默认是自动提交的,因此需要暂时将自动commit关闭

    set autocommit = 0;

    9、行锁的注意事项

    (1)如果没有索引,行锁自动转为表锁。

    (2)行锁只能通过事务解锁。

    (3)InnoDB默认采用行锁

    优点:并发能力强,性能高,效率高

    缺点:比表锁性能损耗大

    高并发用InnoDb,否则用MyISAM。

    🍅 作者简介:CSDN2021博客之星亚军🏆、新星计划导师✌、博客专家💪

    🍅 哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师

    🍅 关注公众号【哪吒编程】,回复1024,获取Java学习路线思维导图、大厂面试真题、加入万粉计划交流群、一起学习进步

    关注公众号,回复1024,获取Java学习路线思维导图、加入万粉计划交流群

    展开全文
  • Oracle Database 11gR2性能调整与优化

    热门讨论 2015-08-25 11:15:42
    本书详细介绍了最新的监控、故障排除和优化的方法,取自真实世界的案例研究和最佳实践遍及全书,从书中可以学会如何找出和解决瓶颈问题、如何配置存储设备、如何有效运行查询以及如何开发无错误的SQL和PL/SQL代码。
  • 智能优化算法:灰狼优化算法-附代码

    万次阅读 多人点赞 2020-07-31 16:31:41
    智能算法:灰狼优化算法-附代码 摘要:受 灰 狼 群 体 捕 食 行 为 的 启 发,Mirjalili等[1]于 2014年提出了一种新型群体智能优化算法:灰狼优化算法。GWO通过模拟灰狼群体捕食行为,基于狼群群体协作的机制来达到...
  • Sql优化总结!详细!(2021最新面试必问)

    万次阅读 多人点赞 2021-04-09 16:20:03
    Sql优化基础Sql优化查询SQL尽量不要使用select *,而是具体字段避免在where子句中使用or来连接条件使用varchar代替char尽量使用数值替代字符串类型查询尽量避免返回大量数据使用explain分析你SQL执行计划是否使用了...
  • 深度学习算法在许多情况下都涉及优化。例如,模型中的进行推断涉及求解优化问题。我们经常使用解析优化去证明或设计算法。在深度学习涉及的诸多优化问题中,最难的是神经网络训练。甚至是用几百台机器投入几天到几个...
  • 本篇博客主要优化 MySQL 中的插入操作,核心实现 insert 优化任务。 load data infile 导入数据 使用上述命令,可以大幅度提高批量插入数据。 如果希望使用该命令,可以用 show 命令进行测试 show variables like '%...
  • 智能优化算法:鲸鱼优化算法-附代码

    万次阅读 多人点赞 2020-07-24 13:29:56
    智能算法:鲸鱼优化算法-附代码 文章目录智能算法:鲸鱼优化算法-附代码1.算法原理1.1包围猎物1.2 狩猎行为1.3 搜索猎物1.4 算法流程2. 算法结果:参考文献: 摘要:鲸鱼优化算法 (whale optimization algorithm,WOA...
  • 数据库优化——慢查询MySQL定位优化流程

    万次阅读 多人点赞 2021-05-19 11:07:25
    如何定位并优化慢查询SQL?如何使用慢查询日志?本文带来慢查询例子演示,新手都能看懂!那查询语句慢怎么办?explain带你分析sql执行计划!当主键索引、唯一索引、普通索引都存在,查询优化器如何选择?本文带你一...
  • 数据库优化的四大方法

    千次阅读 多人点赞 2021-12-26 10:53:58
    正如上图所示,数据库优化可以从架构优化,硬件优化,DB优化,SQL优化四个维度入手。 此上而下,位置越靠前优化越明显,对数据库的性能提升越高。我们常说的SQL优化反而是对性能提高最小的优化。 接下来我们再看看...
  • mysql优化方案

    千次阅读 2021-12-27 08:34:10
    一、优化方向二、硬件方面优化三、软件配置1、网络方面的配置,要修改/etc/sysctl.conf文件2、mysql本身的一些优化mysql配置文件 /etc/my.cnf3、innodb方案4、MyISAM 参数配置4.1,设置索引缓存区大小4.2,设置读...
  • 2016中国人工智能大会(CCAI 2016),机器学习的明天论坛,北京大学信息科学技术学院机器感知与智能教育部重点实验室教授林宙辰的演讲PPT。报道:http://geek.csdn.net/news/detail/97844
  • Ceres Solver 非线性优化

    千次阅读 2022-03-02 11:01:18
    Ceres Solver 非线性优化
  • matlab优化工具箱 非线性优化 约束优化 线性规划、二次规划
  • nginx 静态资源优化配置

    万次阅读 多人点赞 2022-05-28 09:28:40
    nginx 静态资源优化配置
  • 本书从CPU与编译器的运行机制讲起,带领读者一步步了解程序的执行成本、编译器的优化选项等,总结出许多c程序性能优化的技巧,并以实验的方式进行了讲解,简明易懂,使人印象深刻。书中带有大量的代码实例,使读者...
  • 【最优化】最优化理论的基本概念

    千次阅读 多人点赞 2020-11-19 14:42:25
    优化理论的基本概念 一. 最优化理论的定义与分类 1. 一般形式 (1)公式定义: minf(x),且有x∈Xmin f(x),且有 x∈Xminf(x),且有x∈X 其中,对于上式中的字母,含义如下: f(x):代价函数(目标函数) x:决策向量...
  • 鲁棒优化(一)

    千次阅读 2021-09-11 21:08:38
    初步认识鲁棒优化
  • 4 Classification of optimization problem (IP: integer programming, MINLP: mixed integer non-linear programming, MILP: mixed integer linear programming, LP: linear ...其中,离散优化又称组合优化. me..
  • Android性能优化总结

    万次阅读 多人点赞 2019-06-18 21:45:50
    安卓开发大军浩浩荡荡,经过近十年的发展,Android技术优化日异月新,如今Android 9.0 已经发布,Android系统性能也已经非常流畅,可以在体验上完全媲美iOS。 但是,到了各大厂商手里,改源码、自定义系统,使得...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,629,050
精华内容 1,451,620
关键字:

优化