18 SQL优化

1.SQL语句优化的通常步骤
 
     1).了解各类SQL的执行频率
        客户端链接成功后,能够经过SHOW [SESSION | GLOBAL] STATUS 命令来查看服务器状态信息; 
        也能够在操做系统上使用 mysqladmin extended-status 命令得到这些消息.
            SESSION : 表示当前链接,做为默认值
            GLOBAL : 自数据库启动至今
 
        demo : SHOW STATUS LIKE 'Com_%' ;
 
        Com_xxxx 表示每一个xxx语句执行的次数.咱们一般比较关心的是如下几个统计参数.
                Com_select : 每次查询累计加1
                Com_insert : 执行INSERT的次数, 对于批量插入的INSERT操做,只累加1;
                Com_update : 执行UPDATE操做的次数.
                Com_delete : 执行DELETE的次数.
        上面的参数是全部存储引擎通用的.
 
        下面这几个参数只针对InnoDB存储引擎,累加算法也略有不一样.
                Innodb_rows_read : SELECT 查询返回的次数
                Innodb_rows_inserted : 执行INSERT操做插入的行数.
                Innodb_rows_updated : 执行UPDATE操做更新的行数.
                Innodb_rows_deleted : 执行DELETE操做删除的行数.
 
        其中更新操做的计数,是对执行次数的计数,不论提交仍是回滚都会累加.
        对于事务型的应用,经过Com_commit 和Com_rollback能够了解事务提交和回滚的状况,对于回滚操做很是频繁的数据库,可能意味着应用编写存在问题.
        
        此外,如下几个参数便于用户了解数据库的基本状况.
            Connections : 试图链接MySQL服务器的次数.
            Uptime : 服务器工做时间.
            Slow_queries : 慢查询次数.
 
 
     2).定位执行效率较低的SQL语句.
        方式1:
            经过慢查询日志定位执行效率较低的SQL语句.
            用--log-slow-queries[=file_name]选项. 启动时,mysqld写一个包含全部执行时间超过long_query_time秒的SQL语句日志文件.
        方式2:
            使用 SHOW PROCESSLIST 命令查看当前MySQL在进行的线程
                包括线程的状态,是否锁表等.能够实时查看SQL的执行状况,同时对一些锁表操做进行优化.
    
     3).经过EXPLAIN分析低效SQL的执行计划
        经过EXPLAIN 或 DESC 命令来获取 MySQL如何执行SELECT 语句的信息.
        包括SELECT语句执行过程当中如何链接以及链接顺序.
 
        返回的结果中,有不少信息,包括select_type , table , type , possable_keys , key , key_lenth , rows , Extra 等. 
 
                select_type : 表示SELECT的类型.常见的取值有
                        SIMPLE(不使用表链接/子查询)
                        PRIMARY(主查询,即外层的查询)
                        UNION(UNION中的第二个或者后面的查询语句)
                        SUBQUERY(子查询中的第一个SELECT)
 
                table:输出结果集的表.
 
                type:表示链接类型.性能由好到差以下:
                        system -> const -> eq_ref -> ref -> ref_or_null -> index_merge -> unique_subquery -> index_subquery -> range -> index -> all
 
                        system : 表中仅有一行,即常量表.
                        const : 单表中最多有一个匹配行 , 例如primary key或者unique index
                        eq_ref : 对于前面的每一行,在此表中只查询一条记录.简单来讲,就是多表链接使用primary key 或unique index)
                        ref : 与eq_ref相似,区别在于使用的是普通索引.
                        ref_or_null : 与ref相似,区别在于包含对NULL的查询
                        index_merge : 索引合并优化
                        unique_subquery : IN的后面是一个查询主键字段的子查询.
                        range : 单表中的范围查询.
                        index : 对于前面的每一行,都经过查询索引来获得数据.
                        all : 对于前面的每一行,都经过全表扫描来获得数据.
 
                possible_keys : 表示查询时,可能使用的索引.
                key : 表示实际使用的索引.
                key_len : 索引字段的长度.
                rows : 扫描行的数量.
                Extra : 执行状况的说明和描述.
            
 
 
 
     4).肯定问题并采起相应的优化措施
        书中例子是a表的type为all,因此为WHERE中的列加了个索引,type变为ref
 
 
 
