对比项 | MyISAM | InnoDB |
---|---|---|
主外键 | 不支持 | 支持 |
事务 | 不支持 | 支持 |
行表锁 | 表锁,即使操作一条记录也会锁住整个表,不适合高并发的操作 | 行锁,操作时只锁某一行,不对其他行有影响,适合高并发的操作 |
缓存 | 只缓存索引,不缓存真实数据 | 不仅缓存索引还要缓存真实数据,对内存要求较高,而且内存大小对性能有决定性的影响。 |
表空间 | 小 | 大 |
关注点 | 性能 | 事务 |
默认安装 | Y | Y |
mysql逻辑架构分四层
查看引擎
show engines; show variables like '%engine%';
查询语句写的烂
索引失效
单值索引(在查的时候只给某个表的某一个字段建索引)
select * from user where name = ''; /* 根据user表的name字段建立索引 */ create index idx_user_name on user(name);
复合索引
select * from user where name = '' and email = ''; /* 根据user表的name字段和email建立复合索引 */ create index idx_user_nameEmail on user(name,email);
关联查询太多join(设计缺陷或不得已的需求)
服务器调优及各个参数设置(缓冲、线程数等)
mysql优化的总结:
创建
CREATE INDEX indexName ON mytable(字段);
删除
DROP INDEX [indexName] ON mytable;
查看
show index from mytable;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tgSEfa4Q-1621561685749)(C:\Users\p0152550\Desktop\image\40f0ade6f1bdf1d770d324d13785cdff.png)]
单表索引优化:查询category_id为1,且comments>1的情况下,views最多的article_id
CREATE table if not exists `article`( `id` int(10) unsigned not null primary key auto_increment, `author_id` int(10) unsigned not null, `category_id` int(10) unsigned not null, `views` int(10) unsigned not null, `comments` int(10) unsigned not null, `title` varbinary(255) not null, `content` text not null ); insert into `article`(`author_id`,`category_id`,`views`,`comments`,`title`,`content`) values (1,1,1,1,'1','1'), (2,2,2,2,'2','2'), (1,1,3,3,'3','3'); select * from article;
explain select id, author_id from article where category_id=1 and comments>1 order by views DESC limit 1;
create index idx_article_ccv on article(category_id, comments, views);
create index idx_article_cv on article(category_id, views);
两表索引优化:两表索引优化,左拼给右表加索引。
create table if not exists `class` ( `id` int(10) unsigned not null AUTO_INCREMENT, `card` int(10) unsigned not null, primary key(`id`) ); create table if not exists `book` ( `bookid` int(10) unsigned not null AUTO_INCREMENT, `card` int(10) unsigned not null, primary key(`bookid`) ); insert into class(card) values(FLOOR(1 + (RAND() * 20))); insert into book(card) values(FLOOR(1 + (RAND() * 20)));
explain select * from class left join book on class.card=book.card;
Alter table `book` add index Y (`card`);
三表索引优化:三表连接,用小表驱动大表,索引建立在left 或 right 后面的表
create table if not exists `phone`( `phoneid` int(10) unsigned not null auto_increment, `card` int(10) unsigned not null, primary key(`phoneid`) )engine=InnoDB; insert into phone(card) values(floor(1+(rand()*20))); insert into phone(card) values(floor(1+(rand()*20))); show index from book; drop index Y on book; alter table `phone` add index z (`card`); alter table `book` add index Y (`card`); explain select * from class left join book on class.card=book.card left join phone on book.card=phone.card;
create table staffs( id int primary key auto_increment, NAME varchar(24) not null default '' COMMENT '姓名', age int not null default 0 comment '年龄', pos varchar(20) not null default '' COMMENT '职位', add_time timestamp not null default current_timestamp comment '入职时间' )charset utf8 comment '员工记录表'; insert into staffs(NAME, age, pos, add_time) values('z3', 22, 'manager', NOW()); insert into staffs(NAME, age, pos, add_time) values('July', 23, 'dev', NOW()); insert into staffs(NAME, age, pos, add_time) values('2000', 23, 'dev', NOW()); alter table staffs add index idx_staffs_nameAgePos(name, age, pos); select * from staffs where age=25 and pos='dev'; select * from staffs where pos='dev'; explain select * from staffs where age=25 and pos='dev'; explain select * from staffs where pos='dev';
经过分析,发现上面索引失效。
防止索引失效:
口诀:
全职匹配我最爱,最左点缀要遵守
带头大哥不能死,中间兄弟不能断
索引列上少计算,范围之后全失效
like百分写最右,覆盖索引不写星
不等空值还有or,索引失效要少用
var引号不可丢 ,SQL高级也不难
create table test03 ( id int primary key not null auto_increment, c1 char(10), c2 char(10), c3 char(10), c4 char(10), c5 char(10) ); insert into test03(c1,c2,c3,c4,c5) values('a1','a2','a3','a4','a5'); insert into test03(c1,c2,c3,c4,c5) values('b1','b2','b3','b4','b5'); insert into test03(c1,c2,c3,c4,c5) values('c1','c2','c3','c4','c5'); insert into test03(c1,c2,c3,c4,c5) values('d1','d2','d3','d4','d5'); insert into test03(c1,c2,c3,c4,c5) values('e1','e2','e3','e4','e5'); select * from test03; create index idx_test03_c1234 on test03(c1,c2,c3,c4); show index from test03;
问题:根据以下sql分析下索引使用情况
1. explain select * from test03 where c1='a1' and c2='a2' and c3='a3' and c4='a4'; 2. explain select * from test03 where c1='a1' and c2='a2' and c4='a4' and c3='a3'; 3. explain select * from test03 where c4='a4' and c3='a3' and c2='a2' and c1='a1'; 4. explain select * from test03 where c1='a1' and c2='a2' and c3>'a3' and c4='a4'; 5. explain select * from test03 where c1='a1' and c2='a2' and c4>'a4' and c3='a3'; 6. explain select * from test03 where c1='a1' and c2='a2' and c4='a4' order by c3; 7. explain select * from test03 where c1='a1' and c2='a2' order by c3; 8. explain select * from test03 where c1='a1' and c2='a2' order by c4; 9. explain select * from test03 where c1='a1' and c5='a5' order by c2,c3; 10. explain select * from test03 where c1='a1' and c5='a5' order by c3,c2; 11. explain select * from test03 where c1='a1' and c2='a2' order by c2,c3; 12. explain select * from test03 where c1='a1' and c2='a2' order by c3,c2;
第一条sql:正常
第二条sql:和第一条一样,正常,因为mysql会自动调优,但不推荐这样写
第三条sql:和第一条一样,正常,因为mysql会自动调优,但不推荐这样写
第四条sql:出问题,type为range,ref为null,using index condition,只用到了3个索引,因为范围之后全失效。
第五条sql:type为range,ref为null,using index condition,用到了4个索引,因为mysql底层会自动调优,把C3放到C4前面。
第六条sql:type为ref,只用到了c1,c2两个索引,其实c3也用到了,但是只使用了c3的排序功能,所以没有统计到explain里面。
第七条sql:和第六条一样,但filtered仅为20%
第八条sql:用到了c1,c2索引,但是有文件内排序错误,因为违背了最佳左前缀法则,没写c3直接写的c4
第九条sql:只用c1一个字段索引,c2,c3用于排序,无filesort。
第十条sql:出现了filesort,因为违背了最佳左前缀法则。
第十一条sql:只用c1,c2两个字段索引,c2,c3用于排序,无filesort。
第十二条sql:没有filesort,因为前面有c2字段索引,所以后面group by没有filesort。建议与第十条对比。
查询优化的原则:
select * from tbl_emp; -- 大表 select * from tbl_dept; -- 小表 explain select * from tbl_emp e where e.deptId in (select id from tbl_dept d); -- 当tbl_dept的数据集小于tbl_emp的数据集时,用in优于用exists explain select * from tbl_emp e where exists (select 1 from tbl_dept d where d.id=e.deptId); -- 当tbl_emp的数据集小于tbl_dept的数据集时,用exists优于用in /** mysql中exists和in的区别: 1. in是把外表和内表做hash连接,先查询内表,也就是先查询in括号里面的表; 2. exists是对外表做loop循环,循环后拿外表的每一项对内表进行验证,看看外表的每一项在内表是否存在,也就是说,EXISTS()查询是将主查询的结果集放到子查询中做验证,根据验证结果是true或false来决定主查询数据结果是否得以保存; 在外表大的时用in效率更快,内表大用exists更快。 **/
Order by排序优化
ORDER BY满足两种情况,会使用Index方式排序:
提高order by 的方法:
使用order by大忌使用select * ,应该只查询需要的字段
当查询的字段的大小总和<max_length_for_sort_data而且排序字段不是text/BLOB时,会使用改进算法–单路排序,否则使用对路排序,速度慢
单路多路都可能超过sort_buffer的容量(单路可能性更大),超出后会创建temporary,导致慢速
尝试提高sort_buffer_size
尝试提高max_length_for_sort_data,但是设置太高,容易超过sort_buffer_size
group by :同order by,但能用where就不用having
开启:set global slow_query_log=1;
查看是否开启:show variables like ‘%slow_query_log%’;
查看慢查询设置时间:show variables like ‘%long_query_time%’;
设置阈值为1秒:set global long_query_time=1;
显示当前系统中较慢的sql 条数:show global status like ‘%show_queries%’;
若执行超过阈值的sql会在慢查询中显示:select sleep(2);
create database bigData; use bigData; -- 部门表 create table dept( id int unsigned primary key auto_increment, deptno mediumint unsigned not null default 0, dname varchar(20) not null default "", loc varchar(13) not null default "" )engine=innodb default charset=GBK; -- 员工表 create table emp( id int unsigned primary key auto_increment, 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=innodb default charset=GBK; -- 这个值要设为ON show variables like 'log_bin_trust_function_creators'; -- 随机产生字符串 DELIMITER $$ create function rand_string(n int) returns varchar(255) begin declare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ'; 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 $$ -- 随机产生部门编号 DELIMITER $$ create function rand_num() returns int(5) begin declare i int default 0; set i = floor(100+rand()*10); return i; end $$ -- 假如要删除 -- drop function rand_num; -- 创建存储过程 DELIMITER $$ create procedure insert_emp(in start int(10), in max_num int(10)) begin declare i int default 0; -- set autocommit = 0; repeat set i = i + 1; insert into emp(empno, ename, job, mgr, hiredate, sal, comm, deptno) values ((start+i), rand_string(6), 'SALESMAN', 0001, curdate(), 2000, 400, rand_num()); until i = max_num end repeat; commit; end $$ -- 创建存储过程,往dept表添加随机数据 DELIMITER $$ create procedure insert_dept(in start int(10), in max_num int(10)) begin declare i int default 0; set autocommit = 0; repeat set i = i + 1; insert into dept(deptno, dname, loc) values ((start + i), rand_string(10),rand_string(8)); until i = max_num end repeat; commit; end $$ -- drop procedure insert_dept; -- 调用存储过程 DELIMITER ; CALL insert_dept(100, 10); DELIMITER ; CALL insert_emp(100001, 500000);
Show Profile是mysql提供的可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL的调优的测量
默认情况下,参数处于关闭状态,并保存最近15次的运行结果
show variables like '%profiling%'; set profiling = on; show profiles; show profile cpu, block io for query 17;#一条sql内部执行的完整生命周期 /** #如果status出现以下条目,降低速度 1 coverting HEAP to MyISAM :查询结果太大,内存不够,往磁盘上搬了 2 creating tpm table :创建临时表,[拷贝数据到临时表][用完再删除] 3 cooying to tmp table on disk :把内存中临时表复制到磁盘。危险!! 4 locked **/
从对数据操作的粒度来分:
从对数据操作的类型:
use test01; show open tables from test01; lock table tbla read, book write; unlock tables; -- 释放锁 select * from tbla;