Mysql优化(深入浅出mysql数据库开发阅读笔记)

《深入浅出Mysql数据库开发、优化与管理维护》笔记
1、优化sql的一般步骤
  1.1 使用 show status 命令了解各种sql的执行频率
      进入mysql控制台执行
        show session status 查询当前连接的统计结果
        show global status 查询自数据库上次启动至今的统计结果

      或者在操作系统的终端执行
        mysqladmin extended-status
     
      可以通过like语句来查询一些特定的内容
      比如 show global status like "Com_%";就查看上次启动以来的每个Com_xxx语句执行的次数
     Com_select: 执行select查询的次数
     Com_insert: 执行insert的次数
     Com_update: 执行update的次数
     Com_delete: 执行delete的次数

     这些参数会都mysql所有存储引擎的表操作都做记录
     另外还有专门针对某一些存储引擎的,比如:
     Innodb_rows_read
     Innodb_rows_insert
     Innodb_rows_update
     Innobd_rows_delete

     通过上面这些数据的比较可以判断数据库是写为主,还是查询为主

     关于事务的信息 可以通过 Com_commit 和 Com_rollback 来了解提交和回滚的情况。如果回滚操作非常的频繁,说明应用编写存在很大的问题
    
     另外几个比较重要的,可以展示数据库基本情况的句子:
     Connections: 试图连接Mysql数据库的次数
     Uptime: 服务器工作时间
     Slow_queries: 慢查询的次数


   1.2 定位执行效率比较低的sql语句
      有两种方法:
      一、通过慢查询日志来定位。用--log-slow-queries[=filename]选项启动时,mysqld写一个包含所有执行时间超过long_query_time秒得sql语句的日志文件
      二、使用show processlist命令查看当前MySQL在进行的线程,包括线程的状态,是否锁表等,可以实时的查看sql的执行情况,同时对一些锁表操作进行优化。

   1.3 通过Explain分析低效SQL的执行计划
       再通过上面步骤,查到低效率的sql语句之后,可以通过 explain和desc命令获取mysql如何执行select语句的信息,包括select执行过程中表如何连接和连接的顺序。
       比如
explain select * from sum(money) from sales a, company b where a.company_id = b.id and a.year = 2006
+----+-------------+-------+--------+---------------+------+---------+------+------+---------------------+
| id | select_type | table | type   | possible_keys | key  | key_len | ref  | rows | Extra               |
+----+-------------+-------+--------+---------------+------+---------+------+------+---------------------+
|  1 | SIMPLE      | user  | system | NULL          | NULL | NULL    | NULL |    0 | const row not found |
+----+-------------+-------+--------+---------------+------+---------+------+------+---------------------+
1 row in set (0.00 sec)


       这个显示的结果是横向的,不够友好,在后面 追加参数“\G”可以每一项一列信息的显示
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: user
         type: system
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 0
        Extra: const row not found
1 row in set (0.00 sec)

ERROR:
No query specified

上面每一列的含义说明
   select_type: 表示select的类型(SIMPLE=>简单表,不使用表连接或者子查询,PRIMARY=>主查询,外层的查询,UNION=>UNION中的第二个或者后面的查询语句,SUBQUERY=>子查询中的第一个select)
   table; 输出结果集的表
   possible_keys: 表示查询时,可能使用的索引
   key: 表示实际使用的索引
   key_len: 索引字段的长度
   rows; 扫描的行的数量
   Extra: 执行情况的说明和描述
   type: 表示表的连接类型,性能有好到差的链接类型为:
      system=>只有一行,也就是常量表,
      const=>单表中最多有一个匹配行,例如primary key或者unique index,
      eq_ref=>对于前面的每一行,在此表中只查询一条记录,简单来说,就是多表连接中使用primary key或者unique index, 
      ref=>与eq_ref类似,区别在于不是使用primay key或者unique index而是使用普通的索引,
      ref_or_null=>与ref类似,区别在于条件中包含对null的查询,
      index_merge=>索引合并优化,
      unique_subquery=>in的后面是一个查询主键字段的子查询,
      index_subquery=>与unique_subquery类似,区别在于in的后面是查询非唯一索引字段的子查询,
      range=>单表中的查询范围,
      index=>对于前面的每一行,都通过查询索引来得到数据,
      all=>对于前面的每一行,都通过全表扫描来得到数据


   
