Mysql性能优化一
Mysql数据库的优化技术
对mysql优化是一个综合性的技术,主要包括
- 表的设计合理化(符合3NF)
- 添加适当索引(index) [四种: 普通索引、主键索引、唯一索引unique、全文索引]
- 分表技术(水平分割、垂直分割)
- 读写[写: update/delete/add]分离
- 存储过程 [模块化编程,可以提高速度]
- 对mysql配置优化 [配置最大并发数my.ini, 调整缓存大小 ]
- mysql服务器硬件升级
- 定时的去清除不需要的数据,定时进行碎片整理(MyISAM)
数据库优化工作
对于一个以数据为中心的应用,数据库的好坏直接影响到程序的性能,因此数据库性能至关重要。一般来说,要保证数据库的效率,要做好以下四个方面的工作:
① 数据库设计
② sql语句优化
③ 数据库参数配置
④ 恰当的硬件资源和操作系统
此外,使用适当的存储过程,也能提升性能。
这个顺序也表现了这四个工作对性能影响的大小
数据库表设计
1、通俗地理解三个范式,对于数据库设计大有好处。在数据库设计中,为了更好地应用三个范式,就必须通俗地理解三个范式(通俗地理解是够用的理解,并不是最科学最准确的理解):
-
第一范式:1NF是对属性的原子性约束,要求属性(列)具有原子性,不可再分解;(只要是关系型数据库都满足1NF)
-
第二范式:2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;
-
第三范式:3NF是对字段冗余性的约束,它要求字段没有冗余。 没有冗余的数据库设计可以做到。
但是,没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是: 在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余。
2、数据库的分类
-
关系型数据库: mysql/oracle/db2/informix/sysbase/sql server
-
非关系型数据库: (特点: 面向对象或者集合)
-
NoSql数据库: MongoDB(特点是面向文档)
3、举例说明什么是适度冗余,或者说有理由的冗余!
上面这个就是不合适的冗余,原因是:
在这里,为了提高学生活动记录的检索效率,把单位名称冗余到学生活动记录表里。单位信息有500条记录,而学生活动记录在一年内大概有200万数据量。 如果学生活动记录表不冗余这个单位名称字段,只包含三个int字段和一个timestamp字段,只占用了16字节,是一个很小的表。而冗余了一个 varchar(32)的字段后则是原来的3倍,检索起来相应也多了这么多的I/O。而且记录数相差悬殊,500 VS 2000000 ,导致更新一个单位名称还要更新4000条冗余记录。由此可见,这个冗余根本就是适得其反。
订单表里面的Price就是一个冗余字段,因为我们可以从订单明细表中统计出这个订单的价格,但是这个冗余是合理的,也能提升查询性能。
从上面两个例子中可以得出一个结论:
1---n 冗余应当发生在1这一方。
选择合适的数据类型
1、在精度要求高的应用中,建议使用定点数来存储数值,以保证结果的准确性。能用deciaml就不要用float
create table temp1( t1 float(10,2), t2 decimal(10,2)); insert into temp1 values(1000000.32,1000000,32); 发现 t1 成了 1000000.31 所以有问题.
2、对于存储引擎是MyISAM的数据库,如果经常做删除和修改记录的操作,要定时执行optimize table table_name;功能对表进行碎片整理。
create table temp2( id int) engine=MyISAM; insert into temp2 values(1); insert into temp2 values(2); insert into temp2 values(3); insert into temp2 select * from temp2;--复制 delete from temp2 where id=1; --发现该表对应的数据文件没有变小
3、日期类型要根据实际需要选择能够满足应用的最小存储的日期类型
create table bbs(id int ,con varchar(1024) , pub_time int); date('Ymd',时间-3*24*60*60); 2038年-1-19
选择合适的存储引擎
1、常用的存储引擎
MyISAM:Mysql5.5默认的MySQL存储引擎。如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性要求不是很高。其优势是访问的速度快。
InnoDB:Mysql5.6默认的MySQL存储引擎,提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比MyISAM,写的处理效率差一些并且会占用更多的磁盘空间。
Memory:数据存在内存中,服务重启时,数据丢失
MyISAM: 在插入数据时,默认放在最后. ,删除数据后,空间不回收.(不支持事务和外键)
InnoDB 支持事务和外键,对应我们程序员说,常用的存储引擎主要是 myisam / innodb / memory,heap 表
如果选用小原则:
-
1.如果追求速度,不在乎数据是否一直保存,也不考虑事务,请选择 memory 比如存放用户在线状态.
-
2.如果表的数据要持久保存,应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性要求不是很高。选用MyISAM
-
3.如果需要数据持久保存,并提供了具有提交、回滚和崩溃恢复能力的事务安全,请选用Innodb
SQL语句优化
SQL优化的一般步骤
- 通过show status命令了解各种SQL的执行频率。
- 定位执行效率较低的SQL语句-(重点select)
- 通过explain分析低效率的SQL
- 确定问题并采取相应的优化措施
1.保证不查询多余的列与行。
- 尽量避免select * 的存在,使用具体的列代替*,避免多余的列
- 使用where限定具体要查询的数据,避免多余的行
- 使用top,distinct关键字减少多余重复的行
2.慎用distinct关键字
distinct在查询一个字段或者很少字段的情况下使用,会避免重复数据的出现,给查询带来优化效果。
当查询很多字段时,如果使用distinct,数据库引擎就会对数据进行比较,过滤掉重复数据,然而这个比较,过滤的过程则会毫不客气的占用系统资源,cpu时间。
3.慎用union关键字
此关键字主要功能是把各个查询语句的结果集合并到一个结果集中返回给你。
满足union的语句必须满足:1.列数相同。 2.对应列数的数据类型要保持兼容。
执行的第三步:对结果集进行排序,过滤重复记录,效率低,然而不对结果集排序过滤,显然效率是比union高的,使用union all能对union进行一定的优化。
4.判断表中是否存在数据
select count(*) from product select top(1) id from product
很显然下面完胜
5.连接查询的优化
首先你要弄明白你想要的数据是什么样子的,然后再做出决定使用哪一种连接,这很重要,减少连接表的数据数量可以提高效率。
各种连接的取值大小为:
- 内连接结果集大小取决于左右表满足条件的数量
- 左连接取决与左表大小,右相反。
- 完全连接和交叉连接取决与左右两个表的数据总数量
6、insert插入优化
insert into select批量插入,明显提升效率,所以以后尽量避免一个个循环插入。
7、优化修改删除语句
如果你同时修改或删除过多数据,会造成cpu利用率过高从而影响别人对数据库的访问。如果你删除或修改过多数据,采用单一循环操作,那么会是效率很低,也就是操作时间过程会很漫长。折中的办法就是,分批操作数据。
SQL语句优化-定位慢查询
1、如何从一个大项目中,迅速的定位执行速度慢的语句. (定位慢查询)
首先我们了解mysql数据库的一些运行状态如何查询(比如想知道当前mysql运行的时间/一共执行了多少次select/update/delete.. / 当前连接)
为了便于测试,我们构建一个大表(400 万)-> 使用存储过程构建,默认情况下,mysql认为10秒才是一个慢查询.
- 修改mysql的慢查询.
show variables like 'long_query_time' ; //可以显示当前慢查询时间 set long_query_time=1 ;//可以修改慢查询时间
- 构建大表->大表中记录有要求, 记录是不同才有用,否则测试效果和真实的相差大.创建:
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定义一个变量 chars_str,类型是 varchar(100),默认值'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'; declare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'; declare return_str varchar(255) default ''; declare i int default 0; while i < n do 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 insert_emp(in start int(10),in max_num int(10)) begin declare i int default 0; #set autocommit =0 把autocommit设置成0 set autocommit = 0; repeat set i = i + 1; insert into emp values ((start+i) ,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand()); until i = max_num end repeat; commit; end
2、调用刚刚写好的函数, 1800000条记录,从100001号开始
- call insert_emp(100001,4000000);
这时我们如果出现一条语句执行时间超过1秒中,就会统计到。如果把慢查询的sql记录到我们的一个日志中,在默认情况下,低版本的mysql不会记录慢查询,需要在启动mysql时候,指定记录慢查询才可以。
-
bin\mysqld.exe - -safe-mode - -slow-query-log [mysql5.5 可以在my.ini指定]
-
bin\mysqld.exe –log-slow-queries=d:/abc.log [低版本mysql5.0可以在my.ini指定]
该慢查询日志会放在data目录下在mysql5.0这个版本中时放在 mysql安装目录/data/下];
在 mysql5.5.19下是需要查看my.ini 的 datadir="C:/Documents and Settings/All Users/Application Data/MySQL/MySQL Server 5.5/Data/“来确定.
在mysql5.6中,默认是启动记录慢查询的,my.ini的所在目录为:C:\ProgramData\MySQL\MySQL Server 5.6,其中有一个配置项slow-query-log=1;
针对 mysql5.5启动慢查询有两种方法:
-
bin\mysqld.exe - -safe-mode - -slow-query-log
-
也可以在my.ini 文件中配置:
[mysqld] # The TCP/IP Port the MySQL Server will listen on port=3306 slow-query-log
通过慢查询日志定位执行效率较低的SQL语句。慢查询日志记录了所有执行时间超过long_query_time所设置的SQL语句。
show variables like 'long_query_time';
set long_query_time=2;
为dept表添加数据
desc dept; ALTER table dept add id int PRIMARY key auto_increment; CREATE PRIMARY KEY on dept(id); create INDEX idx_dptno_dptname on dept(deptno,dname); INSERT into dept(deptno,dname,loc) values(1,'研发部','康和盛大厦5楼501'); INSERT into dept(deptno,dname,loc) values(2,'产品部','康和盛大厦5楼502'); INSERT into dept(deptno,dname,loc) values(3,'财务部','康和盛大厦5楼503'); UPDATE emp set deptno=1 where empno=100002;
****测试语句***[对emp表的记录可以为3600000 ,效果很明显慢
select * from emp where empno=(select empno from emp where ename='研发部')
如果带上order by e.empno 速度就会更慢,有时会到1min多.
测试语句
select * from emp e,dept d where e.empno=100002 and e.deptno=d.deptno;
查看慢查询日志:默认为数据目录data中的host-name-slow.log。低版本的mysql需要通过在开启mysql时使用- -log-slow-queries[=file_name]来配置
建立适当的索引
1、索引的原理说明
btree类型的索引,就是使用的二分查找法,肯定快啊,算法复杂度是log2N,也就是说16条数据查4次,32条数据查5次,64条数据查6次....依次类推。使用索引跟没使用索引的区别,就跟我们使用新华字典查字,一个是根据拼音或者笔画查找,一个是从头到尾一页一页翻。
2、索引的代价
-
磁盘占用
-
对dml(update delete insert)语句的效率影响
3、哪些列上适合添加索引
-
较频繁的作为查询条件字段应该创建索引
select * from emp where empno = 1;
- 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件
select * from emp where sex = '男'
- 更新非常频繁的字段不适合创建索引
select * from emp where logincount = 1
- 不会出现在WHERE子句中的字段不该创建索引
4、索引的类型
-
主键索引,主键自动的为主索引 (类型Primary)
-
唯一索引 (UNIQUE)
-
普通索引 (INDEX)
-
全文索引 (FULLTEXT) [适用于MyISAM] ——》sphinx + 中文分词 coreseek [sphinx 的中文版 ]
-
综合使用=>复合索引
5、四种索引的区别
-
PRIMARY 索引 =》在主键上自动创建
-
UNIQUE 索引=> 只要是UNiQUE 就是Unique索引.(只能在字段内容不重复的情况下,才能创建唯一索引)
-
INDEX 索引=>就是普通索引
-
FULLTEXT => 只在MYISAM 存储引擎支持, 目的是全文索引,在内容系统中用的多, 在全英文网站用多(英文词独立). 中文数据不常用,意义不大,国内全文索引通常使用 sphinx来完成,全文索引只能在 char varchar text字段创建.
- 使用全文索引,不能使用like语句,这样就不会使用到全文索引了.
6、索引的使用
下列几种情况下有可能使用到索引:
-
对于创建的多列索引,只要查询条件使用了最左边的列,索引一般就会被使用。
explain select * from dept where dname='研发部'; 会显示使用到了索引myind explain select * from dept where loc='MsBDpMRX'; 不会显示使用到了索引myind
- 对于使用like的查询,查询如果是 '%aaa' 不会使用到索引, 'aaa%' 会使用到索引。
explain select * from dept where dname like '%研发部'; 不会显示使用到了索引myind explain select * from dept where dname like '研发部%'; 会显示使用到了索引myind
下列的表将不使用索引:
-
如果条件中有or,即使其中有条件带索引也不会使用。
-
对于多列索引,不是使用的第一部分,则不会使用索引。
-
like查询是以%开头
-
如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使用索引。(添加时,字符串必须'')
select * from dept from dname=1234; //不会使用到索引 select * from dept from dname='1234'; //会使用到索引
7、查看索引的使用情况
-
show status like 'Handler_read%'
-
handler_read_key:这个值越高越好,越高表示使用索引查询到的次数。
-
handler_read_rnd_next:这个值越高,说明查询低效。