最上层是一些客户端和连接服务,包含本地socket通信和大多数基于客户端/服务端工具实现的类似于tcp/ip的通信。
主要完成一些类似于连接处理、授权认证、及相关的安全方案。
在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。
同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化及部分内置函数的执行。
所有跨存储引擎的功能也在这一层实现,如过程、函数等。
在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化:如确定查询表的顺序,是否利用索引等,最后生成相应的执行操作。
如果是select语句,服务器还会查询内部的缓存。如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。
· 接受用户的SQL命令,并且返回用户需要查询的结果。比如select from就是调用SQL Interface
· SQL命令传递到解析器的时候会被解析器验证和解析。
· SQL语句在查询之前会使用查询优化器对查询进行优化。
· 如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。
· 这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key缓存,权限缓存等
存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。后面介绍MyISAM和InnoDB
数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
了解查询语句底层执行的过程:查看是否开启计划。
修改配置文件/etc/my.cnf,先开启查询缓存
新增一行:query_cache_type=1
重启mysql:systemctl restart mysqld
再开启查询执行计划
show variables like '%profiling%';
set profiling=1;
执行语句两次:select * from mydb.mytbl where id=1 ;
显示最近执行的语句
show profiles;
显示执行计划
show profile cpu,block io for query 6;
执行编号7时,比执行编号6时少了很多信息,从下面截图中可以看出查询语句直接从缓存中获取数据;
注意:SQL必须是一致的,否则,不能命中缓存。
如果对数据库表进行 insert ,update ,delete 这个时候,缓存会失效!
如:select * from mydb.mytbl where id=2
和 select * from mydb.mytbl where id>1 and id<3
虽然查询结果一致,但并没有命中缓存。
数据结构有很多种,一般来说,按照数据的逻辑结构对其进行简单的分类,包括线性结构和非线性结构两类。
•线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系。
•线性结构有两种不同的存储结构,即顺序存储结构和链式存储结构。
◦顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的
◦链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息
•线性结构常见的有:数组、链表、队列和栈。
非线性结构包括:二维数组,多维数组,树结构,图结构
常见的算法时间复杂度由小到大依次为:O---理解为一个函数
Ο(1)<Ο(log2N)<Ο(n)<Ο(nlog2N)<Ο(n^2)<Ο(n^3)< Ο(n^k) <Ο(2^n)
查看mysql提供什么存储引擎:show engines;
FOJWGHSDOL
(佛叫我干活速度上线(OL)):大型范围过滤,越靠前越好# t_emp表(以下称为A表) CREATE TABLE `t_emp` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(20) DEFAULT NULL, `age` INT(3) DEFAULT NULL, `deptId` INT(11) DEFAULT NULL, `empno` INT(11) NOT NULL, PRIMARY KEY (`id`), KEY `idx_dept_id` (`deptId`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; # t_dept表(一下称为B表) CREATE TABLE `t_dept` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `deptName` VARCHAR(30) DEFAULT NULL, `address` VARCHAR(40) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; # 插入数据 INSERT INTO t_dept(id,deptName,address) VALUES(1,'华山','华山'); INSERT INTO t_dept(id,deptName,address) VALUES(2,'丐帮','洛阳'); INSERT INTO t_dept(id,deptName,address) VALUES(3,'峨眉','峨眉山'); INSERT INTO t_dept(id,deptName,address) VALUES(4,'武当','武当山'); INSERT INTO t_dept(id,deptName,address) VALUES(5,'明教','光明顶'); INSERT INTO t_dept(id,deptName,address) VALUES(6,'少林','少林寺'); INSERT INTO t_emp(id,NAME,age,deptId,empno) VALUES(1,'风清扬',90,1,100001); INSERT INTO t_emp(id,NAME,age,deptId,empno) VALUES(2,'岳不群',50,1,100002); INSERT INTO t_emp(id,NAME,age,deptId,empno) VALUES(3,'令狐冲',24,1,100003); INSERT INTO t_emp(id,NAME,age,deptId,empno) VALUES(4,'洪七公',70,2,100004); INSERT INTO t_emp(id,NAME,age,deptId,empno) VALUES(5,'乔峰',35,2,100005); INSERT INTO t_emp(id,NAME,age,deptId,empno) VALUES(6,'灭绝师太',70,3,100006); INSERT INTO t_emp(id,NAME,age,deptId,empno) VALUES(7,'周芷若',20,3,100007); INSERT INTO t_emp(id,NAME,age,deptId,empno) VALUES(8,'张三丰',100,4,100008); INSERT INTO t_emp(id,NAME,age,deptId,empno) VALUES(9,'张无忌',25,5,100009); INSERT INTO t_emp(id,NAME,age,deptId,empno) VALUES(10,'韦小宝',18,NULL,100010);
SELECT a.*,b.* FROM t_emp a INNER JOIN t_dept b ON a.deptId = b.id;
SELECT a.*,b.* FROM t_emp a LEFT JOIN t_dept b ON a.deptId = b.id;
SELECT a.*,b.* FROM t_emp a RIGHT JOIN t_dept b ON a.deptId = b.id; # 或者下面写法 SELECT a.*,b.* FROM t_dept b LEFT JOIN t_emp a ON b.id=a.deptId;
SELECT a.*,b.* FROM t_emp a LEFT JOIN t_dept b ON a.deptId=b.id WHERE b.id is null
SELECT a.*,b.* FROM t_emp a RIGHT JOIN t_dept b ON a.deptId=b.id WHERE a.deptId is null
union
(去重排序拼接,效率较低)|union all
(无脑拼接,效率高) 进行SQL拼接了;(UNION在使用时,两张表的字段保证一致,如果不一致,请在slect后面列选字段,不要使用*)注意:使用
union
或者union all
时,两张拼接的结果集必须要结构一模一样
SELECT a.*,b.* FROM t_emp a LEFT JOIN t_dept b ON a.deptId = b.id UNION SELECT a.*,b.* FROM t_emp a RIGHT JOIN t_dept b ON a.deptId = b.id;
SELECT a.*,b.* FROM t_emp a LEFT JOIN t_dept b ON a.deptId = b.id UNION ALL SELECT a.*,b.* FROM t_emp a RIGHT JOIN t_dept b ON a.deptId = b.id;
注意上图的
union
和union all
的区别(面试题喔~)
# A的独有 (查询没有加入任何部门的员工) SELECT a.*,b.* FROM t_emp a LEFT JOIN t_dept b ON a.deptId=b.id WHERE b.id is null union # B的独有(查询没有任何员工的部门) SELECT a.*,b.* FROM t_emp a RIGHT JOIN t_dept b ON a.deptId=b.id WHERE a.deptId is null
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。
索引的本质:索引是数据结构。可以简单理解为“排好序的快速查找数据结构”。
为了充分利用聚簇索引的聚簇的特性,所以innodb表的主键列尽量选用有序的顺序id,而不建议用无序的id,比如uuid这种。
分组 = 排序 + 去重 ,也就是说,真正的分组其实就是先将数据排序,然后再看根据distinct进行去重. 所以分组比排序更消耗性能
where 后面带的字段,一定考虑建索引:
EXPLAIN SELECT * FROM t_emp;
1.select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
2.三种情况
1)id相同,执行顺序由上至下
Explain select * from t1,t2,t3;
2)id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
explain select t1.id from t1 where t1.id = (select t2.id from t2 where t2.id = (select t3.id from t3 where t3.content='t3_897'));
id相同,不同,同时存在;
id如果相同,可以认为是一组,从上往下顺序执行;
在所有组中,id值越大,优先级越高,越先执行;
关注点:id号每个号码,表示一趟独立的查询。一个sql的查询趟数越少越好。
1.有哪些
2.查询的类型,主要是用于区别 普通查询、联合查询、子查询等的复杂查询
1)SIMPLE
简单的 select 查询,查询中不包含子查询或者UNION
EXPLAIN SELECT * FROM t1;
2)PRIMARY
查询中若包含任何复杂的子部分,最外层查询则被标记为primary
EXPLAIN select t1.id from t1 where t1.id = (select t2.id from t2 where t2.id = (select t3.id from t3 where t3.content='t3_897'));
3)SUBQUERY
在SELECT或WHERE列表中包含了子查询
4)DEPENDENT SUBQUERY
在SELECT或WHERE列表中包含了子查询,子查询基于外层
EXPLAIN SELECT * FROM t3 WHERE id = ( SELECT id FROM t2 WHERE content = t3.content);
5)UNCACHEABLE SUBQUREY
表示这个subquery的查询要受到外部系统变量的影响
EXPLAIN SELECT * FROM t3 WHERE id = ( SELECT id FROM t2 WHERE content = @@character_set_server);
6)UNION
若第二个SELECT出现在UNION之后,则被标记为UNION; 若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
EXPLAIN SELECT * FROM (SELECT * FROM t1 UNION SELECT * FROM t2) aa;
7)UNION RESULT
从UNION表获取结果的SELECT
显示这一行的数据是关于哪张表的
代表分区表中的命中情况,非分区表,该项为null
https://dev.mysql.com/doc/refman/5.7/en/alter-table-partition-operations.html
表仅有一行记录,必须是系统表,这是const类型的特例,查询起来非常迅速。
explain SELECT * from mysql.proxies_priv WHERE User
='root';
explain select * from t1 where id = 1;
表示通过索引一次就找到了,const用于primary key或者unique索引。
因为只匹配一行数据,所以很快 如将主键置于where列表中,MySQL就能将该查询转换为一个常量
explain select * from t1,t2 where t1.id = t2.id;
唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见主键或唯一索引扫描
create index idx_content on t1(content);
EXPLAIN SELECT * FROM t1, t2 WHERE t1.content = t2.content;
非唯一性索引扫描,返回匹配某个单独值的所有行. 本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而, 它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
explain select * from t2 where id >1 and id <5;
explain select id from t1;
explain select * from t2;
Full Table Scan,将遍历全表以找到匹配的行
显示可能应用在这张表中的索引,一个或多个。 查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用
实际使用的索引。如果为NULL,则没有使用索引
okey_len表示索引使用的字节数,根据这个值可以判断索引的使用情况,特别是在组合索引的时候,判断该索引有多少部分被使用到非常重要。值越大越好。不损失精度情况下key_len越小 索引效果越好
EXPLAIN SELECT SQL_NO_CACHE * FROM t_emp WHERE t_emp.age=30 AND t_emp.name LIKE 'ab%';
create index idx_age_name on t_emp (age,name);
如何计算
key_len的长度计算公式:
varchr(10)变长字段且允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)+2(变长字段)
varchr(10)变长字段且不允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)+2(变长字段)
char(10)固定字段且允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)
char(10)固定字段且不允许NULL = 10 * ( character set:utf8=3,gbk=2,latin1=1)
显示将哪些列或常量与键列中命名的索引进行比较,以从表中选择行。
explain select * from t1,t2 where t1.id = t2.id;
rows列显示MySQL认为它执行查询时必须检查的行数。值越小越好
这个字段表示存储引擎返回的数据在mysql server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数
包含不适合在其他列中显示,但十分重要的额外信息
出现filesort的情况:order by 没有用上索引。
优化后(给deptno和ename字段建立复合索引),去掉filesort
create index idx_deptno_ename on emp (deptno,ename);
查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度 说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。 MySQL中无法利用索引完成的排序操作称为“文件排序”
出现Using temporary情况:分组没有用上索引。产生临时表。注意:分组操作是需要先排序后分组的。所以,也会出现Using filesort。
优化前存在 using temporary 和 using filesort
优化后(给deptno和ename建立复合索引)去掉using temporary 和 using filesort,性能发生明显变化:
create index idx_deptno_ename on emp (deptno,ename);
使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by 和分组查询group by。
表示使用了覆盖索引 [content是一个索引]
如果同时出现using where,表明索引被用来执行索引键值的查找;
如果没有同时出现using where,表明索引用来读取数据而非执行查找动作。
o表明使用了where过滤!
如果有它则表明关联字段没有使用索引!
使用了连接缓存
where 后面筛选条件有错误!
口 | 诀 |
---|---|
全值匹配我最爱 | 最左前缀要遵守 |
带头大哥不能死 | 中间兄弟不能断 |
索引列上少计算 | 范围之后全失效 |
LIKE百分写最右 | 覆盖索引不写* |
不等空值还有OR | 索引影响要注意 |
VAR引号不可丢 | SQL优化有诀窍 |
口 | 诀 |
---|---|
无过滤 | 不索引 |
顺序错 | 必排序 |
方向反 | 必排序 |
LIKE百分写最右 | 覆盖索引不写* |
不等空值还有OR | 索引影响要注意 |
VAR引号不可丢 | SQL优化有诀窍 |
SHOW VARIABLES LIKE '%slow_query_log%';
默认情况下slow_query_log的值为OFF,表示慢查询日志是禁用的
set global slow_query_log=1;
只对窗口生效,重启服务失效
SHOW VARIABLES LIKE '%long_query_time%';
SHOW GLOBAL VARIABLES LIKE 'long_query_time';
设置临时的sql慢查询时间
SET SESSION long_query_time=0.1;
# 建一张表 CREATE TABLE `dept` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `deptName` VARCHAR(30) DEFAULT NULL, `address` VARCHAR(40) DEFAULT NULL, `ceo` INT NULL , PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; # 创建二个函数 DELIMITER $$ CREATE FUNCTION rand_num (from_num INT ,to_num INT) RETURNS INT(11) BEGIN DECLARE i INT DEFAULT 0; SET i = FLOOR(from_num +RAND()*(to_num - from_num+1)) ; RETURN i; END$$ DELIMITER $$ CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255) BEGIN DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'; 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 PROCEDURE `insert_dept`( max_num INT ) BEGIN DECLARE i INT DEFAULT 0; SET autocommit = 0; REPEAT SET i = i + 1; INSERT INTO dept ( deptname,address,ceo ) VALUES (rand_string(8),rand_string(10),rand_num(1,500000)); UNTIL i = max_num END REPEAT; COMMIT; END$$ # 调用存储过程添加数据
2.查看mysqldumpslow的帮助信息
a)mysqldumpslow --help
得到返回记录集最多的10个SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/localhost-slow.log
得到访问次数最多的10个SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/localhost-slow.log
得到按照时间排序的前10条里面含有左连接的查询语句
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/localhost-slow.log
另外建议在使用这些命令时结合 | 和more 使用 ,否则有可能出现爆屏情况
mysqldumpslow -s r -t 10 /var/lib/mysql/localhost-slow.log | more
CREATE VIEW view_name AS SELECT column_name(s) FROM table_name WHERE condition
select * from view_name
CREATE OR REPLACE VIEW view_name AS SELECT column_name(s) FROM table_name WHERE condition
drop view view_name;
mysql 视图会随着表中的数据变化而动态变化!
docker pull mysql:5.7
先下载mysql镜像文件
docker run -d -p 3307:3306 \ -v /mysql01/data:/var/lib/mysql \ -v /mysql01/conf:/etc/mysql/conf.d \ -e MYSQL_ROOT_PASSWORD=123456 \ --name mysql01 --restart=always --privileged=true \ mysql:5.7 docker run -d -p 3308:3306 \ -v /mysql02/data:/var/lib/mysql \ -v /mysql02/conf:/etc/mysql/conf.d \ -e MYSQL_ROOT_PASSWORD=123456 \ --name mysql02 --restart=always --privileged=true \ mysql:5.7 docker run -d -p 3309:3306 \ -v /mysql03/data:/var/lib/mysql \ -v /mysql03/conf:/etc/mysql/conf.d \ -e MYSQL_ROOT_PASSWORD=123456 \ --name mysql03 --restart=always --privileged=true \ mysql:5.7
# 创建一个base.cnf配置文件 vim base.cnf [client] default-character-set=utf8 [mysqld] character-set-server=utf8 collation-server=utf8_general_ci
[mysqld] server-id=2 relay-log=mysql-relay
docker restart mysql01 mysql02 mysql03
GRANT REPLICATION SLAVE ON *.* TO 'slave'@'%' IDENTIFIED BY '123456';
flush privileges;
show master status;
CHANGE MASTER TO MASTER_HOST='192.168.137.72', MASTER_USER='slave',MASTER_PASSWORD='123456',MASTER_PORT=3307, MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=0; START SLAVE;
show slave status;
主机创建一个数据库,刷新从机会发现从机也有了
创建表,添加记录,会发现从机也会立马同步过去的