与客户端进行连接的服务 主要完成一些类似连接处理,授权认证 及相关的安全方案. 在该层上引入了连接池的概念,
提供核心的服务功能 如果sql接口,完成缓存的查询,sql的分析和优化部分及内置函数的执行. 所有跨存储引擎的功能都在这一层实现. 服务器会解析查询并创建相应的内部解析权,并对其完成相应的优化,生成相应的执行操作 服务器还会查询内部的缓存,如果缓存空间足够大,这样可以解决大量读操作的环境中,能够很好的提升系统性能
存储引擎是真正负责MYSQL中数据的存储和提取,服务器通过API与存储引擎进行通信 不同的存储引擎提供的功能不同,可以根据自己的实际需求来进行选取
主要是将数据存储在运行的计算机文件系统之上,并完成与存储引擎的交互
逻辑架构图

哪个出问题,就去找哪个 插件式的存储引擎,将查询处理和其它的系统任务以及数据的提取相分离 可以根据业务的需求和业务的需要选择合适的存储引擎
MySQL中的数据用各种不同的技术存储在文件(或者内存)中。 这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力。 通过选择不同的技术,你能够获得额外的速度或者功能,从而改善你的应用的整体功能。 这些不同的技术以及配套的相关功能在 MySQL中被称作存储引擎
例如:如果你在研究大量的临时数据,你也许需要使用内存存储引擎。内存存储引擎能够在内存中存储所有的表格数据。 也许需要一个支持事务处理的数据库(以确保事务处理不成功时数据的回退能力) 选择支持事务的存储引擎
show engines;
show variables like '%storage_engine%';MyISAM:不支持 InnoDB:支持
MyISAM:不支持 InnoDB:支持
MyISAM:表锁 操作一条记录也会锁住整个表,不适合高并发的操作 InnoDB:行锁 操作时,只锁某一行,不对其它行有影响,适合高并发的操作
MyISAM:只缓存索引,不缓存数据 InnoDB:不仅缓存索引,还要缓存真实数据,对内存要求比较高,而且内存大小对性能有决定性的影响
MyISAM:小 InnoDB:小
MyISAM:性能 InnoDB:事务
MyISAM:是 InnoDB:是
MyISAM
b.frm :描述表结构文件,字段长度等
b.MYD(MYData):数据信息文件,存储数据信息(如果采用独立表存储模式)
b.MYI(MYIndex):索引信息文件
InnoDB
b.frm :描述表结构文件,字段长度等
b.ibd:存储数据信息和索引信息select distinct 查询字段 from 表名 JOIN 表名 ON 连接条件 where 查询条件 group by 分组字段 having 分组后条件 order by 排序条件 limit 查询起始位置, 查询条数
from 表名 ON 连接条件 JOIN 表名 where 查询条件 group by 分组字段 having 分组后条件 select distinct 查询字段 order by 排序条件 limit 查询起始位置, 查询条数
1.先对多表进行关系,根据条件找出符合条件的记录 2.在符合条件的基础上进行再次where条件筛选 3.对筛选出来的内容进行分组操作 4.分组完成后, 使用having再次筛选出满足条件的记录 5.取所满足条件的记录 6.对取出的记录进行排序 7.最终从取出的记录当中获取多少条记录显示出来