2.索引问题
 
     1).索引的存储分类
        MyISAM存储引擎的表,数据和索引时自动分开存储的,各自是独立的文件.
        InnoDB存储引擎的表,数据和索引时存储在同一个表空间里面,但能够由多个文件组成.
 
        MySQL中的存储类型目前只有两种:BTREE和HASH.
 
        MyISAM和InnoDB都只支持BTREE索引;
        MEMORY/HEAP能够支持HASH以及BTREE索引.
 
        MySQL目前不支持函数索引,可是能对列的前面某一部分进行索引,称为前缀索引.
            这个特性能够大大缩小索引文件的大小.
 
     2).如何使用索引
        查询要使用索引最主要的条件是
            查询条件中须要使用索引关键字.
            若是是多列索引,那么只有查询条件使用多列关键字最左边的前缀时,才可使用索引,不然将不能使用索引.
 
        a.使用索引
 
            (1) 对于建立的多列索引,只要查询条件中用到了最左边的列,索引通常就会被使用.
                例如对col1,col2(假设col1在前)创建联合索引,查询的WHERE条件中没必要必定要用两个列都参与筛选,只要最左边的(这是是col1)参与条件筛选了,就可使用到索引了.这就是索引的前缀特性.
 
            (2) 对于使用LIKE的查询,后面若是是常量,而且只有%不在第一个字符,索引才可能会被使用.
                另外,若是LIKE后面跟的是一个列的名字,那么索引也不会被使用.
            (3) 若是对大的文本进行搜索,使用全文索引而不使用 LIKE '%...%'
            (4) 若是列名是索引,使用column_name is null将使用索引.
 
 
        b.存在索引,但不使用索引
            (1) 若是MySQL估计 使用索引比全表扫描还慢,则不实用索引.
            (2) 若是使用 MEMORY/HEAP 表而且WHERE条件中不使用'='进行索引列,那么不会用到索引. heap表只有在'='条件下才会使用索引.
            (3) 用OR 分割开的条件,若是OR前的条件中的列有索引,然后面的列中没有索引,那么涉及到的索引都不会被使用.--简而言之,OR语句中全部条件必须都是用到了索引.
            (4) 若是不是索引列的第一部分,复合索引,第一列必须参与条件筛选,才可能hi用到索引.
            (5) LIKE 问题 , 参见a.(2)
            (6) 若是列类型字符串,必需要有引号,不然MySQL会认为是数值型,不实用索引了.
 
 
        c.查看索引使用状况
            SHOW STATUS LIKE 'Handler_read%';
 
            Handler_read_key表明了一个行被索引值读的次数.很低的值代表增长索引获得的性能改善不高,由于该索引并不常用.
 
            Handler_read_rnd_next的值高意味着查询运行低效,而且应该创建索引补救.
                这个值的含义是在数据文件中读下一行的请求数.若是正在进行大量的表扫描,该值将较高,说明索引不正确或写入的查询没有利用索引.
 
 
     3).两个简单实用的优化方法
        a.按期分析表和检查表.
            分析表 : 
                ANALYZE [LOCAL|NO_WRITE_TO_BINLOG] TABLE tbl_name [,tbl_name ...]
                本语句用于分析和存储 表的关键字的分布,分析的结果将可使得系统获得准确的统计信息.
                在分析期间使用一个读取锁定对表进行锁定.这对于MyISAM/BDB/InnoDB表有做用.
                对于MyISAM表 , 本语句与是一哦那个myisamchk -a至关.
 
 
            检查表: CHECK TABLE tbl_name [,tbl_name ...] [option] ... option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED} ;
            CHECK TABLE 对MyISAM 和 InnoDB表有做用.对于MyISAM表,关键字统计数据被更新.同时CHECK TABLE 也能够检查视图是否有错误.
 
        b.按期优化表
            OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [,tbl_name ..];
            若是已经删除了表的一大部分,或者若是已经对含有可变长度行的表(含有VARCHAR,BLOB或TEXT列的表)进行了不少更改,
            则应使用OPTIMIZE TABLE 命令进行空间碎片整理,实现表优化.
            可是OPTIMIZE TABLE只对MyISAM/BDB/InnoDB表起做用.
 
 
     4).经常使用SQL的优化
 
        a.优化大批量插入数据
            当用load命令导入数据的时候,适当设置能够提升导入的速度.
 
            对于MyISAM存储引擎的表,能够经过如下方式快速导入大量数据.
 
            ALTER TABLE tbl_name DISABLE KEYS ;
                ## here loading the data
                LOAD DATA INFILE '/home/mysql/film_test.txt' INTO TABLE film_test ;
            ALTER TABLE tbl_name ENABLE KEYS ;
 
                DISABLE/ENABLE KEYS 用来关闭/打开MyISAM表中 非惟一索引 的更新.
                对于导入大量数据到一个 空的MyISAM表时,默认就是先导入数据而后才建立索引,因此不用进行设置.
 
 
            对于InnoDB 表, 能够有如下几种方式提升InnoDB表的导入效率.
                (1)将导入的数据按照主键的顺序排列,能够有效提升导入数据的效率.
 
                (2)在导入数据前执行SET UNIQUE_CHECKS=0 , 即关闭惟一性校验.
                    导入结束后 SET UNIQUE_CHECKS=1 恢复惟一性校验,能够提升导入效率.
 
                (3)若是应用使用自动提交的方式,建议导入前执行SET AUTOCOMMIT=0,关闭自动提交;导入结束后再执行 SET AUTOCOMMIT = 1 .
 
 
        b.优化INSERT 语句
            (1) 使用INSERT INTO tbl_name VALUES(..),(..),(..)..;
                这种比分开执行单个INSERT语句快不少,减小了客户端与数据库之间的链接/关闭等形式的资源消耗.
 
            (2) 若是从不一样客户端插入不少行,经过使用INSERT DELAYED 语句获得更高的速度.
                    DELAYED的含义是让INSERT语句立刻执行,其实数据都被放在内存的队列中,并无真正写入磁盘;
                    LOW_PRIORITY恰好相反,在全部其余用户对表的对鞋完成后,才进行插入.
 
            (3) 利用建表中的选项,将索引文件和数据文件分在不一样的磁盘上存放.
 
            (4) 若是进行批量插入,能够增长bulk_insert_buffer_size的阀值.可是只对MyISAM表起做用.
 
            (5) 从文本文件加载数据,使用LOAD DATA INFILE.一般比使用不少INSERT语句快20倍.
 
 
        c.优化GROUP BY 语句
            若是查询包括GROUP BY,但用户想避免排序结果的消耗,可使用 ORDER BY NULL ;
            例如 EXPLAIN SELECT id , SUM(money) FROM sale GROUP BY id ORDER BY NULL ; -- 这样就不使用 filesort 了,而filesort每每很耗时.
 
        
 
        d.优化ORDER BY语句
            可使用索引代替ORDER BY子句,就不须要额外排序了.
            同时知足下面三个条件的SQL语句,可使用索引代替ORDER BY:
                WHERE 条件和ORDER BY使用相同的索引
                而且 ORDER BY的顺序与索引顺序相同
                而且 ORDER BY的字段 都是升序/降序.
                
            Demo :
                SELECT * FROM t ORDER BY key_part1,key_part2 ,.. ;
                SELECT * FROM t WHERE key_part1 = 1 ORDER BY key_part1 DESC , key_part2 DESC ;
                SELECT * FROM t ORDER BY key_part1 DESC , key_part2 DESC ;
 
            而如下几种状况不能使用索引,demo:
 
                # order by的字段混合了 ASC 和DESC
                SELECT * FROM t ORDER BY col1 DESC , col2 ASC ;
 
                # 查询行的关键字 与 ORDER BY 中所使用的不相同
                SELECT * FROM t WHERE col2 = xx ORDER BY col1 ;
 
                # 对不一样关键字使用ORDER BY
                SELECT * FROM t ORDER BY key1 , key2 ;
 
 
 
        e.优化嵌套查询
            子查询使用SELECT语句来建立一个单列的查询结果,而后把这个结果做为过滤条件用在另外一个查询中.
            使用子查询能够一次性地完成不少逻辑上须要多个步骤才能完成的SQL操做,同时也能够避免事务或者表锁死,写起来也容易.
 
            可是,有些状况下,子查询能够被更有效率的链接(JOIN)替代.
 
            例如:
                EXPLAIN SELECT * FROM sales WHERE company_id NOT IN (SELECT id FROM company) ;
            能够替换为
                EXPLAIN SELECT * FROM sales LEFT JOIN company ON sales.company_id = company.id WHERE sales.company_id IS NULL ;
 
            链接之因此更高效一些,是由于MySQL不须要在内存中建立临时表来完成这个逻辑上的须要两个步骤的查询工做.
 
 
    
        f.优化 OR 条件
            增长OR条件的索引.--这 tm 不太好吧
            这种方式,对联合索引不起做用. --也tm 没给解决方式.
 
 
 
        g.使用SQL提示
            SQL HINT 是优化数据库的一个重要手段,就是在SQL语句中加入一些认为的提示来达到优化操做的目的.
            例如: SELECT SQL_BUFFER_RESULTS * FROM ..
            这个语句强制MySQL生成一个临时结果集.结果集生成后,表上的锁定被释放.能够尽快释放资源.
            如下是MySQL中经常使用的一些SQL提示.
 
            (1) USING INDEX    
                在查询语句中表名后面,添加USE INDEX 来提供但愿MySQL去参考的索引列表,就可让MySQL再也不考虑使用其余可用索引.
                例如: EXPLAIN SELECT * FROM sales USE INDEX (idx_sales_id) WHERE id = 3 ;
 
            (2) IGNORE INDEX
                若是用户只是想忽略某个或者多个索引则可使用IGNORE INDEX做为HINT.
                例如: EXPLAIN SELECT * FROM sales IGNORE INDEX(idx_sales_id) WHERE id = 3 ;
 
 
            (3) FORCE INDEX
                为强制MySQL使用一个特定索引.这是MySQL留给用户的一个自行选择执行计划的权利.
                例如: EXPLAIN SELECT * FROM sales FROCE INDEX (idx_sales_id) WHERE id > 0 ;
相关文章
相关标签/搜索