Video-No.03 韩顺平_MySQL优化

1、MySQL数据库优化技术

    1)表的设计合理化(符合3NF)

    2)添加适当的索引(index):普通索引、主键索引、唯一索引、全文索引、复合索引

    3)分表技术(水平分割、垂直分割)

    4)读写分离(读:select,写:insert、update、delete)

    5)存储过程、触发器(模块化编程,提前编译sql语句,提交执行速度)

    6)对mysql配置文件进行优化(最大并发数、缓存大小)

    7)mysql服务器升级

    8)定时清理不需要的数据,定时进行碎片整理(MyISAM)

2、3NF(范式)

    1)1NF:列具有原子性,不可分割,数据库是关系型数据库就符合1NF

    2)2NF:表的行记录是唯一的,符合2NF,设置主键

    3)3NF:表中没有冗余数据

3、反3NF:没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是: 在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余。

4、Sql语句优化

1)定位慢查询

    a、获取mysql的一些运行状态(如:当前连接数、select次数、update次数、insert次数、delete次数等

        show [global|session] status like '' 查询相关运行状态

        netstat –anb 查询端口

SHOW STATUS; #显示参数信息列表
SHOW STATUS LIKE '%quer%'; #模糊匹配

SHOW STATUS LIKE 'uptime'; #显示MySQL运行时间
SHOW GLOBAL STATUS LIKE 'com_select'; #显示select次数
SHOW GLOBAL STATUS LIKE 'com_update'; #显示update次数
SHOW GLOBAL STATUS LIKE 'com_insert'; #显示insert次数
SHOW GLOBAL STATUS LIKE 'com_delete'; #显示delete次数
SHOW STATUS LIKE 'connections'; #显示当前并发连接数
SHOW GLOBAL STATUS LIKE 'slow_queries'; #查询慢查询次数

    b、开启慢查询日志:

SHOW VARIABLES LIKE 'long_query_time'; #慢查询记录最短时间
SET long_query_time = 1; #设置慢查询时间

SET GLOBAL slow_query_log = ON; #开启慢查询日志
SHOW VARIABLES LIKE 'slow_query_log'; #显示慢查询日志状态
SHOW VARIABLES LIKE 'slow_query_log_file'; #显示慢查询日志文件路径

    日志记录:

# Time: 151007 12:01:37
# User@Host: root[root] @ localhost [127.0.0.1]  Id:     5
# Query_time: 15.434343  Lock_time: 0.000000 Rows_sent: 0  Rows_examined: 13000000
use hsp_mysql;
SET timestamp=1444190497;
select * from emp where empno = 12321312312
 LIMIT 0, 1000;

2)添加索引

  • 创建索引

        主键索引:当设置表的主键时,该主键字段即为主键索引字段

        1)创建表的时候添加:primary key

CREATE TABLE t_aaa(
	id 	INT UNSIGNED	PRIMARY KEY 	AUTO_INCREMENT,
	aname	VARCHAR(32)	NOT NULL,
	isvalid	TINYINT		DEFAULT 1
);

        2)语句添加:alter table 表名 add primary key(列名)

CREATE TABLE t_bbb(
	id 	INT UNSIGNED,
	bname	VARCHAR(32)	NOT NULL,
	isvalid	TINYINT		DEFAULT 1
);

ALTER TABLE t_bbb ADD PRIMARY KEY(id);
ALTER TABLE t_bbb MODIFY id INT UNSIGNED AUTO_INCREMENT;

DESC t_bbb;
SHOW INDEXES FROM t_bbb;
SHOW KEYS FROM t_bbb;

        普通索引:创建表之后在列上添加索引

        create [fulltext|unique] index 索引名称 on 表名(列名1, 列名2....)

CREATE TABLE t_ccc(
	id 	INT UNSIGNED,
	cname	VARCHAR(32)	NOT NULL,
	isvalid	TINYINT		DEFAULT 1
);

CREATE INDEX i_ccc_id ON t_ccc(id);

DESC t_ccc;

SHOW INDEXES FROM t_ccc;

CREATE INDEX i_emp_no ON emp(empno);

SHOW INDEXES FROM emp;

        全文索引:针对文件的检索,比如文章,全文索引只针对MyISAM有效

        create fulltext index 索引名 on 表名(列名, 列名....)

CREATE TABLE articles (
	id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
	title VARCHAR(32) NOT NULL,
	body TEXT,
	FULLTEXT KEY full_text_articles (title, body) #创建全文索引
) ENGINE=MYISAM DEFAULT CHARSET=utf8;

