存储引擎
MySQL体系结构
- 连接层:
- 最上层是一些客户端和连接服务,主要完成一些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
- 服务层:
- 第二层架构主要完成大多数的核心服务功能,如SQL接口,经完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现。如 过程、函数等。
- 引擎层:
- 存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以感觉自己的需要,来选去合适的存储引擎。
- 存储层:
- 主要是将数据存储在文件系统之上,并完成与存储引擎的交互。
存储引擎简介
存储引擎就是存储数据、建立索引、更新、查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,所有存储引擎又称为表类型。
-- -------------------------------------------------进阶
-- 查询创建表语句
show create table dept;
-- 查询支持的存储引擎
show engines ;
-- 创建一个my_MyISAMy表,用MyISAM引擎
create table my_MyISAM(
id int,
name varchar(10)
)engine=MyISAM;
存储引擎特点
- InnoDB
- 介绍
- InnoDB是一种兼顾可靠性和高性能的通用存储引擎,在MySQL5.5之后,InnoDB是默认的MySQL存储引擎。
- 特点
- DML操作遵循ACID模型,支持事务;
- 行级锁,提高并发访问性能
- 支持外键约束,保证数据的完整性和正确性
- 文件
- xxx.ibd:xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文还能,存储该表的表结构、数据和索引
- 参数是:innodb_file_per_table
- 介绍
- MyISAM
- 介绍
- MyISAM是MySQL早期的默认存储引擎。
- 特点
- 不支持事务,不支持外键
- 支持表锁,不支持行锁
- 访问速度快
- 文件
- xxx.sdi:存储表结构信息
- xxx.MYD:存储数据
- xxx.MYI:存储索引
- 介绍
- Memory
- 介绍:
- Memory引擎的表数据是存储在内存中,由于受到硬件的问题或断电问题的影响,只能做为临时表或缓存使用
- 特点:
- 内存存放
- hash索引(默认)
- 文件:
- xxx.sdi:存储表结构信息
- 介绍:
存储引擎选择
在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组 合。
- lnnoDB:是Mysql的默认存储引擎,支持事务、外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致 性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。
- MylISAM:如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那 么选择这个存储引擎是非常合适的。
- MEMORY:将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。MEMORY的缺陷就是对表的大小有限制,太大的表 无法缓存在内存中,而且无法保障数据的安全性。
小结
索引
介绍
索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些 数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
优缺点
优势
- 提高数据检索的效率,降低数据库的I0成本
- 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消 耗。
劣势
- 索引列也是要占用空间的。
- 索引大大提高了查询效率,同时却也降低更新表的速度,如对表进 行INSERT、UPDATE、DELETE时,效率降低。
索引结构
MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种:
索引结构 | 描述 |
B+Tree索引 | 最常见的索引类型,大部分引擎都支持B+树索引 |
Hash索引 | 底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询 |
R-tree(空间索引) | 空间索引是MylSAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少 |
Full-text(全文索引) | 是一种通过建立倒排索引,快速匹配文档的方式。类似于Lucene,Solr,ES |
我们平常所说的索引,如果没有特别指明,都是指B+树结构组织的索引。
索引 | InnoDB | MyISAM | Memory |
B+tree索引 | 支持 | 支持 | 支持 |
Hash索引 | 不支持 | 不支持 | 支持 |
R-tree索引 | 不支持 | 支持 | 不支持 |
Full-text | 5.6版本之后支持 | 支持 | 不支持 |
B-Tree(多路平衡查找树)
以一颗最大度数(max-degree)为5(5阶)的b-tree为例(每个节点最多存储4个key,5个指针):
具体动态变化的过程可以参考网站:https://www.cs.usfca.edu/~galles/visualization/BTree.html
B+Tree
相对于B-Tree区别:
- ①.所有的数据都会出现在叶子节点
- ②.叶子节点形成一个单向链表
MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。
Hash
哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。 如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),可以通过链表来解决。
Hash索引特点
- Hash索引只能用于对等比较(=,in),不支持范围查询(between,>,<,.)
- 无法利用索引完成排序操作
- 查询效率高,通常只需要一次检索就可以了,效率通常要高于B+tree索引
存储引擎支持
在MySQL中,支持hash索引的是Memory引擎,而InnoDB中具有自适应hash功能,hash索引是存储引擎根据B+Tree索引在指定条件下自动构建的。
为什么InnoDB存储引擎选择使用B+tree索引结构?
- 相对于二叉树,层级更少,搜索效率高;
- 对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一 页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的 高度,导致性能降低;
- 相对Hash索引,B+tree支持范围匹配及排序操作;
索引分类
在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:
聚集索引选取规则:
- 如果存在主键,主键索引就是聚集索引。
- 如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引。
- 如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引。
回表查询:先在二级索引中找到对应的主键值,再到聚集索引中拿到这一行的行数据。
索引语法
创建索引
create [unique|fulltext] index index_name on table_name(index_col_name);
查看索引
show index from table_name;
删除索引
drop index index_name on table_name
SQL性能分析工具
SQL执行频率
MySQL客户端连接成功后,通过show[sessionlglobal] status命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的 INSERT、UPDATE、DELETE、SELECT的访问频次:
show global status like 'Com_______';
慢查询日志
慢查询日志记录了所有执行时间超过指定参数(long_query_time, 单位:秒,默认10秒)的所有SQL语句的日志。
查询慢查询日志状态:
show variables like 'slow_query_log';
MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:
#开启MySQL慢日志查询开关
slow_query_log=1
#设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2
profile详情
执行一系列的业务SQL的操作,然后通过如下指令查看指令的执行耗时:
#查看每一条SQL的耗时基本情况
show profiles;
#查看指定query_id的SQL语句各个阶段的耗时情况
show profile for query query_id;
#查看指定query_id的SQL语句CPU的使用情况
show profile cpu for query query_id;
explain执行计划
EXPLAIN 或者 DESC命令获取 MySQL如何执行SELECT语句的信息,包括在SELECT语句执行过程中表如何连接和连接的顺序。
语法:
#直接在select语句之前加上关键字explain/ desc
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件
EXPLAIN 执行计划各字段含义:
- ld select查询的序列号,表示查询中执行select子句或者是操作表的顺序(id相同,执行顺序从上到下;id不同,值越大,越先执行)。
- select_type 表示SELECT的类型,常见的取值有SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、 UNION(UNION中的第二个或者后面的查询语句)、SUBQUERY(SELECT/WHERE之后包含了子查询)等
- type 表示连接类型,性能由好到差的连接类型为NULL、system、const、eq_ref、ref、range、index、all。
- possible_key 显示可能应用在这张表上的索引,一个或多个。
- Key 实际使用的索引,如果为NULL,则没有使用索引。
- Key_len 表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好。
- rows MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是准确的。
- filtered 表示返回结果的行数占需读取行数的百分比,filtered的值越大越好。
SQL优化
视图/存储过程/触发器
视图
介绍
视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。
通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。
语法
创建
CREATE [OR REPLACE] VIEW 视图名称[(列名列表)]AS SELECT语句[WITH[CASCADED|LOCAL]CHECK OPTION]
查询
查看创建视图语句:SHOW CREATE VIEW 视图名称;
查看视图数据:SELECT*FROM 视图名称.…;
修改
方式一:CREATE OR REPLACE VIEW 视图名称[(列名列表)]AS SELECT语句[WITH[CASCADED|LOCAL]CHECK OPTION]
方式二:ALTER VIEW 视图名称[(列名列表)]AS SELECT语句[WITH[CASCADED|LOCAL]CHECK OPTION]
删除
DROP VIEW [IF EXISTS] 视图名称[视图名称]…
视图的检查选项
当使用WITH CHECK OPTION子句创建视图时,MySQL会通过视图检查正在更改的每个行,例如插入,更新,删除,以使其符合视图的定 义。
MySQL允许基于另一个视图创建视图,它还会检查依赖视图中的规则以保持一致性。为了确定检查的范围,mysql提供了两个选项: CASCADED和LOCAL,默认值为CASCADED。 CASCADED :
create or replace view stu_v1 as select sid, sname from student where sid<20;
select * from stu_v1;
insert into stu_v1 values (21,'hi');-- 插入成功
insert into stu_v1 values (19,'ho');-- 插入成功,不会检查增删改操作
create or replace view stu_v3 as select sid, sname from stu_v1 where sid>5 with cascaded check option ;
select * from stu_v3;
insert into stu_v3 values (21,'hii');-- 插入失败
insert into stu_v3 values (3,'hoo');-- 插入失败
insert into stu_v3 values (11,'hio');-- 插入成功
create or replace view stu_v5 as select sid, sname from stu_v3 where sid<=15;
insert into stu_v5 values (17,'hii');-- 插入成功
insert into stu_v5 values (21,'hoo');-- 插入失败
insert into stu_v5 values (5,'hio');-- 插入失败
创建视图时加with cascaded(级联的) check option的话,会检查当前视图还会检查当前视图所以依赖的所有视图它的条件是否满足;
local
create or replace view stu_v2 as select sid, sname from student where sid<20;
select * from stu_v2;
insert into stu_v2 values (21,'hi');-- 插入成功,不满足v2
insert into stu_v2 values (19,'ho');-- 插入成功,不会检查增删改操作
create or replace view stu_v4 as select sid, sname from stu_v2 where sid>5 with local check option ;
select * from stu_v4;
insert into stu_v4 values (21,'hii');-- 插入成功,不满足v2
insert into stu_v4 values (3,'hoo');-- 插入失败,不满足v4
insert into stu_v4 values (11,'hio');-- 插入成功
create or replace view stu_v6 as select sid, sname from stu_v4 where sid<=15;
insert into stu_v6 values (17,'hii');-- 插入成功,不满足v6
insert into stu_v6 values (21,'hoo');-- 插入成功,不满足v2
insert into stu_v6 values (5,'hio');-- 插入失败,不满足v4
定义视图时,有检查条件,就进行检查,没有就不进行检查;递归的检查依赖
视图的更新
要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。
如果视图包含以下任何一项,则该视图不可更新:
1.聚合函数或窗口函数(SUM()、MIN()、MAX()、COUNT()等)
2. DISTINCT Aae
3. GROUP BY
4. HAVING
5.UNION或者UNION ALL
作用
- 简单
- 视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视图,从而使得用户不必为以后的操作 每次指定全部的条件。
- 安全
- 数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据
- 数据独立
- 视图可帮助用户屏蔽真实表结构变化带来的影响。
触发器
介绍
触发器是与表有关的数据库对象,指在insert/update/delete之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特 性可以协助应用在数据库端确保数据的完整性,日志记录,数据校验等操作。
使用别名OLD和NEW来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还只支持行级触发,不支持语句级触发。
语法
创建
CREATE TRIGGER trigger_name
BEFORE/AFTER INSERT/UPDATE/DELETE
ON tbl name FOR EACH ROW --行级触发器
BEGIN
trigger_stmt ;
END;
查看
SHOW TRIGGERS ;
删除
DROP TRIGGER [schema_name.]trigger_name;-如果没有指定schema_name,默认为当前数据库。
-- 准备工作:日志表
create table emoloyee_logs(
id int(11) not null auto_increment primary key ,
operate varchar(20) not null ,
operate_time datetime not null,
operate_id int(11) not null ,
operate_params varchar(500)
);
-- 插入数据触发器
create trigger tb_employee_insert_trigger
after insert on employee for each row
begin
insert into emoloyee_logs(id,operate,operate_time,operate_id,operate_params) values
(null,'insert',now(),new.eno,concat('插入的数为:id',new.eno));
end
-- 查看触发器
show triggers ;
-- 插入数据
insert into employee values (7,'小2','男','项目部',1500);
-- 修改数据的触发器
create trigger tb_employee_update_trigger
after update on employee for each row
begin
insert into emoloyee_logs(id,operate,operate_time,operate_id,operate_params) values
(null,'update',now(),new.eno,
concat('更新之前的数为:name:',old.ename,'| 更新之后的数:name:',new.ename));
end
-- 更新数据
update employee set ename='张1' where employee.eno<3;
-- 删除数据的触发器
create trigger tb_employee_delete_trigger
after delete on employee for each row
begin
insert into emoloyee_logs(id,operate,operate_time,operate_id,operate_params) values
(null,'delete',now(),old.eno,
concat('删除之前的数为:name:',old.ename));
end
-- 删除数据
delete from employee where eno=6;
案例1:
create trigger sc_update_trigger
before update on sc for each row
begin
if old.grade-new.grade>=10 then
insert into sc_G values(new.sno,new.cno,old.grade,now());
end if;
end;
-- ----
show triggers ;
-- 更新
update sc set grade=grade-30 where sno='10060101'&&cno='07294001';
update sc set grade=grade-5 where sno='10060102'&&cno='07294002';
update sc set grade=grade-10 where sno='10060103'&&cno='07294002';
-- ----------------------------------------2
create table avg_sno(
sno varchar(20) not null ,
avg_sno double comment '平均成绩'
);
-- 触发器
create trigger avg_sno_trigger
after update on sc for each row
begin
insert into avg_sno values(NEW.sno,(select avg(new.grade) from sc));
end;
-- ------
update sc set grade=grade-20 where sno='10060101'&&cno='07294001';
update sc set grade=grade-5 where sno='10060102'&&cno='07294002';
update sc set grade=grade-10 where sno='10060103'&&cno='07294002';