MySQL优化

1. 优化数据库对象

1)选择表合适存储引擎:

MyISAM存储引擎:

        应用时以读和插入操作为主,只有少量的更新和删除,并且对事务的完整性,并发性要求不是很高的。

      * 表锁:MyISAM表有表级锁,注意不要让它成为瓶颈。

      * 不支持自动数据恢复如果MySQL服务器崩溃或掉电,就应该在使用之前进行检查和执行可能的修复。如果有大型表,这可能会花几个小时。

       * 不支持事务MyISAM表不支持事务。实际上,MyISAM甚至不保证单个命令完成。如果在多行UPDATER的中途有错误发生,一些行会被更新,而另外一些行则不会。

       * 只有索引被缓存在内存中MyISAM只缓存了MySQL进程内部的索引,并保存在键缓冲区。操作系统缓存了表的数据,因此在MySQL5.0中须进行昂贵的系统调用来取得它。

       * 紧密存储行被紧紧保存在一起,这样磁盘上的数据就能得到小的磁盘占用和快速的全表扫描。

InnoDB存储引擎:

          事务处理,以及并发条件下要求数据的一致性。除了插入和查询外,包括很多的更新和删除。(Innodb有效地降低删除和更新导致的锁定)。对于支持事务的InnoDB类型的表来说,影响速度的主要原因是AUTOCOMMIT默认设置是打开的,而且程序没有显式调用BEGIN 开始事务,导致每插入一条都自动提交,严重影响了速度。可以在执行sql前调用begin,多条sql形成一个事物(即使autocommit打开也可以),将大大提高性能。

       * 事务性:InnoDB支持事务和四种事务隔离级别

       * 外键:在MySQL5.0中,InnoDB是唯一支持外键的存储引擎。

       * 行级锁:锁设定于行一级,不会向上传递并且也不会阻塞选择——标准选择根本不会设定任何锁,它有很好的并发特性。

       * 多版本:InnoDB使用多版本并发控制,这样在默认情况下可能会选择读取陈旧的数据。

       * 按主键聚集:所有的InnoDB表都是按主键聚集的,可以在架构设计中运用这一点。

       * 所有索引包含主键列:索引按照主键引用行,因此,如果不把主键维持得很短,索引就增长得很长。

       * 优化的缓存:InnoDB把数据和内存缓存在缓冲区池里。它也会自动构建哈希索引以加快行读取。

       * 未压缩的索引:索引没有使用前缀压缩,因此可能会比MyISAM表的索引大很多。

       * 数据装载缓慢:在MySQL5.0中,InnoDB不会特别优化数据加载。它一次构建一行的索引,而不是按照排序进行构建。这会导致数据加载很慢。

       * 阻塞AUTO_INCREMENT:在MySQL5.1之前的版本中,InnoDB使用了表级锁来产生每个新的AUTO_INCREMENT值。这在高并发的情况,AUTO_INCREMENTE有可能成为热点。

       * 没有缓存的COUNT(*)值:和MyISAM表或Memory表不同,InnoDB表不会把表的行数保存在表中,这意味着没有WHERE子句的COUNT(*)查询不会被优化掉,并且需要全表或索引扫描。

Memory存储引擎:

      数据保存在RAM,可以快速访问数据。但要求表不能太大或者对mysql异常终止后不用恢复数据的情况。

       *  表锁:支持表级锁。但这通常不是问题,因为Memory表上的查询都较快。

       * 不支持动态行:也就是可变长度行,因此它不支持BLOB和TEXT字段。即使VARCHAR(5000)也会变成CHAR(5000)。

       * 哈希索引是默认索引类型:

       * 没有索引统计:

       * 重启后丢失数据:Memory表不会把任何数据持久到磁盘,因此,当服务器重启后即使表的定义还在,数据也会丢失。

      