2 索引问题
  2.1 索引的存储分类
     MyISAM存储索引的表的数据和索引是自动分开存储的,各自是独立的文件
     InnoDB存储引擎的表的数据和索引是存储在同一个表的空间里面,但是可以有多个文件组成
     Mysql中的存储类型木匾只有两种,BTREE和HASH,具体情况和表的存储引擎有关。
     MyISAM和InnoDb存储引擎都支持BTREE索引,
     MEMORY/HEAP存储引擎可以支持HASH和BTREE索引
    
     mysql不支持函数索引,但是能对队列的前面的某一部分进行索引,例如name字段,可以只取name的前四个字符来进行索引。

2.2 使用索引
     对相关列使用索引是提高select性能的最佳途径
     使用索引的条件:a、查询条件中有索引关键字,b、多列索引只有查询条件使用了多列关键字最左边的前缀时,才可以使用索引,否则将不能使用索引。
    2.2.1 使用索引
     一下情况中会使用到索引:
     (1) 多列索引,只要查询的条件中用到了最左边的列,索引一般就会被使用
     (2) 对于使用like查询,后面如果是常量,只有%号不在第一个字符时,索引才可能被用到 比如 like "%3" 不会用索引, like "3%"就会走索引
     (3) 对大文本进行搜索的时候,使用全文索引,而不是使用 like '%...%'
     (4) 如果列名是索引,使用column_name is null将使用索引,
      例如: select * from aaa where name is null(name是索引列)
    2.2.2 存在索引但不使用索引
      在下列情况下,虽然mysql存在索引,但是并不会使用到索引
     (1)如果Mysql估计使用索引比全表扫描更慢,则不使用索引。例如如果列key_1 均匀分布在1和100之间,那么查询 select * from table where key_1 > 1 and key_1 < 90;
     (2)如果使用MEMPRY/HEAP表并且,where条件中不使用“=”进行索引列,那么不会用到索引。heap表只有在使用“=”的时候,才使用索引
     (3)用or隔开的条件,如果or前面的列中有索引,而后面的列中没有索引,那么涉及的索引都不会被用到(or中有一个条件中的列没有索引就用不到索引)
     (4)如果不是索引列的第一部分(复合索引的第一部分)
     (5)如果like是%开始的
     (6)如果列类型是字符串,那么一定记得在where条件中把字符常量值用引号引起来,否则即便是这个列上有索引,也不用用到(比如name字段是字符串的,却写了name=123。要改成“123”)
    
   2.3 查看索引使用情况
    如果索引正在工作,Handler_read_key的值将很高,这个值代表了一个行被索引值读的次数,很低得知表明增加索引得到的性能改善不高
    Handler_read_rnd_next的值高则意味着查询的效率低效,并且应该建立索引补救。它的含义是:数据文件中读下一行的请求数。如果正在进行大量的表扫描,Handle_read_rnd_next的值较高,则通常说明表索引不正确或者写入的查询没有利用索引
    查看方法:show status like 'Handler_read%'

3 简单的优化方法
   3.1 定期分析表和检查表
   分析表的语法:
   analyze [local | no_write_to_binlog] table tab1_name [, ta1_name] ...
   用于分析和存储表的关键字分布
   分析结果可以使得系统得到更准确地统计信息,使得sql能够正确的执行计划。
   如果用户感觉实际执行计划并不是与预期的执行计划,执行一次分析表可能会解决问题。
   在分析期间,使用一个读取锁对表进行锁定,这对于MyISAM,BDB和InnoDb表有作用。对于MyISAM表,与使用myisamchk -a 相当
   例如:
   analyze table user;
mysql> analyze table user;
+------------+---------+----------+----------+
| Table      | Op      | Msg_type | Msg_text |
+------------+---------+----------+----------+
| mysql.user | analyze | status   | OK       |
+------------+---------+----------+----------+
1 row in set (0.05 sec)


   检查表的语法如下:
   check table tab1_name [,tab1_name] ... [option] .. option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED}
   作用是:检查一个或者多个表中是否有错误。他对MyISAM和InnoDB表有作用,对于MyISAM表,关键字统计数据被更新。例如:
  check table user;
mysql> check table user;
+------------+-------+----------+----------+
| Table      | Op    | Msg_type | Msg_text |
+------------+-------+----------+----------+
| mysql.user | check | status   | OK       |
+------------+-------+----------+----------+
1 row in set (0.00 sec)


  check table也可以检查视图是否有错误,例如:在视图定义中被引用的表不存在