示例表数据 部门表
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`deptName` varchar(30) DEFAULT NULL,
`address` varchar(40) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
INSERT INTO `department` VALUES ('1', '研发部(RD)', '2层');
INSERT INTO `department` VALUES ('2', '人事部(HR)', '3层');
INSERT INTO `department` VALUES ('3', '市场部(MK)', '4层');
INSERT INTO `department` VALUES ('4', '后勤部(MIS)', '5层');
INSERT INTO `department` VALUES ('5', '财务部(FD)', '6层');员工表
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`dep_id` int(11) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`salary` decimal(10,2) DEFAULT NULL,
`cus_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=109 DEFAULT CHARSET=utf8;
INSERT INTO `employee` VALUES ('1', '鲁班', '1', '10', '1000.00', '1');
INSERT INTO `employee` VALUES ('2', '后裔', '1', '20', '2000.00', '1');
INSERT INTO `employee` VALUES ('3', '孙尚香', '1', '20', '2500.00', '1');
INSERT INTO `employee` VALUES ('4', '凯', '4', '20', '3000.00', '1');
INSERT INTO `employee` VALUES ('5', '典韦', '4', '40', '3500.00', '2');
INSERT INTO `employee` VALUES ('6', '貂蝉', '6', '20', '5000.00', '1');
INSERT INTO `employee` VALUES ('7', '孙膑', '6', '50', '5000.00', '1');
INSERT INTO `employee` VALUES ('8', '蔡文姬', '30', '35', '4000.00', '1');图示

作用:查询两张表的共有部分 语句
Select <select_list> from tableA A Inner join tableB B on A.Key = B.Key示例
SELECT * from employee e INNER JOIN department d on e.dep_id= d.id;
图示

作用:把左边表的内容全部查出,右边表只查出满足条件的记录 语句
Select <select_list> from tableA A Left Join tableB B on A.Key = B.Key示例
SELECT * from employee e LEFT JOIN department d on e.dep_id= d.id;
图示

作用:把右边表的内容全部查出,左边表只查出满足条件的记录 语句
Select <select_list> from tableA A Left Join tableB B on A.Key = B.Key示例
SELECT * from employee e RIGHT JOIN department d on e.dep_id= d.id;
图示

作用:查询A的独有数据 语句
Select <select_list> from tableA A Left Join tableB B on A.Key = B.Key where B.key IS NULL 示例
SELECT * from employee e LEFT JOIN department d on e.dep_id= d.id WHERE d.id IS NULL;
图示

作用:查询B的独有数据 语句:
Select <select_list> from tableA A Right Join tableB B on A.Key = B.Key where A.key IS NULL 示例
SELECT * from employee e RIGHT JOIN department d on e.dep_id= d.id WHERE e.id IS NULL;
图示

作用:查询两个表的全部信息 语句
Select <select_list> from tableA A Full Outter Join tableB B on A.Key = B.Key
注:Mysql 默认不支持此种写法 Oracle支持示例
SELECT * from employee e LEFT JOIN department d on e.dep_id= d.id
UNION
SELECT * from employee e RIGHT JOIN department d on e.dep_id= d.id
图示

作用: 查询A和B各自的独有的数据 语句
Select <select_list> from tableA A Full Outter Join tableB B on A.Key = B.Key where A.key = null or B.key=null示例
SELECT * from employee e LEFT JOIN department d on e.dep_id= d.id WHERE d.id is NULL
UNION
SELECT * from employee e RIGHT JOIN department d on e.dep_id= d.id WHERE e.depart_id is NULL
如何写出高质量的sql 如何保证索引不失效
帮助Mysql高效获取数据的数据结构 索引就是数据结构 类似新华字典的索引目录,可以通过索引目录快速查到你想要的字 排好序的快速查找数据


提高查询效率
没有排序之前一个一个往后找
通过索引进行排序之后,可以直接定义到想要的位置
排好序的快速查找数据结构-->就是索引索引类似大学图书馆建立的书目索引,提高数据检索的效率,降低数据库的IO成本 通过索引对数据项进行排序,降低数据排序成本,降低了CPU的消耗
一般来说, 索引本身也很大, 索引往往以文件的形式存储到磁盘上 索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录.所以索引也是要占磁盘空间的 虽然索引提高了查询速度,但是会降低更新表的速度. 因为更新表时, MYSQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段, 会调整因为更新所带来的键值变化后索引的信息
一个索引只包含间个列,一个表可以有多个单值索引 一般来说, 一个表建立索引不要超过5个
索引列的值必须唯一,但允许有空值
一个索引包含多个列
MySQL全文检索是利用查询关键字和查询列内容之间的相关度进行检索,可以利用全文索引来提高匹配的速度。
概述
在我们存数据时, 如果建立索引 数据库系统会维护一个满足特定查找算法的数据结构,这些数据结构以某种方式引用数据 可以在这些数据结构之上,实现高级查找算法,这种结构就是索引 一般来说, 索引本身也很大, 不可能全部存储在内存中, 因此索引往往以索引文件的形式存储在磁盘上 为了加快数据的查找,可以维护二叉查找树, 每个节点分别包含索引键值和一个指向对应数据记录的物理地址的指针, 这样就可以运用二叉查找在一定的复杂度内获取相应的数据,从而快速的检索出符合条件 的记录 除了二叉树还有Btree索引 我平时所说的索引,如果没有特别指定, 都是指B树结构组织的索引 其中聚焦索引,次要索引,复合索引,前缀索引,唯一默认都是B+树索引 除B+树索引之外, 还有哈希索引(Hash index)等
特性:左子树的键值小于根的键值,右子树的键值大于根的键值

平衡多路查找树
特性
m阶B-Tree满足以下条件:
0.根节点至少包括两个孩子 1.树中每个节点最多有m个孩子(m>=2) 2. 除了根节点和叶子节点外,其它每个节点至少有Ceil(m/2)个孩子。 3. 所有叶子节点都在同一层 5. ki(1=1…n)为关键字,且关键字按顺序升序排列k(i-1) < k 8 < 9 6. 关键字的个数n满足:ceil(m/2)-1 <= n <= m-1 (非叶子节点关键字个数比指向孩子的指针少1个) 7. 非叶子结点的指针p[1],p[2],…p[m] 其中p1指向关键字小于k[1]的子树 3 < 8 p[m]指针关键字大于k[m-1]的子树 15 > 12 p[i]指向关键字属于(k[i-1],k[i])的子树 9,10 是位于8 和 12之间
示例图

B+树是B树的变体,基本与B-Tree相同
不同点

create [UNIQUE] index 索引名称 ON 表名(字段(长度))
alter 表名 add [unque] index[索引名称] on(字段(长度))
show index from 表名

drop index[索引名称] on 表名
alter table tab_name add primary key(column_list)
#添加一个主键,索引必须是唯一索引,不能为NULLalter table tab_name add unque index_name(column_list)
# 创建的索引是唯一索引,可以为NULLalter table tab_name add index index_name(column_list)
# 普通索引,索引值可出现多次alter table tab_name add fulltext index_name(column_list)
#全文索引 满足关系数据库的三范式:
1NF
是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值
第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库
2NF
要求数据库表中的每个实例或行必须可以被惟一地区分
设置主键
3NF
要求一个数据库表中不包含已在其它表中已包含的非主关键字信息
两张表不要重复的字段 ,通常都是设置外键大表拆小表,有大数据的列单独拆成小表
在一个数据库中,一般不会设计属性过多的表; 在一个数据库中,一般不会有超过500/1000万数据的表 拆表 有大数据的列单独拆成小表(富文本编辑器,CKeditor);
SQL的执行过程;
查询优化器
写的任何sql,到底是怎么样真正执行的,按照什么条件查询,最后执行的顺序,可能都会有多个执行方案 查询优化器根基对数据表的统计信息(比如索引,有多少条数据),在真正执行一条sql之前,会根据自己内部的数据,进行综合的查询, 根据mysql自身的统计信息, 从多种执行方案当中, 选择一个它认为是最优的执行方案,来去执行
做优化,做什么
做优化, 就是想让查询优化器按照我们的想法,帮我们选择最优的执行方案, 让优化器选择符合程序员计划的执行语句,来减少查询过程中产生的IO

CPU饱和 磁盘I/0读取数据大小 服务器硬件比较底
Explain 查询执行计划
使用explain关键字,可以模拟优化器执行的SQL语句 从而知道MYSQL是如何处理sql语句的 通过Explain可以分析查询语句或表结构的性能瓶颈
作用
查看表的读取顺序 数据读取操作的操作类型 查看哪些索引可以使用 查看哪些索引被实际使用 查看表之间的引用 查看每张表有多少行被优化器执行
使用方法
explain sql语句
select查询的序列号 包含一组数字,表示查询中执行select子句或操作表的顺序
值的三种情况
EXPLAIN SELECT * from employee e,department d,customer c
where e.dep_id = d.id and e.cus_id = c.id;

EXPLAIN SELECT * from department
WHERE id = (SELECT id from employee WHERE id=(SELECT id from customer WHERE id = 1))

如果是子查询,id的序号会递增,id值越大优先级越高,优先被执行

EXPLAIN select * from department d,
(select * from employee group by dep_id) t
where d.id = t.dep_id;
相同,顺序走 不同,看谁大 大的先执行
作用: 查询类型,主要用于区别普通查询,联合查询,子查询等复杂查询 结果值:




显示这一行的数据是关于哪张表的
如果查询是基于分区表的话, 会显示查询访问的分区
访问类型排列
结果值最好到最差









一般来说,保证查询至少达到range级别 最好能达到ref
key与keys主要作用,是查看是否使用了建立的索引, 也即判断索引失效 在建立多个索引 的情况下, mysql最终用到了哪一个索引
possible_keys
显示可能应用在这张表中的索引,一个或者多个
查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用
可能自己创建了4个索引,在执行的时候,可能根据内部的自动判断,只使用了3个实际使用的索引,如果为NULL,则没有使用索引 查询中若使用了覆盖索引 ,则该索引仅出现在key列表中 possible_keys与key关系 理论应该用到哪些索引 实际用到了哪些索引 覆盖索引 查询的字段和建立的字段刚好吻合,这种我们称为覆盖索引

表示索引中使用的字节数,可通过该列计算查询中使用的索引长度 .

索引是否被引入到, 到底引用到了哪几个索引



根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数 每长表有多少行被优化器查询过


满足查询的记录数量的比例,注意是百分比,不是具体记录数 值越大越好,filtered列的值依赖统计信息,并不十分准确
产生的值







建立复合索引(name,age,salary)


如果索引的多列,要遵守最左前缀法则,指的就是从索引的最左列开始 并且不跳过索引中的列
跳过第一个,索引失效

跳过前两个, 索引失效

跳过中间一个 ,只有第一个生效

顺序可以乱,

计算,函数,类型转换 会导致索引失效而转向全表扫描
正常状态

添加了运算

全部使用

使用了范围

等于

不等于




使用%开头

使用%结尾

使用覆盖索引解决两边%




覆盖索引 查询的字段和建立的字段刚好吻合,这种我们称为覆盖索引
索引做为排序时

只用到了 name和age salary是作为排序,而不是查找
使用order by排序时, 如果没有按照索引顺序,会出现Using filesort


当使用*时 order by即使使用了 全部索引,也会也filesort

当索引字段为常量时 可以当作是存在索引的


使用排序一升一降会造成filesort

使用group by时,使用不当, 会出现Using temporary

解决办法和排序一样, 都要按索引顺序进行分组
假设建立复合索引(a,b,c),请说出以下条件是否使用到了索引及使用情况
建立表
DROP TABLE IF EXISTS `testemployee`;
CREATE TABLE `testemployee` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`dep_id` int(11) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`salary` decimal(10,2) DEFAULT NULL,
`cus_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=109 DEFAULT CHARSET=utf8;随机生成字符串
#随机生成一个指定个数的字符串
delimiter $$
create function rand_str(n int) RETURNS VARCHAR(255)
BEGIN
#声明一个str 包含52个字母
DECLARE str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
#记录当前是第几个
DECLARE i int DEFAULT 0;
#生成的结果
DECLARE res_str varchar(255) default '';
while i < n do
set res_str = CONCAT(res_str,substr(str,floor(1+RAND()*52),1));
set i = i + 1;
end while;
RETURN res_str;
end $$
delimiter ;
set global log_bin_trust_function_creators=TRUE批量插入数据
delimiter $$
create procedure insert_emp(in max_num int)
BEGIN
declare i int default 0;
set autocommit = 0;
repeat
set i = i + 1;
insert into testemployee (name,dep_id,age,salary,cus_id) values(rand_str(5),floor(1 + rand()*10),floor(20 + rand()*10),floor(2000 + rand()*10),floor(1 + rand()*10));
until i = max_num
end REPEAT;
commit;
end $$
delimiter ;使用limit 随着offset增大, 查询的速度会越来越慢, 会把前面的数据都取出,找到对应位置
select * from employ e inner join (SELECT id from employ limit 500000 ,10 ) et on e.id = et.id
select * from employee where id >=(SELECT id from employee limit 500000 , 1) limit 10select * from employee where id between 1000000 and 1000100 limit 100;
select * from orders_history where id >= 1000001 limit 100;小表驱动大表,即小的数据集驱动大得数据集
类似嵌套循环
for(int i=5;.......)
{
for(int j=1000;......)
{}
}如果小的循环在外层,对于数据库连接来说就只连接5次,进行1000次操作 如果1000在外,则需要进行1000次数据库连接,从而浪费资源,增加消耗。这就是为什么要小表驱动大表
in与exists
in