2)选择合适的数据类型:

           数据类型的选择原则:更小通常更好,数据类型简单就好,所有字段应有默认值,尽量避免NULL。

        MySQL难以优化引用了可空列的查询,它会使索引、索引统计和值更加复杂,同样,同空列需要更多的存储空间,还需要在MySQL内部进行特殊处理。

        一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。

        MySQL可以对整数类型定义宽度,这对大多数应用程序是没有意义的,它不会限制值的范围,只规定了MySQL的交互工具(如客户端)用来显示字符的个数。对于存储和计算,INT(1)和INT(20)是一样的。

        对于int类型的,如果不需要存取负值,最好加上unsigned;对于经常出现在where语句中的字段,考虑加索引,整型的尤其适合加索引。

       MySQL同时支持精确和非精确类型的实数,但由于需要额外的空间和计算开销,只有在需要对小数进行精确计算的时候才使用DECIMAL,比如保存金融数据。

       注意的是:FLOAT或DOUBLE数据类型与具有数值类型的数值进行比较,不能使用等式(=)比较。这是因为FLOAT和DOUBLE类型支持使用标准的浮点运算进行近似计算,会存在浮点数精度的问题,从而产生误差。但浮点运算由于可直接在CPU上运行,故浮点运算会比DECIMAL运算快一些。

      MySQL提供两种相似的时间数据类型:DATETIME和TIMESTAMP。DATETIME占用8个字节,而TIMESTAMP占用4个字节,只用了一半,同时TIMESTAMP表示的范围是1970—2037适合做更新时间,通常应该使用TIMESTAMP。

       在字符串数据类型char,varchar,text的选择上,应考虑:
       (1)长度的区别,char范围是0~255,varchar最长是64k,但是注意这里的64k是整个row的长度,要考虑到其它的column,还有如果存在not null的时候也会占用一位,对不同的字符集,有效长度还不一样,比如utf8的,最多21845,还要除去别的column,但是varchar在一般情况下存储都够用了。如果遇到了大文本,考虑使用text,最大能到4G。 

       (2) 效率来说基本是char>varchar>text,但是如果使用的是Innodb引擎的话,推荐使用varchar代替

       (3)默认值  charchar和varchar可以有默认值,text不能指定默认值

       BLOB和TEXT分别以二进制和字符形式保存大量数据。BLOB和TEXT唯一的区是BLOG保存的是二进制数据,没有字符集和排序规则,而TEXT有字符集和排序规则。MySQL不能索引BLOB和TEXT的完整长度,也不能为排序使用索引。

       有时可以使用ENUM列来代替传统的字符串类型。MySQL会把ENUM列压缩到1到2个字节中,可以存储65535个不同的字符串。在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。

        对大多数程序,最好能避免使用BIT类型。如果只想保存一位的true/false,可创建一个可空的CHAR(0)列,它能保存空值null或0长度的值(空字符串)。

      数据库选择合适的数据类型存储还是很有必要的,越小的列通常会更快,一旦选择了数据类型,要确保在相关表中使用同样的类型。类型之间要精确匹配,包括诸如UNSIGNED这样的属性。混合不同的数据类型会导致性能问题,即使没有性能问题,隐式的类型转换也能导致难以察觉的错误。

3)选择正常的主键类型

        主键类型的选择不仅要考虑存储类型,还要考虑MySQL如何对它们进行计算和比较。

        主要原则是选择最小的数据类型满足所需值的范围,并且为将来留出增长空间。

         *  整数类型:整数通常是标识符的最佳选择,因为它速度快,并且能使用AUTO_INCREMENT。

         *  ENUM和SET:ENUM和SET通常不适合用作标识符。ENUM和SET列适合用来保存订单的状态、产品的类型或性别这样的信息。

         * 字符串类型:尽可能避免使用字符串来做标识符。特别注意不要在MyISAM表上使用字符串标识符。MyISAM默认情况下为字符串使用压缩索引(Packed Index),这使查找更为缓慢。

        还要特别注意的是完全“随机”的字符串,例如由MD5()、SHAL()或UUID()产生的,它们产生的每一个新值都会被任意地保存在很大的空间范围内,这会减慢INSERT及一些SELECT查询。

        随机值会导致缓存对所有类型的查询性能都很差。如果整个数据集都变得同样“热”的时候,那么把特定部分的数据缓存到内存中就没有任何的优势,并且如果工作集不能被装入内存中,缓存就会进行很多刷写的工作,并且会导致很多缓存未命中。

        如果需要保存UUID值,就应该移除其中的短横线,更好的办法是使用UHEX()把UUID值转化为16字节的数字,并把它保存在BINARY(16)列中。可以使用HEX(0)函数以十六进制数的格式把值提取出来。

        使用UUID()产生的值和其他哈希函数得到的值比起来有特殊的性质:UUID值不是均匀分布的,并且有一定程度的顺序性。但它还是比不上单调递增的整数。

