MySQL基础
mysql四层架构
解析四层架构部件
connectors:不同语言中与sql的交互
connection pool:线程连接池
1. 管理缓冲用户连接,线程处理等需要缓存的需求。负责监听对 MySQL Server 的各种请求,接收连接请求,转发所有连接请求到线程管理模块。
2. 每一个连接上 MySQL Server 的客户端请求都会被分配(或创建)一个连接线程为其单独服务。而连接线程的主要工作就是负责 MySQL Server 与客户端的通信。接受客户端的命令请求,传递 Server 端的结果信息等。线程管理模块则负责管理维护这些连接线程。包括线程的创建,线程的 cache 等。
Management Services & Utilities:备份,容灾恢复,安全,复制,集群等。
SQL interface:SQL接口,接收用户的SQL命令,并返回用户需要查询的结果,比如select from就是调用SQL interface。存储过程,视图触发器等。
Parser:解析器,解析转换,sql语句加载从from开始,mysql将他重组过滤然后从from开始去解析。
1. 将SQL语句进行语义和语法的分析,分解成数据结构,然后按照不同的操作类型进行分类,然后做出针对性的转发到后续步骤,以后SQL语句的传递和处理就是基于这个结构的。
2. 如果在分解构成中遇到错误,那么就说明这个sql语句是不合理的
Optimizer:查询优化器,主人给我一个sql,是1,2,3,4顺序,但是我mysql会按照我自己认为最优的方式去执行,有可能我做了之后就是2,3,1,4执行顺序。但是这个并不是DBA程序员认为最优的sql方式去执行。如果极端业务的场景下,公司制定了优化方式让mysql按照他的方式执行,而不走mysql自己的优化步骤。所以后面如果出现索引失效可能就是这层优化器出现了问题。
Caches & Buffers:查询缓存,将返回结果cache到内存中,与该query的一个hash值做对比,数据发生变化后,自动让cache失效。读写多的系统中,性能提高显著,内存消耗也大。
引擎层:分层可拔插组件式的存储引擎,常用MyISAM和InnoDB;例子:去不同地理环境对跑车的路况,轮胎,底盘等不同,一种环境一种跑车成本太高;没办法一种环境一种车的情况下,一辆车去不同环境,就换掉车的引擎,这样更加优化经济。类似策略模式。==注意:存储引擎是基于表的,不是数据库。
存储层:file system:系统文件;files & logs:文件和日志。硬盘有关
查询语句的执行过程:
#查看mysql支持的存储引擎
mysql> show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine | Support | Comment | Transactions | XA | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES |
| MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO |
| MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO |
| BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO |
| MyISAM | YES | MyISAM storage engine | NO | NO | NO |
| CSV | YES | CSV storage engine | NO | NO | NO |
| ARCHIVE | YES | Archive storage engine | NO | NO | NO |
| PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO |
| FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
9 rows in set (0.00 sec)
#查看默认的存储引擎
mysql> show variables like '%storage_engine%';
+----------------------------------+--------+
| Variable_name | Value |
+----------------------------------+--------+
| default_storage_engine | InnoDB |
| default_tmp_storage_engine | InnoDB |
| disabled_storage_engines | |
| internal_tmp_disk_storage_engine | InnoDB |
+----------------------------------+--------+
4 rows in set, 1 warning (0.02 sec)
mysql>
索引优化分析
select <select_list> from tableA A
inner join tableB B
on A.key=B.key;
select <select_list> from tableA A
left join tableB B
on A.key=B.key;
select <select_list> from tableA A
right join tableB B
on A.key=B.key;
select <select_list> from tableA A
left join tableB B
on A.key=B.key
where B.key is null;
select <select_list> from tableA A
right join tableB B
on A.key=B.key
where A.key is null;
select <select_list> from tableA A
full outer join tableB B
on A.key=B.key
select <select_list> from tableA A
full outer join tableB B
on A.key=B.key
where A.key is null
or B.key is null;
建表语句
#建库
create database db0629;
#使用建好的库
use db0629;
#建表
CREATE TABLE tbl_dept(
id INT(11) NOT NULL AUTO_INCREMENT,
deptName VARCHAR(30) DEFAULT NULL,
locAdd VARCHAR(40) DEFAULT NULL,
PRIMARY KEY(id)
)ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
#建表
CREATE TABLE tbl_emp (
id INT(11) NOT NULL AUTO_INCREMENT,
NAME VARCHAR(20) DEFAULT NULL,
deptId INT(11) DEFAULT NULL,
PRIMARY KEY (id),
KEY fk_dept_Id (deptId)
#CONSTRAINT 'fk_dept_Id' foreign key ('deptId') references 'tbl_dept'('Id')
)ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
#插入数据
INSERT INTO tbl_dept(deptName,locAdd) VALUES('RD',11);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('HR',12);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('MK',13);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('MIS',14);
INSERT INTO tbl_dept(deptName,locAdd) VALUES('FD',15);
INSERT INTO tbl_emp(NAME,deptId) VALUES('z3',1);
INSERT INTO tbl_emp(NAME,deptId) VALUES('z4',1);
INSERT INTO tbl_emp(NAME,deptId) VALUES('z5',1);
INSERT INTO tbl_emp(NAME,deptId) VALUES('w5',2);
INSERT INTO tbl_emp(NAME,deptId) VALUES('w6',2);
INSERT INTO tbl_emp(NAME,deptId) VALUES('s7',3);
INSERT INTO tbl_emp(NAME,deptId) VALUES('s8',4);
INSERT INTO tbl_emp(NAME,deptId) VALUES('s9',51);
tbl_emp表的数据
mysql> select * from tbl_emp;
+----+------+--------+
| id | NAME | deptId |
+----+------+--------+
| 1 | z3 | 1 |
| 2 | z4 | 1 |
| 3 | z5 | 1 |
| 4 | w5 | 2 |
| 5 | w6 | 2 |
| 6 | s7 | 3 |
| 7 | s8 | 4 |
| 8 | s9 | 51 |
+----+------+--------+
8 rows in set (0.00 sec)
mysql>
tbl_dept表的数据
mysql> select * from tbl_dept;
+----+----------+--------+
| id | deptName | locAdd |
+----+----------+--------+
| 1 | RD | 11 |
| 2 | HR | 12 |
| 3 | MK | 13 |
| 4 | MIS | 14 |
| 5 | FD | 15 |
+----+----------+--------+
5 rows in set (0.00 sec)
mysql> select * from tbl_emp a inner join tbl_dept b on a.deptid=b.id;
+----+------+--------+----+----------+--------+
| id | NAME | deptId | id | deptName | locAdd |
+----+------+--------+----+----------+--------+
| 1 | z3 | 1 | 1 | RD | 11 |
| 2 | z4 | 1 | 1 | RD | 11 |
| 3 | z5 | 1 | 1 | RD | 11 |
| 4 | w5 | 2 | 2 | HR | 12 |
| 5 | w6 | 2 | 2 | HR | 12 |
| 6 | s7 | 3 | 3 | MK | 13 |
| 7 | s8 | 4 | 4 | MIS | 14 |
+----+------+--------+----+----------+--------+
7 rows in set (0.00 sec)
mysql> select * from tbl_emp a left join tbl_dept b on a.deptid=b.id;
+----+------+--------+------+----------+--------+
| id | NAME | deptId | id | deptName | locAdd |
+----+------+--------+------+----------+--------+
| 1 | z3 | 1 | 1 | RD | 11 |
| 2 | z4 | 1 | 1 | RD | 11 |
| 3 | z5 | 1 | 1 | RD | 11 |
| 4 | w5 | 2 | 2 | HR | 12 |
| 5 | w6 | 2 | 2 | HR | 12 |
| 6 | s7 | 3 | 3 | MK | 13 |
| 7 | s8 | 4 | 4 | MIS | 14 |
| 8 | s9 | 51 | NULL | NULL | NULL |
+----+------+--------+------+----------+--------+
8 rows in set (0.00 sec)
mysql> select * from tbl_emp a right join tbl_dept b on a.deptid=b.id;
+------+------+--------+----+----------+--------+
| id | NAME | deptId | id | deptName | locAdd |
+------+------+--------+----+----------+--------+
| 1 | z3 | 1 | 1 | RD | 11 |
| 2 | z4 | 1 | 1 | RD | 11 |
| 3 | z5 | 1 | 1 | RD | 11 |
| 4 | w5 | 2 | 2 | HR | 12 |
| 5 | w6 | 2 | 2 | HR | 12 |
| 6 | s7 | 3 | 3 | MK | 13 |
| 7 | s8 | 4 | 4 | MIS | 14 |
| NULL | NULL | NULL | 5 | FD | 15 |
+------+------+--------+----+----------+--------+
8 rows in set (0.00 sec)
mysql> select * from tbl_emp a left join tbl_dept b on a.deptid=b.id where b.id is null;
+----+------+--------+------+----------+--------+
| id | NAME | deptId | id | deptName | locAdd |
+----+------+--------+------+----------+--------+
| 8 | s9 | 51 | NULL | NULL | NULL |
+----+------+--------+------+----------+--------+
1 row in set (0.04 sec)
mysql> select * from tbl_emp a right join tbl_dept b on a.deptid=b.id where a.deptid is null;
+------+------+--------+----+----------+--------+
| id | NAME | deptId | id | deptName | locAdd |
+------+------+--------+----+----------+--------+
| NULL | NULL | NULL | 5 | FD | 15 |
+------+------+--------+----+----------+--------+
1 row in set (0.00 sec)
mysql中没有full outer join;所以改变办法。
mysql> select * from tbl_emp a full outer join tbl_dept b on a.deptid=b.id;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'full outer join tbl_dept b on a.deptid=b.id' at line 1
mysql> select * from tbl_emp a left join tbl_dept b on a.deptid=b.id
-> union
-> select * from tbl_emp a right join tbl_dept b on a.deptid=b.id;
+------+------+--------+------+----------+--------+
| id | NAME | deptId | id | deptName | locAdd |
+------+------+--------+------+----------+--------+
| 1 | z3 | 1 | 1 | RD | 11 |
| 2 | z4 | 1 | 1 | RD | 11 |
| 3 | z5 | 1 | 1 | RD | 11 |
| 4 | w5 | 2 | 2 | HR | 12 |
| 5 | w6 | 2 | 2 | HR | 12 |
| 6 | s7 | 3 | 3 | MK | 13 |
| 7 | s8 | 4 | 4 | MIS | 14 |
| 8 | s9 | 51 | NULL | NULL | NULL |
| NULL | NULL | NULL | 5 | FD | 15 |
+------+------+--------+------+----------+--------+
9 rows in set (0.01 sec)
mysql> select * from tbl_emp a left join tbl_dept b on a.deptid=b.id where b.id is null
-> union
-> select * from tbl_emp a right join tbl_dept b on a.deptid=b.id where a.deptid is null;
+------+------+--------+------+----------+--------+
| id | NAME | deptId | id | deptName | locAdd |
+------+------+--------+------+----------+--------+
| 8 | s9 | 51 | NULL | NULL | NULL |
| NULL | NULL | NULL | 5 | FD | 15 |
+------+------+--------+------+----------+--------+
2 rows in set (0.00 sec)
索引类似于书的目录,可以快速查找到对应的内容
1.对于中大型表:索引非常有效,能较大程度的提升查询的效率
2. 对于较小的表,使用全表扫描更加有效,
3. 对于特大型表:建立索引代价也会增加,后期可以使用分区技术来解决
注意:实际开发中,一般不使用分区技术,因为有全局索引的解决方案,MyCat等
#建索引
CREATE INDEX indexName ON mytable(columnname(length));
#建索引另一种方式
ALTER mytable ADD INDEX [indexName] ON(columnname(length));
#删除索引
drop index [indexName] on mytable;
#查看索引(\G表示将查询到的横向表格纵向输出,方便阅读)
HOW INDEX FROM table_name\G
注意:一个表最好建的索引不要超过5个
2. 唯一索引:索引列的值必须唯一,但允许有空值。
#建索引
CREATE UNIQUE indexName ON mytable(columnname(length));
#建索引另一种方式
ALTER mytable ADD UNIQUE [indexName] ON(columnname(length));
#建索引
CREATE [UNIQUE] INDEX indexName ON mytable(columnname1(length),columnname2(length));
#建索引另一种方式
ALTER mytable ADD [UNIQUE] INDEX [indexName] ON(columnname1(length),columnname2(length));
添加索引的具体类型
主要有六种:
创建索引
#两种创建方式
CREATE [UNIQUE] INDEX indexName ON mytable(columnname(length));
' or '
ALTER mytable ADD [UNIQUE] INDEX [indexName] ON(columnname(length));
#该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。
ALTER TABLE tbl_name ADD PRIMARY KEY(column_list)
#这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)。
ALTER TABLE tbl_name ADD UNIQUE index_name(column_list)
#添加普通索引,索引值可出现多次。
ALTER TABLE tbl_name ADD INDEX index_name(column_list)
#该语句指定了索引为FULLTEXT,用于全文索引。
ALTER TABLE tbl_name ADD FULLTEXT index_name(column_list)
删除索引
DROP INDEX [indexName] ON mytable;
查看索引(\G表示将查询到的横向表格纵向输出,方便阅读)
SHOW INDEX FROM table_name\G
MySQL Query Optimizer(MySQL查询优化器) 的作用:
type:访问类型排列,显示查询使用了何种类型
system>const>eq_ref>ref>fultext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>ALL
system>const>eq_ref>ref>range>index>ALL
,一般来说,得保证查询至少达到range级别,最好能达到refselect col1,col2 from t1;
col1,col2是复合索引,所以如果select后的字段跟我建的复合索引一一对应,那么直接从索引扫描不需要全表扫描。查询字段跟建的索引对应就是覆盖索引
key_len:
表示索引中使用的字节数。
这里的13=char(4)*(utf-8)(3)+允许为null(1)
Extra:(格外信息)
explain查看语句之后,如果type,extra中都是比较坏的情况,就需要进行优化
在复合索引中,如果索引的字段有作为范围出现的,那么会使其后面的索引字段失效,因此需要将有范围的索引字段去掉。
详细例子请看这里
【优化总结口诀】
全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
Like百分写最右,覆盖索引不写星;
不等空值还有or,索引失效要少用;
varchar引号不可丢,SQL高级也不难!
【具体描述】
全值匹配我最爱(使用索引全部字段作为查询条件字段,即是where后面的条件)
最佳前缀法则(使用索引字段要按照顺序出现在查询条件字段,即是where后面的条件)
范围之后索引全失效(使用到范围的索引字段的后面的字段的索引失效了,不包含自己,自己是使用了索引进行排序,并没有进行查找)
虽然ref为null,但是其实是使用了索引,因为是使用了范围,所以用作索引进行排序了,并没有进行查找,看key可以知道用到了索引。
5. 尽量使用覆盖索引(索引与查询字段一致),减少select *;
6. 使用不等于会使索引失效(不管不等于使用在哪个索引字段,他都会使索引全部失效)
7. is null ,is not null也无法使用索引(使用了is null is not null的字段的索引和后面的索引会失效,前面的不会失效)
8. like开头也会失效(使用了like%开头的,自己和后面的索引失效,前面的不会失效)
使用覆盖索引可以解决%在前面导致索引失效的问题
9. 字符串不加单引号也会失效(自己和后面的失效,前面的不会失效)
10. 用or也会失效。 (只要用到or索引就会全部失效)
【自己总结的口诀】
全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中件兄弟不能断;
索引列上少计算,自己后面都失效;
范围之后都失效,索引排序不查找;
少用or不等于,不管前后都失效;
空值还有like百分,自己后面都失效;
哇所不加单引号,自己后面都失效;
覆盖索引不写星,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');
create index idx_test03_c1234 on test03(c1,c2,c3,c4);
全值匹配:
注意:如果索引全用到了,但是使用的顺序跟索引建的顺序不一样,那么也是全部索引都用到了。比如建索引c1,c2,c3,c4的顺序建立了复合索引;查询的时候是按照c1,c2,c4,c3的顺序查找的,这里是全部使用了索引。因为有优化器的存在,优化器会按照他自己认为最优的方式进行索引
c1,c2,c3都用到了,c3用于排序。c3的作用是用于排序而不是查找,所以这里没有显示c3用了索引,但是其实是用了的。c4没有用到索引
跟上面一样,c4列没有用到索引
详细例子看这里
数据库调优步骤
exists、in、not in、not exists的描述
用法举例
mysql> select * from tbl_emp;
+----+------+--------+
| id | NAME | deptId |
+----+------+--------+
| 1 | z3 | 1 |
| 2 | z4 | 1 |
| 3 | z5 | 1 |
| 4 | w5 | 2 |
| 5 | w6 | 2 |
| 6 | s7 | 3 |
| 7 | s8 | 4 |
| 8 | s9 | 51 |
+----+------+--------+
8 rows in set (0.00 sec)
mysql> select * from tbl_dept;
+----+----------+--------+
| id | deptName | locAdd |
+----+----------+--------+
| 1 | RD | 11 |
| 2 | HR | 12 |
| 3 | MK | 13 |
| 4 | MIS | 14 |
| 5 | FD | 15 |
+----+----------+--------+
5 rows in set (0.00 sec)
mysql> select * from tbl_emp e where e.deptId in(select id from tbl_dept d);
+----+------+--------+
| id | NAME | deptId |
+----+------+--------+
| 1 | z3 | 1 |
| 2 | z4 | 1 |
| 3 | z5 | 1 |
| 4 | w5 | 2 |
| 5 | w6 | 2 |
| 6 | s7 | 3 |
| 7 | s8 | 4 |
+----+------+--------+
7 rows in set (0.00 sec)
mysql> select * from tbl_emp e where exists(select 1 from tbl_dept d where d.id=e.deptId);
+----+------+--------+
| id | NAME | deptId |
+----+------+--------+
| 1 | z3 | 1 |
| 2 | z4 | 1 |
| 3 | z5 | 1 |
| 4 | w5 | 2 |
| 5 | w6 | 2 |
| 6 | s7 | 3 |
| 7 | s8 | 4 |
+----+------+--------+
7 rows in set (0.04 sec)
where a=const order by b,c;
提高Order By的速度
默认情况:
查看慢查询日志是否开启:
SHOW VARIABLES LIKE '%slow_query_log%';
查看 mysql 的慢查询日志是否开启开启慢查询日志:
set global slow_query_log = 1;
开启慢查询日志set global slow_query_log=1;
开启了慢查询日志只对当前数据库生效,如果MySQL重启后则会失效。[mysqld]
slow_query_log =1
slow_query_log_file=/var/lib/mysql/Heygo-slow.log
查看慢SQL的阈值
SHOW VARIABLES LIKE 'long_query_time%';
查看慢 SQL 的阈值设置SQL的阈值
set global long_query_time=3;
来修改这个阈值,修改完成后需要重新连接或者重新开一个会话才可以看到修改后的值。查看当前系统中有多少条慢查询记录:show global status like '%Slow_queries%';
往表中插入超多数据
CREATE TABLE dept
(
deptno int unsigned primary key auto_increment,
dname varchar(20) not null default "",
loc varchar(8) 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;
delimiter $$ # 两个 $$ 表示结束
create function rand_string(n int) returns varchar(255)
begin
declare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyz';
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 $$
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 $$
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 $$
DELIMITER ;
CALL insert_emp(100001, 500000);
概念
查看当前版本是否支持show prifile(是否开启)
运行SQL
# 开启
general_log=1
# 记录日志文件的路径
general_log_file=/path/logfile
# 输出格式
log_output=FILE
set global general_log=1;
set global log_output='TABLE';
详细例子
加读锁:会话A对表a加了一把读锁,出现以下情况
加写锁:会话A给表a添加写锁,出现以下情况
如果可以,请换成不同的id进行测试,因为mysql聪明有缓存,第二次的条件会从缓存中取得,影响锁效果演示。
特点
数据库事务详解
事务四大特性ACID
原子性,一致性,隔离性,持久性
并发事务处理带来的问题
更新丢失,脏读,不可重复读,幻读
事务隔离级别
读未提交,读已提交,可重复读,可串行化(可序列化)
行锁定基本演示
加行锁其实就是将自动提交关闭:set autocommit=0;
操作同一行数据:
这里会话1修改了数据,还没有提交,会话2查到的数据还是之前的,如果还没有提交,会话2查到的数据是修改后的,就是发生了脏读。
修改同一行,会话2阻塞
操作不同行数据
总结:就是锁住一行的时候,只有本会话可以修改这一行,还有本会话也可以修改其它行,其它会话不能修改这一行,但是可以修改其它行,都是要commit之后才生效的,如果没有commit本会话查询的时候是修改后的数据,但是另一个会话只能查询到修改之前的,如果查到修改之后的,估计就是出现了脏读。
无索引会导致行锁升级为表锁
mysql的配置文件
因为发生多次 IO, 存在延时问题
详细参考这里