exist

当A 表中数据多于 B 表中的数据时,这时我们使用IN 优于 EXISTS 当B表中数据多于 A 表中的数据时,这时我们使用EXISTS 优于 IN 因此是使用IN 还是使用EXISTS 就需要根据我们的需求决定了。但是如果两张表中的数据量差不多时那么是使用IN 还是使用 EXISTS 差别不大 EXISTS 子查询只返回TRUE 或 FALSE ,因此子查询中的SELECT * 可以是SELECT 1 或者其他
概念
锁,在现实生活中是为我们想要隐藏于外界所使用的一种工具 在计算机中,是协调多个进程或线程并发访问某一资源的一种机制 在数据库当中,除了传统的计算资源(CPU、RAM、I/O等等)的争用之外,数据也是一种供许多用户共享访问的资源 如何保证数据并发访问的一致性、有效性,是所有数据库必须解决的一个问题, 锁的冲突也是影响数据库并发访问性能的一个重要因素
举例
在购买商品时, 商品库存只有1个时 ,两个人同时买时, 谁买到的问题 会用到事务, 先从库存表中取出物品的数据, 然后插入订单,付款后,插入付款表信息 更新商品的数量, 在这个过程中, 使用锁可以对有限的资源进行保护,解决隔离和并发的矛盾
偏向MYISAM存储引擎,开销小, 加锁快, 无死锁,锁定粒度大, 发生锁冲突的概率最高, 并发最底 整张表就只能一个人使用
示例 1.建立一张Myisam引擎的表
CREATE TABLE `locktest` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;2.查看表有没有被锁过
show open tables;3.对表加锁
lock table locktest read, locktest2 write;4.对表进行解锁
unlock tables 另个连接
show status like 'table%';Table_locks_immediate 产生表级锁定的次数 表示可以立即获取锁的查询次数
Table_locks_waited: 出现表级锁定争用而发生等待的次数
Myisam的读写锁调度是写优先,这也是myisam不适合做写为主表的引擎 因为写锁后, 其它线程不能做任何操作,大量更新会使用查询很难得到锁, 从而造成永久阻塞
偏向InnoDB存储引擎,开销大, 加锁慢, 会出现死锁;锁定粒度最小, 发生锁冲突的概率最底,并发度也最高
InnoDB与MyISAM的最大不同点:一是支持事务, 二是采用了行级锁
ACID属性
老板要给程序员发工资,程序员的工资是3.6万/月。 但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交, 就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。 但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交 实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。 事务A读到了事务B已修改,但尚未提交的数据
解决办法:Read committed!读提交,能解决脏读问题
程序员拿着工资卡(卡里当然是只有3.6万),当他买单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万, 就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。 当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了。程序员就会很郁闷,明明卡里是有钱的… 一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读 事务A读取到了事务B已经提交的修改数据
解决办法:Repeatable read
程序员拿着工资卡(卡里还是有3.6万),当他买时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。 这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(妻子事务开启) 看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。 当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。
解决办法:Serializable 但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
对应关系
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | |
不可重复读(read-committed) | 否 | 是 | |
可重复读(repeatable-read) | 否 | 否 | |
串行化(serializable) | 否 | 否 |
select @@global.tx_isolation,@@tx_isolation;全局的
set global transaction isolation level read committed; 当前会话
set session transaction isolation level read committed;演示行锁 5.5以后, 默认后, 事务会自动提交
由于演示,开两个连接 都关闭自动提交
set autocommit = 0做以下操作
使用varchar类型时, 没有添加引号, 导致索引失效 就会造成行锁变表锁, 另一个连接更新数据时, 会造成阻塞
概念 当我们使用范围条件,而不是相等条件检索数据,并请求共享或排它锁时, InnoDB会给符合条件的已有数据记录的索引项加锁 对于键值在条件范围内但并不存在的记录 叫做"间隙" InnoDB也会对这个"间隙"加锁,我种锁机制,就是所谓的间隙锁
在查询之后添加for update 其它操作会被阻塞,直到锁定的行提交commit;
show status like 'innodb_row_lock%';就是很悲观,它对于数据被外界修改持保守态度,认为数据随时会修改, 所以整个数据处理中需要将数据加锁。悲观锁一般都是依靠关系数据库提供的锁机制, 事实上关系数据库中的行锁,表锁不论是读写锁都是悲观锁
顾名思义,就是很乐观,每次自己操作数据的时候认为没有人回来修改它,所以不去加锁 但是在更新的时候会去判断在此期间数据有没有被修改,需要用户自己去实现 不会发生并发抢占资源,只有在提交操作的时候检查是否违反数据完整性
对于读操作远多于写操作的时候,大多数都是读取,这时候一个更新操作加锁会阻塞所有读取,降低了吞吐量。 最后还要释放锁,锁是需要一些开销的,我们只要想办法解决极少量的更新操作的同步问题。 换句话说,如果是读写比例差距不是非常大或者你的系统没有响应不及时,吞吐量瓶颈问题,那就不要去使用乐观锁,它增加了复杂度,也带来了额外的风险。