4)选择合适的字符编码

        字符集直接决定了数据在MySQL中的存储编码方式,由于同样的内容使用不同字符集表示所占用的空间大小会有较大的差异,所以通过使用合适的字符集,可以帮助我们尽可能减少数据量,进而减少IO操作次数。

  1.纯拉丁字符能表示的内容,没必要选择 latin1 之外的其他字符编码,因为这会节省大量的存储空间。

  2.如果我们可以确定不需要存放多种语言,就没必要非得使用UTF8或者其他UNICODE字符类型,这回造成大量的存储空间浪费。

  3.MySQL的数据类型可以精确到字段,所以当我们需要大型数据库中存放多字节数据的时候,可以通过对不同表不同字段使用不同的数据类型来较大程度减小数据存储量,进而降低 IO 操作次数并提高缓存命中率。

5)正则化与非正则化(适当冗余)

        正则化优势

           *  正则化更新通常比非正则化更新快

           *  当数据被很好地正则化之后,就很少,甚至没有重复数据,因此改动的数据会为少。

           *  正则化表通常较小,因此更容易被装入内存并且性能更好。

           *  由于缺少冗余数据,那么在取得数据的时候会较少采用DISTINCT或GROUP BY。

        缺点在于数据抓取,任何一个在正则化架构上的非一般性查询都至少需要一个联接,或者多个。例如:下列场景中:

  1.被频繁引用且只能通过 Join 2张(或者更多)大表的方式才能得到的独立小字段。

  2.由于每次Join仅仅只是为了取得某个小字段的值,Join到的记录又大,会造成大量不必要的 IO。

       这种情况,可以通过空间换取时间的方式来优化,即通过冗余的方式(非正则化架构)实现。

       非正则化的优势

       *  非正则化架构由于所有数据都在一个表里,避免了联接,所以性能不错。

       *  如果不需要联接表,对于大多数查询,最坏情况——甚至不使用索引的查询——是全表扫描。在数据不能被装入数据库的时候,这会比联接快得多,因为它避免了随机I/O。

        不过,冗余的同时需要确保不会破坏数据的一致性,即确保更新的同时冗余字段也被更新。

        在实际中,通常要结合正则化和非正则化,也许是采用部分正则化架构、缓存表及其他技巧。非正则化数据最常见的技巧是复制、缓存,把一个表中的数据选到另一个表中。

        注:normalize 就是为了减少数据的容余,提高数据的一致性而规范化的一个操作,一般认为达到第三范式,数据库的设计就比较规范了(从第一范式到第三范式,数据的冗余越来越少)。 而denormalize 就是反规范化,即人为在表上添加一些冗余信息。

6)在InnoDB数据表设计中,需要注意几点:

    1.   显式的定义一个 INT 类型自增字段的主键,这个字段可以仅用于做主键,不做其他用途
    2.   如果不显式定义主键的话,可能会导致InnoDB每次都需要对新数据行进行排序,严重损害性能
    3.   尽量保证不对主键字段进行更新修改,防止主键字段发生变化,引发数据存储碎片,降低IO性能
    4.   如果需要对主键字段进行更新,请将该字段转变成一个唯一索引约束字段,另外创建一个没有其他业务意义的自增字段做主键
    5.   主键字段类型尽可能小,能用SMALLINT就不用INT,能用INT就不用BIGINT
    6.   主键字段放在数据表的第一顺序