INSERT INTO articles (title,body) VALUES
     ('MySQL Tutorial','DBMS stands for DataBase ...'),
     ('How To Use MySQL Well','After you went through a ...'),
     ('Optimizing MySQL','In this tutorial we will show ...'),
     ('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
     ('MySQL vs. YourSQL','In the following database comparison ...'),
     ('MySQL Security','When configured properly, MySQL ...');

CREATE FULLTEXT INDEX ft_body ON articles(body);
mysql> SELECT * FROM articles WHERE MATCH(title, body) AGAINST ('DataBase'); #全文搜索
+----+-------------------+------------------------------------------+
| id | title             | body                                     |
+----+-------------------+------------------------------------------+
|  5 | MySQL vs. YourSQL | In the following database comparison ... |
|  1 | MySQL Tutorial    | DBMS stands for DataBase ...             |
+----+-------------------+------------------------------------------+
2 rows in set (0.00 sec)

#被查询出来的概率
mysql> select match(title, body) against('DataBase') from articles;
+----------------------------------------+
| match(title, body) against('DataBase') |
+----------------------------------------+
|                     0.6554583311080933 |
|                                      0 |
|                                      0 |
|                                      0 |
|                     0.6626645922660828 |
|                                      0 |
+----------------------------------------+
6 rows in set (0.00 sec)

        注意:1、全文索引只有MyISAM有效;2、只支持英文;3、使用match...against('关键字');4、全文索引中有一个词叫停止词,如果全文索引每一个单词及其组合都要创建索引,则非常庞大,所以对于一些常用词和字符,全文索引不会创建,而这些词称为停止词

        唯一索引:列唯一

        唯一索引值可以为null,并且可以为多个null,其他值不能重复,主键索引不能为null

        1)如果创建表时字段添加unique约束时,则自动为唯一索引

CREATE TABLE t_ddd (
	id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
	task_id SMALLINT NOT NULL,
	user_id SMALLINT NOT NULL,
	orderno VARCHAR(32) UNIQUE -- 创建唯一索引
);

SHOW INDEXES FROM t_ddd;

        2)创建表后在创建唯一索引:create unique index 索引名 on 表名(列名, 列名....)

CREATE UNIQUE INDEX i_ddd_userid ON t_ddd(user_id, task_id);

INSERT INTO t_ddd(task_id, user_id, orderno) VALUES (1, 2, NULL);
INSERT INTO t_ddd(task_id, user_id, orderno) VALUES (2, 2, NULL);
INSERT INTO t_ddd(task_id, user_id, orderno) VALUES (2, 1, '');

mysql> select * from t_ddd;
+----+---------+---------+---------+
| id | task_id | user_id | orderno |
+----+---------+---------+---------+
|  1 |       1 |       2 | NULL    |
|  3 |       2 |       2 | NULL    |
|  4 |       2 |       1 |         |
+----+---------+---------+---------+
3 rows in set (0.00 sec)
  • 查询索引

        1)desc 表名:无法获取索引名称

mysql> desc t_aaa;
+---------+------------------+------+-----+---------+----------------+
| Field   | Type             | Null | Key | Default | Extra          |
+---------+------------------+------+-----+---------+----------------+
| id      | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| aname   | varchar(32)      | NO   |     | NULL    |                |
| isvalid | tinyint(4)       | YES  |     | 1       |                |
+---------+------------------+------+-----+---------+----------------+
3 rows in set (0.03 sec)

        2)show index(es) from 表名

        创建的索引以二叉树的形式保存索引id,对应的存储物理地址

mysql> SHOW INDEXES FROM t_ccc\G;
*************************** 1. row ***************************
        Table: t_ccc
   Non_unique: 1
     Key_name: i_ccc_id
 Seq_in_index: 1
  Column_name: id
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:
1 row in set (0.00 sec)

    3)show keys from 表名

  • 删除索引

        alter table 表名 drop index 索引名

        drop index 索引名 on table 表名

        删除主键:alter table 表名 drop primary key

DROP INDEX i_ddd_userid ON t_ddd -- 删除索引;
ALTER TABLE t_ddd DROP INDEX orderno; -- 删除索引
ALTER TABLE t_ddd MODIFY id INT UNSIGNED;
alert TABLE t_ddd ADD PRIMARY KEY;
ALTER TABLE t_ddd DROP PRIMARY KEY; -- 删除主键,必须去掉自增
  • 修改索引

        修改索引先删除再创建

  • 索引原理分析

    

Video-No.03 韩顺平_MySQL优化_第1张图片

  • 索引代价:

        1)创建的索引文件占用磁盘空间

        2)对DML操作语句有影响,影响其性能

  • 什么时候使用索引:

    • 比较频繁的查询的列(where条件后)适合为该列创建索引

    • 唯一性太差的字段列不适合创建索引

    • 更新频繁的字段列不适合创建索引

  • 使用索引注意事项:

    • 对于创建的多列复合索引,只有最左边的列被使用的情况下,这个复合索引才会被使用,如果最左边列没有被使用,则索引无效

ALTER TABLE t_ddd ADD PRIMARY KEY(id);
CREATE UNIQUE INDEX i_unique ON t_ddd(task_id, user_id); -- 创建复合索引,task_id为最左边的列

mysql> EXPLAIN SELECT * FROM t_ddd WHERE task_id = 1\G -- 条件语句包含最task_id
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t_ddd
         type: ref
possible_keys: i_unique
          key: i_unique
      key_len: 2
          ref: const
         rows: 1
        Extra: NULL
1 row in set (0.00 sec)

