存储引擎
解释
存储引擎层, 存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。数据库中的索引是在存储引擎层实现的。
基础使用
查询建表语句 — 默认存储引擎
show create table account; ##查看account表默认引擎
建表时指定存储引擎
CREATE TABLE 表名(
字段1 字段1类型 [ COMMENT 字段1注释 ] ,
......
字段n 字段n类型 [COMMENT 字段n注释 ]
) ENGINE = INNODB [ COMMENT 表注释 ] ;
查询当前数据库支持的存储引擎
show engines;
实例
一是实例,也是为后面介绍做铺垫
创建表 my_memory , 指定Memory存储引擎
create table my_memory(
id int,
name varchar(10)
) engine = Memory;
创建表 my_myisam , 并指定MyISAM存储引擎
create table my_myisam(
id int,
name varchar(10)
) engine = MyISAM;
InnoDB
InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在 MySQL 5.5 之后,InnoDB是默认的 MySQL 存储引擎
特点
DML操作遵循ACID模型,支持事务
行级锁,提高并发访问性能
支持外键FOREIGN KEY约束,保证数据的完整性和正确性
文件
xxx.ibd:xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm-早期的 、sdi-新版的)、数据和索引
参数
innodb_file_per_table
查看参数值,系统变量,查看是否开启 值为 on
show variables like 'innodb_file_per_table';
找到你创建的库——表 ——进入找到你创建的表,若存储引擎为innodb,则ibd后缀结尾
可以看到里面有很多的ibd文件,每一个ibd文件就对应一张表,比如:我们有一张表 account,就有这样的一个account.ibd文件,而在这个ibd文件中不仅存放表结构、数据,还会存放该表对应的索引信息。 而该文件是基于二进制存储的,不能直接基于记事本打开,我们可以使用mysql提供的一个指令 ibd2sdi ,通过该指令就可以从ibd文件中提取sdi信息,而sdi数据字典信息中就包含该表
Memory
介绍
Memory引擎的表数据时存储在内存中的,由于受到硬件问题、或断电问题的影响,只能将这些表作为临时表或缓存使用
特点
内存存放
hash索引(默认)
文件
xxx.sdi:存储表结构信息
MyISAM
介绍
MyISAM是MySQL早期的默认存储引擎。
特点
不支持事务,不支持外键
支持表锁,不支持行锁
访问速度快
文件
xxx.sdi:存储表结构信息
xxx.MYD: 存储数据
xxx.MYI: 存储索引
区别以及特点
特点 | InnoDB | MyISAM | Memory |
存储限制 | 64TB | 有 | 有 |
事务安全 | 支持 | / | / |
锁机制 | 支持 | 表锁 | 表锁 |
B+tree索引 | 支持 | 支持 | 支持 |
Hash索引 | / | / | 支持 |
全文索引 | 支持(5.6版本之后) | 支持 | / |
空间使用 | 高 | 低 | N/A |
内存使用 | 高 | 低 | 中等 |
批量插入速度 | 低 | 高 | 高 |
支持外键 | 支持 | / | / |
面试题
面试题
InnoDB引擎与MyISAM引擎的区别 ?
①. InnoDB引擎, 支持事务, 而MyISAM不支持。
②. InnoDB引擎, 支持行锁和表锁, 而MyISAM仅支持表锁, 不支持行锁。
③. InnoDB引擎, 支持外键, 而MyISAM是不支持的。
索引
提示:
若你只是为了学习mysql的索引,建议从索引的分类看起,前面的只是我深挖
索引概述
介绍
索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外,数据库系统还维护着满足 特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这样就可以在这些数据结构 上实现高级查找算法,这种数据结构就是索引
演示
例如我们需要使用SQL语句为:
select * from user where age = 45
那么数据的查询就要从第一行开始,一直扫描到最后一行,我们称之为 全表扫描,性能很低。
而当我们有了索引,假设索引结构就是二叉树,那么也就意味着,会对age这个字段建 立一个二叉树的索引结构
此时我们在进行查询时,只需要扫描三次就可以找到数据了,极大的提高的查询的效率
特点
优势 | 劣势 |
提高数据检索的效率,降低数据库 的IO成本 | 索引列也是要占用空间的 |
通过索引列对数据进行排序,降低 数据排序的成本,降低CPU的消 耗 | 索引大大提高了查询效率,同时却也降低更新表的速度, 如对表进行INSERT、UPDATE、DELETE时,效率降低 |
索引结构
概述
MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的索引结构
索引 | InnoDB | MyISAM | Memory |
B+tree索引 | 支持 | 支持 | 支持 |
Hash 索引 | 不支持 | 不支持 | 支持 |
R-tree 索引 | 不支持 | 支持 | 不支持 |
Full-text | 5.6版本后支持 | 支持 | 不支持 |
此处介绍二叉树,红黑树,btree,b+tree,hash结构,只说明在数据库中的使用方式,不介绍原理,若想要了解树结构,可以查看:数据结构
二叉树
这里假如MySQL的索引结构采用二叉树的数据结构,比较理想的结构如下:
需要注意的是,插入的数据是不是按照大小顺序插入排序的(比较理想的结构)
插入数据依次为:36,22,33,48,53,19,45,,17,23,20
查看上图,我们发现,若想要通过索引找到45,使用二叉树,第三次36——48——45,即可找到45,但是,若不使用索引,你需要7次才能查到
若不是理想结构呢?
上述图,符合二叉表的使用规范,但不是这里指定的理想结构,它插入的时候是按照大小顺序插入的,最终成为了单链表,最终索引的时候和不使用索引无异
所以,我们可以得到总结,如果选择二叉树作为索引结构,会存在以下缺点:
顺序插入时,会形成一个链表,查询性能大大降低。
大数据量情况下,层级较深,检索速度慢
红黑树
那么此处我们就会想到红黑树,因为红黑树是一颗自平衡二叉树,即使是顺序插入数 据,最终形成的数据结构也是一颗平衡的二叉树
这里使用一样的数据:36,22,33,48,53,19,45,,17,23,20,考虑大小顺序顺序插入
但是,即使如此,由于红黑树也是一颗二叉树,所以也会存在一个缺点:
大数据量情况下,层级较深,检索速度慢。
最后,我们得到:
在MySQL的索引结构中,并没有选择二叉树或者红黑树,而选择的是B+Tree
B-Tree
简介
B-Tree,B树是一种多叉路衡查找树,相对于二叉树,B树每个节点可以有多个分支,即多叉。
以一颗最大度数(max-degree)为5(5阶)的b-tree为例,那这个B树每个节点最多存储4个key,5个指针。
同理,以最大度数为4(4阶),则该b树每个节点最多储存3个key,4个指针。
特点
5阶的B树,每一个节点最多存储4个key,对应5个指针
一旦节点存储的key数量到达5,就会裂变,中间元素向上分裂
在B树中,非叶子节点和叶子节点都会存放数据
B+Tree
B+Tree是B-Tree的变种,我们以一颗最大度数(max-degree)为4(4阶)的b+tree为例,来看一 下其结构示意图
特点:
绿色框框起来的部分,是索引部分,仅仅起到索引数据的作用,不存储数据
红色框框起来的部分,是数据存储部分,在其叶子节点中要存储具体的数据
最终我们看到,B+Tree 与 B-Tree相比,主要有以下三点区别
B+tree所有的数据都会出现在叶子节点
B+tree叶子节点形成一个单向链表
B+tree非叶子节点仅仅起到索引数据作用,具体的数据都是在叶子节点存放的
mysql标准化
上述我们所看到的结构是标准的B+Tree的数据结构,再来看看MySQL中优化之后的B+Tree
MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点 的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能,利于排序
Hash
结构
哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在
hash表中
但是,如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),这个问题可以通过链表来解决,如红黑树
特点
Hash索引只能用于对等比较(=,in),不支持范围查询(between,>,< ,…)
无法利用索引完成排序操作
查询效率高,通常(不存在hash冲突的情况)只需要一次检索就可以了,效率通常要高于B+tree索引
存储引擎支持
在MySQL中,支持hash索引的是Memory存储引擎,而在InnoDB中,有自适应hash索引的功能,hash索引是InnoDB存储引擎根据B+Tree索引在指定条件下自动构建的
面试题
为什么InnoDB存储引擎选择使用B+tree索引结构?
相对于二叉树,层级更少,搜索效率高;
对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储 的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低;
相对Hash索引,B+tree支持范围匹配及排序操作
索引分类
索引分类
在MySQL数据库,将索引的具体类型主要分为以下几类:主键索引、唯一索引、常规索引、全文索引
分类 | 含义 | 特点 | 关键词 |
主键索引 | 针对于表中主键创建的索引 | 默认自动创建,最多只有一个 | primary |
唯一索引 | 避免同一个表中某数据列中的值重复 | 可以有多个 | unique |
常规索引 | 快速定位特定数据 | 可以有多个 | |
全文索引 | 全文索引查找的是文本中的关键词,而不是比 较索引中的值 | 可以有多个 | fulltext |
聚集索引&二级索引
在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两
分类 | 含义 | 特点 |
聚集索引(Clustered Index) | 将数据存储与索引放到了一块,索引结构的叶子 节点保存了行数据 | 必须有,而且只 有一个 |
二级索引(Secondary Index) | 将数据与索引分开存储,索引结构的叶子节点关 联的是对应的主键 | 可以存在多个 |
聚集索引选取规则
如果存在主键,主键索引就是聚集索引。
如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引。
如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引
聚集索引和二级索引的具体结构,采用的是B+tree
聚集索引的叶子节点下挂的是这一行的数据 ,如主键值为5,包含:id,name,gender
二级索引的叶子节点下挂的是该字段值对应的主键值,只包含主键值
例如,当我们执行此SQL语句时,其过程为
select * from user where name =’Arm’
由于是根据name字段进行查询,所以先根据name=’Arm’到name字段的二级索引中进行匹配查 找。但是在二级索引中只能查找到 Arm 对应的主键值 10.此处我们需要进行回表查询,回到聚集索引.
由于查询返回的数据是*,所以此时,还需要根据主键值10,到聚集索引中查找10对应的记录,最终找到10对应的行row,最后得到改行的全部数据.
回表查询
这种先到二级索引中查找数据,找到主键值,然后再到聚集索引中根据主键值,获取 数据的方式,就称之为回表查询
索引语法
创建索引
create [ unique | fulltext ] index index_name on table_name (
index_col_name,... );
查看索引
show index from table_name;
删除索引
drop index index_name on table_name;
实例
例如现在有一张表名为tb_user,其字段分别有:name, phone, email, profession, age, gender, status, createtime
那么我们需要完成以下需求:
1.name字段为姓名字段,该字段的值可能会重复,为该字段创建索引
create index index_name on tb_user(name)
2.phone手机号字段的值,是非空,且唯一的,为该字段创建唯一索引
create unique index index_phone on tb_user(phone)
3.为profession、age、status创建联合索引
create index index_profession_age_status on tb_user(profession,age,status)
4.为email建立合适的索引来提升查询效率
create index index_email on tb_user(email);
索引使用
验证索引效率
select * from tb_sku where id = 1\G;
类似界面局部优化,只能mysql服务端使用,图形化界面用不了
当你使用原表查询1000W条数据的时候,你会发现直接使用查询语句,查询的非常缓慢,这个时候就需要建立索引,当你不建表查询,可能需要40s,当你建表后查询,可能只需要1秒。
最左前缀法则
法则
如果索引了多列(联合索引),要遵守最左前缀法则。
最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将会该部分失效(后面的字段索引失效):例如profession,age,status,跳过age,索引profession,status,就不会索引age部分,即该部分失效,跳过status部分同样道理,但是无论如何,都必须要有profession索引字段,即最左列。
通过实例演示其特点
在 tb_user 表中,有一个联合索引,这个联合索引涉及到三个字段,顺序分别为:profession,
age,status,通过下图,我们可以看到查询的key_len值为54,,使用了‘index_profession_age_status’这个联合索引。,不一一在下面展示,profession的key值为47,age为2,status为5,
’
最左列,不意味着在最左边,最左列在最右边也可以,可以交换位置,但是必须有该字段
范围查询
联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。此处age是范围查询:age>20,所以status索引失效
而在顺序颠倒后,如下图,那么范围查询左侧列索引失效
而当使用>= 或者<= 的时候,无论最左列在左在右,索引都不失效。所以,在业务允许的情况下,尽可能的使用类似于 >= 或 <= 这类的范围查询,而避免使用 > 或 <
索引失效情况
在索引列上进行运算操作, 索引将失效
当dcl根据phone字段进行等值查询,索引正常
当dcl根据phone字段进行函数运行,索引失效,这里以截取为例,可见索引失效
字符串不加引号
当索引值为字符串,但是字符串的值没有加引号,索引也会失效,看图可知,key_len只有49,得出status的索引失效了
如果字符串不加单引号,对于查询结果,没什么影响,但是数据库存在隐式类型转换,索引将失效
模糊查询
如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效
从以下三个例子展示:’软件工程’
‘软件%’
‘%工程’
‘%工%’
在like模糊查询中,在关键字后面加%,索引可以生效。而如果在关键字前面加了%,索引将会失效,而前后都加了%依然失效
or连接条件
用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到
从下图可得,id为主键索引,但是age无索引,所以索引失效
数据分布影响
在mysql评估使用索引比全表更慢,则不使用索引,通常在小表内
因为MySQL在查询时,会评估使用索引的效率与走全表扫描的效率,如果走全表扫描更快,则放弃索引,走全表扫描。 因为索引是用来索引少量数据的,如果通过索引查询返回大批量的数据,则还不如走全表扫描来的快,此时索引就会失效,例如想要查看表的全部数据,那么肯定是选择全表扫描,
这里以null,not null为例,若设置表的profession值为null,profession的联合索引还在,那么我在此处查询profession的数据是否为null,那么就会使用全表扫描,而不是索引,相反,若我查询profession的值是否为not null,那么就会使用索引
SQL提示
SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的
例如我们为profession创建单索引,这时候profession就有两个索引了,一个是‘tb_user_profession’的单索引,一个是’tb_user_profession_age_status’的联合索引(复合索引),而数据库会默认选择最优的一个索引,但是我们可以指定索引类型
explain select * from tb_user use index(索引类型) where 条件; ##仅仅建议,mysql还会自行评估
explain select * from tb_user ignore index(索引类型) where 条件 ##忽略指定索引,即不用某个索引
explain select * from tb_user enforce index(索引类型) where 条件 ##强制指定使用索引
覆盖索引
尽量使用覆盖索引,减少select *。 那么什么是覆盖索引呢? 覆盖索引是指查询使用了索引,并且该索引需要返回的列,在该索引中已经全部能够找到
如select id, profession:通过二级查询profession找到了id值,覆盖查询
如select id,profession,age, status,通过二级profession联合查询,找到了profession,age, status,覆盖查询找到id
但是如select id,profession,age, status,name,此处通过二级查询profession,找到profession,age, status,通过二级查询找到了主键索引id,但是并没有在该索引中找到全部值,进行回表查询至聚合查询,最后查询到name
注意,覆盖查询时,若有多个索引,主键默认为聚合查询,二级查询根据mysql自行优化选择最优,若通过输入命令,其实可以看到上述三条区别在于extra
extra | 含义 |
Using where; UsingIndex | 查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据 |
Using indexcondition | 查找使用了索引,但是需要回表查询数据 |
前缀索引
当字段类型为字符串(varchar,text,longtext等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO, 影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率
前缀长度
那么如何选择前缀长度,可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高, 唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的
select count(distinct 字段名) / count(*) from 表名 ;
select count(distinct substring(字段名,1,5)) / count(*) from 表名r ;
创建前缀索引
create index 索引名 on 表名(字段(前缀长度)) ;
前缀索引的查询流程
单列索引与联合索引
单列索引:即一个索引只包含单个列
联合索引:即一个索引包含了多个列
当我们使用单列索引查询的时候,若同时查询age,status两个字段,假设其为单列索引,此时mysql会选择最优的一个进行查询,但是最后都需要进行回表查询
而当我们使用联合索引的时候,若查询的字段为联合索引内包含的字段,那么就会覆盖索引,不需要回表查询
联合查询图示
索引设计原则
1.针对于数据量较大,且查询比较频繁的表建立索引
2. 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引
3. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
4. 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引
5. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
6. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率
7. 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含
NULL值时,它可以更好地确定哪个索引最有效地用于查询。
SQL性能优化
SQL执行频率
MySQL 客户端连接成功后,通过 show [session|global] status 命令可以提供服务器状态信 息。通过如下指令,可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次
show [session|global] status like 'Com_______'; ##七个下划线
session:局部 global:全局 'Com_' 固定格式,正则表达式,查询符合此格式的全部数据
部分数据解释
Com_delete: 删除次数
Com_insert: 插入次数
Com_select: 查询次数
Com_update: 更新次数
注意:通过上述指令,我们可以查看到当前数据库到底是以查询为主,还是以增删改为主,从而为数据 库优化提供参考依据。 如果是以增删改为主,我们可以考虑不对其进行索引的优化。 如果是以 查询为主,那么就要考虑对数据库的索引进行优化了
慢查询日志
我们已经知道了在查询的时候,最好对数据库的索引进行优化,慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志
MySQL的慢查询日志默认没有开启,我们可以查看一下系统变量 slow_query_log
show variables like 'slow_query_log'
## 若为off需要进行下面操作,若为on则已开启
然后再配置文件配置以下内容:window在my.ini,linux在mysql.conf
# 开启MySQL慢日志查询开关
slow_query_log=1
# 设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2
查询日志:
tail -f /var/lib/mysql/localhost-slow.log
profile详情
show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去
查看当前版本能否支持profiling
select @@have_profiling;
查看当前profile级别
select @@profiling;
设置profile级别
set profiling = 1;
通过如下指令查看指令的执行耗时
-- 查看每一条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可以用来查看在mysql中select如何执行select语句,以及包括在select语句执行 过程中表如何连接和连接的顺序
-- 直接在select语句之前加上关键字 explain / desc
explain select 字段列表 from 表名 where 条件:
Explain 执行计划中各个字段的含义
字段 | 含义 |
id | 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 的值越大越好 |
extra | 额外的内容 |
视图
含义
视图(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) (视图名称)
视图内的dml语句
上面演示了视图的创建,修改,删除,其实在视图内也可以进行数据的增删改,但是这些增删改,实际上是作用到视图创建所使用的表上的,比如我们是根据account表创建的视图,那么增删改最终是作用于account表上的
实例:
选择根据account表创建的aacount_a视图,
在视图内增加数据
最终作用于account表内,account_a视图中理论上也会有,但是前提是在给的where条件内
但是此时,使用查询语句select * from account_a,是查不到 ‘’6,李琪琪‘’的,是因为我在创建该视图的时候指定了id< 5,指定了规则,所以查询不到,但是在基表内师存在的,而在视图内不存在
检查选项
含义
当使用with check option子句创建视图时,MySQL会通过视图检查正在更改的每个行,例如 插 入,更新,删除,以使其符合视图的定义。 MySQL允许基于另一个视图创建视图,它还会检查依赖视 图中的规则以保持一致性。为了确定检查的范围,mysql提供了两个选项:cascaded和 local,默认值为 cascaded
语法
create or replace view (视图名) as select语句 with cascaded / local check option
##with cascaded / local check option 必带
cascaded
级连
比如,v2视图是基于v1视图的,如果在v2视图创建的时候指定了检查选项为 cascaded,但是v1视图 创建时未指定检查选项。 则在执行检查时,不仅会检查v2,还会级联检查v2的关联视图v1
案例:
在此案例中,account_a2 添加了cascaded检查语句,cascaded检查语句具有继承性,如本案例在account_a2中添加了,那么account_a1中也会有该检查语句
根据insert into 语句执行结果来看:
(15,’李双双’):该语句执行成功,因为在account_a2索引中,它15处于交集状态20<=15<=10,执行成功,且在account_a2视图中有该数据
(25,’俄珞狮娜娜’):该语句执行失败,在account_a2中执行成功,但是在基表account中失败,因为cascaded的继承性,account_a1中具有检查语句,25>20最终执行失败
(30,‘开心姐’):该语句执行失败,因为处于三个视图限度值之外,不多做解释
(17,’张兵’):该语句执行成功,因为a3没有使用检查语句,所以开始成功的,除了a1,a2限值之类,在a1,a2视图内存在,但并不存在于a3中,因为不在该限值之类
(14,’王琨’):该语句执行成功,处于a1,a2,a3阈值之类,会存在于三个视图内,在a3中,处于该阈值内,存在于该视图内,在a1,a2中,符合检查语句,存在于a1,a2,且不会报错
local
本地
比如,v2视图是基于v1视图的,如果在v2视图创建的时候指定了检查选项为 local ,但是v1视图创 建时未指定检查选项。 则在执行检查时,知会检查v2,不会检查v2的关联视图v1
实例:
同样的数据,更改检查语句为’with local check option’,local 检查不会继承,即不会向上至a4
同样以添加数据语句来看:
李双双: 执行成功,处于a4,a5交集
俄珞狮娜娜:执行成功,因为只有a5有检查语句,而刚好处于检查语句内,local不会向上继承,所以此处成功,但是只会在基表有数据
开心姐:执行成功,但是不会再a4,a5,a6任何一个视图内,而会存在于基表中,
张兵:执行成功,但是不会存在于a6,只会存在于a4,a5
王琨:执行成功,会存在于a6,a5,a4
cascaded与local的区别:
cascaded会向上继承其检查语句,而local不会向上继承其检查语句,但并不意味着不管上面语句的限值,如where<20,只是不会添加检查语句,当不满足其条件属性,不会报错而已
视图的更新
要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。如果视图包含以下任何一 项,则该视图不可更新:
A. 聚合函数或窗口函数(SUM()、 MIN()、 MAX()、 COUNT()等)
B. DISTINCT
C. GROUP BY
D. HAVING
E. UNION 或者 UNION ALL
只有一个单行单列的数据,如果我们对这个视图进行更新或插入的,将会报错
视图作用
1.简单: 视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些被经常使用的查询可以被定义为视 图,从而使得用户不必为以后的操作每次指定全部的条件。
2. 安全 :数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据
3.数据独立: 视图可帮助用户屏蔽真实表结构变化带来的影响。
存储过程
概述
介绍
存储过程是事先经过编译并存储在数据库中的一段SQL 语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。存储过程思想上很简单,就是数据库SQL 语言层面的代码封装与重用。
特点
封装,复用 ———————–> 可以把某一业务SQL封装在存储过程中,需要用到的时候直接调用即可
可以接收参数,也可以返回数据 ——–> 再存储过程中,可以传递参数,也可以接收返回值
减少网络交互,效率提升 ————-> 如果涉及到多条SQL,每执行一次都是一次网络传输。而如果封装在存储过程中,我们只需要网络交互一次可能就可以了
基本语法
修改结束符号
delimiter +符号
如: 结束符修改为’$$’ 后无法再次通过 ‘;’执行命令
创建
create procedure 存储过程名称(参数列表)
begin
--sql语句;
end;
##在类似datagrip的可视化图形界面应用上,命令行需要修改结束符
例如:创建p1存储过程
调用
call 存储过程名称(参数);
查看
select * from INFORMATION_`SCHEMA.ROUTINES where ROUTINE_SCHEMA='xxx'; ##查询指定数据库的存储过程及状态信息
show create procedure 存储过程名称 ##查询某个存储过程的定义
删除
drop procedure if exists + 存储过程名称
在命令行中,执行创建存储过程的SQL时,需要通过关键字delimiter 指定SQL语句的结束符。
变量
在MySQL中变量分为三种类型: 系统变量、用户定义变量、局部变量。
系统变量
系统变量是MySQL服务器提供,不是用户定义的,属于服务器层面。分为全局变量(GLOBAL)、会话
变量(SESSION)
查看系统变量
show global| session variables;
show global| session variables like '' ##模糊查询法
select @@(session|global). 系统变量名
注意
如果没有指定SESSION/GLOBAL,默认是SESSION,会话变量
mysql服务重新启动之后,所设置的全局参数会失效,要想不失效,可以在/etc/my.cnf中配置
A. 全局变量(GLOBAL): 全局变量针对于所有的会话
B. 会话变量(SESSION): 会话变量针对于单个会话,在另外一个会话窗口就不生效了
设置系统变量
set global|session 系统变量名 = 值 ;
select @@(global|session) .系统变量名= 值;
用户自定义变量
类似全局
用户定义变量是用户根据需要自己定义的变量,用户变量不用提前声明,在用的时候直接用”@变量名” 使用就可以。其作用域为当前连接
赋值
set @自定义变量名 = 值 , @自定义变量名 = 值;
set @自定义变量名 := 值,@自定义变量名 = 值;
##对于set 可以使用 = 也可以使用:=
select @自定义变量名 := 值,@自定义变量名 = 值;
select 字段名 into @自定义变量名 from 表名;
使用
select @自定义用变量;
用户定义的变量无需对其进行声明或初始化,只不过获取到的值为NULL。意思为自定义用户变量若没有给定值,返回null而不报错
类似局部变量
局部变量是根据需要定义的在局部生效的变量,访问之前,需要DECLARE声明。可用作存储过程内的局部变量和输入参数,局部变量的范围是在其内声明的BEGIN … END块
声明
declare 变量名 变量类型 (default 0);
变量类型就是数据库字段类型:INT、BIGINT、CHAR、VARCHAR,DATE、TIME等
赋值
set 变量名 = 值
set 变量名 := 值
select 字段名 into 自定义变量名 from 表名;
例如:1.–声明局部变量-declare 2.–赋值 3 — 使用变量名 4–调用存储过程
if
介绍
if 用于做条件判断,具体的语法结构为
if 条件1 then
.....
elseif 条件2 then -- 可选
.....
else -- 可选
.....
end if;
在if条件判断的结构中,ELSE IF 结构可以有多个,也可以没有。 ELSE结构可以有,也可以没有
案例
根据定义的分数score变量,判定当前分数对应的分数等级
score >= 85分,等级为优秀
score >= 60分 且 score < 85分,等级为及格
score < 60分,等级为不及格
create procedure p3()
begin
declare score int default 58;
declare result varchar(10);
if score >= 85 then
set result := '优秀';
elseif score >= 60 then
set result := '及格';
else
set result := '不及格';
end if;
select result;
end;
call p3(); 调用
上述的需求我们虽然已经实现了,但是也存在一些问题,比如:score 分数我们是在存储过程中定义
死的,而且最终计算出来的分数等级,我们也仅仅是最终查询展示出来而已。而把score分数动态的传递进来,需要使用参数
参数
介绍
参数的类型,主要分为以下三种:IN、OUT、INOUT。 具体的含义如下:
类型 | 含义 | 备注 |
in | 该类参数作为输入,也就是需要调用时传入值 | 默认 |
out | 该类参数作为输出,也就是该参数可以作为返回值 | |
inout | 既可以作为输入参数,也可以作为输出参数 |
用法
CREATE PROCEDURE 存储过程名称 ([ IN/OUT/INOUT 参数名 参数类型 ])
BEGIN
-- SQL语句
END ;
案例
根据传入参数score,判定当前分数对应的分数等级,并返回。
score >= 85分,等级为优秀。
score >= 60分 且 score < 85分,等级为及格。
score < 60分,等级为不及格
create procedure p4(in score int, out result varchar(10))
begin
if score >= 85 then
set result := '优秀';
elseif score >= 60 then
set result := '及格';
else
set result := '不及格';
end if;
end;
-- 定义用户变量 @result来接收返回的数据, 用户变量可以不用声明
call p4(18, @result);
select @result;
case
介绍
case结构及作用,和我们在基础篇中所讲解的流程控制函数很类似。有两种语法格式:
语法一:
-- 含义: 当case_value的值为 when_value1时,执行statement_list1,当值为 when_value2时,
执行statement_list2, 否则就执行 statement_list
CASE case_value
WHEN when_value1 THEN statement_list1
[ WHEN when_value2 THEN statement_list2] ...
[ ELSE statement_list ]
END CASE;
语法二
-- 含义: 当条件search_condition1成立时,执行statement_list1,当条件search_condition2成
立时,执行statement_list2, 否则就执行 statement_list
CASE
WHEN search_condition1 THEN statement_list1
[WHEN search_condition2 THEN statement_list2] ...
[ELSE statement_list]
END CASE;
案例
根据传入的月份,判定月份所属的季节(要求采用case结构)。
1-3月份,为第一季度
4-6月份,为第二季度
7-9月份,为第三季度
10-12月份,为第四季度
create procedure p6(in month int)
begin
declare result varchar(10);
case
when month >=1 and month <=3 then
set result := '第一季度';
when month >=4 and month <=6 then
set result := '第二季度';
when month >=7 and month <=9 then
set result := '第三季度';
when month >=10 and month <=12 then
set result := '第四季度';
else
set result :='非法参数';
end case;
select concat('你输入的月份为:',month,'所属的季度为:',result);
end;
call p6(13);
while
介绍
while 循环是有条件的循环控制语句。满足条件后,再执行循环体中的SQL语句。具体语法为:
-- 先判定条件,如果条件为true,则执行逻辑,否则,不执行逻辑
WHILE 条件 DO
SQL逻辑...
END WHILE;
案例
计算从1累加到n的值,n为传入的参数值。
-- A. 定义局部变量, 记录累加之后的值;
-- B. 每循环一次, 就会对n进行减1 , 如果n减到0, 则退出循环
create procedure p7(in n int)
begin
declare total int default 0;
while n >0 do
set total := total +n;
set n := n-1;
end while;
select total;
end;
call p7(180);
repeat
介绍
repeat是有条件的循环控制语句, 当满足until声明的条件的时候,则退出循环 。具体语法为:
-- 先执行一次逻辑,然后判定UNTIL条件是否满足,如果满足,则退出。如果不满足,则继续下一次循环
REPEAT
SQL逻辑...
UNTIL 条件
END REPEAT;
案例
计算从1累加到n的值,n为传入的参数值。(使用repeat实现)
create procedure p8(in n int )
begin
declare total int default 0;
repeat
set total := total+n;
set n := n -1;
until n<=0
end repeat;
select total;
end;
call p8(1080);
loop
LOOP 实现简单的循环,如果不在SQL逻辑中增加退出循环的条件,可以用其来实现简单的死循环。
LOOP可以配合以下两个语句使用:
LEAVE :配合循环使用,退出循环。跳出本层循环
ITERATE:必须用在循环中,作用是跳过当前循环剩下的语句,直接进入下一次循环。跳出本次循环
leave 和lterate,类似break和continue,前者跳出本层循环,后者跳出本次循环
语法
[begin_label:] LOOP
SQL逻辑...
END LOOP [end_label];
LEAVE label; -- 退出指定标记的循环体
ITERATE label; -- 直接进入下一次循环
上述语法中出现的 begin_label,end_label,label 指的都是我们所自定义的标记
案例一
计算从1累加到n的值,n为传入的参数值
-- A. 定义局部变量, 记录累加之后的值;
-- B. 每循环一次, 就会对n进行-1 , 如果n减到0, 则退出循环 ----> leave xx
create procedure p9(in n int)
begin
declare total int default 0;
sum:loop
if n <=0 then
leave sum; ##跳出本参循环,因为给定n值不符
end if;
set total := total + n;
set n := n -1;
end loop;
select total;
end;
call p9(15);
案例二
计算从1到n之间的偶数累加的值,n为传入的参数值。
-- A. 定义局部变量, 记录累加之后的值;
-- B. 每循环一次, 就会对n进行-1 , 如果n减到0, 则退出循环 ----> leave xx
-- C. 如果当次累加的数据是奇数, 则直接进入下一次循环. --------> iterate xx
create procedure p11(in n int)
begin
declare total int default 0;
sum:loop
if n <=0 then
leave sum;
end if;
if n%2 = 1 then
set n := n-1;
iterate sum;
end if;
set total := total + n;
set n := n -1;
end loop;
select total;
end;
call p11(10);
游标
介绍
游标(CURSOR)是用来存储查询结果集的数据类型 , 在存储过程和函数中可以使用游标对结果集进
行循环的处理。游标的使用包括游标的声明、OPEN、FETCH 和 CLOSE,其语法分别如下
声明游标
DECLARE 游标名称 CURSOR FOR 查询语句 ;
打开游标
OPEN 游标名称 ;
获取游标记录
FETCH 游标名称 INTO 变量 [, 变量 ] ;
关闭游标
CLOSE 游标名称 ;
案例
根据传入的参数uage,来查询用户表tb_user中,所有的用户年龄小于等于uage的用户姓名
(name)和专业(profession),并将用户的姓名和专业插入到所创建的一张新表
-- 逻辑:
-- A. 声明游标, 存储查询结果集
-- B. 准备: 创建表结构
-- C. 开启游标
-- D. 获取游标中的记录
-- E. 插入数据到新表中
-- F. 关闭游标
create procedure p12(in uage int)
begin
declare uname varchar(100);
declare upro varchar(100); ##普通声明要在游标声明之前
declare u_cursor cursor for select name,profession from tb_user where age < uage; ##声明游标, 存储查询结果集 将 tb_user的数据存储到u_cursor中
drop table if exists tb_user_pro; ##创造表结构,为数据遍历到tb_user_pro做准备
create table if not exists tb_user_pro(
id int primary key auto_increment,
name varchar(100),
profession varchar(100)
);
open u_cursor; ##开启游标
while true do
fetch u_cursor into uname,upro; ##遍历u_cursor数据,将数据遍历到tb_user_pro中,获取游标中的记录
insert into tb_user_pro values (null,uname,upro); ##插入数据到tb_user_pro中
end while;
close u_cursor; #关闭游标
end;
call p12(40); ##调用存储过程,会生成一个新表t_user_pro
但是通过此过程,会报错,翻译为:没有数据去获取遍历了,是因为我们的循环没有定义条件,数据库不知道如何结束循环,这个报错通常使用条件处理程序来解决
条件处理程序
介绍
条件处理程序(Handler)可以用来定义在流程控制结构执行过程中遇到问题时相应的处理步骤。具体
语法为
DECLARE handler_action HANDLER FOR condition_value [, condition_value]
... statement ;
handler_action 的取值:
CONTINUE: 继续执行当前程序
EXIT: 终止执行当前程序
condition_value 的取值:
SQLSTATE sqlstate_value: 状态码,如 02000
SQLWARNING: 所有以01开头的SQLSTATE代码的简写
NOT FOUND: 所有以02开头的SQLSTATE代码的简写
SQLEXCEPTION: 所有没有被SQLWARNING 或 NOT FOUND捕获的SQLSTATE代码的简写
由上述报错我们可以知道原因是找不到数据报错02000,则选择SQLSTATUE,或者NOT FOUND来解决当前问题
create procedure p12(in uage int)
begin
declare uname varchar(100);
declare upro varchar(100);
declare u_cursor cursor for select name,profession from tb_user where age < uage;
declare exit handler for SQLSTATE '02000' close u_cursor; ## 声明条件程序,需要执行退出操作,当报错02000异常的时候,并同时关闭游标
drop table if exists tb_user_pro;
create table if not exists tb_user_pro(
id int primary key auto_increment,
name varchar(100),
profession varchar(100)
);
open u_cursor;
while true do
fetch u_cursor into uname,upro;
insert into tb_user_pro values (null,uname,upro);
end while;
close u_cursor;
end;
更多可参考:
存储函数
介绍
存储函数是有返回值的存储过程,存储函数的参数只能是IN类型的。具体语法如下
CREATE FUNCTION 存储函数名称 ([ 参数列表 ])
RETURNS type [characteristic ...]
BEGIN
-- SQL语句
RETURN ...;
END ;
characteristic说明:
deterministic:相同的输入参数总是产生相同的结果
not sql :不包含 SQL 语句。
reads sql data:包含读取数据的语句,但不包含写入数据的语句
注意
在mysql8.0以后,二进制日志默认开启,必须描述特性(characteristic)
存储函数必须要有返回值
存储函数用的比较少,存储过程用的较多
案例
计算从1累加到n的值,n为传入的参数值
create function fun01( n int)
returns int deterministic
begin
declare total int default 0;
while n >0 do
set total := total +n;
set n := n-1;
end while;
return total;
end;
select fun01(10000);
触发器
介绍
触发器是与表有关的数据库对象,指在insert/update/delete之前(BEFORE)或之后(AFTER),触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性, 日志记录 , 数据校验等操作 。
使用别名OLD和NEW来引用触发器中发生变化的记录内容,这与其他的数据库是相似的。现在触发器还
只支持行级触发,不支持语句级触发。
触发器类型 | NEW 和 OLD |
INSERT 型触发器 | NEW 表示将要或者已经新增的数据 |
UPDATE 型触发器 | OLD 表示修改之前的数据 , NEW 表示将要或已经修改后的数据 |
DELETE 型触发器 | OLD 表示将要或者已经删除的数据 |
语法
创建
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,默认为当前数
据库
案例
通过触发器记录 tb_user 表的数据变更日志,将变更日志插入到日志表user_logs中, 包含增加,
修改 , 删除 ;
插入数据触发器
create trigger tb_user_insert_trigger ##指定触发器名称tb_user_insert_trigger
after insert on tb_user for each row ##指定在sql语句之后触发after,类型为insert添加,指定为行触发器 each row
begin
insert into user_logs(id, operation, operate_time, operate_id, operate_params) values
(null, 'insert', now(), new.id, concat('插入的数据内容为:id=',new.id,',name=',new.name, ', phone=', NEW.phone, ', email=', NEW.email, ',profession=', NEW.profession)); ##new为新添加数据,insert也只可以new
end;
验证
当我们在insert触发器中,user表中添加数据的时候,在logs表,user表中会添加相应记录
insert into tb_user(id, name, phone, email, profession, age, gender, status, createtime)
VALUES (25,'二皇子','18809091212','erhuangzi@163.com','软件工程',23,'1','1',now());
修改数据触发器
create trigger tb_user_update_trigger
after update on tb_user for each row ##指定在sql语句之后触发after,类型为update修改,指定为行触发器 each row
begin
insert into user_logs(id, operation, operate_time, operate_id, operate_params) VALUES
(null, 'update', now(), new.id,
concat('更新之前的数据: id=',old.id,',name=',old.name, ', phone=',old.phone, ', email=', old.email, ', profession=', old.profession,
' | 更新之后的数据: id=',new.id,',name=',new.name, ', phone=',NEW.phone, ', email=', NEW.email, ', profession=', NEW.profession)); ##old为旧数据,update能指定new,也可old
end;
当我们修改user表中的数据的时候,在logs表,user表中会添加相应记录
update tb_user set profession = '会计' where id <= 5;
删除数据触发器
create trigger tb_user_delete_trigger
after delete on tb_user for each row ##指定在sql语句之后触发after,类型为delete删除,指定为行触发器 each row
begin
insert into user_logs(id, operation, operate_time, operate_id, operate_params)
VALUES
(null, 'delete', now(), old.id,concat('删除之前的数据: id=',old.id,',name=',old.name, ', phone=',old.phone, ', email=', old.email, ', profession=', old.profession)); ##删除只能指定old旧数据
end;
查看已有的所有触发器(insert,update,delete)
查看删除日志
delete from tb_user where tb_user.id = 25;
锁
概述
锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、
RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有
效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个
角度来说,锁对数据库而言显得尤其重要,也更加复杂
MySQL中的锁,按照锁的粒度分,分为以下三类:
全局锁:锁定数据库中的所有表。
表级锁:每次操作锁住整张表。
行级锁:每次操作锁住对应的行数据。
全局锁
介绍
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。但是可以执行DQL
其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整
性
为何需要全局锁?
例如在实际情况中,我们需要进行全部逻辑数据备份,当没有锁的时候,我们对库表进行备份的同时,数据是在增加的,是在不停处理的,若在两个相关联的表中,前表进行了备份,然后又在两表添加了数据进入,我们此时在添加数据后备份第二张表,那么两表的数据是不一致的。所以我们需要全局锁锁定全局备份数据保持数据一致性
对数据库进行进行逻辑备份之前,先对整个数据库加上全局锁,一旦加了全局锁之后,其他的DDL、DML全部都处于阻塞状态,但是可以执行DQL语句,也就是处于只读状态,而数据备份就是查询操作。那么数据在进行逻辑备份的过程中,数据库中的数据就是不会发生变化的,这样就保证了数据的一致性和完整性
语法
加全局锁
flush tables with read lock ;
##注意加全局锁和释放锁,必须在同一窗口下,若在开锁期间推出了数据库,那么该锁会失效
数据备份
mysqldump -uroot –p1234 itcast > itcast.sql(选择备份目录)
##注意mysqldump并不是mysql中的语法,而是一个bin命令,所以需要退出窗口执行
释放锁
unlock tables ;
特点
数据库中加全局锁,是一个比较重的操作,存在以下问题:
如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆。
如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟。
在InnoDB引擎中,我们可以在备份时加上参数 –single-transaction 参数来完成不加锁的一致性数据备份
mysqldump --single-transaction -uroot –p123456 itcast > itcast.sql
##添加--single-transaction之后不需要在开启全局锁
表锁
对于表锁,分为两类:
表共享读锁(read lock)
表独占写锁(write lock)
语法
加锁
lock tables 表名... read/write
释放锁
unlock tables / 客户端断开连接
特点
读锁
在表读锁内,若在客服端一内开启了表读锁,那么用户一可以执行DQL语句,而不能执行DDL/DML语句,在客户端二内,用户二可以执行DQL语句,而不能执行DDL/DML语句,总结就是在读锁内,可以读取数据(DQL),而不能写入修改数据等(DDL/DML)
写锁
在表写锁内,若在客服端一内开启了表写锁,那么用户一可以执行DQL,DDL/DML,可以读写,但是在客户端二中,用户二既不能执行DQL,也无法执行DDL/DML语句
总结
结论: 读锁不会阻塞其他客户端的读,但是会阻塞写。写锁既会阻塞其他客户端的读,又会阻塞
其他客户端的写入
元数据锁
meta data lock , 元数据锁,简写MDL。
MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作,所以元数据锁一般存在于事物中,当事物开启后,客户端一执行了DML语句,在事物没结束前,客户端二无法执行DDL语句,直到事物结束。为了避免DML与DDL冲突,保证读写的正确性。
这里的元数据,大家可以简单理解为就是一张表的表结构。 也就是说,某一张表涉及到未提交的事务时,是不能够修改这张表的表结构的。
在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)。
常见的SQL操作时,所添加的元数据锁有
对应SQL | 锁类型 | 说明 |
lock tables xxx read / write | SHARED_READ_ONLY / SHARED_NO_READ_WRITE (shared_read_only/shared_no_read_write) | |
select 、select … lock in share mode | SHARED_READ(shared_read) | 与SHARED_READ、 SHARED_WRITE兼容,与 EXCLUSIVE互斥 |
insert 、update、 delete、select … for update | SHARED_WRITE(shared_write) | 与SHARED_READ、 SHARED_WRITE兼容,与 EXCLUSIVE互斥 |
alter table … | EXCLUSIVE(exclusive) | 与其他的MDL都互斥 |
演示
当执行SELECT、INSERT、UPDATE、DELETE等语句时,添加的是元数据共享锁(SHARED_READ /
SHARED_WRITE),之间是兼容的
当执行SELECT语句时,添加的是元数据共享锁(SHARED_READ),会阻塞元数据排他锁
(EXCLUSIVE),之间是互斥的,需要等到事物提交后才会执行
查看数据库中元数据库情况
select object_type,object_schema,object_name,lock_type,lock_duration from
performance_schema.metadata_locks ;
意向锁
介绍
为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查
假如没有意向锁,客户端一对表加了行锁后,客户端二如何给表加表锁呢?
首先客户端一,开启一个事务,然后执行DML操作,在执行DML语句时,会对涉及到的行加行锁。当客户端二,想对这张表加表锁时,会检查当前表是否有对应的行锁,如果没有,则添加表锁,此时就会从第一行数据,检查到最后一行数据,效率较低。
有了意向锁之后 :
客户端一,在执行DML操作时,会对涉及的行加行锁,同时也会对该表加上意向锁。而其他客户端,在对这张表加表锁的时候,会根据该表上所加的意向锁来判定是否可以成功加表锁,而不用逐行判断行锁情况了。
分类
意向共享锁(IS): 由语句select … lock in share mode添加 。 与 表锁共享锁(read)兼容,与表锁排他锁(write)互斥。
意向排他锁(IX): 由insert、update、delete、select…for update添加 。与表锁共享锁(read)及排他锁(write)都互斥,意向锁之间不会互斥。由DML语句即可,不需要for update
一旦事务提交了,意向共享锁、意向排他锁,都会自动释放
通过以下SQL,查看意向锁及行锁的加锁情况
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from
performance_schema.data_locks;
演示
意向共享锁与表读锁是兼容的
意向排他锁与表读锁、写锁都是互斥的
面试题
数据库索引的底层数据结构
二叉树在某些场景下会退化成链表而链表的查找需要从头部开始遍历,而这就失去了加索引的意义。不使用红黑树的原因是:红黑树还是二叉树,在面对表数据数百万数千万的场景时,会导致索引树的层数很高导致查询效率低,而 B+树由 B 树和索引顺序访问方法演化而来,它是为磁盘设计的一种平衡查找树,在 B+树中,所有记录节点都是按键值的大小顺序存放在同一层的叶子节点,各叶子节点通过单向指针进行连接。B+树索引在数据库中的一个特点就是高扇出性,例如在lnnoDB 存储引擎中,每个页的大小为 16KB。数据库中,B+树的高度一般都在 2~4 层,这意味着查找某一键值最多只需要 2 到 4 次0 操作,因为现
在一般的磁盘每秒至少可以做100次I0操作,2~4次的0操作意味着查询时间只需0.02~0.04秒
B 树和 B+树的区别
它们都是平衡多路查找树,是在二叉查找树基础上的改进数据结构。b+树数据存放在叶子结点上,非叶子结点只存 key,而 b 树数据存放在叶子结点和非叶子上,所以相对 b+树的层级要多,B+树为所有叶子节点增加了链接,从而实现了快速的范围查找
数据库的索引是什么结构为什么不用哈希表
MySQL中的索引B+树实现的:
哈希表要求将所有数据载入内存,而数据库存储的数据量级可能会非常大,全部载入内存基本上是不可能实现的:B+树可以分段加载需要的节点数据,可以在内存资源有限的前提下,极大提高查询效率
说说MySQL 索引,以及它们的好处和坏处
MvSQL索引是一种帮助快速查找数据的数据结构,主要有普通索引、唯一索引 (索引列必须唯一,但允许有空值,如果是组合索引,那么组合的列必须唯一)、主键索引(不允许有空值,一般用primary key约束)、外键索引、全文索引、复合索引几种。
合理使用索引能大幅提高匹配 where 条件的检索效率,还能用于排序、分组、表与表连接的加速。索引必定会增加存储资源的消耗,同时也增大了插入、更新和删除操作的维护成本,因为每个增删改操作后相应列的索引都必须被更新。
只要创建了索引,就-定会走索引吗?
不一定,组合索引要按最左前缀法则,否则将不会走该索引。
索引的设计原则
适合创建索引的情况:对查询频次较高且数据量比较大的表,频繁出现在where 字句中的列:使用唯一索引,区分度越高,使用索引的效率越高:表与表连接用于多表查询的约束条件,查询中排序、统计、分组的字段,需要根据范围搜索的列。不适合创建索引的情况:基数较小的表,没必要创建索引;经常增删改的表不适合创建索引;更新频繁的字段不要有索引;字段值唯一性不高的字段,如性别、婚姻情况;定义为text和 bit 数据类型的列不适合创建索引,因为这些列的数据要么很大要么很小,很少使用的列不需男创建索开
索引失效
右边的列不能使用索引,否则右边的索引为失效,如:大于小于以及范围查找between在索引上使用运算函数会导致索引失效,如avg、sum、count、max、min、substring等。
使用复合索引,要遵循最左前缀法则,查询从索引的最左列开始,不能跳过索引中间列不要在where子句中对字段进行 null 值判断和模糊查询以及使用!=或<>操作符
如果索引列是字符串类型的整数,条件查询的时候不加引号会造成索引失效。
如果索引列完全包含查询列,那么查询的时候把要查的列写出来,不要使用 select*
用or 分割开的条件,如果or前面的列有索引,or 后面的列没有索引,那么查询的时候前后索引都会失效,应该用or 连接的字段都加上索引才能生效
聚簇索引和非聚簇索引(聚合索引和二级索引)
聚簇索引是将数据与索引存储到一起,找到索引也就找到了数据,而非聚簇索引是将数
据和索引存储分离开,索引树的叶子节点存储了数据行的地址。
在InnoDB 存储引擎中,可以将 B+树索引分为聚簇索引和非聚簇索引。无论是何种索引,每个页的大小都为16KB且不能更改。聚簇索引是根据主键创建的一棵 B+树,聚簇索引的叶子节点存放了表中的所有记录。非聚簇索引是根据索引键创建的一棵 B+树
与聚簇索引不同的是,其叶子节点仅存放索引键值,以及该索引键值指向的主键。也就是说,如果通过非聚篱索引来查找数据,那么当找到非聚簇索引的叶子节点后,很有可能还需要根据主键值查找聚簇索引来得到数据,这种查找万式又被称为书签查找(回表查询)
数据库引擎有哪些各自有什么区别
InnoDB引擎是MySQL的事务安全存储引擎,具有提交、回滚和崩溃恢复功能来保护用户数据。
MvISAM引擎的表占用空间较小,表级锁限制了读写工作负载的性能,因此它通常用于只读或以读取为主的场景。
Memory引擎是将所有数据存储在内存中,以便在需要快速查找非关键数据的环境中进行快速访问,支持 hash 索引。
Archive引擎非常适合存储大量的独立的,作为历史记录的数据,因为它们不经常被读取。它拥有高效的插入速度,但其对查询的支持相对较差
Cluster/NDB是高余的存储引擎,用多台数据机器联合提供服务以提高整体性能和安全性。适合数据量大,安全和性能要求高的应用。
Federated引擎提供连接单独的 mySQl服务器从多个物服务器创建一个逻辑数据库的能力,非常适合分布式或数据集市环境。