2. 优化索引

  索引对于高性能非常关键,建立索引(Indexing)是现实中性能问题的首要问题。索引可以令数据库服务器以比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(), MIN()和ORDERBY这些命令的时候。

        通常,在联接表或复杂搜索条件的情况下,索引带来的性能提升远比它所占据的磁盘空间要重要得多的时候,就需要考虑建立索引。

      1、如果每次都需要取到所有表记录,无论如何都必须进行全表扫描了,那么是否加索引也没有意义了。

     2、对非唯一的字段,例如“性别”这种大量重复值的字段,增加索引也没有什么意义。

     3、对于记录比较少的表,增加索引不会带来速度的优化反而浪费了存储空间,因为索引是需要存储空间的,而且有个致命缺点是对于update/insert/delete的每次执行,字段的索引都必须重新计算更新。

         通常我们建立的索引包括:

1)普通索引

        普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度。因此,应该只为那些最经常出现在查询条件(WHERE column = …)或排序条件(ORDER BY column)中的数据列创建索引。只要有可能,就应该选择一个数据最整齐、最紧凑的数据列(如一个整数类型的数据列)来创建索引。
      创建索引时,如果索引字段是CHAR,VARCHAR类型,length可以小于字段实际长度;如果是BLOB和TEXT类型,必须指定 length。

         CREATE INDEX indexName ON mytable(username(length));

2)唯一索引

        普通索引允许被索引的数据列包含重复的值。如果能确定某个数据列将只包含彼此各不相同的值,在为这个数据列创建索引的时候就应该用关键字UNIQUE把它定义为一个唯一索引。这么做的好处:

        一是简化了MySQL对这个索引的管理工作,这个索引也因此而变得更有效率;

         二是MySQL会在有新记录插入数据表时,自动检查新记录的这个字段的值是否已经在某个记录的这个字段里出现过了;如果是,MySQL将拒绝插入那条新记录。也就是说,唯一索引可以保证数据记录的唯一性。

        事实上,在许多场合,人们创建唯一索引的目的往往不是为了提高访问速度,而只是为了避免数据出现重复。

        唯一索引与普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。

3)主索引

        主键字段创建的索引就是所谓的”主索引”。主索引与唯一索引的唯一区别是:前者在定义时使用的关键字是PRIMARY而不是UNIQUE。

       PRIMARY KEY索引仅是一个具有名称PRIMARY的UNIQUE索引,即一种特殊的唯一索引,且不允许有空值。

      需要注意:一个表只能有一个主键索引。

4)外键索引
        如果为某个外键字段定义了一个外键约束条件,MySQL就会定义一个内部索引来帮助自己以最有效率的方式去管理和使用外键约束条件。

5)复合索引

        索引可以覆盖多个数据列,如像INDEX(columnA, columnB)索引。这种索引的特点是MySQL可以有选择地使用一个这样的索引:如果查询操作只需要用到columnA数据列上的一个索引,就可以使 用复合索引INDEX(columnA, columnB)。不过,这种用法仅适用于在复合索引中排列在前的数据列组合。比如说,INDEX(A, B, C)可以当做A或(A, B)的索引来使用,但不能当做B、C或(B, C)的索引来使用。
        为了进一步榨取MySQL的效率,就要考虑建立组合索引。

