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>
索引优化分析
手写
机读
总结:mysql是从from开始执行的
join图
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)
inner join左表和右表共有部分
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)
left join左表全有
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)
right join右表全有
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)
左表和B表的独有
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. 使用不等于会使索引失效(不管不等于使用在哪个索引字段,他都会使索引全部失效)
is null ,is not null也无法使用索引(使用了is null is not null的字段的索引和后面的索引会失效,前面的不会失效)
like开头也会失效(使用了like%开头的,自己和后面的索引失效,前面的不会失效)
使用覆盖索引可以解决%在前面导致索引失效的问题
字符串不加单引号也会失效(自己和后面的失效,前面的不会失效)
用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的顺序查找的,这里是全部使用了索引。因为有优化器的存在,优化器会按照他自己认为最优的方式进行索引
这个c3是范围,那么c3后面的c4失效,所以只用到三个索引
c4是范围,所以c4后面全失效,所以只用到了四个
c1,c2,c3都用到了,c3用于排序。c3的作用是用于排序而不是查找,所以这里没有显示c3用了索引,但是其实是用了的。c4没有用到索引
跟上面一样,c4列没有用到索引
数据库调优步骤