DML提交
MYSQL Query Opiimizer优化器
MYSQL逻辑分层
连接层–>提供与客户端连接的服务
服务层–>提供各种用户使用的接口、=提供SQL优化器
引擎层–>提供·各种存储数据的方式(InnoDB、MyISAM)
存储层–>存储数据
InnoDB(MYSQL默认):事务优先(适合高并发操作:行锁)
MyISAM:性能优先(表锁)
# 查询数据支持的引擎
show engines \g;
# 查看当前使用的引擎
show variables like '%storage_engine%';
# 指定数据库表的引擎
CREATE TABLE test(
id int(4) AUTO_INCREMENT,
name varchar(5),
dept varchar(5),
primary key(id)
)ENGINE=MyISAM AUTO_INCREMENT=2
# 设置表引擎为MyISAM,自增长为2
DEFAULT CHARSET=utf8;
优化原因:性能低、执行时间太长、等待时间太长,SQL语句欠佳(连接查询)、索引失效、服务器参数设置不佳
SQL编写过程:
SELECT … FROM … JOIN … ON … WHERE … GROUP BY …HAVING …ORDER BY … LIMIT…
SQL解析过程
FROM … ON … JOIN … WHERE … GROUP BY … HAVING … . SELECT DISTINCT … ORDER BY … HAVING … SELECT …ORDER BY … LIMIT
SQL优化主要是优化索引
索引是帮助MYSQL高效获取数据的*数据结构*。
(树:B树、B+树、Hash树)
索引弊端:
- 索引本身很大(占存很大),可以存放在内存/硬盘(默认硬盘)
- 索引会降低增删改的效率(对表内容进行增删改、也要对索引进行增删改)用空间换时间
索引优势:
1.提高查询效率(降低I/O使用率)
2.降低CPU使用率(ORDER BY age DESC,因为B树索引本身就是一个排好序的结构,因此就可以直接使用)
索引不适合场景:
数据量比较少的字段
频繁更新的字段
很少使用的字段
B+树中,数据全部存放在叶节点中。
B+树中查询任意数据的次数:n次(B+树的高度)
索引分类:
主键索引: 不能重复+NOT NULL
单值索引: 单列,一个表可以有多个单值索引
唯一索引: 唯一索引不能重复
复合索引: 多个列构成(相当于二级目录)
创建索引:
方式一:
CREATE 索引类型 索引名 ON 表(字段)
单值:
CREATE INDEX dept_index ON tb(dept);
唯一:
CREATE unique INDEX name_index ON tb(name);
复合:
CREATE INDEX dept_name_index ON tb(dept,name);
方式二
alter table 表名 索引类型 索引名(字段)
单值:
ALTER TABLE tb ADD INDEX dept_index(dept);
唯一:
ALTER TABLE tb ADD UNIQUE INDEX name_index(name);
复合:
ALTER TABLE tb ADD index dept_name_index(dept,name);
注意:如果一个字段是primary key,则字段默认是主键索引
查询索引
show index from 表名; #行的形式显示索引
show index from 表名 \G #列的形式显示索引
SHOW INDEX FROM tb;
删除索引:
drop index 索引名 on 表名
DROP INDEX name_index on tb;
SQL性能问题:
MYSQL查询优化其会干扰我们的优化
explain可以模拟SQL优化器执行SQL语句,从而知道SQL状况
explain + SQL 语句
EXPLAIN SELECT * FROM tb;
id:查询编号
select_type 查询类型
table类型
partitions
type
possible_keys 预测使用的索引
key 实际使用的索引
key_len 实际使用索引的长度
ref 表与表之间的引用关系
rows 通过索引查到的数据个数
filtered
Extra 额外的信息
# 成绩表
CREATE TABLE cource(
cid int(3),
cname varchar(20),
tid int(3)
);
# 老师表
CREATE TABLE teacher(
tid int(3),
tname varchar(20),
tcid int(30)
);
# 老师证表
CREATE TABLE teacherCard(
tcid int(3),
tcdesc varchar(200)
);
INSERT INTO cource values(1,'java',1);
INSERT INTO cource values(1,'html',1);
INSERT INTO cource values(1,'sql',1);
INSERT INTO cource values(1,'web',1);
INSERT INTO teacher values(1,'tz',1);
INSERT INTO teacher values(1,'tw',2);
INSERT INTO teacher values(1,'tl',3);
INSERT INTO teacherCard values(1,'tzdesc');
INSERT INTO teacherCard values(2,'twdesc');
INSERT INTO teacherCard values(3,'tldesc');
# 查询课程编号为2或教师证件编号为3的老师信息
EXPLAIN SELECT * FROM teacher t ,course c, teacherCard tc
WHERE t.tid = c.tid and t.tcid = tc.tcid
and (c.cid = 2 or tc.tcid=3);
ID值相同
id值相同,从上往下 顺序执行。
表的执行顺序,跟着数据量变化,原理是笛卡尔积 t3-tc3-c4 tc3-c4-t6
数据量小的表优先查询
# 查询教授SQL课程的老师描述desc
# 1.多表连接形式,ID值是相同的
EXPLAIN SELECT tc.tcdesc FROM teacherCard tc, course c, teacher t
WHERE c.tid = t.tid AND t.tcid = tc.tcid
AND c.cname='sql'
# 2.子查询形式,ID值是不相同的
EXPLAIN SELECT tc.tcdesc FROM teacherCard tc WHERE tc.tcid =(SELECT t.tcid FROM teacher t WHERE t.tid
=(SELECT c.tid FROM course c WHERE c.cname = 'sql')
);
# 3. 子查询+多表
SELECT t.tname, tc.tcdesc FROM teacher t, teacherCard tc
WHERE t.tcid = tc.tcid AND t.tid =
(SELECT c.tid FROM course c WHERE cname = 'sql');
ID值不同
ID值不同,ID值大的优先查询
本质:在嵌套查询时,先查内层,再查外层
ID值相同+ID不相同
ID值越大越优先
ID值相同,从上往下 顺序执行
select_type
PRIMARY :包含子查询SQL中的子查询(最外层)
SUBQUERY : 包含子查询SQL中的子查询(非最外层)
simple : 简单查询(不包含子查询、union)
union result: 哪些表之间存在union查询
union: from 子查询中出现table1 union table2,table2就是union
derived : 衍生查询(使用到了临时表 from)
a. 在FROM子查询中只有一张表
SELECT cr.cname FROM (SELECT * FROM course where tid in (1,2)) cr;
b.在FROM子查询中,如果有 table1 union table2,则table1就是derived,table就是union
EXPLAIN SELECT cr.cname FROM (SELECT * FROM course WHERE tid=1 UNION SELECT * FROM course WHERE tid =2 ) cr;
type索引类型、类型
对type进行优化的前提是:有索引
system>const>eq_ref>ref>range>index>all
其中system、const只是理想情况,实际上能达到ref>range
system(忽略):只有一条数据的系统表或衍生表只有一条数据的主查询–>只有一条数据的表或衍生表里只有一条数据
const :仅仅只能查到一条数据的SQL,用于Primary key或unique索引(查询类型与索引类型有关)
eq_ref :唯一性索引:对于每个索引键的查询,返回匹配唯一行数据(有且只能有一个,不能多、不能少),常见于唯一索引和主键索引
ref : 非唯一性索引,对于每个索引键,返回匹配的所有行(0行,多行)
range : 检索指定范围的行,where后面是一个范围查询(between and, <, >, >=, <=, in有时候会失效,转为all)
index :查询全部索引的数据
explain select tid from teacher; tid是索引,只需要扫描索引表,不需要所有表中所有的数据
all : 查询全部表中的数据
explain select cid from course; cid不是索引,需要全表所有,既需要所有表中的所有数据
posible_keys 可能用到的索引,是一种预测
key实际使用到的索引
用于判断复合索引是否被完全使用
create table test_kl(
name char(20) not null default ''
);
# 添加索引
alter table test_kl add index index_name(name);
explain select * from test_kl where name = '';
在mysql中utf8一个字符占三个字节,所有key_len=60=20*3;
alter table test_kl add column name1 char(20); # 可以为空
alter table test_kl add index index_name1(name1);
此时key_len=61,如果索引字段可以为空null,则会使用1个字节用于标识。
drop index index_name on test_kl;
drop index index_name1 on test_kl;
#增加一个复合索引
alter table test_kl add index name_name1_index(name,name1);
explain select * from test_kl where name1=''; # key_len=121
explain select * from test_kl where name=''; # key_len=60
alter table test_kl add column name2 varchar(20); #变长字段
alter table test_kl add index name2_inex(name2);
explain select * from test_kl where name2='';
# 此时key_len=63= 20*3 + 1(null) + 2 (标识可变长度)
utf8:1个字符3个字节
gbk:一个字符2个字节
latin:1个字符1个字节
作用:指明当前表所参照的字段。
select … where a.c = b.x;
若:b.x是常亮,const
若:b.x是其他,引用
被索引优化查询的数据个数
select * from course c, teacher t where c.tid = t.tid
and t.tname='tz';
常见值:
1. using fileSort:性能消耗大,需要‘额外’的一次排序(查询)
对于单索引:
如果排序和查找是同一字段,不会出现using filesort
如果排序和查找不是同一个字段就会出现using filesort
复合索引:不能跨列(最佳左前缀)
where和order by 按照复合索引的顺序使用,不能跨列或无序使用
using temporary:性能消耗大,用到了临时表,常见于order by语句中
using index:性能提升,索引覆盖(此SQL查询,不读取源文件,只从索引文件中获取数据(不需要回表查询))
用到的列,全部都在索引中,就是索引覆盖
会对possible_keys和key造成影响。
a.如果没有where,则索引只出现在key中
b.如果有where,则索引出现在key和possible_keys中
using where:需要回表查询
age是索引列,但name不是,select age,name from user where age > 18;SQL语句必须回表查Name
using impossible where:where子句永远为false
select * from test02 where a1=‘x’ and a1=‘y’;
using join buffer : MYSQL引擎使用了连接缓存
using filesort样例
create table test02(
a1 char(3),
a2 char(3),
a3 char(3),
index idx_a1(a1),
index idx_a2(a2),
index idx_a3(a3)
);
# 1. 单索引情况
# Extra值 : using where
explain select * from test02 where a1='' order by a1;
# Extra值 : using where;using filesort
explain select * from test02 where a1='' order by a2;
drop index indx_a1 on test02;
drop index indx_a2 on test02;
drop index indx_a3 on test02;
# 复合索引
alter table test02 add index idx_a1_a2_a3(a1,a2,a3);
# a1 a3跨列了
explain select * from test02 where a1='' order by a3;
# a2 a3 也跨列了
explain select * from test02 where a2='' order by a3;
# a1,a2不跨列
explain select * from test02 where a1='' order by a2;
using temporary样例
# 没有临时表的情况
select a1 from test02 where a1 in ('1','2','3') group by a1;
# 有临时表的情况,就会出现 using temporary
select a1 from test02 where a1 in ('1','2','3') group by a2;
解析过程
from ...on ... join... where... group by... having... select distinct... order by... limit
select * from test03 where a2=2 and a4=4 group by a2,a4; # 没有出现using temporary
select * from test03 where a2=2 and a4=4 group by a3;# 出现了using temporary,因为出现了临时表
using index样例
# 用到的列都在索引中,就是索引覆盖
select id from user where id > 10;
create table test03(
a1 int(4) not null,
a2 int(4) not null,
a3 int(4) not null
);
alter table test03 add index idx_a1_a2_a3_a4(a1,a2,a3,a4);
# 索引得到使用(WHERE)顺序与复合索引顺序一致、推荐写法
explain select a1,a2,a3,a4 from test03 where a1=1 and a2=2 and a3=3 and a4=4;
# 虽然编写的顺序和索引顺序不一致,但SQL在真正执行前,经历过了SQL优化器的调整,结果与上条SQL一致
explain select a1,a2,a3,a4 from test03 where a4=4 and a1=1 and a2=2 and a3=3;
# using index,用到了a1和a2索引,不需要回表查询
# using where a4跨列使用,造成索引失效,需要回表查询
通过key_len=8=4+4校验,使用了a1和a2索引
索引顺序为 a1,a2,a3,(a4失效)
explain select a1,a2,a3,a4 from test03 where a1=1 and a2=2 and a4=4 order by a3;
# filesort 多了额外的排序,不要跨列使用(where和order拼起来不要跨列使用)
索引顺序是a1,a3(跨列了)(a4失效)
explain select a1,a2,a3,a4 from test03 where a1=1 a4=4 order by a3;
explain select a1,a2,a3,a4 from test03 where a1=1 a4=4 order by a2,a3; # where和order by拼接起来,是按顺序的
1.如果(a,b,c,d)复合索引和使用顺序全部一致,则复合索引全部使用
2.<如果部分一致(且不跨列使用),则在使用时部分使用
create table book(
bid int(4) primary key,
name varchar(20) not null,
authorid int(4) not null,
pulibcid int(4) not null,
typeid int(4) not null
);
insert into book values(1,'tjava',1,1,2);
insert into book values(2,'tc',2,1,2);
insert into book values(3,'wx',3,2,1);
insert into book values(4,'math',4,2,3);
commit;
# 查询authorid=1且typeid为2或3的bid
explain select bid from book where typeid in(2,3) and authorid =1 order by typeid desc;
# type=all、Extra=using where,using filesort
优化:加索引
alter table book add index idx_bta(bid,typeid,authorid);
再查询
# type=index、 Extra=using where,using filesort,using index
根据SQL实际解析的顺序,调整索引顺序(from后select),所以索引顺序为(typeid,authorid,bid)合适
drop index idx_bta on book;
alter table book add index_idx_bta(typeid,authorid,bid);
再查询
# type= index、 Extra=using where,using index
再优化(in查询容易失效,所以type放在最后)
drop index idx_bta on book;
alter table book add index_idx_bta(authorid,typeid,bid);
再查询
# type= ref、 Extra=using where,using index
a.索引不能跨列使用(最佳左前缀),保持索引的定义和使用的顺序一致性
b.索引需要逐步优化
c.讲含in的范围查询放到where条件的最后,防止失效
create table teacher2(
tid int(4) primary key,
cid int(4) not null
);
insert into teacher2 values(1,2);
insert into teacher2 values(2,1);
insert into teacher2 values(3,3);
commit;
create table course2(
cid int(4) ,
cname varchar(20)
);
insert into course2 values(1,'java');
insert into course2 values(2,'python');
insert into course2 values(3,'kotlin');
commit;
select * from teacher2 t left outer join course2 c on t.cid = c.id where c.cname = ‘java’;
如何对这条SQL语句进行优化呢?索引往哪张表加?
小表驱动大表|索引要建立再经常使用的字段
数据量小的放外层,数据量大的放内层
一般情况对于左外连接,给左表加索引,右外连接,给右表加索引
编写where … on t.cid=c.cid时,将数据量小的表放在左边(假设此时t表数据量小),SQL语句中,t.cid字段使用频繁,因此给该字段加索引
explain select * from teacher2 t left outer join course2 c on t.cid = c.id where c.cname = 'java';
table t type= all、 Extra=
table t type= all、 Extra=using where,using join buffer
-------------------------------
# 给t.cid加索引
alter table teacher2 add index index_teacher2_cid(cid);
再执行SQL
table t type= index、 Extra=using index
table c type= all、 Extra=using where,using join buffer
-------------------------------
alter table course2 add index index_course2_cname(cname);
再执行SQL
table c type= ref、 Extra=using where
table t type= ref、 Extra=using index
a.小表驱动大表
b.索引建立在经常查询的字段
复合索引
a. 复合索引,不要跨列或无序使用(最佳左前缀)
b. 尽量使用全索引匹配(对复合索引按顺序完完全全匹配)
c. 使用in 进行查询时,也可能回导致索引失效
不要在索引上进行任何操作
a.计算、函数、类型转换,都会导致索引失效
b.对于复合索引
左侧失效,右侧全部失效
右侧失效,左侧任然有效
c.复合索引不能使用不等于(!=)或is null(is not null),否则自身及其右侧全部失效。
d.复合索引中有(> ),则自身和右侧索引全部失效
一般而言:范围查询( > < in)之后的索引失效,所以要尽量使用索引覆盖(using index)
3.SQL优化是一种概率层面上的优化,至于是否实际使用了优化,需要通过explain进行推测。
原因是:服务层中有SQL优化器,会影响优化
like尽量以‘‘常量’开头,不要以’%'开头,否则索引失效,如果必须使用like ‘%’,可以使用索引覆盖,挽救一部分。
尽量不要使用类型转换(显示、隐示),否则索引失效
尽量不要使用or,否则索引失效
explain select * from teacher where tname=’’ or tcid>1;将or左侧的tname失效了。
exist、in、order by 优化
select … from table where exist/in(子查询);
1.如果主查询的数据量大,用IN
2.如果子查询的数据量大,用exist
exist语法:将子查询结果,放到子查询结果中进行条件校验(是否有数据),如果复合校验,保留数据。
select tname from teacher where exists (select * from teacher);
等价于
select tname from teacher;
in
select … from table where tid in (1,3,5);
select * from A where id in (select id form B);
order by
using filesort有两种算法:双路排序、单路排序(根据I/O次数)
MYSQL4.1之前,默认使用 双路排序(扫描两次磁盘,第一次:从磁盘读取排序字段(order by的字段,buff缓冲区中进行排序)),第二次,扫描其他字段(select后的字段))
MYSQL4.1之后,默认使用 单路排序(只读取一次(全部字段),在buffer中进行排序),单路排序不一定真的一次I/O,也可能是多次I/O,因为数据量特别大,无法将所有字段的数据一次性读取完毕,需要‘‘分片读取、多次读取’’
注:单路排序比双路排序,比双路排序占用更多的buffer,单路排序时,如果数据大,可以调大buffer容量大小
---------设置buffer容量大小-------------
set max_length_for_sort_data = 1024; #单位是byte
如果max_length_for_sort_data值太低,会自动从单路–>双路排序(太低:需要排序的列的总大小超过了max_length_for_sort_data )
提高order by 查询策略
a.提高使用单路、双路,调整buffer容量大小
b.避免使用select *…
c.复合索引不要跨列使用,避免using filesort
d.保证全部的排序字段:排序一致性(都是升序、降序)
MYSQL 提供的一种日志记录,用于记录MYSQL中响应时间超过阀值的SQL语句(long_query_time,默认10秒)
慢查询日志默认是关闭的: 开发调优时打开,最终部署时关闭
检查是否开启了慢查询日志
show variables like ‘%slow_query_log’
临时开启慢查询日志
set global slow_query_log=1;
永久开启慢查询日志
/etc/my.cnf˙的mysqld中追加
set global slow_query_log=1;
set global slow_query_log_file=/日志文件路径;
查看慢查询阀值
show variables like ‘%long_query_time%’
临时设置阀值
set global long_query_time=5; #修改完毕后,重新登录生效(重启MYSQL服务后失效)
永久设置阀值
/etc/my.cnf˙的mysqld中追加
long_query_time=3
查询超过阀值的SQL
show global status like ‘%slow_queries%’;
(1)慢查询的SQL被记录在了日志中,因此可以通过查看日志文件,查看具体的慢SQL
(2)通过mysqldumpslow工具查看SQL
s:排序方式 r:逆序 l:锁定时间 g:正则表达式
—获取返回记录组最多得的3个SQL
mysqldumpslow -s -r -t 3 /日志文件路径
—获取访问次数最多得的3个SQL
mysqldumpslow -s c -t /日志文件路径
—按时间排序,前10条包含left join 查询语句
mysqldumpslow -s t -t 10 -g ‘left join’ /日志文件路径
模拟海量数据
# 模拟海量数据
create table dept(
dno int(5) primary key default 0,
dname varchar(20) not null default '',
loc varchar(30) default ''
)engine=innodb default charset=utf8;
create table emp(
eid int(5) primary key,
ename varchar(20) not null default '',
job varchar(20) not null default '',
deptno int(5) not null default 0
)engine=innodb default charset=utf8;
# 存储函数插入海量数据
#1.创建存储函数 randstring(6)随机产生6个长度字符串
delimiter $ #规定结束符,防止;造成语义中断
create function randstring(n int) returns varchar(255)
begin
declare all_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
declare return_str varchar(255) default '';
declare i int default 0 ;
while i<n
do
set return_str = concat( return_str,substring(all_str,FLOOR(rand()*52+1),1) );
set i=i+1;
end while;
return return_str;
end $
#查看慢日志与创建存储过程/函数是否冲突
show variables like '%log_bin_trust_function_createors%'
# 临时解决
set global log_bin_trust_function_createors=1;
# 2.产生随机整数
create function ran_num() retuns int(5)
begin
declare i int default 0;
set i = floor(rand()*100);
return i;
end $
# 3插入海量数据
# 3.1通过存储过程插入 emp表
create procedure insert_emp(in eid_start int(10) ,in data_times int(10))
begin
declare i int default 0 ;
set autocommit = 0; # 关闭自动提交
repeat
insert into emp values(eid_start+i,randstring(5),'other',ran_num());
set i = i+1;
until i=data_times
end repeat;
commit;
end $
# 3.2 通过存储过程插入 dept表
create procedure insert_dept(in dno_start int(10),in data_times int(10))
begin
declare i int default 0;
set autocommit = 0; # 关闭自动提交
repeat
insert into dept values(dno_start+i,randstring(6),randstring(8));
set i = i+1;
until i=data_times
end repeat;
commit;
end$
# 将终结符定义为;
delimiter ;
# 3.3开始插入
call insert_emp(1000,800000);
cal insert_dept(10,30);
分析海量数据
show profiles; # 默认关闭
show variables like '%profiling%'; # 查看是否关闭
set profiling = on; # 打开,会记录所有profiling 打开之后,全部SQL查询所花费的时间
show profile all for query上一步查询的SQL的id,Query_ID
#查看cpu,io相关
show profile cup,block io for query上一步查询的SQL的
全局查询日志
记录开启之后的全部SQL语句(调优、开发过程打开,最终部署要关闭)
show variables like '%general_log%'; # 查看是否打开
set global general_log=1; # 打开全局查询日志
---------将执行的所有SQL记录在表中
# 开启全局日志,开启后,记录所有SQL,会被记录到mysql.general_log表中
set global log_output='table'
select * from mysql.general_log;
---------将执行的所有SQL记录在文件中
# 设置将全部的SQL记录到文件中
set global log_output='file' ;
set global general_log_file=/路径;
解决因资源共享,而造成的并发问题
分类:
操作类型:
a.读锁(共享锁):对同一个数据,多个读操作可以同时进行,互不干扰
b.写锁(互斥锁):当前写操作没有完毕,则无法进行其他的读锁、写锁
操作范围:
a.表锁:一次性对一张表整体加锁,MyISAM存储引擎用表锁,开销小、加锁快、无死锁,但锁的范围大,容易锁冲突,并发度低
b.行锁:一次性对一条数据加锁,InnoDB存储引擎使用行锁,开销小,加锁慢,容易发生死锁,所得范围小,不易发生锁冲突,并发度高(很少见概率高并发问题:脏读、幻读、不可重复读、丢失更新)
c.页锁:
示例:表锁
#表锁
create table tablelock(
id int primary key auto_increment,
name varchar(20)
)engine myisam; #表锁
insert into tablelock (name) values('a1');
insert into tablelock (name) values('a2');
insert into tablelock (name) values('a3');
insert into tablelock (name) values('a4');
insert into tablelock (name) values('a5');
commit;
--增加锁
# lock table 表 read/write , 表2 read/write....
# show open tables; # 查看加锁的表
--释放锁
unlock table 表名;
--读锁
# 会话session0 # 会话:每一个访问数据的dos命令行,数据库客户端工具,都是一个会话
lock table tablelock read;
select * from tablelock; -- 读OK
delet from tablelock where id = 1; -- 不能写(增删改)
select * from emp; --读,不可以
delete from emp where eno = 1; ---写,不可以
---------------------------------
如果一个会话,对A表加了read锁,则该回话跨域对A表进行读操作,不能进行写操作,且该回话不能对其他表进行读、写操作
既:如果给A表加了读锁,则当前会话只能对A表进行读操作
---------------------------------
# 会话session1
select * from tablelock; --读可以
delete from tablelock where id=1; ---写,会等待,只到读锁解锁
select * from emp; --可以读
delete from emp where eno = 1; --可以写
---------------------------------
会话0对A表加了锁,其他会话的操作:
a.可以对其他表(A表以外的表)可以进行读、写操作
b.对A表可以读,但是写的话需要等待释放锁
---------------------------------
--写锁
会话0
lock table tablelock write;
当前会话0,可以对加了写锁的表进行任何操作(CRUD),但是不能操作(CRUD)其他表
会话1
其他会话0可以对加写锁的表,进行增删改查的前提是:等待会话0释放写锁
MyISAM表级锁的锁模式
MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁
在执行更新操作(DML)前,会自动给涉及的表加写锁
所有对MyISAM表进行操作有,如下情况
a.对MyISAM表进行读操作(加读锁),不会阻塞其他进程(会话)对同一个表的读请求,但会阻塞对同一个表的写操作(加写锁),只有当读锁释放后,才会执行其他进行的写操作
b.对MyISAM表的写操作(加写锁),会阻塞其他进程(会话)对同一个表的读和写操作,只有当写操作释放后,才会执行其他进程的读写操作。
分析表锁定
show open tables; 查看那些表加了锁,1代表被加了锁
show status like ‘table%’; 分析表锁定的严重程度
Table_locks_immediate即可能获取到的锁
Table_locks_waited需要等待的表锁数(如果值越大,说明存在越大的锁竞争)
一般建议:Table_locks_immediate/Table_locks_waited>5000,建议采用InnoDB引擎,否则MyISAM引擎
示例:行锁
create table linelock(
id int(5) primary key auto_increment,
name varchar(20)
)engine=innodb;
insert into linelock(name) values('1');
insert into linelock(name) values('2');
insert into linelock(name) values('3');
insert into linelock(name) values('4');
insert into linelock(name) values('5');
set autocommit;
会话0 写操作
insert into linelock values('6'); --会话没有提交,时间点:1
会话1 写操作,相同的数据
update linelock set name='ax' where id = 6; --时间点:2,更新时发现此数据被加锁了,直到其它会话将该锁释放后(commit、rollback),才能操作
对行锁情况
1.如果会话x对某条数据a进行DML操作(研究是:关闭了自动commit情况下),则其他会话必须等待会话x结束事务后,才能对数据a进行操作
2.表锁通过unlock tables;解锁 或通过事务解锁
行锁通过事务解锁
行锁,操作不同数据
会话0 写操作
insert into linelock values('8'); --会话没有提交,时间点:1
会话1 写操作,不同的数据
update linelock set name='ax' where id = 5; --时间点:2
行锁,一次锁一行数据,因此操作不同的数据,互不干扰。
行锁变表锁
行锁注意事项:
a.如果没有索引,则行锁会转为表锁
show index from linelock;
alter table linelock add index idx_linelock_name(name);
会话0 写操作
update linelock set name=‘ai’ where name = ‘3’; --时间点:1
会话1 写操作,不同的数据
update linelock set name=‘aiX’ where name = ‘4’; --时间点:2
--------------------------单引号去掉之后—>没有索引
会话0 写操作
update linelock set name=‘ai’ where name = 3; --时间点:1
会话1 写操作,不同的数据
update linelock set name=‘aiX’ where name = 4; --时间点:2
可以发现,数据被阻塞了(加锁)
—原因:发生类型转换,则索引失效,因此此次操作,从2行锁转为表锁
行锁一种特殊情况:间隙锁,值在范围内,但却不存在
select * from linelock;
-- 如果在(1,9)中没有id=7的数据
update linelock set name='x' where id > 1 and id <9; --自动给id为7加一个间隙锁
此时另一个会话insert into linelock value(7,'7');
间隙:mysql会自动给间隙加锁--->间隙锁(行锁)
行锁:如果有WHERE,则实际加锁的范围,就是where后面的范围,(不是实际的值)
行锁:
InnoDB默认采用行锁
缺点:比表锁性能消耗大
有点:并发能力强,效率高
因此,高并发用InnoDB,否则用MyISAM
行锁分析
show status like '%innodb_row_lock%'
Innodb_row_lock_current_waits当前正在等待的锁的数量
Innodb_row_lock_time等待总时长,从系统启动到现在一共等待的时间
Innodb_row_lock_time_avg平均等待时长,从系统启动到现在平均等待的时间
Innodb_row_lock_waits等待次数,从系统启动到现在一共等待的次数
通过for update对Query语句进行枷锁
for update进行加锁
select * from linelock where id = ‘2’; --自动提交
select * from linelock where id = ‘2’ for update; —将select语句进行加锁
关闭自动提交方式
set autocommit=0;
begin;
start
type= index、 Extra=using where,using index
普通索引index、unique索引
MYSQL编码格式
查看编码格式
show variables like ‘%char%’;
发现编码为latin,需要统一设置为utf-8
etc/my.cnf文件
[mysql]内 (服务端MYSQL)
添加 default-character=utf8
[client] (客户端MYSQL)
添加 default-character=utf8
[mysqld]
character_set_server-utf8
character_set_client=utf8
collation_server=utf8_general_ci
x修改编码,只对之后的数据库生效
MYSQL清屏
system clear;
集群:
1.负载均衡
2.失败迁移
如果要远程连接数据库,则需要授权远程访问
A远程访问B,则在B计算机的MYSQL下执行命令
GANT ALL PRIVILEGES ON *.* TO ‘root’@’%’ IDENTIFIED BY ‘root’ WITH GRANT OPTION;
FLUSH PRIVILEGES;
实现主从同步(主从复制)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IyQ9EIqM-1626490742618)(/Users/wujiewei/Desktop/wcw/01.03.主从同步.png)]
-- 同步核心 二进制日志binlog
1. master将改变的数据,九记录在本地的二进制日志文件(binary log)
2.slaver将master的binary log拷贝到自己的relay log(中继日志文件)中
3.中继日志事件,将数据读取到自己的数据库之中。
MYSQL主从复制是异步的,串行化、有延迟
master:slave=1:n
配置
windows(mysql: my.ini)
my.ini
server-id标识
log-bin=".../mysql-bin"路径 二进制日志文件路径
log-error=".../mysql-error"路径 错误记录文件
binlog-ignore-db=数据库名称 主从同步忽略的数据库
binlog=do-db=数据库名称 主从同步-同步的数据库名称
主机授权哪台计算机,是自己从计算机
GANT PRIVILEGES slave,reload,super ON *.* TO 'root'@'从计算机IP地址' IDENTIFIED BY 'root';
查看主数据库的状态
show master status;
linux(mysql: etc/my.cof)
MYSQL优化官网
https://dev.mysql.com/doc/refman/8.0/en/optimization.html
show index from book;
explain select * from book authorid = 1 and typeid =2;