应该为哪些字段建立索引

     1)   一般说来,索引应建立在那些将用于JOIN, WHERE判断和ORDER BY排序的字段上。

     2)   有时需要索引很长的字符列,通常可以通过可以索引开始的几个字符(前缀),而不是全部值,以节约空间并得到好的性能。为了决定好的前缀长度,须找到最常见的值,以及最常见的前缀进行对比。

        计算合适前缀长度的一种办法就是平均选择率,即计算全列的选择性,并使前缀的选择接近于它。平均来说,如果前缀的选择率能接近0.31,基本就可以了。

        可以在同一个查询中针对许多不同的前缀长度进行计算,这对于大表非常有用。例:

        SELECT COUNT(DISTINCT LEFT(a, 3) / COUNT(*) AS sel1, COUNT(DISTINCT LEFT(a, 4) / COUNT(*) AS sel2, COUNT(DISTINCT LEFT(a, 5) / COUNT(*) AS sel 3 FROM tb;

         语句中,对字段a分别计算取长度为3、4、5时的选择率,从而选出最接近0.31基数的前缀长度。

        平均选择率不够理想时,另一种方法是查看不同前缀时,字段数据出现分布均匀性。例:

          SELECT COUNT(*) AS cnt, LEFT(a, 4) AS pref FROM tb GROUP BY pref ORDER BY cnt;

       3)尽量不要对表中某个含有大量重复的值的字段建立索引。

        对于一个ENUM类型的字段来说,出现大量重复值是很有可能的情况,例如表示性别的字段,在这样的字段上建立索引将不会有什么帮助;相反,还有可能降低数据库的性能。

限制

     1)  通常MySQL只对<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE才会使用索引。(但在以通配符%和_开头作查询时,MySQL不会使用索引(在使用LIKE语句时应该注意这一点)。

       如果WEHERE子句的查询条件里有不等号(WHERE coloum != …),MySQL将无法使用索引。

   2) 如果WHERE子句的查询条件里使用了函数(WHERE DAY(column) = …)或索引字段是表达式的一部分,MySQL同样也无法使用索引。

        如:select * from course where class + 1 = 5; 该查询不会使用class上的索引。MySQL却不会帮你求解方程,应该养成简化WHERE子句的习惯,这样就会把被索引的列单独放在比较运算符的一边。

          下面是另外一种常见的错误:

                 SELECT … WHERE TO_DAYS(CURRENT_DATE) – TO_DAYS(date_col) <= 10;

        这个查询将会查找date_col值离今天不超过10天的所有行,但是它不会使用索引,因为使用了TO_DAYS()函数。下面是一种较好的方式:

                 SELECT … WHERE date_col >= DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY) ;

       使用CURRENT_DATE将会阻止查询缓存把结果缓存起来,可以用常量替换CURRENT_DATE的值对上条语句进行改进:

                 SELECT … WHERE date_col >=DATE_SUB(‘2008-01-17’, INTERVAL 10 DAY);

   3)  在JOIN操作中(需要从多个数据表提取数据时),MySQL只有在主键和外键的数据类型相同时才能使用索引。
   4)  如果WHERE子句的查询条件里使用比较操作符LIKE和REGEXP,MySQL只有在搜索模板的第一个字符不是通配符的情况下才能使用索引。比如说, 如果查询条件是LIKE ‘abc%’,MySQL将使用索引;如果查询条件是LIKE ‘%abc’,MySQL将不使用索引。
   5)  在ORDER BY操作中,MySQL只有在排序条件不是一个查询条件表达式的情况下才使用索引。(虽然如此,在涉及多个数据表查询里,即使有索引可用,那些索引在加快 ORDER BY方面也没什么作用)
   6)  如果某个数据列里包含许多重复的值,就算为它建立了索引也不会有很好的效果。比如说,如果某个数据列里包含的净是些诸如”0/1″或”Y/N”等值,就没有必要为它创建一个索引。

   7)  索引不会包含有NULL值的列

        只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。

使用索引的注意事项

        使用索引时,有以下一些技巧和注意事项:

◆ 索引不会包含有NULL值的列

        只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。

        把NULL改为NOT NULL带来的性能提升很小,所以除非确定它引入了问题,否则就不要把它当成优先的优先措施。然而,如果计划对列进行索引,就要尽量避免把它设置为空。

◆ 使用短索引

        对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。

