mysql逻辑架构
连接层
最上层是一些客户端和连接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于tcp/ip的通信
服务层
第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化及部分内置函数的执行
引擎层
存储引擎真正的负责了MYSQL中数据的存储和提取,服务器通过API与存储引擎进行通信
存储层
主要是将数据存储在运行于裸设备的文件系统之上,并完成于存储引擎的交互
1
2
3select x from tableA A left join tableB B on A.key=B.key
select x from tableA A right join tableB B on A.key=B.key where A.key is NULL
select x from tableA A Full outer join tableB on A.key=B.key where A.key is Null or B.key is Bull建表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15CREATE 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;
索引:索引是帮助MYSQL高效获取数据的数据结构(排好序的快速查找数据结构)
索引分类
单值索引
唯一索引
复合索引
基本语法
1
2
3
4
5
6
7
8
9
10创建
CREATE [UNIQUE] INDEX indexName ON mytable(columnname(length));
ALTER mytable ADD [UNIQUE] INDEX [indexName] ON (columnname(length))
删除
DROP INDEX [indexName] ON mytable;
查看
SHOW INDEX FROM table_name\G哪些情况需要创建索引
主键自动建立唯一索引
频繁作为查询条件的字段应该创建索引
查询中与其他表关联的字段,外键关系建立索引
频繁更新的字段不适合创建索引
Where条件里用不到的字段不创建索引
哪些情况不需要创建索引
表记录太少
经常增删改的表
数据重复且分布平均的表字段
MYSQL常见瓶颈
CPU:CPU在饱和的时候一般发生在数据装入内存或从磁盘上读取数据时候
I/O:磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
服务器硬件的性能瓶颈:top,free,uistat和vmstat来查看系统的性能状态
explain+SQL
1
id|select_type|table|type|possible_keys|key|key_len|ref|rows|Extra
- select_type
- SIMPLE:简单的select查询,不包含子查询或者UNION
- PRIMARY:查询中若包含任何复杂的子部分,最外层查询被标记为
- SUBQUERY:在SELECT或WHERE列表中国呢包含了子查询
- DERIVED:在FROM列表中包含的子查询被标记为DERIVED(衍生)
- UNION:若第二个SELECT出现在UNION之后,则被标记为UNION
- UNION RESULT:从UNION表获取结果的SELECT
- type
1
system>const>eq_ref>ref>range>index>ALL
- possible_keys:理论上应该用到哪些索引
- key:真正用到的索引
- ref:显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或常量被用于查找索引列上的值
- Extra(包含不适合在其他列中显示但十分重要的额外信息)
- Using filesort:说明mysql会对数据使用过一个外部的索引排序,而不是按照表内的索引顺序进行读取(文件内排序)
- Using temporary:使用了临时表保存中间结果,mysql在对查询结果排序时使用临时表。
- Using index:表示相应的select操作中使用了覆盖索引,避免访问了表的数据行
索引失效(避免索引失效)
- 全局匹配我最爱
- 最佳左前缀法则
- 不在索引列上做任何操作,会导致索引失效转向全表扫描
- 存储引擎不能使用索引中范围条件右边的列
- 尽量使用覆盖索引,减少select *
- mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
- is null , is not null也无法使用索引
- like以通配符开头(‘%abc…’)mysql索引失效会变成全表扫描的操作
- 字符串不加单引号索引失效
少用or,用它来连接时会索引失效
查询截取分析
- 慢查询的开启与捕获
- explain+慢SQL分析
- show profile查询SQL在mysql服务器里面的执行细节和生命周期情况
- SQL数据库服务器的参数调优
优化原则:小表驱动大表
- 当B表的数据集必须小于A表的数据集时,用in优于exists
1
select * from A where id in (select id from B)
- 当A表的数据集必须小于B表的数据集时,用exists优于in
1 | select * from A where exists (select 1 from B where B.id=A.id) |
批量数据脚本
- 建表
- 设置参数log_bin_trust_function_creators
- 创建函数,保证每条数据都不同
- 创建存储过程
- 调用存储过程
MYSQL的表级锁有两种模式
表共享读锁
表独占写锁
读锁会阻塞写,但是不会堵塞读,而写锁会把读和写都堵塞。
并发事物处理带来的问题
更新丢失:当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题。
脏读:事务A读取到了事务B已修改但尚未提交的数据。
不可重复读:事务A读取到了事务B已经提交的修改数据,不符合隔离性
幻读:事务A读取到了事务B已提交的新增数据,不符合隔离性。
MYSQL事务隔离级别
- 读未提交(read uncommitted)
- 读已提交(read committed)
- 可重复读(repeatable read)
- 可序列化(serializable)
MYSQL复制过程分成三步
- master将change记录到二进制日志(binary log),这些记录过程叫做二进制日志事件,binary log events
- slave将master的binary log events拷贝到它的中继日志(relay log)
- slave重做中继日志中的事件,将改变应用到自己的数据库中,MYSQL复制是异步的且串行化的
MySQL题目
sql文件源码如下
1 | DROP TABLE IF EXISTS EMP; |
source
加载数据库表
取得每个部门最高薪水的人员名称
首先取得每个部门的最高薪水
1 | mysql> select deptno,max(sal) as maxsal from emp group by deptno; |
将以上的查询结果最为一张临时表t和emp表进行连接,查询条件是t.deptno=e.deptno and t.maxsal=e.sal
1 | mysql> select e.ename,t.* |
哪些人的薪水在部门的平均薪水之上
首先取得每个部门的平均薪水
1 | mysql> select deptno,avg(sal) as avgsal from emp group by deptno; |
将以上的查询结果最为一张临时表t和emp表进行连接,查询条件是t.deptno=e.deptno and e.sal>t.avgsal
1 | mysql> select e.ename,e.sal,t.* |
取得部门中(所有人的)平均的薪水等级
第一步先找出每个员工的薪水等级
1 | mysql> select e.ename,e.sal,e.deptno,s.grade |
基于上面的表按照部门编号分组之后求grade的平均值
1 | mysql> select e.deptno,avg(s.grade) |
不使用max函数,取得最高薪水
降序之后limit
1 | mysql> select ename,sal from emp order by sal desc limit 1; |
表的自连接
1 | mysql> select sal from emp where sal not in (select distinct a.sal from emp a jo |
取得平均薪水最高的部门的部门编号
第一步找出每个部门的平均薪水
1 | mysql> select deptno,avg(sal) as avgsal from emp group by deptno; |
第二步找出薪水最高的部门编号
1 | mysql> select deptno,avg(sal) as avgsal from emp group by deptno order by avgsal desc limit 1; |
取得平均薪水最高的部门的部门名称
1 | mysql> select |
找出平均薪水的等级最低的部门的部门名称
第一步,找出每个部门的平均薪水
1 | mysql> select d.dname,avg(e.sal) from dept d join emp e on e.deptno=d.deptno group by d.dname; |
第二步,将上述表和salgrade表连接找出每个部门的平均薪水的等级
1 | mysql> select t.dname,s.grade,t.avgsal |
第三步,找出最低等级
1 | mysql> select grade |
第四步,给第二步得到的表加一个grade=3
的限制条件
1 | mysql> select t.dname,s.grade,t.avgsal |
取出比普通员工(员工代码没有在mgr字段上出现的)的最高薪水还要高的领导人姓名
第一步,找出员工代码在mgr字段上出现的,排除NULL
1 | mysql> select distinct mgr from emp where mgr is not null; |
第二步,找出普通员工的最高薪水
1 | mysql> select max(sal) from emp where empno not in (select distinct mgr from emp where mgr is not null); |
第三步,找出薪资高于1600的
1 | mysql> select ename,sal |
取得每个薪水等级有多少员工
第一步,获取每个员工的薪水等级
1 | mysql> select e.ename,e.sal,s.grade |
根据上述表,对等级进行分组,并统计每个分组的人数
1 | mysql> select t.grade,count(t.grade) |
列出所有员工及领导的姓名
使用左外连接
1 | mysql> select |
找出受雇日期早于其直接上级的所有员工的编号,姓名,部门名称
第一步,找出受雇日期早于其直接上级的所有员工和领导
1 | mysql> select a.ename,a.hiredate,b.ename,b.hiredate |
第二步,找出员工的编号和部门编号
1 | mysql> select a.ename,a.empno,a.deptno,a.hiredate,b.ename,b.hiredate |
连接部门表根据部门编号找出员工所在部门
1 | mysql> select a.ename,a.empno,d.dname |
列出部门名称和这些部门的员工信息
使用外连接
1 | mysql> select |
列出至少有5个员工的所有部门
第一步,按照部门编号进行分组计数
1 | mysql> select count(deptno) from emp group by deptno; |
连接dept表,列出部门名称
1 | mysql> select d.dname,count(e.deptno) from emp e join dept d on e.deptno=d.deptno group by e.deptno; |
使用having
找出至少有5个员工的部门
1 | mysql> select d.dname,count(e.deptno) |
列出薪资比’SMITH’多的所有员工信息
1 | mysql> select ename,sal |
列出所有’CLERK’的姓名及其部门名称,部门的人数
先找出所有的’CLERK’
1 | mysql> select ename,job,deptno from emp where job = 'clerk'; |
连接dept表找出所有’CLERK’的部门名称
1 | mysql> select e.ename,e.job,d.dname,e.deptno |
找出每个部门的人数
1 | mysql> select deptno,count(*) from emp group by deptno; |
使用这张表和上一张表做连接
1 | mysql> select t1.*,t2.deptcount |
列出最低薪资大于1500的各种工作及从事此工作的全部雇员人数
按照工作岗位分组并计数,使用having
统计最低薪资大于1500的人
1 | mysql> select job,count(*) from emp group by job having min(sal) > 1500; |
列出薪资高于公司平均薪资的所有员工,所在部门,上级领导,雇员的工资等级
1 | mysql> select e.ename '员工',d.dname,l.ename '领导',s.grade |
列出与’SCOTT’从事相同工作的所有员工及部门名称
第一步,找出’SCOTT’从事的工作
1 | mysql> select job from emp where ename = 'scott'; |
第二步,连接dept表,找出和’SCOTT’从事相同工作的人和部门名称
1 | mysql> select e.ename,d.dname |
列出薪资等于部门30中员工的薪资的其他员工的姓名和薪资
第一步,找出部门30的所有薪资
1 | mysql> select distinct sal from emp where deptno = 30; |
第二步,找出其他员工的姓名和薪资
1 | mysql> select distinct sal from emp where deptno = 30; |
列出在每个部门工作的员工数量,平均工资和平均服务期限
1 | mysql> select d.deptno,count(e.ename),ifnull(avg(e.sal),0) '平均薪资',avg(TimeStampDiff(year,hiredate,now())) '服务期限' |
求出员工领导的薪水超过3000的员工名称与领导
1 | mysql> select e.ename,l.ename,l.sal |
求部门名称中,带’S’字符的部门员工的工资合计、部门人数
1 | mysql> select d.deptno,d.dname,d.loc,count(e.ename),ifnull(sum(e.sal),0) |