mysql优化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql优化相关的知识,希望对你有一定的参考价值。

mysql优化技术

1 ????mysql优化概述????1

1.1????常用的方法有????1

2 ????3NF的讲解????2

2.1????1NF????2

2.2????2NF????2

2.3????3nf????2

2.4????3NF????3

3 ????定位慢查询????4

3.1????构建一个海量表(400w)????4

?

  1. mysql优化概述

    前面我们讲的页面静态化,memcached是通过减少对mysql操作来提示访问速度,提高大并发,但是一个网站总是要操作数据库,这时我们如何提示对mysql操作的速度.

    1. 常用的方法有
      1. 创建的表要满足3NF(3范式), 即要满足3个规范,最高级6NF.
      2. 创建适当索引[主键索引|唯一索引|普通索引|全文索引 fulltext|空间索引]
      3. 优化程序中sql语句(定位慢查询)
      4. 创建适当的存储过程,函数,视图,触发器
      5. 读写分离
      6. 分表技术[水平分表,垂直分表, 逻辑]和分区技术[把海量数据分配到不同磁盘分区]
      7. my.ini 配置优化
      8. 硬件升级
  2. 3NF的讲解
    1. 1NF

    我们的表要满足两个条件: 1。表的属性(列)要具有原子性(不可分割)2: 表不能有重复的列, 只要是关系型数据库,就天然的满足1NF

    关系型数据库: mysql , sql server, oracle , informix , db2, postgresql

    非关系型数据: 面向对象和集合

    nosql数据库: mongodb[面向文档.]

    1. 2NF

    表要满足: 不能存在完全相同的两条记录, 通常是通过设置一个主键来实现. 主键一般是非业务逻辑主键.

    技术分享

    1. 3nf

    表中不能存在冗余数据. 表的列的值,如果可过通过显示推导或者隐式的推导出,则就不应该设置该列.

    举例

    技术分享

    1. 反3NF

    在实际开发,我们遵守 3NF, 但是有时处于效率的考虑,我们也可能用的反3NF,举例

    技术分享

  3. 定位慢查询

    如何快速把一个项目中有问题的sql语句定位,然后在优化。

    1. 构建一个海量表(400w)
      1. 创建一个测试数据库

        create database temp;

      2. use temp;
      3. set names gbk;

    CREATE TABLE dept( /*部门表*/

    deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,

    dname VARCHAR(20) NOT NULL DEFAULT "",

    loc VARCHAR(13) NOT NULL DEFAULT ""

    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;

    ?

    CREATE TABLE emp

    (empno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, /*编号*/

    ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/

    job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/

    mgr MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*上级编号*/

    hiredate DATE NOT NULL,/*入职时间*/

    sal DECIMAL(7,2) NOT NULL,/*薪水*/

    comm DECIMAL(7,2) NOT NULL,/*红利*/

    deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部门编号*/

    )ENGINE=MyISAM DEFAULT CHARSET=utf8 ;

    ?

    ?

    #工资级别表

    CREATE TABLE salgrade

    (

    grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,

    losal DECIMAL(17,2) NOT NULL,

    hisal DECIMAL(17,2) NOT NULL

    )ENGINE=MyISAM DEFAULT CHARSET=utf8;

    ?

    #测试数据

    INSERT INTO salgrade VALUES (1,700,1200);

    INSERT INTO salgrade VALUES (2,1201,1400);

    INSERT INTO salgrade VALUES (3,1401,2000);

    INSERT INTO salgrade VALUES (4,2001,3000);

    INSERT INTO salgrade VALUES (5,3001,9999);

    ?

    #定义一个新的命令结束符合,防止创建存储过程冲突

    delimiter $$

    ?

    create function rand_string(n INT)

    returns varchar(255) #该函数会返回一个字符串

    begin

    #定义了一个变量 chars_str, 类型 varchar(100)

    #默认给 chars_str 初始值 ‘abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ‘

    declare chars_str varchar(100) default

    ‘abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ‘;

    declare return_str varchar(255) default ‘‘;

    declare i int default 0;

    while i < n do

    # concat 函数 : 连接函数

    set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1));

    set i = i + 1;

    end while;

    return return_str;

    end $$

    ?

    #这里我们又自定了一个函数,返回一个随机的部门号

    create function rand_num( )

    returns int(5)

    begin

    declare i int default 0;

    set i = floor(10+rand()*500);

    return i;

    end $$

    ?

    #随即添加雇员 max_num条 ,雇员的编号从 start

    create procedure insert_emp(in start int(10),in max_num int(10))

    begin

    declare i int default 0;

    #set autocommit =0 把autocommit设置成0

    #autocommit = 0 含义: 不要自动提交

    set autocommit = 0; #默认不提交sql语句

    repeat

    set i = i + 1;

    #通过前面写的函数随机删除字符和数组来添加不同记录到 emp表

    insert into emp values ((start+i) ,rand_string(6),‘SALESMAN‘,0001,curdate(),2000,400,rand_num());

    until i = max_num

    end repeat;

    #commit整体提交所有sql语句,提高效率

    commit;

    end $$

    ?

    #调用刚刚写好的函数, 4000000条记录,从100001号开始

    call insert_emp(100001,4000000);

    1. 当数据量大,我们看看问题

    技术分享

    1. 如何解决-如何定位慢查询

    在默认情况下,mysql不会记录慢查询语句. , 在默认情况下,慢查询的时间是10s.

    show variables like ‘long_query_time‘;

    1. 以记录慢查询的方式来启动mysql

      {%mysql%}>bin/mysqld.exe --safe-mode --slow-query-log

      先把mysql关闭后,再重启

      技术分享

    2. 慢查询日志存放在mysql的data目录下. 在my.ini 配置文件有data目录

    #Path to the database root

    datadir="C:/Documents and Settings/All Users/Application Data/MySQL/MySQL Server 5.5/Data/"

    1. 为了测试,我们修改一下mysql默认的慢查询时间

      set long_query_time=1;

    2. 慢查询日志的查看

      技术分享

    3. 我们需要明确,为什么这个select 慢.

      这里给大家介绍一款工具 explain 工具,可以帮助我们分析mysql数据库在执行一个sql语句的时候,是安装什么方式执行[获得关于MySQL如何执行SELECT语句的信息].

      explain sql\\G

      explain select * from emp where empno=347677\\G

      技术分享

      ?

    4. 通过刚才的分析,我们初步判断是没有创建索引造成.

      在empno 这个字段上创建主键索引

      alter table 表名 add primary key(列名,列名...)

      alter table emp add primary key (empno);

      技术分享

      创建完索引后,我们发现索引文件变大了,这说明索引有开销,要占用磁盘空间.

    5. 测试看看效果如何.

      技术分享

    1. 简单分析一把索引的原理

    技术分享

    1. 索引的代价是

      占用跟多的磁盘空间

      对dml(update ,insert , delete )速度有影响

    2. 目前的索引类型
  • hash的索引
  • bintree 二叉树索引
  1. 创建索引的注意事项

    技术分享