◆ 索引列排序

        MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

◆ like语句操作

        一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。

◆ 不要在列上进行运算

        select * from users where YEAR(adddate)<2007;
        将在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成

        select * from users where adddate<‘2007-01-01’;
◆ 不使用NOT IN和<>操作

        以上,就对其中MySQL索引类型进行了介绍。

◆ 避免多个范围条件

覆盖索引

        如果索引包含满足查询的所有数据,就称为覆盖索引。MySQL可以使用覆盖索引来查询列的数据,这样就可以不用读取行数据。

       只需要读取索引而不用读取数据有以下一些优点:
     (1) 索引项通常比记录要小,所以MySQL访问更少的数据;
     (2) 索引都按值的大小顺序存储,相对于随机访问记录,需要更少的I/O;
     (3) 大多数据引擎能更好的缓存索引。比如MyISAM只缓存索引。
     (4) 覆盖索引对于InnoDB表尤其有用,因为InnoDB使用聚集索引组织数据,如果二级索引中包含查询所需的数据,就不再需要在聚集索引中查找了。InnoDB的第二索引拥有了用来覆盖查询的额外列(主键),即索引能覆盖第二索引中的列+主键列。

InnoDB数据表的索引
        与MyISAM数据表相比,索引对InnoDB数据的重要性要大得多。在 InnoDB数据表上,索引不仅会在搜索数据记录时发挥作用,还是数据行级锁定机制的实现基础。”数据行级锁定”的意思是指在事务操作的执行过程中锁定正 在被处理的个别记录,不让其他用户进行访问。这种锁定将影响到(但不限于)SELECT…LOCK IN SHARE MODE、SELECT…FOR UPDATE命令以及INSERT、UPDATE和DELETE命令。
        出于效率方面的考虑,InnoDB数据表的数据行级锁定实际发生在它们的索引上,而不是数据表自身上。显然,数据行级锁定机制只有在有关的数据表有一个合 适的索引可供锁定的时候才能发挥效力。

        InnoDB存储引擎支持聚集索引(数据表的物理存储顺序和表的逻辑存储顺序一致),一般是按照下面的规则来设定聚集索引:

      1)假如表包含PRIMARY KEY,InnoDB使用它作为聚集索引,也就是说数据按照主键顺序存放。

      2)假如表没有定义PRIMARY KEY,InnoDB将第一个只包含NOT NULL属性列的UNIQUE INDEX作为主键并且将它设置为聚集索引

      3)前两者都不满足的时候,mysql就增加一个隐藏的主键然后在其上进行聚集

        Innodb中使用主键一定要特别的注意,如果使用随机数据做为主键的话每可能会造成大量的数据移动从而减低服务器的性能。

        因此,在InnoDB表中定义主键时,最好避免随机(乱序)聚集键。最简单的办法是使用AUTO_INCREMENT列。这会保证行是顺序插入的并且能提高使用主键联接的性能。

       如果主键被创建在列的前缀上,InnoDB也会包含整个列及剩下的所有列。

聚集索引

        术语“聚集”指实际的数据行和相关的键值都保存在一起。每个表只能有一个聚集索引。但是,覆盖索引可以模拟多个聚集索引。存储引擎负责实现索引,因此不是所有的存储索引都支持聚集索引。当前,SolidDB和InnoDB是唯一支持聚集索引的存储引擎。

优点:

    • 可以把相关数据保存在一起。这样从磁盘上提取几个页面的数据就能把某个用户的邮件全部抓取出来。如果没有使用聚集,读取每个邮件都会访问磁盘。

   •  数据访问快。聚集索引把索引和数据都保存到了同一棵B-TREE中,因此从聚集索引中取得数据通常比在非聚集索引进行查找要快。

