精华内容
下载资源
问答
  • ①网速问题加载 ②本机的硬件配置低(CPU或者是内存被占满的时候,打开网页很是会很的) 2.连接过程 ①DNS解析(在http请求的过程中,域名解析和建立连接占的时间很多) ②接受数据时间过长。(因为下载的...

    想借这篇博客总结一下~

    1.本机问题

    ①网速问题加载慢

    ②本机的硬件配置低(CPU或者是内存被占满的时候,打开网页很是会很慢的)

    2.连接过程

    ①DNS解析慢(在http请求的过程中,域名解析和建立连接占的时间很多)

    ②接受数据时间过长。(因为下载的内容太重,例如大图片、大脚本等。这类问题可以使用GZIP压缩、图片压缩或者JS/CSS的minify等手段来解决)

    ③服务端收到的请求过多,处理起来会变慢(并发)

    ④单个资源加载过慢,导致了整个网页变慢。原因有可能是:1)资源在第三方站点上,他们很慢;2)这个资源太大了;3)这个资源使用的域名有问题

    3.前后端代码问题

    ①前端:JS阻塞请求(写的js代码出现问题,解析就会花费很长时间,这两个js请求之间会出现一个很大的空隙,就会导致这段时间的资源加载都被阻塞住);前端页面请求的资源过多,速度自然会慢的,如果请求的资源不存在,那么速度将会更慢

    ②后端:后端代码问题,主要有代码冗余、数据库发生锁死、动态请求时间过长等,这就需要RD优化一切可以优化的东西了

     

    优化方法:

           一、网站的空间线路

      没这个前提,就算你网站做的再好,内容再少,服务器多强,打开需要半天没的说,还有可能经常出现打不开的现象,所以,要像网站打开的快,我们就得把这个地基打好,下面楼需要建多少层才好说话。通常我们常用的有电信,网通,或双线路,三线路。无论你选了那种,都要好要快就行了。

      二、网站需要全站静态化生成

      就是页面的后缀是.html或.php等其它的,为什么这样说呢?你想一下,一般动态的网站,打开页面是,首先他就必须去读取数据库的资料,然后在显示到页面上,走了个弯路,并还有数据库访问的速度和数据库数据多少,数据库的大小所决定。你想不慢都难,静态页面就不同了,它不需要访问数据库,因为它在生成出来时就已通过读取数据库的资料,资料生成出一个页面来了,走的是直路,想不快都难;

      三、压缩或精简Javascript代码和css代码

      无论什么类型的网站,用户点击网站的时候,都会加载网站的Javascript和css代码,假设这些代码冗多,势必会降低网站打开速度。

      如果有这样的问题,就要对代码进行压缩或精简。压缩方面,有大量的第三方工具如站长之家的CSS\JS压缩工具可供大家直接免费试用,经过压缩的代码,普遍能减小40%到65%。精简方面,能用一个样式控制就不用两个,思路是既能满足网站的页面布局,又是****的代码量。

      四、减少HTTP请求数

      用户在打开一个网页的时候,后台程序响应用户所需的时间并不多,用户等待的时间主要花费在下载网页元素上了,即HTML、CSS、JavaScript、Flash、图片等,统计显示,每增加一个元素,网页载入的时间就增加25-40毫秒(取决于用户的带宽情况)。所以,想要提高网页打开速度,就要减少HTTP请求数,方法有3种:

      1)、减少不必要的HTTP请求

      例如用CSS圆角代替圆角图片,减少图片的使用。

      2)、合并文件

      对于文本文件,可以直接合并内容。例如将多个JS(JavaScript的简称)文件合并成一个,将多个CSS文件合并成一个。

      3)、优化缓存

      对于没有变化的网页元素(如页头、页尾等),用户再次访问的时候没有必要重新下载,直接从浏览器缓存里读取就可以了。

           五、 服务器端的缓存设置

      假设用户访问了你的网站,那么用户下一次再次访问你的站点,速度就会更快。这是什么原因呢?原因在于浏览器缓存。

      大多数服务器,或空间已经默认设置了缓存功能,站长或者seoer要做的就是在后台设定相应文件类型以及过期时间。在此,建议为:如果是windows主机,就将图片,css和Javascript文件的缓存时间设置为30天;如果是Apache Web服务器,则使用mod_cache模块开启缓存功能。

      六、优化css中的引用图片

      假设一个页面需要大量的周边图片,如下图标等,如果这些图片都是单独调用,那么请求数就会加大,这就说明:cssimages过多,请求数就会增大,网站打开速度就会变慢。

      如果我们将页面所需要使用的图片合并在一张图上,那么这些图片请求数就会变为1,页面速度相对就会加快。如何将需要的图片放在一张图上并在相应的位置调用呢?方法在于坐标定位。即:通过css坐标取得图片路径,标签中引用相应坐标。

      如果你对前端了解,那么这些都不是大问题,如果你不懂css,那么CSS图片合并生成器类工具(百度即可)能直接提供相应内容:合并css图片,同时生成相应代码。

      七、图片压缩处理

      有数据显示,图片加载时间是造成网站速度的因素,将图片加载速度问题解决,页面速度问题就解决了一大半。假设网站有大量jpg等格式的图片,需要在不影响视觉的前提下,尽量对其压缩,以减少图片本身的大小。与

           八、 服务器端开启gzip压缩功能

      网页中的每个元素越小,下载所需的时间就越少,这个很好理解。gzip压缩是效率较高的一种数据压缩格式。如果开启gzip压缩,压缩率一般能达到75%左右,效果非常明显。大部分服务器如虚拟主机,VPS等都已经默认开启了这一项功能,如果没有开启,你对相应设置又不会,建议直接联系空间商,让他们解决。

      网站打开速度的快与慢关系到用户体验的问题,关系到网站对搜索引擎传递的友好度问题,利用可实现的手段加快网站加载速度,显得不可或缺。在提升网页打开速度经常被忽视的一个问题是响应。对于用户来说,每次的操作,不管返回结果是慢,还是快,都要及时予以响应,最典型的例子就是:当用户点击打开一张图片时,是否有百分比数字显示的进度条,就是一个典型的响应设计。

           压缩Javascript文件类似,有大量的在线工具提供相应压缩服务,有大量的图像编辑软件如美图秀秀等,都有压缩功能。

     

    展开全文
  • Unity资源机制 1、概述  本文意在阐述Unity资源机制相关的信息,以及一些关于个人的理解与试验结果。另外还会提及一些因机制问题可能会出现的异常以及处理建议。大部分机制信息来源于官方文档,另外为自我验证后...

    Unity资源机制

    1、概述

          本文意在阐述Unity资源机制相关的信息,以及一些关于个人的理解与试验结果。另外还会提及一些因机制问题可能会出现的异常以及处理建议。大部分机制信息来源于官方文档,另外为自我验证后的结果。

     

    2、资源

    概述

          Unity必须通过导入将所支持的资源序列化,生成AssetComponents后,才能被Unity使用。以下是Unity对Assets的描述:

          Assets are the models,textures,sounds and all other “content”files from which you make your game。

          资源(Asset)是硬盘中的文件,存储在Unity工程的Assets文件夹内。有些资源的数据格式是Unity原声支持的,有些资源则需要转换为源生的数据格式后才能被使用。

            对象(UnityEngine.Object),代表序列化数据的集合,表示某个资源的具体实例。它可以是Unity使用的任何类型的资源,所有对象都是UnityEngine.Object基类的子类

            资源与对象时一对多的关系。

    名称

     

     

    描述

     

     

    支持格式

     

    Audio Clip

     

    音频剪辑

     

     

    音频:.aif .wav .mp3 .ogg音轨:.xm .mod .it .s3m

     

    Cubemap Texture

    立方体贴图纹理

     

     

    Flare

     

    耀斑

     

     

    Font

     

    字体

     

     

    .ttf

     

    Material

     

    材质

     

     

    Meshes

    网格

    .FBX .dae .3DS .dxf .obj

    Movie Texture

     

    电影贴图

     

     

    .mov .mpg .mpeg .mp4 .avi .asf (导入需要QuickTime)

     

    Procedural Material Assets

    程序材质资源

     

    Render Texture

     

    渲染纹理

     

     

    Text Asset

     

    文本资源

     

    .txt .html .htm .xml .bytes

    Texture 2D

     

    二维纹理

     

     

    PSD TIFF JPG TGA PNG GIF BMP IFF PICT

     

            除此之外,想使用Unity不支持导入,或者未经导入的资源,只能使用IO Stream或者WWW 方法,这些将在下文对应栏目中说明。 

            注意:AssetBundle不是资源组件,故无法用资源组件的方式载入,只能使用WWW或者AssetBundle相关接口载入与读取

     

    GUID与fileID(本地ID)

            Unity会为每个导入到Assets目录中的资源创建一个meta文件,文件中记录了GUID,GUID用来记录资源之间的引用关系。还有fileID(本地ID),用于标识资源内部的资源。资源间的依赖关系通过GUID来确定;资源内部的依赖关系使用fileID来确定。

     

    InstanceID(实例ID)

            Unity为了在运行时,提升资源管理的效率,会在内部维护一个缓存表,负责将文件的GUID与fileID转换成为整数数值,这个数值在本次会话中是唯一的,称作实例ID(InstanceID)。

            程序启动时,实例ID缓存与所有工程内建的对象(例如在场景中被引用),以及Resource文件夹下的所有对象,都会被一起初始化。如果在运行时导入了新的资源,或从AssetBundle中载入了新的对象,缓存会被更新,并为这些对象添加相应条目。实例ID仅在失效时才会被从缓存中移除,当提供了指定文件GUID和fileID的AssetBundle被卸载时会产生移除操作。

            卸载AssetBundle会使实例ID失效,实例ID与其文件GUID和fileID之间的映射会被删除以便节省内存。重新载入AssetBundle后,载入的每个对象都会获得新的实例ID。

     

     资源的生命周期

            Object从内存中加载或卸载的时间点是定义好的。Object有两种加载方式:自动加载与外部加载。当对象的实例ID与对象本身解引用,对象当前未被加载到内存中,而且可以定位到对象的源数据,此时对象会被自动加载。对象也可以外部加载,通过在脚本中创建对象或者调用资源加载API来载入对象(例如:AssetBundle.LoadAsset) 

    对象加载后,Unity会尝试修复任何可能存在的引用关系,通过将每个引用文件的GUID与FileID转化成实例ID的方式。一旦对象的实例ID被解引用且满足以下两个标准时,对象会被强制加载:

            实例ID引用了一个没有被加载的对象。

            实例ID在缓存中存在对应的有效GUID和本地ID。

            如果文件GUID和本地ID没有实例ID,或一个已卸载对象的实例ID引用了非法的文件GUID和本地ID,则引用本身会被保留,但实例对象不会被加载。在Unity编辑器中表现为空引用,在运行的应用中,或场景视图里,空对象会以多种方式表示,取决于丢失对象的类型:网格会变得不可见,纹理呈现为紫红色等等。

     

     MonoScripts

            一个MonoScripts含有三个字符串:程序库名称,类名称,命名空间。 

    构建工程时,Unity会收集Assets文件夹中独立的脚本文件并编译他们,组成一个Mono程序库。Unity会将Assets目录中的语言分开编译,Assets/Plugins目录中的脚本同理。Plugin子目录之外的C#脚本会放在Assembly-CSharp.dll中。而Plugin及其子目录中的脚本则放置在Assembly-CSharp-firstpass.all中。 

    这些程序库会被MonoScripts所引用,并在程序第一次启动时被加载。

     

    3、资源文件夹

     Assets

            为Unity编辑器下的资源文件夹,Unity项目编辑时的所有资源都将置入此文件夹内。在编辑器下,可以使用以下方法获得资源对象:

            AssetDatabase.LoadAssetAtPath("Assets/x.txt"); 

            注意:此方法只能在编辑器下使用,当项目打包后,在游戏内无法运作。参数为包含Assets内的文件全路径,并且需要文件后缀。 

            Assets下的资源除特殊文件夹内,或者在会打入包内的场景中引用的资源,其余资源不会被打入包中。

     

    Resources

    资源载入

            Assets下的特殊文件夹,此文件夹内的资源将会在项目打包时,全部打入包内,并能通过以下方法获得对象:

            Resources.Load("fileName"); 

            Resources.Load("fileName"); 

            注意:函数内的参数为相对于Resource目录下的文件路径与名称,不包含后缀。Assets目录下可以拥有任意路径及数量的Resources文件夹,在运行时,Resources下的文件路径将被合并。

            例:Assets/Resources/test.txt与 Assets/TestFloder/Resources/test.png在使用Resource.Load("test")载入时,将被视为同一资源,只会返回第一个符合名称的对象。如果使用Resource.Load(“test”)将返回text.txt;

            如果在Resources下有相同路径及名称的资源,使用以上方法只能获得第一个符合查找条件的对象,使用以下方法能或得到所有符合条件的对象:

    Object[] assets = Resources.LoadAll("fileName"); 

    TextAsset[] assets = Resources.LoadAll("fileName"); 

     

     相关机制

            在工程进行打包后,Resource文件夹中的资源将进行加密与压缩,打包后的程序内将不存在Resource文件夹,故无法通过路径访问以及更新资源。

            依本文2.3章节所述,在程序启动时会为Resource下的所有对象进行初始化,构建实例ID。随着Resource内资源的数量增加,此过程耗时的增加是非线性的。故会出现程序启动时间过长的问题,请密切留意Resource内的资源数量。

     

    卸载资源

            所有实例化后的GameObject 可以通过Destroy函数销毁。请留意Object与GameObject之间的区别与联系

    Object可以通过Resources中的相关Api进行卸载

    Resources.UnloadAsset(Object);//卸载对应Object 

    Resources.UnloadUnusedAssets();//卸载所有没有被引用以及实例化的Object 

            注意以下情况:

    Object obj = Resources.Load("MyPrefab"); 

    GameObject instance = Instantiate(obj) as GameObjct; 

    ...... 

    Destroy(instance); 

    Resources.UnloadUnusedAssets(); 

            此时UnloadUnusedAssets将不会生效,因为obj依然引用了MyPrefab,需要将obj = null,才可生效。

     

    StreamingAssets

    概述

            StreamingAssets文件夹为流媒体文件夹,此文件夹内的资源将不会经过压缩与加密,原封不动的打包进游戏包内。在游戏安装时,StreamAssets文件件内的资源将根据平台,移动到对应的文件夹内。StreamingAssets文件夹在Android与IOS平台上为只读文件夹. 

            你可以使用以下函数获得不同平台下的StreamingAssets文件夹路径:

    Application.streamingAssetsPath 

            请参考以下各平台下StreamingAssets文件夹的等价路径,Application.dataPath为程序安装路径。Android平台下的路径比较特殊,请留意此路径的前缀,在一些资源读取的方法中是不必要的(AssetBundle.LoadFromFile,下详)

    Application.dataPath+"/StreamingAssets"//Windows OR MacOS 

    Application.dataPath+"/Raw" //IOS 

    "jar:file://"+Application.dataPath+"!/assets/" //Android 

     

    文件读取

            StreamingAssets文件夹下的文件在游戏中只能通过IO Stream或者WWW的方式读取(AssetBundle除外)

    IO Stream方式

    using(FileStream stream =  

    File.Open(Application.streamingAssetsPath+"fileName", 

    FileMode.Open)) 

    //处理方法 

    WWW方式(注意协议与不同平台下路径的区别)

    using(WWW www = new WWW( 

    Application.streamingAssetsPath+"fileName")) 

    yield return www; 

    www.text; 

    www.texture; 

            AssetBundle特有的同步读取方式(注意安卓平台下的路径区别)

    string assetbundlePath = 

    #if UNITY_ANDROID 

    Application.dataPath+"!/assets"; 

    #else 

    Application.streamingAssetsPath; 

    #endif  

    AssetBundle.LoadFromFile(assetbundlePath+"/name.unity3d"); 

     

     PersistentDataPath

    Application.persistentDataPath 

            Unity指定的一个可读写的外部文件夹,该路径因平台及系统配置不同而不同。可以用来保存数据及文件。该目录下的资源不会在打包时被打入包中,也不会自动被Unity导入及转换。该文件夹只能通过IO Stream以及WWW的方式进行资源加载。

     

    4、WWW载入资源

     概述

            WWW是一个Unity封装的网络下载模块,支持Http以及file两种URL协议,并会尝试将资源转换成Unity能使用的AssetsComponents(如果资源是Unity不支持的格式,则只能取出byte[])。具体对应的格式参考第一章表格。WWW加载是异步方法。

    byte[] bytes = WWW.bytes; 

    string text = WWW.text; 

    Texture2D texture = WWW.texture; 

    MovieTexture movie = WWW.movie; 

    AssetBundle assetbundle = WWW.assetBundle; 

    AudioClip audioClip = WWW.audioClip; 

     

    相关机制

     new WWW

            每次new WWW时,Unity都会启用一个线程去进行下载。通过此方式读取或者下载资源,会在内存中生成WebStream,WebStream为下载文件转换后的内容,占用内存较大。使用WWW.Dispose将终止仍在加载过程中的进程,并释放掉内存中的WebStream。 

            如果WWW不及时释放,将占用大量的内存,推荐搭配using方式使用,以下两种方式等价。

    WWW www = new WWW(Application.streamingAssetsPath+"fileName"); 

    try 

    yield return www; 

    www.text; 

    www.texture; 

    finally 

    www.Dispose(); 

    using(WWW www = new WWW( 

    Application.streamingAssetsPath+"fileName")) 

    yield return www; 

    www.text; 

    www.texture; 

            如果载入的为Assetbundle且进行过压缩,则还会在内存中占用一份AssetBundle解压用的缓冲区Deompresion Buffer,AssetBundle压缩格式的不同会影响此区域的大小。

     

    WWW.LoadFromCacheOrDownload

    int version = 1; 

    WWW.LoadFromCacheOrDownload(PathURL+"/fileName",version); 

            使用此方式加载,将先从硬盘上的存储区域查找是否有对应的资源,再验证本地Version与传入值之间的关系,如果传入的Version>本地,则从传入的URL地址下载资源,并缓存到硬盘,替换掉现有资源,如果传入Version<=本地,则直接从本地读取资源;如果本地没有存储资源,则下载资源。此方法的存储路径无法设定以及访问。使用此方法载入资源,不会在内存中生成 WebStream(其实已经将WebStream保存在本地),如果硬盘空间不够进行存储,将自动使用new WWW方法加载,并在内存中生成WebStream。在本地存储中,使用fileName作为标识符,所以更换URL地址而不更改文件名,将不会造成缓存资源的变更。 

    保存的路径无法更改,也没有接口去获取此路径

     

    5、 AssetBundle

    概述

            AssetBundles let you stream additional assets via the WWW class and instantiate them at runtime. AssetBundles are created via BuildPipeline.BuildAssetBundle.

            AssetBundle是Unity支持的一种文件储存格式,也是Unity官方推荐的资源存储与更新方式,它可以对资源(Asset)进行压缩,分组打包,动态加载,以及实现热更新,但是AssetBundle无法对Unity脚本进行热更新,因为其需要在打包时进行编译。

     

    Assetbundle打包

    平台兼容性

            AssetBundle适用于多种平台,但不同平台所使用的AssetBundle并不相同,在创建AssetBundle时需要通过参数来指定目标平台,其关系如下表

     

    Standalone

    WebPlayer

    IOS

     

    Android

    Standalone

    WebPlayer

     

     

    IOS

     

     

     

     

    Android

     

     

     

     

    创建API

    public enum BuildAssetBundleOptions 

    None = 0, 

    //Build assetBundle without any special option. 

    UncompressedAssetBundle = 1, 

    //Don't compress the data when creating the asset bundle. 

    CollectDependencies = 2, 

    //Includes all dependencies. 

    CompleteAssets = 4, 

    //Forces inclusion of the entire asset. 

    DisableWriteTypeTree = 8, 

    //Do not include type information within the AssetBundle. 

    DeterministicAssetBundle = 16, 

    //Builds an asset bundle using a hash for the id  

    ForceRebuildAssetBundle = 32, 

    //Force rebuild the assetBundles. 

    IgnoreTypeTreeChanges = 64, 

    //Ignore the type tree changes when doing the incremental build check. 

    AppendHashToAssetBundleName = 128, 

    //Append the hash to the assetBundle name. 

    ChunkBasedCompression = 256 

    //Use chunk-based LZ4 compression when creating the AssetBundle. 

    AssetBundleManifest manifest =  

    BuildPipeline.BuildAssetBundles("OutputPath",BuildAssetBundleOptions,tragetPlatform); 

           在Unity的5.3版本中,简化了AssetBundle的打包方式,只留下了一个api与寥寥几个设置参数,而之前最让人头痛的资源依赖管理,也被默认进行处理。 而在每个Asset文件的Inspector面板上都会多出一个Asset Labels的设定栏:

     

     

          AssetBundle name:需要将此资源打包的AssetBundle名称

          AssetBundle Variant:需要将此资源打包的AssetBundle的变体名

     

    Variant

             Variant是5.3以后新添加的一个概念,这个值其实是一个尾缀,将添加在对应AssetBundle的名称之后,如:ddzgame.hd,hd就是Variant(从此以后AssetBundle的尾缀已经跟其文件类型本身没有任何联系)。

     

    自动打包脚本

             从以上可知,如果需要一个一个的对资源设置AssetBundle Name与Variant实在太过繁琐与麻烦,也可能出现纰漏,好在可以通过脚本去批量设置这两个参数:

    AssetImporter assetImporter = AssetImporter.GetAtPath("path");

    assetImporter.assetBundleName = "Assetbundle Name";

    assetImporter.assetBundleVariant = "Assetbundle Variant";

        其中path是资源在Assets目录下的路径。

     

    Scene打包

             Scene打包跟资源打包无异,唯一需要注意的是:Scene只能与Scene打入同一个AssetBundle内,而无法与其他资源打入同一个AssetBundle。

             PS:AssetBundle内的Scene需要在AssetBundle加载后,通过SceneManager来加载。

     

    AssetBundle依赖

    依赖机制

             假设有AssetBundleA与 AssetBundleB两个AssetBundle,AssetBundle中的资源引用了AssetBundleB中的资源,则称AssetBundleA依赖于AssetBundleB。具体实例请看下图注意被依赖AssetBundle需要加载的时机

     

     

             注意其依赖的机制: AssetBundle中保存有其中所有资源的GUID,FileID等序列化信息,AssetBundle只会在内存中寻找其依赖资源所在的AssetBundle,并自动从中加载出所需资源。具体可参考本文2.3章节

     

     Manifest

      

     

             在前面有提到,在5.3中,Unity会自动处理AssetBundle中资源的依赖关系。在默认情况下,如果AssetBundle间有交叉的资源引用,不会再重复打包,在打包AssetBundle后,会发现其在输出目录多出了一个与目录名称相同的无后缀AssetBundle文件,其为自动生成的AssetBundleManifest文件,其内保存有此次生成的所有AssetBundle之间的依赖关系与清单。我们可以在载入这个AssetBundle后使用以下方法获得此对象。

    AssetBundle.LoadAsset("AssetBundleManifest");

              Manifest保存有重要的依赖信息,在载入AssetBundle时,可以通过Manifest查询其是否有依赖的AssetBundle,然后我们手动对其进行管理,避免依赖项丢失而出现bug

    string[] fullnames = AssetBundle.GetDirectDependencies(fullname);

    string[] fullnames = AssetBundle.GetAllDependencies(fullname);

             Direct方法会返回所有直接依赖的AssetBundle名称数组,All方法会返回所有依赖的AssetBundle名称数组,fullname包括名称与Variant。推荐使用Direct方法做递归处理,避免重复载入。

     

    AssetBundle加载

    加载方式

             之前已经提及,不再详细说明,使用WWW 或者 AssetBundle相关API加载,其中AssetBundle的API只能进行本地加载。

    AssetBundle.LoadfromMemory(byte[] bytes)

             此API是一个例外,用来对加密的Assetbundle进行读取,可以结合WWW使用。

     

    压缩

             LZMA(Ziv-Markov chain algorithm)格式

             Unity打包成AssetBundle时的默认格式,会将序列化数据压缩成LZMA流,使用时需要整体解包。优点是打包后体积小,缺点是解包时间长,且占用内存。

     

    LZ4格式

             5.3新版本添加的压缩格式,压缩率不及LZMA,但是不需要整体解压。LZ4是基于chunk的算法,加载对象时只有响应的chunk会被解压。

             压缩格式在打包时通过AssetBundleOption参数选择。

     

    内存占用

     

     

           AssetBundle加载后会在内存中生成AssetBundle的序列化架构的占用,一般来说远远小于资源本身,除非包含复杂的序列化信息(复杂多层级关系或复杂静态数据的prefab等)

     

    AssetBundle卸载

    卸载API

    AssetBundle.Unload(bool unloadAllLoadedObjects);

           AssetBundle只有唯一的一个卸载函数,传入的参数用来选择是否将已经从此AssetBundle中加载的资源一起卸载。另外,已经从AssetBundle中加载的资源可以通过Resources.UnloadAsset(Object)卸载。如果想通过Resources.UnloadUnusedAssets()卸载从AssetBundle加载的资源,一定要先将AssetBundle卸载后才能生效。

     

    资源卸载总览

     

    内存关系图

     

           当AssetBundle被卸载后,实例ID与其文件GUID和本地ID之间的映射会被删除, 即其无法被其后加载的依赖于它的资源所查找及引用。详情请参考本文2.3章节

     

     案例分析

         案例1 游戏切换到后台一段时候切回,出现shader或者Texture丢失。

         在移动平台,当程序切到主界面或者在后台长时间运行时,GPU会自动对后台程序的资源进行清理。如果shader或者Texture是从AssetBundle中加载出来,而此AssetBundle已经被卸载的话,Unity无法在程序恢复时从内存中加载这些资源,从而造成丢失。有人会问,这些资源不是已经加载到内存中了么?但是,他们在被加载到GPU之后会被从内存中清除。因此要防止此状况最稳健的方法,就是在场景切换前,不要卸载掉其所属的AssetBundle。

            案例2 当经常使用AssetBundleB.Unload(false)卸载时,有时会发现AssetBundle中的资源在内存中有多份同时存在。

           问题的根源在于从AssetBundle中加载出来的资源,在该AssetBundle卸载之后与其的联系就断开了。

     

     

           例如:从AssetBundleA中加载出来一个Prefab p1,p1依赖资源tex1也会自动加载到内存中。然后用AssetBundle.Unload(false)卸载AssetBundleA,此时p1与AssetBundleA的联系断开。之后,从AssetBundleA中加载Prefab p2,p2也依赖资源tex1,那么在加载p2时tex1会再次被加载到内存中,导致重复。

     

    展开全文
  • Android资源图片读取机制

    千次阅读 2014-09-28 14:59:57
    在isBetterThan函数里,density是当前资源的密度,o.density是之前的循环中已有的最合适的资源的密度,reqValue则是请求密度。 三个if, 第一个if:如果density和o.density都小于reqValue,那么大的那个比较...

    在新建一个Android项目时,在res目录下会自动生成几个drawable文件夹,drawable-ldpi,drawable-mdpi,drawable-hdpi,一直以来都对此不太清楚,图片应该放到哪个文件夹下面,有什么不同的影响?以前一直都是干脆再新建一个不带后缀的drawable文件夹,图片都丢进去,现在决定彻底搞清楚这个事儿。

     1、基础知识

    density(密度):简单的说就是一个比例系数,用来将Dip(设备独立像素)转换成实际像素px。具体公式是:

    px = dip*density+0.5f;

    densityDpi:The screen density expressed asdots-per-inch.简单的说就是densityDpi = density*160

      

     drawable文件夹除了这些密度类的后缀,还有例如-en表示英语环境,-port表示用于竖屏等,这里不做讨论,可以参考http://developer.android.com/guide/topics/resources/providing-resources.html

    另附一张官方的屏幕大小与密度的对应表:



     2、为什么要缩放

    为了适应这么多乱七八糟的设备,Android官方就建议大家针对不同密度的设备制作不同的图片:

    36x36 (0.75x) for low-density

     48x48 (1.0xbaseline) for medium-density

     72x72 (1.5x) for high-density

     96x96 (2.0x) for extra-high-density

     180x180 (3.0x) for extra-extra-high-density

     192x192 (4.0x) for extra-extra-extra-high-density(launcher icon only; see note above)

     

     问题就来了,如果你不听建议,就整了一种密度的图片呢?那么当遇到不同密度的手机时,系统就会好(无)心(情)的对你的图进行缩放了,按文档的说法,这是为了你的应用更好看。

    缩放公式:缩放后大小= 图片实际大小 × (手机密度/图片密度)

    其中图片密度由图片所在drawable文件夹的后缀决定

    比如一张100X100的图放在mdpi文件夹里,在hdpi的手机上,缩放后大小= 100 * (1.5/1) = 150

    就成了一张150*150的图片。


    3、android:anyDensity

    (网上有些博客对这个属性的解释是错的,这里特意提一下)

    在AndroidManifest.XML文件里可以设置这么一个属性:<supports-screens android:anyDensity="true"/>

    不设置的话默认为true。

    按文档的说法(http://developer.android.com/guide/practices/screens_support.html),这个值如果为true,缩放机制为预缩放(pre-scaling),如果为false,缩放机制为自动缩放(auto-scaling),区别是预缩放是在读取时缩放,自动缩放时在绘制的时候缩放,从速度来说预缩放要快一些。另外还有一个很重要的区别,就是如果<supports-screensandroid:anyDensity="false"/>,应用在请求屏幕参数时,系统会欺骗应用,告诉它你现在跑在一个density为1的手机上,而不管手机实际density是多少,比如实际手机是hdpi,尺寸480*800,系统会告诉应用屏幕尺寸是320(400/1.5)*533(800/1.5),然后当应用将图片绘制到(10,10)到(100,100)的区域时,系统会将其转换到(15,15)到(150,150),这时如果你去直接操作这些缩放后的图,就会出些不可预期的问题。总之就是建议不要把这个属性设为false。

    按我的个人理解,这个false就是告诉系统这个应用不支持多分辨率,于是系统就认为你只支持默认分辨率(mdpi),系统就会给你虚拟一个mdpi的设备,让你显示在上面,系统再从这上面拉伸或者缩小到实际设备上。这样既速度慢又效果不好,所以就不推荐。


     4、各目录读取优先级

    假设项目内有如下drawable目录:

    drawable

    drawable-nodpi

    drawable-ldpi

    drawable-mdpi

    drawable-hdpi

    drawable-xhdpi.

     (如果不想系统对图片进行缩放,可以把图片放到drawable-nodpi目录下,从该目录读的图片系统不会进行任何缩放。)

    (由下文可知,不带后缀的drawable目录下的图片按照drawable-mdpi处理.)

     

    如果这些目录下都可能有一张同名图片,那系统该读哪一张呢?

    毋庸置疑,如果手机密度相同的相应的密度目录下有该图片,那就是它了,如果没有呢?

    跟踪源码看看系统是如何选择图片的(基于android4.4.2):

     

    ImageView.java:

    setImageResource()

    resolveUri() 

     

    Resources.java:

    getDrawable()

    getValue()

     

    AssetManager.java:

    getResourceValue()

    native loadResourceValue()

     

    frameworks/base/core/jni/android_util_AssetManager.cpp:

    android_content_AssetManager_loadResourceValue()

     

    frameworks/base/libs/androidfw/AssetManager.cpp:

    AssetManager::getResources()

    AssetManager::getResTable()

     

    frameworks/base/libs/androidfw/ResourceTypes.cpp:

    ResTable::getResource()

    ResTable::getEntry()

     

    ssize_t ResTable::getEntry(
       const Package* package, int typeIndex, int entryIndex,
       const ResTable_config* config,
       const ResTable_type** outType, const ResTable_entry** outEntry,
       const Type** outTypeClass) const
    {
       ********省略*******
       
       const size_t NT = allTypes->configs.size();
       for (size_t i=0; i<NT; i++) {
           const ResTable_type* const thisType = allTypes->configs[i];
           if (thisType == NULL) continue;
           
           ResTable_config thisConfig;
           thisConfig.copyFromDtoH(thisType->config);
     
              ********省略*******
           
           if (type != NULL) {
               // Check if this one is less specific than the last found.  If so,
               // we will skip it.  We checkstarting with things we most care
               // about to those we least care about.
               if(!thisConfig.isBetterThan(bestConfig, config)) {  //就是这里
                    TABLE_GETENTRY(ALOGI("Thisconfig is worse than last!\n"));
                    continue;
               }
           }
            
           type = thisType;
           offset = thisOffset;
           bestConfig = thisConfig;
           TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n"));
           if (!config) break;
        }
         ********省略*******
       return offset + dtohs(entry->size);
    }

     

    ResTable_config::isBetterThan()

     

    bool ResTable_config::isBetterThan(const ResTable_config& o,
    
            const ResTable_config* requested) const {
    
        if (requested) {
    
              **************
    
    
            if (screenType || o.screenType) {
    
                if (density != o.density) {
    
                    // density is tough.  Any density is potentially useful
    
                    // because the system will scale it.  Scaling down
    
                    // is generally better than scaling up.
    
                    // Default density counts as 160dpi (the system default)
    
                    // TODO - remove 160 constants
    
                    int h = (density?density:160);
    
                    int l = (o.density?o.density:160);
    
                    bool bImBigger = true;
    
                    if (l > h) {
    
                        int t = h;
    
                        h = l;
    
                        l = t; 
    
                        bImBigger = false;
    
                    }
    
    
    
                    int reqValue = (requested->density?requested->density:160);
    
                    if (reqValue >= h) {
    
                        // requested value higher than both l and h, give h
    
                        return bImBigger;
    
                    }
    
                    if (l >= reqValue) {
    
                        // requested value lower than both l and h, give l
    
                        return !bImBigger;
    
                    }
    
                    // saying that scaling down is 2x better than up
    
                    if (((2 * l) - reqValue) * h > reqValue * reqValue) {
    
                        return !bImBigger;
    
                    } else {
    
                        return bImBigger;
    
                    }
    
                }
    
    
    
                ***********
            }
    
    
    
        }
    
        return isMoreSpecificThan(o);
    
    }
    


    关键部分已用红字标明,在多个drawable下都有同名图片时,一个资源ID对应不止一个图片,在getEntry里面就有一个循环,用isBetterThan()函数在循环里把最合适的图片选出来。

    可以看见,如果该图片没有指明density,density就默认为160,这也是drawable文件夹下的图片被默认为mdpi的原因。

    在isBetterThan函数里,density是当前资源的密度,o.density是之前的循环中已有的最合适的资源的密度,reqValue则是请求密度。

    三个if,

    第一个if:如果density和o.density都小于reqValue,那么大的那个比较合适

    第二个if:   如果density和o.density都大于reqValue,那么小的那个比较合适

    第三个if:   如果reqValue大小在density和o.density之间,先判断

     if(((2 * l) - reqValue) * h > reqValue * reqValue)

    这个判断大意就是请求密度和较小的密度相差很小而与较大的一个密度相差很大。那么就认为较小的密度更合适。

     

    测试环境: 模拟器+Android4.4.2,其中xh和xxh是用真机+Android4.4.2测的;其中ldpi除Android4.4.2外也用Android2.3.1,hdpi除Android4.4.2外也用了Android2.1,结果并无不同。

    测试目录:drawable-ldpi,drawable-mdpi,drawable-hdpi,drawable-xhdpi,drawable-nodpi,drawable

    测试结果():

    怎么drawable-nodpi有时候在前面有时候在后面?附两处源码你就明白了

    frameworks/base/include/androidfw/ResourceTypes.h:

        enum {
            DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT,
            DENSITY_LOW =ACONFIGURATION_DENSITY_LOW,
            DENSITY_MEDIUM =ACONFIGURATION_DENSITY_MEDIUM,
            DENSITY_TV = ACONFIGURATION_DENSITY_TV,
            DENSITY_HIGH =ACONFIGURATION_DENSITY_HIGH,
            DENSITY_XHIGH = ACONFIGURATION_DENSITY_XHIGH,
            DENSITY_XXHIGH =ACONFIGURATION_DENSITY_XXHIGH,
            DENSITY_XXXHIGH =ACONFIGURATION_DENSITY_XXXHIGH,
            DENSITY_NONE =ACONFIGURATION_DENSITY_NONE
        };

    frameworks/native/include/android/configuration.h:

        ACONFIGURATION_DENSITY_DEFAULT = 0,
        ACONFIGURATION_DENSITY_LOW = 120,
        ACONFIGURATION_DENSITY_MEDIUM = 160,
        ACONFIGURATION_DENSITY_TV = 213,
        ACONFIGURATION_DENSITY_HIGH = 240,
        ACONFIGURATION_DENSITY_XHIGH = 320,
        ACONFIGURATION_DENSITY_XXHIGH = 480,
        ACONFIGURATION_DENSITY_XXXHIGH = 640,
        ACONFIGURATION_DENSITY_NONE = 0xffff,

     

    可见drawable-nodpi目录下的图片密度值为0xffff,即65535,带入判断一算,结果正如测试所得。

    无图无真相,所以贴一张hdpi环境的测试图:

    想要知道会读取到哪张图,可以这样:

    TypedValue typedValue = new TypedValue();
    getResources().getValue(R.drawable.test,typedValue,true);
    //然后typedValue.string的值就是实际读取的图片路径

     

     

     

     

    展开全文
  • mysql如何获取慢SQL,以及查询的解决方式

    万次阅读 多人点赞 2019-02-07 21:00:00
    数据存放的在磁盘的内、外磁道上,数据获取的效率都是不一样的; 7、放弃关系数据库的某些特性 引入NoSQL数据库; 换种思路存放数据,例如搜索中的倒排表;   一、EXPLAIN 做MySQL优化,我们要善用 ...

    浅谈MySQL中优化sql语句查询常用的30种方法

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。 

    2.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。 

    3.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: 
    select id from t where num is null 
    可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: 
    select id from t where num=0 

    4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如: 
    select id from t where num=10 or num=20 
    可以这样查询: 
    select id from t where num=10 
    union all 
    select id from t where num=20 

    5.下面的查询也将导致全表扫描: 
    select id from t where name like '%abc%' 
    若要提高效率,可以考虑全文检索。 

    6.in 和 not in 也要慎用,否则会导致全表扫描,如: 
    select id from t where num in(1,2,3) 
    对于连续的数值,能用 between 就不要用 in 了: 
    select id from t where num between 1 and 3 

    7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描: 
    select id from t where num=@num 
    可以改为强制查询使用索引: 
    select id from t with(index(索引名)) where num=@num 

    8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如: 
    select id from t where num/2=100 
    应改为: 
    select id from t where num=100*2 

    9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如: 
    select id from t where substring(name,1,3)='abc'--name以abc开头的id 
    select id from t where datediff(day,createdate,'2005-11-30')=0--'2005-11-30'生成的id 
    应改为: 
    select id from t where name like 'abc%' 
    select id from t where createdate>='2005-11-30' and createdate<'2005-12-1' 

    10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。 

    11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。 

    12.不要写一些没有意义的查询,如需要生成一个空表结构: 
    select col1,col2 into #t from t where 1=0 
    这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样: 
    create table #t(...) 

    13.很多时候用 exists 代替 in 是一个好的选择: 
    select num from a where num in(select num from b) 
    用下面的语句替换: 
    select num from a where exists(select 1 from b where num=a.num) 

    14.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。 

    15.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。 

    16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。 

    17.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。 

    18.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。 

    19.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。 

    20.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。 

    21.避免频繁创建和删除临时表,以减少系统表资源的消耗。 

    22.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。 

    23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。 

    24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。 

    25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。 

    26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。 

    27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。 

    28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。 

    29.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。 

    30.尽量避免大事务操作,提高系统并发能力。

    31、优化count(my_col)和count(*)

      如果你使用MyISAM表,在没有where子句的情况下使用count(*)速度是很快的,因为行数量的统计是非常精确的,因此MySQL不会一行一行地去找,进而得出行数,如my_col列没有空值,那么和前面说的情况会一样,即count(my_col)速度也会很快。

      如果有where子句时使用count( ),基本上就无法进行更多优化了,在where子句中超出了明显的索引列,对于复杂的where子句,只有使用覆盖索引才有用。

      除了上面的建议外,你还可以使用汇总表,它们让你可以对表的内容保持更新,你可以使用触发器,或者应用程序逻辑保持汇总表总是最新状态,或者定期运行一个批处理作业保持填充最新的数据信息,如果你采用后者,你的信息将会非常接近,但不是精确的,依赖于批处理作业多久运行一次,这需要权衡应用程序对精确信息的需要,和保持数据更新的系统开销,要在这二者之间找到一个平衡点。

    查询慢原因分析

    主要原因1:后台数据库中的数据过多,没做数据优化导致后台查询数据很慢

    次要原因2:前端数据请求-解析-展示过程处理不当

    次要原因3:网络问题所致

    那么我们应该怎么做后台数据优化呢?

    解决问题

    这里总结了几种方案,如何提高数据库查询的速度,大家参考.

    1、缓存,在持久层或持久层之上做缓存

    使用ehcache缓存,这个一般用于持久层的缓存,提供持久层、业务层的快速缓存,hibenate默认使用的二级缓存就是ehcache;

    2、数据库表的大字段剥离

    假如一个表的字段数有100多个,学会拆分字段,保证单条记录的数据量很小;

    3、恰当地使用索引

    必要时建立多级索引,分析MySQL的执行计划,通过表数据统计等方式协助数据库走正确的查询方式,该走索引就走索引,该走全表扫描就走全表扫描;

    4、表的拆分

    表分区和拆分,无论是业务逻辑上的拆分(如一个月一张报表、分库)还是无业务含义的分区(如根据ID取模分区);

    5、字段冗余

    减少跨库查询和大表连接操作;,数据通过单个或多个JOB生成出来,减少实时查询;

    6、从磁盘上做文章

    数据存放的在磁盘的内、外磁道上,数据获取的效率都是不一样的;

    7、放弃关系数据库的某些特性

    引入NoSQL数据库;

    换种思路存放数据,例如搜索中的倒排表;

     

    一、EXPLAIN

    做MySQL优化,我们要善用 EXPLAIN 查看SQL执行计划。

    下面来个简单的示例,标注(1,2,3,4,5)我们要重点关注的数据

    mysql之我们终将踩过的坑(优化)

    1. type列,连接类型。一个好的sql语句至少要达到range级别。杜绝出现all级别

    2. key列,使用到的索引名。如果没有选择索引,值是NULL。可以采取强制索引方式

    3. key_len列,索引长度

    4. rows列,扫描行数。该值是个预估值

    5. extra列,详细说明。注意常见的不太友好的值有:Using filesort, Using temporary

    二、SQL语句中IN包含的值不应过多

    MySQL对于IN做了相应的优化,即将IN中的常量全部存储在一个数组里面,而且这个数组是排好序的。但是如果数值较多,产生的消耗也是比较大的。再例如:select id from t where num in(1,2,3) 对于连续的数值,能用 between 就不要用 in 了;再或者使用连接来替换。

    三、SELECT语句务必指明字段名称

    SELECT *增加很多不必要的消耗(cpu、io、内存、网络带宽);增加了使用覆盖索引的可能性;当表结构发生改变时,前断也需要更新。所以要求直接在select后面接上字段名。

    四、当只需要一条数据的时候,使用limit 1

    这是为了使EXPLAIN中type列达到const类型

    五、如果排序字段没有用到索引,就尽量少排序

    六、如果限制条件中其他字段没有索引,尽量少用or

    or两边的字段中,如果有一个不是索引字段,而其他条件也不是索引字段,会造成该查询不走索引的情况。很多时候使用 union all 或者是union(必要的时候)的方式来代替“or”会得到更好的效果

    七、尽量用union all代替union

    union和union all的差异主要是前者需要将结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的CPU运算,加大资源消耗及延迟。当然,union all的前提条件是两个结果集没有重复数据。

    八、不使用ORDER BY RAND()

    select id from `dynamic` order by rand() limit 1000;

    上面的sql语句,可优化为

    select id from `dynamic` t1 join (select rand() * (select max(id) from `dynamic`) as nid) t2 on t1.id > t2.nid limit 1000;

    九、区分in和exists, not in和not exists

    select * from 表A where id in (select id from 表B)

    上面sql语句相当于

    select * from 表A where exists(select * from 表B where 表B.id=表A.id)

    区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询。所以IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。

    关于not in和not exists,推荐使用not exists,不仅仅是效率问题,not in可能存在逻辑问题。如何高效的写出一个替代not exists的sql语句?

    原sql语句

    select colname … from A表 where a.id not in (select b.id from B表)

    高效的sql语句

    select colname … from A表 Left join B表 on where a.id = b.id where b.id is null

    取出的结果集如下图表示,A表不在B表中的数据

    mysql之我们终将踩过的坑(优化)

    十、使用合理的分页方式以提高分页的效率

    select id,name from product limit 866613, 20

    使用上述sql语句做分页的时候,可能有人会发现,随着表数据量的增加,直接使用limit分页查询会越来越慢。

    优化的方法如下:可以取前一页的最大行数的id,然后根据这个最大的id来限制下一页的起点。比如此列中,上一页最大的id是866612。sql可以采用如下的写法:

    select id,name from product where id> 866612 limit 20

    十一、分段查询

    在一些用户选择页面中,可能一些用户选择的时间范围过大,造成查询缓慢。主要的原因是扫描行数过多。这个时候可以通过程序,分段进行查询,循环遍历,将结果合并处理进行展示。

    如下图这个sql语句,扫描的行数成百万级以上的时候就可以使用分段查询

    mysql之我们终将踩过的坑(优化)

    十二、避免在 where 子句中对字段进行 null 值判断

    对于null的判断会导致引擎放弃使用索引而进行全表扫描。

    十三、不建议使用%前缀模糊查询

    例如LIKE “%name”或者LIKE “%name%”,这种查询会导致索引失效而进行全表扫描。但是可以使用LIKE “name%”。

    那如何查询%name%?

    如下图所示,虽然给secret字段添加了索引,但在explain结果果并没有使用

    mysql之我们终将踩过的坑(优化)

    那么如何解决这个问题呢,答案:使用全文索引

    在我们查询中经常会用到select id,fnum,fdst from dynamic_201606 where user_name like ‘%zhangsan%’; 。这样的语句,普通索引是无法满足查询需求的。庆幸的是在MySQL中,有全文索引来帮助我们。

    创建全文索引的sql语法是:

    ALTER TABLE `dynamic_201606` ADD FULLTEXT INDEX `idx_user_name` (`user_name`);

    使用全文索引的sql语句是:

    select id,fnum,fdst from dynamic_201606 where match(user_name) against('zhangsan' in boolean mode);

    注意:在需要创建全文索引之前,请联系DBA确定能否创建。同时需要注意的是查询语句的写法与普通索引的区别

    十四、避免在where子句中对字段进行表达式操作

    比如

    select user_id,user_project from user_base where age*2=36;

    中对字段就行了算术运算,这会造成引擎放弃使用索引,建议改成

    select user_id,user_project from user_base where age=36/2;

    十五、避免隐式类型转换

    where 子句中出现 column 字段的类型和传入的参数类型不一致的时候发生的类型转换,建议先确定where中的参数类型

    mysql之我们终将踩过的坑(优化)

    十六、对于联合索引来说,要遵守最左前缀法则

    举列来说索引含有字段id,name,school,可以直接用id字段,也可以id,name这样的顺序,但是name;school都无法使用这个索引。所以在创建联合索引的时候一定要注意索引字段顺序,常用的查询字段放在最前面

    十七、必要时可以使用force index来强制查询走某个索引

    有的时候MySQL优化器采取它认为合适的索引来检索sql语句,但是可能它所采用的索引并不是我们想要的。这时就可以采用force index来强制优化器使用我们制定的索引。

    十八、注意范围查询语句

    对于联合索引来说,如果存在范围查询,比如between,>,<等条件时,会造成后面的索引字段失效。

    十九、关于JOIN优化

    mysql之我们终将踩过的坑(优化)

    • LEFT JOIN A表为驱动表

    • INNER JOIN MySQL会自动找出那个数据少的表作用驱动表

    • RIGHT JOIN B表为驱动表

    注意:MySQL中没有full join,可以用以下方式来解决

    select * from A left join B on B.name = A.namewhere B.name is nullunion allselect * from B;

    尽量使用inner join,避免left join

    参与联合查询的表至少为2张表,一般都存在大小之分。如果连接方式是inner join,在没有其他过滤条件的情况下MySQL会自动选择小表作为驱动表,但是left join在驱动表的选择上遵循的是左边驱动右边的原则,即left join左边的表名为驱动表。

    合理利用索引

    被驱动表的索引字段作为on的限制字段。

    利用小表去驱动大表

    mysql之我们终将踩过的坑(优化)

    从原理图能够直观的看出如果能够减少驱动表的话,减少嵌套循环中的循环次数,以减少 IO总量及CPU运算的次数。

    巧用STRAIGHT_JOIN

    inner join是由mysql选择驱动表,但是有些特殊情况需要选择另个表作为驱动表,比如有group by、order by等「Using filesort」、「Using temporary」时。STRAIGHT_JOIN来强制连接顺序,在STRAIGHT_JOIN左边的表名就是驱动表,右边则是被驱动表。在使用STRAIGHT_JOIN有个前提条件是该查询是内连接,也就是inner join。其他链接不推荐使用STRAIGHT_JOIN,否则可能造成查询结果不准确。

    mysql之我们终将踩过的坑(优化)

    这个方式有时可能减少3倍的时间。

    如何在mysql查找效率慢的SQL语句呢?这可能是困然很多人的一个问题,MySQL通过慢查询日志定位那些执行效率较低的SQL 语句,用--log-slow-queries[=file_name]选项启动时,mysqld 会写一个包含所有执行时间超过long_query_time 秒的SQL语句的日志文件,通过查看这个日志文件定位效率较低的SQL 。下面介绍MySQL中如何查询慢的SQL语句

    一、MySQL数据库有几个配置选项可以帮助我们及时捕获低效SQL语句

    1,slow_query_log

    这个参数设置为ON,可以捕获执行时间超过一定数值的SQL语句。

    2,long_query_time

    当SQL语句执行时间超过此数值时,就会被记录到日志中,建议设置为1或者更短。

    3,slow_query_log_file

    记录日志的文件名。

    4,log_queries_not_using_indexes

    这个参数设置为ON,可以捕获到所有未使用索引的SQL语句,尽管这个SQL语句有可能执行得挺快。

    二、检测mysql中sql语句的效率的方法

    1、通过查询日志

    (1)、Windows下开启MySQL慢查询

    MySQL在Windows系统中的配置文件一般是是my.ini找到[mysqld]下面加上

    代码如下

    log-slow-queries = F:/MySQL/log/mysqlslowquery。log
    long_query_time = 2

    (2)、Linux下启用MySQL慢查询

    MySQL在Windows系统中的配置文件一般是是my.cnf找到[mysqld]下面加上

    代码如下

    log-slow-queries=/data/mysqldata/slowquery。log
    long_query_time=2

    说明

    log-slow-queries = F:/MySQL/log/mysqlslowquery。

    为慢查询日志存放的位置,一般这个目录要有MySQL的运行帐号的可写权限,一般都将这个目录设置为MySQL的数据存放目录;
    long_query_time=2中的2表示查询超过两秒才记录;

    2.show processlist 命令

    WSHOW PROCESSLIST显示哪些线程正在运行。您也可以使用mysqladmin processlist语句得到此信息。

    各列的含义和用途:

    ID列

    一个标识,你要kill一个语句的时候很有用,用命令杀掉此查询 /*/mysqladmin kill 进程号。

    user列

    显示单前用户,如果不是root,这个命令就只显示你权限范围内的sql语句。

    host列

    显示这个语句是从哪个ip的哪个端口上发出的。用于追踪出问题语句的用户。

    db列

    显示这个进程目前连接的是哪个数据库。

    command列

    显示当前连接的执行的命令,一般就是休眠(sleep),查询(query),连接(connect)。

    time列

    此这个状态持续的时间,单位是秒。

    state列

    显示使用当前连接的sql语句的状态,很重要的列,后续会有所有的状态的描述,请注意,state只是语句执行中的某一个状态,一个 sql语句,以查询为例,可能需要经过copying to tmp table,Sorting result,Sending data等状态才可以完成

    info列

    显示这个sql语句,因为长度有限,所以长的sql语句就显示不全,但是一个判断问题语句的重要依据。

    这个命令中最关键的就是state列,mysql列出的状态主要有以下几种:

    Checking table
     正在检查数据表(这是自动的)。
    Closing tables
     正在将表中修改的数据刷新到磁盘中,同时正在关闭已经用完的表。这是一个很快的操作,如果不是这样的话,就应该确认磁盘空间是否已经满了或者磁盘是否正处于重负中。
    Connect Out
     复制从服务器正在连接主服务器。
    Copying to tmp table on disk
     由于临时结果集大于tmp_table_size,正在将临时表从内存存储转为磁盘存储以此节省内存。
    Creating tmp table
     正在创建临时表以存放部分查询结果。
    deleting from main table
     服务器正在执行多表删除中的第一部分,刚删除第一个表。
    deleting from reference tables
     服务器正在执行多表删除中的第二部分,正在删除其他表的记录。
    Flushing tables
     正在执行FLUSH TABLES,等待其他线程关闭数据表。
    Killed
     发送了一个kill请求给某线程,那么这个线程将会检查kill标志位,同时会放弃下一个kill请求。MySQL会在每次的主循环中检查kill标志位,不过有些情况下该线程可能会过一小段才能死掉。如果该线程程被其他线程锁住了,那么kill请求会在锁释放时马上生效。
    Locked
     被其他查询锁住了。
    Sending data
     正在处理SELECT查询的记录,同时正在把结果发送给客户端。
    Sorting for group
     正在为GROUP BY做排序。
     Sorting for order
     正在为ORDER BY做排序。
    Opening tables
     这个过程应该会很快,除非受到其他因素的干扰。例如,在执ALTER TABLE或LOCK TABLE语句行完以前,数据表无法被其他线程打开。正尝试打开一个表。
    Removing duplicates
     正在执行一个SELECT DISTINCT方式的查询,但是MySQL无法在前一个阶段优化掉那些重复的记录。因此,MySQL需要再次去掉重复的记录,然后再把结果发送给客户端。
    Reopen table
     获得了对一个表的锁,但是必须在表结构修改之后才能获得这个锁。已经释放锁,关闭数据表,正尝试重新打开数据表。
    Repair by sorting
     修复指令正在排序以创建索引。
    Repair with keycache
     修复指令正在利用索引缓存一个一个地创建新索引。它会比Repair by sorting慢些。
    Searching rows for update
     正在讲符合条件的记录找出来以备更新。它必须在UPDATE要修改相关的记录之前就完成了。
    Sleeping
     正在等待客户端发送新请求.
    System lock
     正在等待取得一个外部的系统锁。如果当前没有运行多个mysqld服务器同时请求同一个表,那么可以通过增加--skip-external-locking参数来禁止外部系统锁。
    Upgrading lock
     INSERT DELAYED正在尝试取得一个锁表以插入新记录。
    Updating
     正在搜索匹配的记录,并且修改它们。

    User Lock
     正在等待GET_LOCK()。
    Waiting for tables
     该线程得到通知,数据表结构已经被修改了,需要重新打开数据表以取得新的结构。然后,为了能的重新打开数据表,必须等到所有其他线程关闭这个表。以下几种情况下会产生这个通知:FLUSH TABLES tbl_name, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE,或OPTIMIZE TABLE。
    waiting for handler insert
     INSERT DELAYED已经处理完了所有待处理的插入操作,正在等待新的请求。
     大部分状态对应很快的操作,只要有一个线程保持同一个状态好几秒钟,那么可能是有问题发生了,需要检查一下。
     还有其他的状态没在上面中列出来,不过它们大部分只是在查看服务器是否有存在错误是才用得着。

    例如如图:

    3、explain来了解SQL执行的状态

    explain显示了mysql如何使用索引来处理select语句以及连接表。可以帮助选择更好的索引和写出更优化的查询语句。

    使用方法,在select语句前加上explain就可以了:

    例如:

    ?

    1

    explain select surname,first_name form a,b where a.id=b.id

    结果如图

    EXPLAIN列的解释

    table

    显示这一行的数据是关于哪张表的

    type

    这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL

    possible_keys

    显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从WHERE语句中选择一个合适的语句

    key

    实际使用的索引。如果为NULL,则没有使用索引。很少的情况下,MYSQL会选择优化不足的索引。这种情况下,可以在SELECT语句 中使用USE INDEX(indexname)来强制使用一个索引或者用IGNORE INDEX(indexname)来强制MYSQL忽略索引

    key_len

    使用的索引的长度。在不损失精确性的情况下,长度越短越好

    ref

    显示索引的哪一列被使用了,如果可能的话,是一个常数

    rows

    MYSQL认为必须检查的用来返回请求数据的行数

    Extra

    关于MYSQL如何解析查询的额外信息。将在表4.3中讨论,但这里可以看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢

     extra列返回的描述的意义

    Distinct

    一旦MYSQL找到了与行相联合匹配的行,就不再搜索了

    Not exists

    MYSQL优化了LEFT JOIN,一旦它找到了匹配LEFT JOIN标准的行,就不再搜索了

    Range checked for each Record(index map:#)

    没有找到理想的索引,因此对于从前面表中来的每一个行组合,MYSQL检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一

    Using filesort

    看到这个的时候,查询就需要优化了。MYSQL需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行

    Using index

    列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候

    Using temporary

    看到这个的时候,查询需要优化了。这里,MYSQL需要创建一个临时表来存储结果,这通常发生在对不同的列集进行ORDER BY上,而不是GROUP BY上

    Where used

    使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题不同连接类型的解释(按照效率高低的顺序排序)

    const

    表中的一个记录的最大值能够匹配这个查询(索引可以是主键或惟一索引)。因为只有一行,这个值实际就是常数,因为MYSQL先读这个值然后把它当做常数来对待

    eq_ref

    在连接中,MYSQL在查询时,从前面的表中,对每一个记录的联合都从表中读取一个记录,它在查询使用了索引为主键或惟一键的全部时使用

    ref

    这个连接类型只有在查询使用了不是惟一或主键的键或者是这些类型的部分(比如,利用最左边前缀)时发生。对于之前的表的每一个行联合,全部记录都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少—越少越好

    range

    这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西时发生的情况

    index

    这个连接类型对前面的表中的每一个记录联合进行完全扫描(比ALL更好,因为索引一般小于表数据)

    ALL

    这个连接类型对于前面的每一个记录联合进行完全扫描,这一般比较糟糕,应该尽量避免

    MySQL - 查看慢SQL
    查看MySQL是否启用了查看慢SQL的日志文件

    (1) 查看慢SQL日志是否启用

    mysql> show variables like 'log_slow_queries'; 
    +------------------+-------+
    | Variable_name    | Value |
    +------------------+-------+
    | log_slow_queries | ON    |
    +------------------+-------+
    1 row in set (0.00 sec)

    (2) 查看执行慢于多少秒的SQL会记录到日志文件中
    mysql> show variables like 'long_query_time';
    +-----------------+-------+
    | Variable_name   | Value |
    +-----------------+-------+
    | long_query_time | 1     |   
    +-----------------+-------+
    1 row in set (0.00 sec)

    这里value=1, 表示1秒

    2. 配置my.ini文件(inux下文件名为my.cnf), 查找到[mysqld]区段,增加日志的配置,如下示例:
    [mysqld]
    log="C:/temp/mysql.log"
    log_slow_queries="C:/temp/mysql_slow.log"
    long_query_time=1
    log指示日志文件存放目录;
    log_slow_queries指示记录执行时间长的sql日志目录;
    long_query_time指示多长时间算是执行时间长,单位s。
    Linux下这些配置项应该已经存在,只是被注释掉了,可以去掉注释。但直接添加配置项也OK啦。
    查询到效率低的 SQL 语句 后,可以通过 EXPLAIN 或者 DESC 命令获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序,比如我们想计算 2006 年所有公司的销售额,需要关联 sales 表和 company 表,并且对 profit 字段做求和( sum )操作,相应 SQL 的执行计划如下: 
    mysql> explain select sum(profit) from sales a,company b where a.company_id = b.id and a.year = 2006\G; 
    *************************** 1. row *************************** 
    id: 1 
    select_type: SIMPLE 
    table: a 
    type: ALL 
    possible_keys: NULL 
    key: NULL 
    key_len: NULL 
    ref: NULL 
    rows: 12 
    Extra: Using where 
    *************************** 2. row *************************** 
    id: 1 
    select_type: SIMPLE 
    table: b 
    type: ALL 
    possible_keys: NULL 
    key: NULL 
    key_len: NULL 
    ref: NULL 
    rows: 12 
    Extra: Using where 
    2 rows in set (0.00 sec)
    每个列的解释如下:
    •select_type :表示 SELECT 的 类型,常见的取值有 SIMPLE (简单表,即不使用表连接或者子查询)、 PRIMARY (主查询,即外层的查询)、 UNION ( UNION 中的第二个或者后面的查询语句)、 SUBQUERY (子查询中的第一个 SELECT )等。
    •table :输出结果集的表。
    •type :表示表的连接类型,性能由好到差的连接类型为 system (表中仅有一行,即常量表)、 const (单表中最多有一个匹配行,例如 primary key 或者 unique index )、 eq_ref (对于前面的每一行,在此表中只查询一条记录,简单来说,就是多表连接中使用 primary key 或者 unique index )、 ref (与 eq_ref 类似,区别在于不是使用 primary key 或者 unique index ,而是使用普通的索引)、 ref_or_null ( 与 ref 类似,区别在于条件中包含对 NULL 的查询 ) 、 index_merge ( 索引合并优化 ) 、 unique_subquery ( in 的后面是一个查询主键字段的子查询)、 index_subquery ( 与 unique_subquery 类似,区别在于 in 的后面是查询非唯一索引字段的子查询)、 range (单表中的范围查询)、 index (对于前面的每一行,都通过查询索引来得到数据)、 all (对于前面的每一行,都通过全表扫描来得到数据)。
    •possible_keys :表示查询时,可能使用的索引。 
    •key :表示实际使用的索引。 
    •key_len :索引字段的长度。 
    •rows :扫描行的数量。 
    •Extra :执行情况的说明和描述。
    在上面的例子中,已经可以确认是 对 a 表的全表扫描导致效率的不理想,那么 对 a 表的 year 字段创建索引,具体如下:
    mysql> create index idx_sales_year on sales(year); 
    Query OK, 12 rows affected (0.01 sec) 
    Records: 12 Duplicates: 0 Warnings: 0
    创建索引后,这条语句的执行计划如下: 
    mysql> explain select sum(profit) from sales a,company b where a.company_id = b.id and a.year = 2006\G; 
    *************************** 1. row *************************** 
    id: 1 
    select_type: SIMPLE 
    table: a 
    type: ref 
    possible_keys: idx_sales_year 
    key: idx_sales_year 
    key_len: 4 
    ref: const 
    rows: 3 
    Extra: 
    *************************** 2. row *************************** 
    id: 1 
    select_type: SIMPLE 
    table: b 
    type: ALL 
    possible_keys: NULL 
    key: NULL 
    key_len: NULL 
    ref: NULL 
    rows: 12 
    Extra: Using where 
    2 rows in set (0.00 sec)

    可以发现建立索引后对 a 表需要扫描的行数明显减少(从全表扫描减少到 3 行),可见索引的使用可以大大提高数据库的访问速度,尤其在表很庞大的时候这种优势更为明显,使用索引优化 sql 是优化问题 sql 的一种常用基本方法,在后面的章节中我们会具体介绍如何使索引来优化 sql 。
    本文主要介绍的是MySQL慢查询分析方法,前一段日子,我曾经设置了一次记录在MySQL数据库中对慢于1秒钟的SQL语句进行查询。想起来有几个十分设置的方法,有几个参数的名称死活回忆不起来了,于是重新整理一下,自己做个笔记。
      对于排查问题找出性能瓶颈来说,最容易发现并解决的问题就是MySQL慢查询以及没有得用索引的查询。

      OK,开始找出MySQL中执行起来不“爽”的SQL语句吧。
      MySQL慢查询分析方法一:
      这个方法我正在用,呵呵,比较喜欢这种即时性的。
      MySQL5.0以上的版本可以支持将执行比较慢的SQL语句记录下来。
      MySQL> show variables like 'long%';
      注:这个long_query_time是用来定义慢于多少秒的才算“慢查询”
      +-----------------+-----------+
      | Variable_name | Value |
      +-----------------+-----------+
      | long_query_time | 10.000000 |
      +-----------------+-----------+
      1 row in set (0.00 sec)
      MySQL> set long_query_time=1;
      注: 我设置了1, 也就是执行时间超过1秒的都算慢查询。
      Query OK, 0 rows affected (0.00 sec)
      MySQL> show variables like 'slow%';
      +---------------------+---------------+
      | Variable_name | Value |
      +---------------------+---------------+
      | slow_launch_time | 2 |
      | slow_query_log | ON |
      注:是否打开日志记录
      | slow_query_log_file | /tmp/slow.log |
      注: 设置到什么位置
      +---------------------+---------------+
      3 rows in set (0.00 sec)
      MySQL> set global slow_query_log='ON'
      注:打开日志记录
      一旦slow_query_log变量被设置为ON,MySQL会立即开始记录。
      /etc/my.cnf 里面可以设置上面MySQL全局变量的初始值。
      long_query_time=1 slow_query_log_file=/tmp/slow.log
      MySQL慢查询分析方法二:
      MySQLdumpslow命令
      /path/MySQLdumpslow -s c -t 10 /tmp/slow-log
      这会输出记录次数最多的10条SQL语句,其中:
      -s, 是表示按照何种方式排序,c、t、l、r分别是按照记录次数、时间、查询时间、返回的记录数来排序,ac、at、al、ar,表示相应的倒叙;
      -t, 是top n的意思,即为返回前面多少条的数据;
      -g, 后边可以写一个正则匹配模式,大小写不敏感的;
      比如
      /path/MySQLdumpslow -s r -t 10 /tmp/slow-log
      得到返回记录集最多的10个查询。
      /path/MySQLdumpslow -s t -t 10 -g “left join” /tmp/slow-log
      得到按照时间排序的前10条里面含有左连接的查询语句。
     
    简单点的方法:
    打开 my.ini ,找到 [mysqld] 在其下面添加   long_query_time = 2 log-slow-queries = D:/mysql/logs/slow.log #设置把日志写在那里,可以为空,系统会给一个缺省的文件 #log-slow-queries = /var/youpath/slow.log linux下host_name-slow.log log-queries-not-using-indexes   long_query_time 是指执行超过多长时间(单位是秒)的sql会被记录下来,这里设置的是2秒。
    以下是mysqldumpslow常用参数说明,详细的可应用mysqldumpslow -help查询。   -s,是表示按照何种方式排序,c、t、l、r分别是按照记录次数、时间、查询时间、返回的记录数来排序(从大到小),ac、at、al、ar表示相应的倒叙。   -t,是top n的意思,即为返回前面多少条数据。 www.jb51.net  -g,后边可以写一个正则匹配模式,大小写不敏感。   接下来就是用mysql自带的慢查询工具mysqldumpslow分析了(mysql的bin目录下 ),我这里的日志文件名字是host-slow.log。   列出记录次数最多的10个sql语句   mysqldumpslow -s c -t 10 host-slow.log   列出返回记录集最多的10个sql语句  mysqldumpslow -s r -t 10 host-slow.log   按照时间返回前10条里面含有左连接的sql语句   mysqldumpslow -s t -t 10 -g "left join" host-slow.log   使用mysqldumpslow命令可以非常明确的得到各种我们需要的查询语句,对MySQL查询语句的监控、分析、优化起到非常大的帮助
     
    在日常开发当中,经常会遇到页面打开速度极慢的情况,通过排除,确定了,是数据库的影响,为了迅速查找具体的SQL,可以通过Mysql的日志记录方法。
    -- 打开sql执行记录功能
    set global log_output='TABLE'; -- 输出到表
    set global log=ON; -- 打开所有命令执行记录功能general_log, 所有语句: 成功和未成功的.
    set global log_slow_queries=ON; -- 打开慢查询sql记录slow_log, 执行成功的: 慢查询语句和未使用索引的语句
    set global long_query_time=0.1; -- 慢查询时间限制(秒)
    set global log_queries_not_using_indexes=ON; -- 记录未使用索引的sql语句
    -- 查询sql执行记录
    select * from mysql.slow_log order by 1; -- 执行成功的:慢查询语句,和未使用索引的语句
    select * from mysql.general_log order by 1; -- 所有语句: 成功和未成功的.
    -- 关闭sql执行记录
    set global log=OFF;
    set global log_slow_queries=OFF;
    -- long_query_time参数说明
    -- v4.0, 4.1, 5.0, v5.1 到 5.1.20(包括):不支持毫秒级别的慢查询分析(支持精度为1-10秒);
    -- 5.1.21及以后版本 :支持毫秒级别的慢查询分析, 如0.1;
    -- 6.0 到 6.0.3: 不支持毫秒级别的慢查询分析(支持精度为1-10秒);
    -- 6.0.4及以后:支持毫秒级别的慢查询分析;
    通过日志中记录的Sql,迅速定位到具体的文件,优化sql看一下,是否速度提升了呢?
     
    本文针对MySQL数据库服务器查询逐渐变慢的问题, 进行分析,并提出相应的解决办法,具体的分析解决办法如下:会经常发现开发人员查一下没用索引的语句或者没有limit n的语句,这些没语句会对数据库造成很大的影...
      
    本文针对MySQL数据库服务器查询逐渐变慢的问题, 进行分析,并提出相应的解决办法,具体的分析解决办法如下:
    会经常发现开发人员查一下没用索引的语句或者没有limit n的语句,这些没语句会对数据库造成很大的影响,例如一个几千万条记录的大表要全部扫描,或者是不停的做filesort,对数据库和服务器造成io影响等。这是镜像库上面的情况。
    而到了线上库,除了出现没有索引的语句,没有用limit的语句,还多了一个情况,mysql连接数过多的问题。说到这里,先来看看以前我们的监控做法 
    1. 部署zabbix等开源分布式监控系统,获取每天的数据库的io,cpu,连接数 
    2. 部署每周性能统计,包含数据增加量,iostat,vmstat,datasize的情况 
    3. Mysql slowlog收集,列出top 10
    以前以为做了这些监控已经是很完美了,现在部署了mysql节点进程监控之后,才发现很多弊端 
    第一种做法的弊端: zabbix太庞大,而且不是在mysql内部做的监控,很多数据不是非常准备,现在一般都是用来查阅历史的数据情况 
    第二种做法的弊端:因为是每周只跑一次,很多情况没法发现和报警 
    第三种做法的弊端: 当节点的slowlog非常多的时候,top10就变得没意义了,而且很多时候会给出那些是一定要跑的定期任务语句给你。。参考的价值不大 
    那么我们怎么来解决和查询这些问题呢
    对于排查问题找出性能瓶颈来说,最容易发现并解决的问题就是MYSQL的慢查询以及没有得用索引的查询。 
    OK,开始找出mysql中执行起来不“爽”的SQL语句吧。

    方法一: 这个方法我正在用,呵呵,比较喜欢这种即时性的。

    Mysql5.0以上的版本可以支持将执行比较慢的SQL语句记录下来。

     mysql> show variables like 'long%'; 注:这个long_query_time是用来定义慢于多少秒的才算“慢查询”
     +-----------------+-----------+
     | Variable_name | Value |
     +-----------------+-----------+
     | long_query_time | 10.000000 |
     +-----------------+-----------+
     1 row in set (0.00 sec)
     mysql> set long_query_time=1; 注: 我设置了1, 也就是执行时间超过1秒的都算慢查询。
     Query OK, 0 rows affected (0.00 sec)
     mysql> show variables like 'slow%';
     +---------------------+---------------+
     | Variable_name | Value |
     +---------------------+---------------+
     | slow_launch_time | 2 |
     | slow_query_log | ON | 注:是否打开日志记录
     | slow_query_log_file | /tmp/slow.log | 注: 设置到什么位置
     +---------------------+---------------+
     3 rows in set (0.00 sec)
     mysql> set global slow_query_log='ON' 注:打开日志记录
     一旦slow_query_log变量被设置为ON,mysql会立即开始记录。
     /etc/my.cnf 里面可以设置上面MYSQL全局变量的初始值。
     long_query_time=1
     slow_query_log_file=/tmp/slow.log

    方法二:mysqldumpslow命令

     /path/mysqldumpslow -s c -t 10 /tmp/slow-log
     这会输出记录次数最多的10条SQL语句,其中:
     -s, 是表示按照何种方式排序,c、t、l、r分别是按照记录次数、时间、查询时间、返回的记录数来排序,ac、at、al、ar,表示相应的倒叙;
     -t, 是top n的意思,即为返回前面多少条的数据;
     -g, 后边可以写一个正则匹配模式,大小写不敏感的;
     比如
     /path/mysqldumpslow -s r -t 10 /tmp/slow-log
     得到返回记录集最多的10个查询。
     /path/mysqldumpslow -s t -t 10 -g “left join” /tmp/slow-log
     得到按照时间排序的前10条里面含有左连接的查询语句。
    最后总结一下节点监控的好处 
    1. 轻量级的监控,而且是实时的,还可以根据实际的情况来定制和修改 
    2. 设置了过滤程序,可以对那些一定要跑的语句进行过滤 
    3. 及时发现那些没有用索引,或者是不合法的查询,虽然这很耗时去处理那些慢语句,但这样可以避免数据库挂掉,还是值得的 
    4. 在数据库出现连接数过多的时候,程序会自动保存当前数据库的processlist,DBA进行原因查找的时候这可是利器
    5. 使用mysqlbinlog 来分析的时候,可以得到明确的数据库状态异常的时间段 
    有些人会建义我们来做mysql配置文件设置

    调节tmp_table_size 的时候发现另外一些参数 
    Qcache_queries_in_cache 在缓存中已注册的查询数目 
    Qcache_inserts 被加入到缓存中的查询数目 
    Qcache_hits 缓存采样数数目 
    Qcache_lowmem_prunes 因为缺少内存而被从缓存中删除的查询数目 
    Qcache_not_cached 没有被缓存的查询数目 (不能被缓存的,或由于 QUERY_CACHE_TYPE) 
    Qcache_free_memory 查询缓存的空闲内存总数 
    Qcache_free_blocks 查询缓存中的空闲内存块的数目 
    Qcache_total_blocks 查询缓存中的块的总数目 
    Qcache_free_memory 可以缓存一些常用的查询,如果是常用的sql会被装载到内存。那样会增加数据库访问速度

     

    参考文献:

    【1】浅谈MySQL中优化sql语句查询常用的30种方法

    【2】谈谈如何优化MYSQL数据库查询

    【3】大牛的经验之谈:数据库查询速度优化技巧及解决方案

    【4】MySQL查询优化方法

    【5】查找MySQL中查询慢的SQL语句方法

     

    展开全文
  • vue关闭当前页面 跳转_vue常见面试题

    千次阅读 2020-11-19 23:08:33
    因为避免了压缩直接进行上传,在打包时会提高一定的效率,但是static中的资源文件由于没有进行压缩等操作,所以文件的体积也就相对于assets中打包后的文件提交大点。在服务器中就会占据更大的空间。 建议:将项目...
  • 拦截H5页面的资源网络请求,并对其进行检测:如果检测到本地具有相同的静态资源,则直接从本地读取,否则发送该资源的网络请求到原始服务器获取 具体实现 重写WebViewClient 的 shouldInterceptRequest 方法,...
  • oracle视图查询速度

    千次阅读 2021-05-06 02:21:03
    Oracle 的 SQL 语句执行效率问题查找与解决方法一、识别占用资源较多的语句的方法(4 种方法) 1. 测试组和最终用户反馈的与反应缓慢有关的问题。 2. 利用 V_$SQLAREA 视图提供了执行的细节。 (执行、读取磁盘和读取...
  • 为什么要获取App资源? 真实开发中,实现一个App里面会用到各种图片资源(包括图标、背景图片等等),这些资源通常是美工提供给我们,我们直接使用即可。但是,如果我们想要模仿一个App,也没有真正的美工人员提供给...
  • 很悲催的事情,在使用ImageList做开发的时候需要用到其中的文件名,那个Name属性也不能用...//index就是要获取文件名的索引  在此做以笔记,以方便后来人,本来很小的一个问题搞了近一个小时。。。。。。
  • 资源依赖关系查询工具

    千次阅读 2017-08-01 16:37:56
    编写目的:学习unity的sprite packer打图集后,发现unity没有提供一个工具去进行查找指定路径资源的双向引用(哪些资源引用的自己以及自己引用了哪些资源),也没有工具去替换指定资源的引用,这样当删除资源或者在工程...
  • 但我知道你一定在烦恼 Android Webview 的性能问题,特别突出的是:加载速度 &amp; 消耗流量 今天,我将针对 Android Webview 的性能问题,提出一些有效解决方案。 目录 1. Andr...
  • 对于前端页面来说,静态资源的加载对页面性能起着至关重要的作用。本文将介绍浏览器提供的两个资源指令-preload/prefetch,它们能够辅助浏览器优化资源加载的顺序和时机,提升页面性能。 一、从一个实例开始 ...
  • JedisPool资源池优化

    万次阅读 2018-03-19 20:23:43
    背景合理的JedisPool资源池参数设置能为业务使用Redis保驾护航,本文将对JedisPool的使用、资源池的参数进行详细说明,最后给出“最合理”配置。一、使用方法以官方的2.9.0为例子(Jedis Release),Maven依赖如下:&...
  • HTTP/1.1 有点,我想优化下!

    千次阅读 2021-02-24 15:49:42
    请求网页的时候,没必要把全部资源获取到,而是只获取当前用户所看到的页面资源,当用户向下滑动页面的时候,再向服务器获取接下来的资源,这样就达到了延迟发送请求的效果。 3 如何减少 HTTP 响应的数据大小? ...
  • oracle中sql优化思路

    千次阅读 2020-07-04 09:59:18
    按照某top sql维度从awr基表中批量获取慢SQL 适合做营销活动前主动的从awr资料库里面抓取最近几天的所有SQL select dbms_lob.substr(sql_text, 100) sqla, AA.*, BB.SQL_TEXT from (select sql_id, plan_hash_...
  • Oracle资源管理器在HIS系统中的应用

    千次阅读 2014-03-20 17:02:15
    【摘要】系统管理员是否遇到过执行某个大数据量统计报表查询或者进行全库RMAN备份的时候,导致整个HIS系统的操作变甚至影响正常业务使用的情况,其实这是典型的服务器资源争用和等待的案例,如何合理的分配和管理...
  • 的响应时间 (slow response time) CPU 空闲时间为零 (zero percent idle CPU) 过高的用户占用 CPU 时间 ( %User Time ) 过高的系统占用 CPU 时间 (%Priviliaged Time :长期大于 90% ...
  • WebView 缓存机制和资源加载方案

    千次阅读 2018-05-23 18:52:04
    1. Android WebView 存在什么性能问题? Android WebView 里 H5 页面加载速度 耗费流量 下面会详细介绍。 1.1 H5 页面加载速度 ...H5 页面加载速度的原因 ...1.1.1 渲染速度 ... 前端页面涉及多 JS 代码...
  • Windows为什么越用越而Linux却不会?

    万次阅读 多人点赞 2019-07-25 22:07:34
    别人我不知道,反正我的经验,Windows就是越用越。这是为什么呢? 本文是下文的姊妹篇: 为什么Windows/iOS操作很流畅而Linux/Android却很卡顿呢? https://blog.csdn.net/dog250/article/details/96362789 所以,...
  • android 原生使用WebView嵌入H5页面 Hybrid开发 一、性能问题 android webview 里H5加载速度 网络流量大 1、H5页面加载速度 ...资源加载 H5页面的资源多 网络请求数量多 ​ H5页面所有资源都需要从网络...
  • 这种模型在读取XML文档时,并没有将整个文档读入内存,而是按顺序将整个文档解析完,在解析过程中,会主动产生事件交给程序中相应的处理函数来处理当前内容。因此这种方式对系统资源要求不高,可以处理海量数据。...
  • ios_swift开发资源整理

    万次阅读 2015-05-28 16:35:33
    当前版本:V1.1.0 最后更新日期:2015.06.28(ps:这兴起事情有点多更新有点少) 注:[New]表示更新内容,此外有优秀文章、资源、框架等等,都可以推荐给我,发送到我的邮箱。 邮箱地址链接:mcxcode@163.com ...
  • 下面是网络中流传最广的一篇sql查询速度的原因及解决方法的文章,其对于处理mysql的查询有借鉴作用。由于此文转载多次,很难找到最开始的原文链接,就附送本人最先看到此文的链接:...
  • 资源编目与规范

    千次阅读 2010-11-10 08:52:00
    <br /> 浅析利用MARC格式进行网络资源编目 <br />[摘要]探析了网络环境下MARC格式的结构、特点和编目的优势及实际应用。 [关键词]MARC格式元数据网络资源编目 [分类号]G254.36 随着...
  • VS2019 团队资源管理器--Git的使用(二)

    千次阅读 2020-07-03 22:44:37
    比如你Clone好了以后, 有人在Github上提交了一个bug修复, 你在当前的镜像地址是没法及时获取到这次提交的. 解决的方法也有, 下面我们先把Opencv的仓库Clone下来, 再来讲怎么解决这个问题. 这个镜像下载的速度真的是...
  • Unity3d 资源加载与内存管理

    千次阅读 2015-04-18 15:22:10
    但是速度比较 或者: IEnumerator OnClick() { WWW image = new www(fileList[n++]) ; yield return image; Texture tex = obj.mainTexture; obj.mainTexture = image.texture; n = (n>=...
  • mysql数据库访问速度过和解决方案

    万次阅读 2019-04-24 16:22:03
    一、检查系统的状态  通过操作系统的一些工具检查系统的状态,...除此之外,还应观注那些占用系统资源(cpu、内存)的进程。  1.使用sar来检查操作系统是否存在IO问题 #sar-u210— 即每隔2秒检察一次,共执行20...
  • Linux监控工具以及监控详解(资源

    万次阅读 2018-07-04 15:37:16
    Sy的值高时,说明内核消耗的CPU资源很多。 根据经验,us+sy的参考值为80%,如果us+sy大于 80%说明可能存在CPU资源不足。   (2)利用sar命令监控系统CPU sar功能很强大,可以对系统的每个方面进行单独的统计,...
  • LoadRunner监控windows资源分析

    千次阅读 2016-05-03 14:20:25
    的响应时间 (slow response time) CPU 空闲时间为零 (zero percent idle CPU) 过高的用户占用 CPU 时间 ( %User Time ) 过高的系统占用 CPU 时间 (%Priviliaged Time :长期大于 90% ...
  • 系统资源详解

    千次阅读 2012-08-02 15:10:05
    的响应时间(slow response time) CPU空闲时间为零(zero percent idle CPU) 过高的用户占用CPU时间(%User Time) 过高的系统占用CPU时间(%Priviliaged Time:长期大于90%或者95%) 长时间的有很长的运行...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 127,702
精华内容 51,080
关键字:

当前读取资源较慢