sql变慢查询步骤
1.开启慢查询日志,跑遍所有的sql语句
2.进入慢查询日志,提取慢sql语句
3.explain分析慢sql语句
4.show profile
5.查看全局日志
代码处理
记得导入驱动,前面加下划线
1.mysql的存储引擎有两种:inodb(三个文件存储表格 .frm (表结构).data(表数据).myi(表索引) )和myisam (.frm表结构 .idb表数据+表索引)
查看索引:show index from 表名
创建索引:
创建索引方法一:
mysql> create index ix_stuname on stuinfo(stuname);
创建索引方法二:
mysql> alter table stuinfo add index ix_address (stuaddress);
创建表时添加索引:
create table emp(
id int,
name varchar(10),
index ix_name (name) # 创建索引
);
删除索引:
语法:drop index 索引名 on 表名
mysql> drop index ix_stuname on stuinfo;
全职匹配我最爱,最左前缀要遵守
带头大哥不能死,中间兄弟不能断
索引列上少计算,范围之后全失效
like百分号写最右,覆盖索引不写*
不等空值还有or,索引失效要少用
var引号不可丢,sql高级也不难
1.慢查询开启并捕获 (观察一天查看生产慢sql的情况,开启慢查询日志)
2.explain+慢查询分析
3.show profile查询sql在服务器里的执行细节和生命周期
4.sql数据库服务的参数调优
注意A表的字段和B表的字段应该建立索引
1.当B表的数据集小于A表的时候用IN由于exit
select * from A where id in(select id from B)
等价于: for select id from B
for select * from A where A.id=B.id
2.当A表的数据集小于B表时,用exist优于in (将主查询中的结果放到子查询中做验证,以验证查询结果是否保留,所以会先执行主查询)
(exists 只返回TRUE或者FALSE),因此子查询中的SELECT*也可以是select1或者其他,官方说法实际中执行会忽略select清单,所以没区别
select * from A where exits(select * from B where A.id=B.id)
等价于: for select * from A
for select * from B where A.id=B.id
为排序使用索引:mysql两种排序方式,文件排序(不建议)或扫描有序索引排序
mysql能为查询和排序使用相同的索引 当无法使用索引列,增大max_length_for_sort_data 参数的设置+增大 sort
KEY_a_b_c(a,b,c)
order by 能使用索引最左前缀
正确的:
-order by a
-order by a,b
-order by a,b,c
-order by a DESC,b DESC,c DESC (mysql中的order by是默认增序的,想在order by中使用索引要么得全增序,要么全降序)
如果where使用索引的最左前缀定义为常量,则order by 能使用索引
-where a=const order by b,c
-where a=const and b=const order by c
-where a=const and b>const order by b,c
不能使用索引进行排序
-order by a ASC,b DESC ,c DESC 排序不一致
-where g=const order by b,c 丢失带头大哥
-where a=const order by c 丢失b索引
-where a=const order by a,d d不是索引的一部分
-where a in(...) order by b,c 带头大哥不是常量,直接就断了
注解// 超过自己设定时间long_query_time 值得sql,会被记录在慢查询日志中,结合explain进行全面分析
1.默认情况下mysql数据库没有开启慢查询日志,需要我们手动来设置这个参数,如果不是性能调优不建议启动改参数,
因为开启慢查询日志会或多或少带来一定的性能影响,慢查询日志支持将日志写入文件
默认 show variables like'%slow_query_log%'; 查看是否开启慢查询日志及存放路径
单次开启 set global slow_query_log=1; 只对当前数据库生效,mysql重启后则会失效
如果要永久生效,需要修改配置文件 my.cnf
将以下两行写入my.cnf后重启数据库
slow_query_log=1
slow_query_log_file=/var/lib/mysql/ubuntu-slow.log(指定慢查询日志文件的存放路径)
2.由参数long_query_time控制,默认为10秒
命令: show variables like ‘long_query_time%’;
也可以在配置文件中修改,只记录查询时间>10s的sql
3.设置阈值的时间 命令:set global long_query_time=3; 查看:show global variables like ‘long_query_time’; !!!注意设置后需要重启数据库,或者重开窗口才可见修改
4.测试 :select sleep(4);切换用户 sudo su itcast 查看慢查询记录条数:show global status like ‘%slow_queries%’;
show variables like’%slow_query_log%’; 查看是否开启慢查询日志及存放路径 进入路径查看文件配置
±--------------------±-------------------------------+
| Variable_name | Value |
±--------------------±-------------------------------+
| slow_query_log | ON | 是否开启慢查询日志
| slow_query_log_file | /var/lib/mysql/itcast-slow.log | 查看慢查询日志的存储路径
±--------------------±-------------------------------+
参数示意: 见下图
1.得到返回记录集最多的10个sql
mysqldumpslow -s r -t 10 日志路径
2.得到访问次数最多的10个sql
mysqldumpslow -s c -t 10 日志路径
3.得到按照时间顺序的前10条里面含有左连接的语句
mysqldumpslow -s t -t 10 -g “left join” 日志路径
4.建议结合more使用,防止出现爆屏
mysqldumpslow -s r -t 10 日志路径 | more //管道结合
函数功能:随机产生字符串和部门编号,每条数据都不同
1.建表:
创建部门表
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=utf8;
//创建员工表
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=utf8;
2.创建函数,假如报错:this function has none of deterministic …
##这是由于开启过慢查询日志,开起了bin-log导致的,我们必须为function指定一个参数
展示函数参数:show variables like ‘log_bin_trust_function_creators’;
改变参数:set global log_bin_trust_function_creators=1;(单次方法)
永久方法:在 /etc/my.cnf下的 my.cnf[mysqld]加上 log_bin_trust_function_creators=1
3.写sql语句
创造随机函数,随机产生字符串
mysql> delimiter $$
mysql> create function rand_string (n int) returns varchar (255)
-> begin
-> declare chars_str varchar(100) default 'qwertyuiopasdfghjklzxcvbnm';
-> declare return_str varchar(255) default '';
-> declare i int default 0;
-> while i set return_str=concat(return_str,substring(chars_str,floor(i+rand()*52),1));
-> set i=i+1;
-> end while;
-> return return_str;
-> end $$
创造随机函数,随机产生部门编号
mysql> delimiter $$
mysql> create function rand_num () returns int(5)
-> begin
-> declare i int default 0;
-> set i=floor(100+rand()*10);
-> return i;
-> end $$
Query OK, 0 rows affected (0.00 sec)
4.随机产生部门编号
DELIMITER $$
CREATE FUNCTION rand_num()
RETURNS INT(50)
BEGIN
DECLARE i INT DEFAULT 0;
SET i= FLOOR(100+RAND()*10);
RETURN i;
END $$
5.创建存储过程
1.往emp表中插入数据
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$$
2.往dept表添加随机数据:
DELIMITER $$
CREATE PROCEDURE insert_dept(IN START INT(10),IN max_num INT(10))
BEGIN
DECLARE i INT DEFAULT 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 $$
6.调用存储过程:
!!!!!(先把;分号为介绍符切换回来:DELIMITER ; )
插入dept:
CALL insert_dept(100,10);
插入emp数据添加500000条数据;
CALL insert_emp(100001,500000);
查阅:
SELECT COUNT(*) FROM emp;
show profile 是mysql用于提供分析当前sql语句中执行的资源消耗情况,可以用sql调优测试,默认关闭,并保存最近运行的15次运行结果
1.查看当前状态 show variables like ‘profiling’;
±--------------±------+
| Variable_name | Value |
±--------------±------+
| profiling | OFF |
±--------------±------+
2.打开 set profiling =on;
3.查看 profiles;
±---------±-----------±-------------------------------+
| Query_ID | Duration | Query |
±---------±-----------±-------------------------------+
| 1 | 0.22734025 | select *from emp limit 150000 |
| 2 | 0.80384475 | select *from emp |
±---------±-----------±-------------------------------+
出现下面四条表示一定需要优化,sql有问题
2.Creating tmp table 创建临时表
3.Coping to tmp table on disk 把内存中的临时表复制到磁盘上去
4.locke 锁定了阻塞
1.开启 set global general_log=1;
2.设置一个存储表 set global log_output=‘TABLE’;
此后编写的sql语句,将会记录到mysql库里的 general_log表中
3.查看 select * from mysql.general_log;
1.查看mysql的隔离级别,show variables like ’ tx_isolation
'; 默认是已提交读
关闭自动提交:set autocommit=0;
2.事务的特性 ACID
A:原子性(atomicity) 事物的操作是一个原子操作单元,其对数据修改,要么全部执行,要么都不执行
C:一致性(consistent):事务的开始和完成,数据都必须保持一致的状态,即所有的数据都必须用事务内的数据
I:隔离性(isolation):事务处理过程的状态,对外不可见
D:持久性(durable):事务完成后,它对数据的修改是永久性的,系统出现故障也能保持
可能出现的问题:1.更新丢失:两个事物同时更新一个文件,只会保留最后一个文件的副本
2.脏读:事务A读到了事务B已经修改但是没有提交的数据,此时事务B回滚,事务A读到的数据无效
3.不接重复读:事务A读到了数据B已经修改但为提交的数据,不符合隔离性
4.幻读:事务A读到了事务B提交的新增数据
1.建表 create table testlock (a int(11) , b varchar(16) );
insert into testlock values(1,‘b2’);
insert into testlock values(3,‘3’);
insert into testlock values(4,‘4000’);
insert into testlock values(5,‘5000’);
insert into testlock values(6,‘6000’);
insert into testlock values(7,‘7000’);
insert into testlock values(1,‘b1’);
2.建立索引: create index index_test_a on testlock(a); create index index_test_b on testlock(b);
3.当 varchar类型修改不加单引号的时候,建立的索引会失效,行锁会变成表锁
4.间隙锁 当范围查找,修改时 会锁定整个范围内的键值,即使这个键值不存在,造成无法插入更改范围内的数据
+++++++++++++++++++++++++++
打开自动事务的提交 set autocommit=1;
开始事务 begin;
查询锁定一行(注意添加 for update) select *from testlock where a=6 for update;
±-----±-----+
| a | b |
±-----±-----+
| 6 | 6000 |
±-----±-----+
1 row in set (0.01 sec)
提交 commit;
行锁的查看
查看行锁 show status like ‘innodb_row_lock%’;
±------------------------------±------+
| Variable_name | Value |
±------------------------------±------+
| Innodb_row_lock_current_waits | 0 | 当前正在等待的锁定的数量
| Innodb_row_lock_time | 9117 | 从系统中启动到现在锁定的 总时间长度
| Innodb_row_lock_time_avg | 9117 | 每次等待的平均长度
| Innodb_row_lock_time_max | 9117 | 从系统中启动到现在等待的最长的时间
| Innodb_row_lock_waits | 1 | 系统从启动到现在总共的等待次数
±------------------------------±------+
1.尽可能的让所有数据通过索引,避免无索引行锁升级为表锁
2.合理设计索引,尽量缩小锁的范围
3.尽可能较少的检索条件,避免间隙锁
4.尽量控制事务大小,减小锁定的资源量和时间
从磁盘读取查询需要的所有的列,按照order by 列在buffer 对它们进行排序,然后扫描排序后的列表进行输出,它的效率高,避免了第二次读取数据,
并且把随机IO变成了顺序IO,但是它会使用更多的空间,因为它把每一行都保存在了内存中
1.mysql的optimizer会给sql语句优化,如:建立索引 :a,b,c 查询 a and b and c ==c and b and a 优化为:
2.范围之后全失效注意优化
3.group by 先分组后排序
现在等待的最长的时间
| Innodb_row_lock_waits | 1 | 系统从启动到现在总共的等待次数
±------------------------------±------+
1.尽可能的让所有数据通过索引,避免无索引行锁升级为表锁
2.合理设计索引,尽量缩小锁的范围
3.尽可能较少的检索条件,避免间隙锁
4.尽量控制事务大小,减小锁定的资源量和时间
从磁盘读取查询需要的所有的列,按照order by 列在buffer 对它们进行排序,然后扫描排序后的列表进行输出,它的效率高,避免了第二次读取数据,
并且把随机IO变成了顺序IO,但是它会使用更多的空间,因为它把每一行都保存在了内存中
1.mysql的optimizer会给sql语句优化,如:建立索引 :a,b,c 查询 a and b and c ==c and b and a 优化为:
2.范围之后全失效注意优化
3.group by 先分组后排序
4.varchar修改数据时一定要加单引号,否则会造成mysql自动的类型转换,索引失效,行锁变成表锁