缺点:

   • 聚集能最大限度地提升I/O密集负载的性能。如果数据能装入内存,那么其顺序也就无所谓了。这样聚集就没有什么用处。

   • 插入速度严重依赖于插入顺序。更新聚集索引列是昂贵的,因为强制InnoDB把每个更新的行移到新的位置。

   • 建立在聚集索引上的表在插入新行,或者在行的主键被更新,该行必须被移动的时候会进行分页。

   • 聚集表可会比全表扫描慢,尤其在表存储得比较稀疏或因为分页而没有顺序存储的时候。

   • 第二(非聚集)索引可能会比预想的大,因为它们的叶子节点包含了被引用行的主键列。第二索引访问需要两次索引查找,而不是一次。 InnoDB的第二索引叶子节点包含了主键值作为指向行的“指针”,而不是“行指针”。 这种策略减少了在移动行或数据分页的时候索引的维护工作。使用行的主键值作为指针使得索引变得更大,但是这意味着InnoDB可以移动行,而无须更新指针。

为排序使用索引扫描

        MySQL有两种产生排序结果的方式:使用文件排序(Filesort)或扫描有序的索引。

        扫描索引本身很快,因为它只需要从一条索引记录移到另外一条记录。然而,如果MySQL没有使用覆盖索引查询,就不得不查找在索引中发现的每一行,这基本是随机I/O,因此以索引顺序读取数据通常比顺序扫描表慢得多,尤其对I/O密集的工作负载。

        按照索引对结果进行排序,只有当索引的顺序和ORDER BY子句中的顺序完全一致,并且所有列排序的方向一样才可以。如果查询联接了多个表,只有在ORDER BY子句的所有列引用 的是第一个表才可以:查找查询中的ORDER BY使用的是索引的最左前缀。在其他所有情况下,MySQL使用文件排序。

        文件排序对少量结果排序是挺快的,但对大数据量的表,有可能要求MySQL花费很多时间来扫描将被丢掉的数据(高偏移量),这样查询也会变慢。非规范化(Denormalizing)、预先计算(Precomputing)或缓存(Caching)有可能是解决该类查询的唯一办法。

        另外一种优化这种查询的好策略是只提取最终需要的行的主键列。然后把它再联接回去以取得所有需要的列。

索引策略总结

        第一个要检查的是响应时间,要考虑为任何耗时很长的查询添加索引。然后检查导致最大负载的查询,并且添加索引支持它们。如果系统正好碰到了内存、CPU或磁盘瓶颈,也要把它们考虑进去。

        在任何可能的地方,都要试着扩展索引,而不是新增索引。通常维护一个多列索引要比维护多个单列索引容易。使用多余索引的主要原因是扩展已有的索引的时候,它会变得很大,这些可以考虑增加多余索引。

       应该避免创建重复索引,并且在发现它的时候移除掉。

3. 表锁的问题

     跟性能相关的最重要的区别就是 MyISAM 和 InnoDB 实现的锁机制不一样! MyISAM 使用的是表锁, 而 InnoDB实现的是行锁。

1) MyISAM为表级锁

        由于MyISAM写进程优先获得锁,使得读锁请求靠后等待队列。不仅如此,即使读请求先到锁等待队列,写请求后 到,写锁也会插到读锁请求之前!这是因为MySQL认为写请求一般比读请求要重要。
        如果在大量更新操作的情况下,使得很难获得读锁。从而造成阻塞。
        这是MyIsam不适合做大量更新操作的原因

2 )INNODB的行锁是基于索引实现,如果不通过索引访问数据,Innodb会使用表锁。

        表级锁更适合以查询为主,只有少量按索引条件更新数据的应用。

        行级锁更适合于有大量按索引条件并发更新少量不同数据,同时又并发查询。因为只锁定要操作的行, 所以可以多个线程同时操作不同的行(只要不操作其他线程已经锁定的行)。

        注意,解释器的Extra列中出现“Using Where“,这意味着MySQL在存储引擎返回行之后使用WHERE过滤条件,这种情况下,即使InnoDB使用了索引,它也能锁定不需要的行。

原文地址:https://www.cnblogs.com/jevo/p/3301291.html