mysql> EXPLAIN SELECT * FROM t_ddd WHERE user_id = 1 AND task_id = 2\G -- 条件语句包含最task_id
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t_ddd
         type: const
possible_keys: i_unique
          key: i_unique
      key_len: 4
          ref: const,const
         rows: 1
        Extra: NULL
1 row in set (0.00 sec)

mysql> EXPLAIN SELECT * FROM t_ddd WHERE user_id = 1\G -- 不包含task_id
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t_ddd
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 3
        Extra: Using where
1 row in set (0.00 sec)
    • 使用like模糊匹配,如果规则为'%'或者'_'开始,则不会使用索引

mysql> explain select * from t_ddd where orderno like '%aaa'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t_ddd
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 3
        Extra: Using where
1 row in set (0.00 sec)

mysql> explain select * from t_ddd where orderno like 'aa%_a'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t_ddd
         type: range
possible_keys: t_orderno
          key: t_orderno
      key_len: 35
          ref: NULL
         rows: 1
        Extra: Using index condition
1 row in set (0.00 sec)
    • 如果where中带or,则所有的条件字段都必须创建索引,否则只要有一个没有创建索引,则索引都会无效

    • 如果列类型是字符串,则一定要用单引号引起来,否则索引失效

mysql> explain select * from t_ddd where orderno = 12345\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t_ddd
         type: ALL
possible_keys: t_orderno
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 3
        Extra: Using where
1 row in set (0.00 sec)

mysql> explain select * from t_ddd where orderno = '12345'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t_ddd
         type: const
possible_keys: t_orderno
          key: t_orderno
      key_len: 35
          ref: const
         rows: 1
        Extra: NULL
1 row in set (0.00 sec)
    • 如果mysql全表扫描比索引快,则会不实用索引

  • 查看索引使用情况:show status like 'Handler_read%'

        Handler_read_key:越高越好,越高表示索引查询的次数越多

        Handler_read_rnd_next:越低越好,越高查询低效

mysql> show status like 'Handler_read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 0     |
| Handler_read_key      | 5     |
| Handler_read_last     | 0     |
| Handler_read_next     | 0     |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 0     |
| Handler_read_rnd_next | 8     |
+-----------------------+-------+
7 rows in set (0.00 sec)

3)explain分析:可以帮助我们在不真正执行某个sql语句时,就执行mysql怎样执行,这样利用我们去分析sql指令

Video-No.03 韩顺平_MySQL优化_第2张图片

4)优化策略

    1、在使用group by 分组查询是,默认分组后,还会排序,可能会降低速度,在group by 后面增加 order by null 就可以防止排序

mysql> explain SELECT user_id from t_ddd group by user_id\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t_ddd
         type: index
possible_keys: i_unique
          key: i_unique
      key_len: 4
          ref: NULL
         rows: 3
        Extra: Using index; Using temporary; Using filesort
1 row in set (0.00 sec)

mysql> explain SELECT user_id from t_ddd group by user_id order by null\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t_ddd
         type: index
possible_keys: i_unique
          key: i_unique
      key_len: 4
          ref: NULL
         rows: 3
        Extra: Using index; Using temporary
1 row in set (0.00 sec)

    2、有些情况下,可以使用连接来替代子查询。因为使用join,MySQL不需要在内存中创建临时表

select * from dept, emp where dept.deptno=emp.deptno; -- 简单处理方式

select * from dept left join emp on dept.deptno=emp.deptno;  -- 左外连接,更ok!

5、MySQL存储引擎

  • MyISAM:如果对表的事务要求不高,以查询和增加为主,考虑使用MyIASM

  • INNODB:对事务要求高,保存重要的数据,建议使用INNODB,比如订单表、新手任务完成记录表

  • Memory:存储在内存中,并不实际存储文件中,当重启mysql时,数据清除,适用于数据变化频繁,不需要入库的数据,比如用户状态表,速度极快,类似于内存缓存

    MyISAM与INNODB区别:

    • MyISAM不支持事务,INNODB支持事务

    • MyISAM支持全文索引,INNODB不支持全文索引

    • MyISAM添加和查询速度要比INNODB快

    • MyISAM是表级锁,INNODB是行级锁

    如果数据库表使用MyISAM存储引擎,一定要记得定时清理碎片:optimize table 表名

#创建MyISAM引擎数据库表
CREATE TABLE t_eee (
	id INT PRIMARY KEY,
	NAME VARCHAR(32) NOT NULL
) ENGINE=MYISAM;

#创建INNODB引擎数据库表
CREATE TABLE t_fff (
	id INT PRIMARY KEY,
	NAME VARCHAR(32) NOT NULL
) ENGINE=INNODB;

#创建Memory引擎数据库表
CREATE TABLE t_fff (
	id INT PRIMARY KEY,
	NAME VARCHAR(32) NOT NULL
) ENGINE=MEMORY;

INSERT INTO t_fff  VALUES (1, 'AAA');
INSERT INTO t_fff VALUES(2, 'BBB');

DELETE FROM t_fff;

OPTIMIZE TABLE t_fff; -- 碎片整理


    


你可能感兴趣的:(Video-No.03 韩顺平_MySQL优化)