Mysql数据库的优化技术
对mysql优化时一个综合性的技术,主要包括
a:表的设计合理化(符合3NF)
b:添加适当索引(index) [四种:普通索引、主键索引、唯一索引unique、全文索引]
c:分表技术(水平分割、垂直分割)
d:读写[写:
update/delete/add]分离
e:存储过程[模块化编程,可以提高速度]
f:对mysql配置优化[配置最大并发数my.ini,调整缓存大小]
g: mysql服务器硬件升级
h:定时的去清除不需要的数据,定时进行碎片整理(MyISAM)
u什么样的表才是符合3NF (范式)
数据库参数配置:
INNODB 重要优化设置【解决慢卡的问题】
1.内存利用方面:
innodb_buffer_pool_size
这个参数和MyISAM的key_buffer_size有相似之处,但也是有差别的。这个参数主要缓存
innodb表的索引,数据,插入数据时的缓冲。为Innodb加速优化首要参数。
该参数分配内存的原则:这个参数默认分配只有8M,可以说是非常小的一个值。这个参数不能动态更改,所以分配需多考虑
。分配过大,会使Swap占用过多,致使Mysql的查询特慢。
设置方法:
例如 innodb_buffer_pool_size=4G
第二个:
innodb_additional_mem_pool:
作用:用来存放Innodb的内部目录
这个值不用分配太大,系统可以自动调。不用设置太高。通常比较大数据设置16M够用了,如果
表比较多,可以适当的增大。如果这个值自动增加,会在error log有中显示的。
mysql 慢查询配置命令
delimiter ;//改变sql语句执行结束符
SHOW VARIABLES LIKE 'long_query_time';//查看一下默认为慢查询的时间10秒
SET global long_query_time = 1;//设置成1秒,加上global,下次进mysql已然生效
SELECT * FROM emp WHERE empno = 1234567;//慢查询语句
SHOW STATUS LIKE 'slow_queries';//查看一下当前有多少慢查询
set global slow_query_log = 'ON';//启用慢查询
show variables like '%slow%';//查看一下慢查询是不是已经开启
innodb和myisam的存储结构:
myism物理文件结构为:
.frm文件:与表相关的元数据信息都存放在frm文件,包括表结构的定义信息等。
.myd文件:myisam存储引擎专用,用于存储myisam表的数据
.myi文件:myisam存储引擎专用,用于存储myisam表的索引相关信息
.frm与表相关的元数据信息都存放在frm文件,包括表结构的定义信息等。
.ibd文件和.ibdata文件:
这两种文件都是存放innodb数据的文件,之所以用两种文件来存放innodb的数据,是因为innodb的数据存储方式能够通过配置来决定是使用共享表空间存放存储数据,还是用独享表空间存放存储数据。
独享表空间存储方式使用.ibd文件,并且每个表一个ibd文件
共享表空间存储方式使用.ibdata文件,所有表共同使用一个ibdata文件
表的范式,是首先符合1NF,才能满足2NF ,进一步满足3NF
1NF:即表的列的具有原子性,不可再分解,即列的信息,不能再分解。
☞数据库的分类
关系型数据库:mysql/oracle/db2/informix/sysbase/sql server
2NF:表中的记录是唯一的,就满足2NF,通常我们设计一个主键来实现
3NF:即表中不要有冗余数据,就是说,表的信息,如果能够被推导出来,就不应该单独的设计一个字段来存放.比如下面的设计就是不满足3NF:
反3NF :但是,没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是:在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余。
Sql语句本身的优化
问题是:如何从一个大项目中,迅速的定位执行速度慢的语句. (定位慢查询)
①首先我们了解mysql数据库的一些运行状态如何查询(比如想知道当前mysql运行的时间/一共执行了多少次select/update/delete.. /当前连接)
show status
常用的:
show status like ‘uptime’ ;
showstauts like ‘com_select’showstauts like ‘com_insert’ ...类推updatedelete
☞show [session|global] status like ....如果你不写[session|global]默认是session会话,指取出当前窗口的执行,如果你想看所有(从mysql启动到现在,则应该global)
show status like ‘connections’;
//显示慢查询次数
show status like ‘slow_queries’;
②如何去定位慢查询
默认情况下,mysql认为10秒才是一个慢查询.
l修改mysql的慢查询.
show variables like ‘long_query_time’ ; //可以显示当前慢查询时间
set long_query_time=1 ;//可以修改慢查询时间
命令执行结束符修改命令:delimiter$$
③set long_query_time=1 ;执行后这时我们如果出现一条语句执行时间超过1秒中,就会统计到.
④如果把慢查询的sql记录到我们的一个日志中
在默认情况下,我们的mysql不会记录慢查询,需要在启动mysql时候,指定记录慢查询才可以
bin\mysqld.exe - -safe-mode- -slow-query-log [mysql5.5可以在my.ini指定]这个是windows平台
先关闭mysql,再启动,如果启用了慢查询日志,默认把这个文件放在
my.ini文件中记录的位置
#Path to the database root
datadir="C:/Documents and Settings/AllUsers/Application Data/MySQL/MySQL Server 5.5/Data/"
⑤测试,可以看到在日志中就记录下我们的mysql慢sql语句.
优化问题.
通过explain语句可以分析,mysql如何执行你的sql语句。
添加索引
u四种索引(主键索引/唯一索引/全文索引/普通索引)
1.添加
1.1主键索引添加
当一张表,把某个列设为主键的时候,则该列就是主键索引
create table aaa
(id int unsigned primary key auto_increment,
name varchar(32) not null defaul ‘’);
这是id列就是主键索引.
如果你创建表时,没有指定主键索引,也可以在创建表后,在添加,指令:
alter table表名add primary key (列名);
举例:
create table bbb (id int , name varchar(32)not null default ‘’);
alter table bbb add primary key (id);
1.2普通索引
一般来说,普通索引的创建,是先创建表,然后在创建普通索引
比如:
create table ccc(
id int unsigned,
name varchar(32)
)
alter table test add index tag_count_idx(tag_count);
1.3创建全文索引
全文索引,主要是针对对文件,文本的检索,比如文章,全文索引针对MyISAM有用.
创建:
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 ‘%mysql%’;【不会使用到全文索引】
证明:
explainselect * from articles where body like ‘%mysql%’
正确的用法是:
select * from articles where
match(title,body) against(‘database’);【可以】
☞说明:
1.在mysql中fulltext索引只针对myisam生效
2.mysql自己提供的fulltext针对英文生效->sphinx
(coreseek)技术处理中文
3.使用方法是match(字段名..) against(‘关键字’)
4.全文索引一个叫停止词,因为在一个文本中,创建索引是一个无穷大的数,因此,对一些常用词和字符,就不会创建,这些词,称为停止词.
1.4唯一索引
①当表的某列被指定为unique约束时,这列就是一个唯一索引
create table ddd(id int primary keyauto_increment , name varchar(32) unique);
alter table test add unique index user_id_idx (user_id);
这时, name列就是一个唯一索引.
unique字段可以为NULL,并可以有多NULL,但是如果是具体内容,则不能重复.
主键字段,不能为NULL,也不能重复.
②在创建表后,再去创建唯一索引
create table eee(id int primary keyauto_increment, name varchar(32));
2.查询索引
desc表名【该方法的缺点是: 不能够显示索引名.】
show index(es) from表名
show keys from表名
3.删除
alter table表名drop index索引名;
如果删除主键索引。
alter table表名drop primary key[这里有一个小问题]
4.修改
先删除,再重新创建.
u为什么创建索引后,速度就会变快?
原理示意图:
u索引使用的注意事项
索引的代价:
1.占用磁盘空间
2.对dml操作有影响,变慢
u在哪些列上适合添加索引?
总结:满足以下条件的字段,才应该创建索引.
a:肯定在where条经常使用b:该字段的内容不是唯一的几个值(sex) c:字段内容不是频繁变化.
u使用索引的注意事项
把dept表中,我增加几个部门:
alter table dept add index my_ind(dname,loc); //dname左边的列,loc就是右边的列
说明,如果我们的表中有复合索引(索引作用在多列上),此时我们注意:
1,对于创建的多列索引,只要查询条件使用了最左边的列,索引一般就会被使用。
explain select * from dept where loc='aaa'\G
就不会使用到索引
2,对于使用like的查询,查询如果是‘%aaa’不会使用到索引
‘aaa%’会使用到索引。
比如: explainselect * from dept where dname like '%aaa'\G
不能使用索引,即,在like查询时,关键的‘关键字’ ,最前面,不能使用%或者_这样的字符.,如果一定要前面有变化的值,则考虑使用全文索引->sphinx.
3.如果条件中有or,即使其中有条件带索引也不会使用。换言之,就是要求使用的所有字段,都必须建立索引,我们建议大家尽量避免使用or关键字
select * fromdept where dname=’xxx’ or loc=’xx’ or deptno=45
4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使用索引。(添加时,字符串必须’’),也就是,如果列是字符串类型,就一定要用‘’把他包括起来.
5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引。
explain可以帮助我们在不真正执行某个sql语句时,就执行mysql怎样执行,这样利用我们去分析sql指令.
u如何查看索引使用的情况:
show status like‘Handler_read%’;
大家可以注意:
handler_read_key:这个值越高越好,越高表示使用索引查询到的次数。
handler_read_rnd_next:这个值越高,说明查询低效。
usql语句的小技巧
1.在使用group by分组查询是,默认分组后,还会排序,可能会降低速度.
比如:
在group by后面增加order by null就可以防止排序.
2.有些情况下,可以使用连接来替代子查询。因为使用join,MySQL不需要在内存中创建临时表。
select * from
dept, emp where dept.deptno=emp.deptno; [简单处理方式]
select * fromdept left join emp on dept.deptno=emp.deptno;[左外连接,更ok!]
如何选择mysql的存储引擎
在开发中,我们经常使用的存储引擎myisam / innodb/ memory
myisam存储:如果表对事务要求不高,同时是以查询和添加为主的,我们考虑使用myisam存储引擎. ,比如bbs中的发帖表,回复表.
INNODB存储:对事务要求高,保存的数据都是重要数据,我们建议使用INNODB,比如订单表,账号表.
问MyISAM和INNODB的区别
1.MySQL默认采用的是MyISAM
2.MyISAM不支持事务,而InnoDB支持
3.InnoDB支持数据行锁定,MyISAM不支持行锁定
4.InnoDB支持外键,MyISAM不支持
5 InnoDB的主键范围更大,最大是MyISAM的2倍。
6 InnoDB不支持全文索引,而MyISAM支持
7 MyISAM支持GIS数据
8 没有where的count(*)使用MyISAM要比InnoDB快得多
Memory存储,比如我们数据变化频繁,不需要入库,同时又频繁的查询和修改,我们考虑使用memory,速度极快.
如果你的数据库的存储引擎是myisam,请一定记住要定时进行碎片整理
举例说明:
create tabletest100(id int unsigned ,name varchar(32))engine=myisam;
insert intotest100 values(1,’aaaaa’);
insert intotest100 values(2,’bbbb’);
insert intotest100 values(3,’ccccc’);
我们应该定义对myisam进行整理
optimize tabletest100;
mysql_query(“optimize
tables $表名”);
如何在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.txthttp://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 -dumphttp://www.phptimer.com/phptimer.php
上面是通过Lynx文本浏览器来请求PHP文件
n分表技术
分表技术有(水平分割和垂直分割)
当一张越来越大时候,即使添加索引还慢的话,我们可以使用分表
以qq用户表来具体的说明一下分表的操作.
思路如图:
首先我创建三张表user0
/ user1 /user2 ,然后我再创建uuid表,该表的作用就是提供自增的id,
走代码:
create table user0(
id int unsigned primary key ,
name varchar(32) not null default '',
pwdvarchar(32) not null default '')
engine=myisam charset utf8;
create table user1(
id int unsigned primary key ,
name varchar(32) not null default '',
pwdvarchar(32) not null default '')
engine=myisam charset utf8;
create table user2(
id int unsigned primary key ,
name varchar(32) not null default '',
pwdvarchar(32) not null default '')
engine=myisam charset utf8;
create table uuid(
id int unsigned primary keyauto_increment)engine=myisam charset utf8;
编写addUser.php
//注册一个用户
$con=mysql_connect("localhost","root","root");
if(!$con){
die("连接失败!");
}
mysql_select_db("temp",$con);
$name=$_GET['name'];
$pwd=$_GET['pwd'];
//这时我们先获取用户id,id是从uuid表获取
$sql="insertinto uuid values(null)";
if(mysql_query($sql,$con)){
$id=mysql_insert_id();
}
//计算表名,就是,你应该把这个用户放入到哪个表
$talname='user'.$id%3;
$sql="insertinto {$talname} values ($id,'$name','$pwd')";
if(mysql_query($sql,$con)){
echo'添加用户到'.$talname.'ok';
}
mysql_close($con);
//
//注册一个用户
$con=mysql_connect("localhost","root","root");
if(!$con){
die("连接失败!");
}
mysql_select_db("temp",$con);
$id=intval($_GET['id']);
//计算表名
$tabname='user'.$id%3;
$sql="selectpwd from {$tabname} where id=$id";
$res=mysql_query($sql,$con);
if($row=mysql_fetch_assoc($res)){
echo"在{$tabname}.中发现id号为{$id}";
}
//.....
n垂直分割
示意图:
一句话:如果一张表某个字段,信息量大,但是我们很少查询,则可以考虑把这些字段,单独的放入到一张表中,这种方式称为垂直分割.