3.2 定期优化表
    语法:
    optimize [local | no_write_to_binlog] table tab1_name [, tab1_name] ...
    适用范围:
    a、删除了表的一部分
    b、对含有可变长度行的表(varchar,blob,text列的表)进行了很多更改。
    这个命令可以将表中的空间碎片进行合并,并且可以消除由于删除或者更新造成的空间浪费。但是这个命令只对MyISAM,BDB和InnoDb表起作用
mysql> optimize table user;
+------------+----------+----------+----------+
| Table      | Op       | Msg_type | Msg_text |
+------------+----------+----------+----------+
| mysql.user | optimize | status   | OK       |
+------------+----------+----------+----------+
1 row in set (0.01 sec)


4 常用SQL的优化
  4.1 大批量插入数据
    当用load命令导入数据的时候,适当的设置可以提高导入的速度
    4.1.1 对于MyISAM存储引擎的表方法如下:
    alter table tab_name disable keys;
    load the data
    alter table tab_name enable keys;

    上面操作是打开或者关闭MyISAM表不是唯一索引的更新
    ps:导入非空数据表的时候,上面方法很有效,但是导入空表的时候,索引是数据导入完毕之后才去创建的,所以没有影响
    4.2.2 对InnoDB表的数据导入
     (1)InnoDB类型的表是按照主键的顺序来存储的,所以导入数据按照主键的顺序排列,可以有效的提高导入的效率
     (2)在导入数据之前,关闭唯一性校验,set unique_checks=0 ,在导入结束之后设置为 1 开启唯一性校验,可以提高导入效率
     (3)如果应用使用自动提交的方式,建议在导入数据的时候执行 set autocomment=0,关闭自动提交,导入结束之后再设置为1,打开自动提交

  4.2 优化insert语句
     (1)如果是从同一客户那里插入很多航,尽量使用多个值表的insert语句,这种方式将大大的缩短客户端与数据库之间的链接,关闭等资源的消耗,使得效率比分开执行的单个insert语句快
     例如:insert to test values(1,2),(1,3),(1,4)....
     (2)如果从不同的客户中杀入多行,能通过使用insert delayed语句得到更高的速度。delayed是让insert语句立马执行,四十数据都被放在内存的队列中,并没有真正的写入磁盘,这比每一条数据分别插入快得多。low_priority 刚好想法,是所有其他用户对表的读写完成后才进行插入
     (3)将索引文件和数据文件分在不同的磁盘上存放
     (4)如果进行批量插入可以增加 bulk_insert_buffer_size 变量值的方法来提高速度,但是这只能对MyIsAM表使用
     (5)当一个文本文件装载一个表时,使用 load file insert。这比通常的sql语句快20倍
4.3 优化group by语句
     如果查询包括group by但是用户想避免白须结果的小号,可以指定order by null禁止排序
   例如:
   select id, sum(money) from sale2 group by id order by null

4.4 优化order by语句
    可以使用索引来满足一个order by语句
    条件:
    where条件和order by使用相同的索引,并且order by的顺序和索引顺序相同,并且order by的字段都是升序或者降序
    比如: order by key1,key2
           where key1=123 order by key1 desc, key2 desc
           order by key1 desc, key2 desc

    但是以下情况不可以
       order by key1 asc, key2 desc 混合使用ASC和DESC
       where key2 = 1 , order by key1 查询的关键字和排序的不一样
       order by key1,key2 对不同的关键字使用排序

   
4.5 优化嵌套查询
     有些情况下,子查询可以被更加有效的join查询代替
     比如 select * from a where b_id not in(select id from b) 
     换成 select * from a left join b on a.b_id = b.id where a.b_id is not null


  4.6 优化OR条件
      or的每一个条件都使用索引
      但是如果or的几个条件正好是复合索引的元素,则不能起到加速的效果
   4.7 使用SQL提示
      在sql中加入一些人为的提示来达到优化操作的目的
      例如:
      select sql_buffer_results * from
      这个语句将强行MySql生成一个临时的结果集,只要临时结果集生成之后,所有表上的锁都会被释放。
      这能解决遇到表锁问题时,或者要花长时间将结果传给客户端时有所帮助,因为这样可以尽快的释放资源

      (1)use index
      在查询语句中表名的后面,添加use index来提供希望mysql去参考的索引列表,就可以让mysql不再考虑其他可用的索引
      select * from a use index(ind_a_id) where id = 1

      (2)ignore index
      如果用户只是单纯的想让mysql忽略一个多个索引,则可以使用ignore index作为hint
       select * from a ignore index(ind_a_id) where id = 1

      (3)force index
      强制MySQL使用一个特定的索引,可在查询中使用


      

你可能感兴趣的:(mysql优化)