?

  1. 索引详解
    1. 索引的创建
      1. 创建主键索引

    主键索引创建有两种情况, 1. 在创建表的时候,直接指定主键索引 2. 创建表后,在增加主键索引.

    举例

    1. create table aaa( id int primary key auto_increment, name varchar(32));
    2. create table bbb(id int , name varchar(32));

      alter table bbb add primary key(id);

    主键索引的特点1. 一个表中最多只有一个主键索引 2. 一个主键索引可以指向多列 3. 主键索引的列,不能有重复的值,也不能有null 4. 主键索引的效率高

    1. 唯一索引的创建

    唯一索引的创建有两种情况: 1. 在创建表时,直接指定唯一索引, 2. 把表创建完后,在指定唯一索引.

    举例:

    1. create table ddd( id int primary key auto_increment, name varchar(32) not null default ‘‘, email varchar(64) unique);
    2. create table eee( id int primary key auto_increment, name varchar(32) not null default ‘‘, email varchar(64));

      添加索引方法有两种

      1. create unique index 索引名字 on 表名 (列名..);

        create unique index uni_email on eee(email);

      2. alter table 表名 add unique [索引名] (列名..)

        alter table eee add unique (email );

    唯一索引的特点: 1. 一个表中可以有多个唯一索引 2. 一个唯一索引可以指向多列 3. 如果你在唯一索引上,没有指定not null ,则该列可以为空,同时可以有多个null

    1. 唯一索引的效率较高.
      1. 普通索引的创建

    一般说,是创建好表后,在指定普通索引,举例:

    create table fff(id int primary key auto_increment, name varchar(32) not null default ‘‘,email varchar(64) not null default ‘‘);

    添加普通索引方法两个

    1. create index 索引名 on 表名 (列名。。)

      create index inx_email on fff(email);

    2. alter table 表名 add index [索引名] (列名..)
      1. ???? 全文索引的创建

    mysql自带的全文索引mysql5.5不支持中文, 支持英文,同时要求表的存储引擎是myisam。如果希望支持中文,有两个方案 1. 使用sphinx中文版 coreseek 2. 插件mysqlcft.

    创建一张表

    CREATE TABLE articles (

    ??? ?? id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,

    ??? ?? title VARCHAR(200),

    ??? ?? body TEXT,

    ??? ?? FULLTEXT (title,body)

    ??? )engine=myisam 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 ...‘);

    ?

    ????//如何使用全文索引.

    ????select * from articles where body like ‘% database %‘; [这样没有使用到索引]

    SELECT * FROM articles WHERE MATCH (title,body) AGAINST (‘database‘)[ok]
    

    全文索引有两个概念[匹配度,停止词]
    

    匹配度: 在使用全文索引去检索数据,匹配到的概率是多大.
    						

    ????技术分享

    停止词: 全文索引对非常普通的词,不会创建索引.

    技术分享

    1. 索引的查询
      1. show index from 表名
      2. show indexes from 表名
      3. show keys from 表名
      4. desc 表名;
    2. 索引的修改

    删除索引,再创建.

    ?

    1. 索引的删除

    drop index索引名 on 表名;

    alter table 表名 drop index 索引名 ;

    主键删除

    alter table 表名 drop primary key;

    ? 如果你在删除主键索引时,该主键是自增的,则需要先去掉自增属性,然后在干掉.

    create table ttt( id int unsigned primary key auto_increment , name varchar(32) not null default ‘‘);

    先去掉auto_increment;

    alter table 表名 modify 列定义;

    (1)alter table ttt modify id int unsigned;

    (2) alter table ttt drop primary key;

    ?

  2. 正确的使用索引,优化sql语句

    有了索引后,我们要正确的使用索引,需要注意的地方如下,为了说清这个问题,我们在 dpet表中添加数据,做测试.

    1. sql语句使用技巧

    技术分享

    1. 创建索引

      技术分享

    2. 对于创建的多列(复合)索引,只要查询条件使用了最左边的列,索引一般就会被使用.

      技术分享

    3. 对于使用like的查询,查询如果是 ‘%aaa‘ 不会使用到索引;‘aaa%‘ 会使用到索引;

      技术分享

    4. 如果条件中有or,则要求or的所有字段都必须有索引,否则不能用的索引
    5. 如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使用索引.

      技术分享

    6. 如果mysql估计使用全表扫描要比使用索引快,则不使用索引
    7. 优化group by 语句

      默认情况,MySQL对所有的group by col1,col2进行排序。这与在查询中指定order by col1, col2类似。如果查询中包括group by但用户想要避免排序结果的消耗,则可以使用order by null禁止排序

      技术分享

    8. 尽量不要使用子查询来处理,可以考虑使用join 来处理.
    1. 索引使用情况的统一

    技术分享

    1. 对应mysql管理员(dba)

      技术分享

      ?


      如何选择mysql的存储引擎

      1. myisam

        表以读和写为主,有少量的删除和修改 ,同时对事务要求不高(比如帖子,公共聊天)

      2. innodb

        表对事务要求高,比如(账号, 积分)

      3. memory/heap 表

        如果没有memcached或者redis, 但是数据操作频繁,可以考虑使用memory存储引擎,比如好友在线状态。

    mysql> create table userstat (id int primary key auto_increment,state tinyint no

    t null default 0)engine=memory charset utf8;

    说明: memory表的数据都在内存中,因此操作速度快,但是缺少是当mysql重启后,数据丢失,但表的结构在.

    1. 数据类型的选择
      1. 在精度要求高的应用中,建议使用定点数来存储数值,以保证结果的准确性。decimal 不要用float

        技术分享

    2. myisam表的定时维护

    对于myisam存储引擎而言,我们需要定时执行 optimize table 表名;

    举例:

    技术分享

    1. 2k38问题

    我们在mysql中存放日期时,可以存放数 (int...) 而int可以存放的数据最大为4294967295, 当php中要显示一个大于2038年日期,该如何处理?

    2k28.php

    技术分享

  3. mysql的分表和分区技术

    概述: 当一个表很大时,比如200G, 这是太大,这时我只靠创建索引也不好搞定,这时我们需要分表和分区. 分表有两种形式(水平分表,垂直分表), 分区技术(按时间).

    1. 水平分表

    核心思想: 把一个大表,分割N个小表,小表和大表的结构是一样一样的,只是把数据分散到不同表中,举例:

    ?

    示意图:

    创建数据库

    mysql> create table uuid(id int unsigned primary key auto_increment);

    mysql> create table qq_user0(id int unsigned primary key, name varchar(32) not n

    ull default ‘‘, pwd char(32) not null default ‘‘,email varchar(64) not null defa

    ult ‘‘);

    create table qq_user1 like qq_user0;

    create table qq_user2 like qq_user0;

    代码: register.php

    技术分享

    login.php

    技术分享

    对代码思考,如果我们需要通过邮件或者用户名登陆,又怎么处理?

    思路:

    技术分享

    这里推出一个的算法把md5值转成一个10进制数据

    1. 垂直分割

    当有一个表中,存在这样字段,1数据量大,2. 很少被查询, 我们可以把这样的字段单独分割取出,放入到另外一张表,然后通过id关联

    技术分享

    1. 分区技术

    把一个海量表的数据分散到不同的磁盘,从而提速, 举一个案例

    技术分享

    上面的情况特别适合使用时间来分区.

    CREATE TABLE part_balance

    (

    id int default NULL,

    name varchar(30) default NULL,

    savetime date default NULL

    ) engine=myisam

    PARTITION BY RANGE (year(savetime)) (PARTITION p0 VALUES LESS THAN (1995),

    PARTITION p1 VALUES LESS THAN (1996) ,

    PARTITION p2 VALUES LESS THAN (1997) ,

    PARTITION p3 VALUES LESS THAN (1998) ,

    PARTITION p4 VALUES LESS THAN (1999) ,

    PARTITION p5 VALUES LESS THAN (2000) ,

    PARTITION p6 VALUES LESS THAN (2001) ,

    PARTITION p7 VALUES LESS THAN (2002) ,

    PARTITION p8 VALUES LESS THAN (2003) ,

    PARTITION p9 VALUES LESS THAN (2004) ,

    PARTITION p10 VALUES LESS THAN (2010),

    PARTITION p11 VALUES LESS THAN MAXVALUE );

    ?

    #普通表

    create table no_part_balance

    (

    id int default NULL,

    name varchar(30) default NULL,

    savetime date default NULL

    ) engine=myisam;

    ?

    create function rand_string(n INT)

    returns varchar(255) #该函数会返回一个字符串

    begin

    #定义了一个变量 chars_str, 类型 varchar(100)

    #默认给 chars_str 初始值 ‘abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ‘

    declare chars_str varchar(100) default

    ‘abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ‘;

    declare return_str varchar(255) default ‘‘;

    declare i int default 0;

    while i < n do

    # concat 函数 : 连接函数

    set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1));

    set i = i + 1;

    end while;

    return return_str;

    end $$

    ?

    CREATE PROCEDURE add_tab()

    begin

    declare v int default 0;

    while v < 2000

    do

    insert into part_balance values (v,rand_string(10),adddate(‘1995-01-01‘,(rand(v)*36520) mod 3652));

    set v = v + 1;

    end while;

    end$$

    ?

    ?

    --执行

    delimiter ;

    call add_tab();

    insert into no_part_balance select * from part_balance;

    --执行看看时间如何,结论分区表的时间要短很多.

    select count(*) from no_part_balance where savetime > ‘1995-01-01‘ and savetime < ‘1995-12-31‘;

    select count(*) from part_balance where savetime > ‘1995-01-01‘ and savetime < ‘1995-12-31‘;

    --使用explain 看看mysql是如何执行的

    explain partitions select count(*) from no_part_balance where savetime > ‘1995-01-01‘ and savetime < ‘1995-12-31‘\\G

    explain partitions select count(*) from part_balance where savetime > ‘1995-01-01‘ and savetime < ‘1995-12-31‘\\G

    --增加未索引字段查询

    select count(*) from part_balance where savetime > ‘1995-01-01‘ and savetime < ‘1996-12-31‘ and name=‘hello‘;

    select count(*) from no_part_balance where savetime > ‘1995-01-01‘ and savetime < ‘1996-12-31‘ and name=‘hello‘;

    --查询数据本身,得出结论也是分区表快

    select * from part_balance where savetime = ‘1995-01-03‘;

    select * from no_part_balance where savetime = ‘1995-01-03‘

  4. PHP定时完成数据库的备份/维护

    在PHP网站运行中,我们经常会有对网站的维护操作,需要定时反复执行,比如定时备份数据库, 定时碎片整理, 等等 。

    1. windows下定时维护

    举一个具体案例,需要在每天凌晨2:00备份temp数据库中的dept表.

    1. 知道如何备份数据库

      cmd>mysqldump.exe –u root –p密码 temp dept > d:/dept.bak;

    2. 直接该指令放入到 mytask.bat文件

      C:\\myenv\\mysql\\bin\\mysqldump.exe -uroot -proot temp dept > d:/dept.bak

    3. 在windows使用任务计划定时的掉bat

      具体配置看一下视频.

      技术分享

    4. 我们需要实现每次备份,不要覆盖原来的文件。思路: 我们创建一个文件mytask.php,然后定时让该文件执行。

      技术分享

      ?

      mytask.php

      技术分享

      mytask.bat

      技术分享

    1. linux下如何定时完成维护工作

    linux如何备份.

    1. 直接执行PHP脚本, 需要在同一个服务器上执行.

    # crontab -e

    00 * * * * /usr/local/bin/php /home/htdocs/phptimer.php

    2.通过HTTP请求来触发脚本, PHP文件允许不在同一服务器上

    # crontab -e

    00 * * * * /usr/bin/wget -q -O temp.txt http://www.phptimer.com/phptimer.php

    上面是通过wget来请求PHP文件, PHP输出会保存在临时文件temp.txt中

    # crontab -e

    00 * * * * /usr/bin/curl -o temp.txt http://www.phptimer.com/phptimer.php

    上面是通过curl -o来请求PHP文件, PHP输出会保存在临时文件temp.txt中

    # crontab –e

    00 * * * * lynx -dump http://www.phptimer.com/phptimer.php

    上面是通过Lynx文本浏览器来请求PHP文件

    ?

    ?

    ?

    ?

以上是关于mysql优化的主要内容,如果未能解决你的问题,请参考以下文章

mysql 优化包括哪些内容?

Mysql的性能优化

mysql 优化包括哪些内容?

Mysql的性能优化

Mysql的性能优化

mysql 子查询 优化