1.前言
上节博客我们一起了解了数据库的联合查询相关的知识,相信大家已经掌握了相关的知识。
那么今天就开始我们新的内容:视图、索引 (本章的重点,HR可能会问)以及事务。
2.视图
2.1.什么视图
在数据库中, 视图(View)是一个虚拟的表 ,它本身并不存储实际的数据,而是基于一个或多个实际表(或其他视图)的查询结果动态生成的。简单来说,视图就像是一个"窗口",通过这个窗口,用户可以看到数据库中特定的数据,但看不到(或不需要关心)底层表的完整结构。
视图的核心特点
1.虚拟性:视图不存储数据,只存储查询定义( 相当于一个"保存的查询语句")。
2.动态性:每次查询视图时,数据库会重新执行视图的定义语句,从底层表获取最新数据。
3.安全性:可以通过视图只暴露用户需要的数据,隐藏敏感信息(比如身份证号、工资等)。
4.简化性:将复杂的多表查询封装成视图,用户只需要查询视图即可,无需关心底层逻辑。
举个例子:

我们可以把数据库的表类比成"超市的仓库",仓库里有各种各样的商品(数据),但顾客不需要直接进入仓库找东西。
而视图就像是超市货架上的"商品价签":
-
签不存储商品本身 (视图不存储数据),它只是告诉你"这个位置有什么商品,价格是多少"。
-
价签展示的是筛选后的信息 (视图可以筛选数据),比如货架上只放"零食"类商品的价签,顾客看不到其他类别的商品。
-
价签信息是动态更新的 (视图数据动态生成),如果仓库里的商品价格变了,价签上的价格也会跟着变。
4.顾客只需看价签 (用户只需查询视图),不需要知道仓库里商品是怎么摆放、怎么管理的。
语法;create view_name[(column_list] as select_statement;
view: 创建视图的关键字
view_name:视图名
(column_list\]:视图中包含的列。 select_statement:视图对应的SQL语句。
示例
1.查询用户的所有信息和考试成绩
学生id,学生姓名,所有班级名,课程和对应的成绩。


所有有这样开发需求的程序员,都需要写这么复杂的SQL
可以把以上SQL定义成一个视图。

出现错误的原因:定义视图的时候出现了重复的列。

这就说明我们成功创建了名为v_student_score的视图。
接下来我们就要查看一下我们写的视图是否存储在我们创建的数据库中

我们还可以查询我们所编写的视图结构。

2.2具体编写示例





删除视图
语法:drop view view_name;

视图的优点
1.简单性:视图可以将复杂的查询封装成一个简单的查询。例如,针对一个复杂的多表连接查询,可以创建一个视图,用户只需查询视图而无需了解底层的复杂逻辑。
2.安全性:通过视图,可以隐藏表中的敏感数据。例如,一个系统的用户表中,可以创建一个不包含密码列视图,普通用户只能访问这个视图,而不能访问原始表。
3.逻辑数据独立性:视图提供了一种逻辑数据独立性,即使底层表结构发生变化,只需修改视图
定义,而无需修改依赖视图的应用程序。使用到应用程序与数据库的解耦。
4.重命名列: 视图允许用户重命名列名,以增强可读性。
总结来说,视图就像一个"定制化的观察窗口",帮助用户更方便、更安全地访问数据库中的特定数据。
3.索引
3.1索引的基本概念
数据库索引是一种数据结构,用于快速查找数据库表中的特定记录。它就像是一本书的目录,可以帮助我们快速找到想要的内容,而不需要逐页翻找。

举个栗子

图书馆的图书查找系统
想象一下,我们去图书馆找一本关于"数据库"的书。如果没有索引系统,我们可能需要在整个图书馆的书架上一本一本地查找,这会非常耗时。
但实际上,图书馆有两种索引系统帮助我们快速找到书籍:
1.按类别分类地书架(比如"计算机科学"区域)
2.图书检索系统(可以通过书名、作者快速找到书籍地位置)
数据库索引就像是图书馆地检索系统,它记录了书籍地位置信息,
让我们能够快速找到所需地书籍,就不需要扫描整个数据库表。
3.1.1为什么使用索引
使用索引地目的只有一个,就是提升数据检索地效率,在应用程序地运行过程中,查询操作地频率远远高于增删改地频率。
3.2索引应该选择哪种数据结构
3.2.1.HASH
时间复杂度:O(1),但是不支持范围查找。
最重要地数据结构,没有之一。
3.2.2.二叉搜索树
中序遍历是一个有序序列-->支持范围查找
时间复杂度:可能会退化成一个单边树O(N)
节点个数过多时,无法保证树的高度。
由于数据库中的数据时磁盘上保存的,每一次访问子节点都会发生一次磁盘IO。
磁盘IO是制约数据库性能的主要因素。


3.2.3.N叉树
每个节点可以有超过两哥的子节点,可以解决树高的问题。

度或阶,每一个节点最多有多少个子节点,一般子节点的个数小于度的值。

时间复杂度:O(logN)
在数据量相同的情况下,可以有效的控制树高,也就是说可以使用更少的IO次数找到目标节点,从而提高数据库的效率。
3.2.4.B+树
B+树是一种经常用于数据库和文件系统等场合的平衡查找树,MySQL索引采用的数据结构,以4阶B+树为例,如下图所示:

时间复杂度:O(logN)
可以有效的控制树高。
B+树与B树对比
1.叶子节点之间有一个相互连接的引用,可以通过一个叶子节点找到它相邻的兄弟节点,
MySQL在组织叶子节点时使用的是双向链表。
2.非叶子节点的值都包含在叶子节点中,MySQL非叶子节点只保存了对子节点的引用,没有保存真实的数据,所有真实的数据都保存在叶子节点中。
3.对于B+树而言,在相同树高的情况下,查找任意元素的时间复杂度都一样。
4.MySQL中的页
4.1.为什么要使用页
在**.ibd** 文件中最重要的结构体是Page(页) ,页是内存与磁盘交互的最小单元。默认大小为,16KB每次内存与磁盘的交互至少读取一页,所以在磁盘中每个页内部的地址都是连续的,之所以这样做,是因为在使用数据的过程中,根据局部性原理,将来要使用的数据大概率与当前访问的在空间上是临近的,所以一次从磁盘中读取一页的数据放入内存中,当下次查询的数据还在这个页中时就可以从内存中直接读取,从而减少磁盘I/O提高性能。

innodb存储引擎后成的表空间文件后缀时**.ibd**
每一个页中即使没有数据也会使用16KB的存储空间,同时与索引的B+树中的节点对应。

查看页大小

16384/1024 = 16KB

Linux操作系统中管理文件的最小单位时4KB,MySQL作为一个数据库程序,以4KB大小管理数据显然太少了,所以定义了16KB一页的默认页大小。
当从内存中往磁盘里写数据页的时候,写到一半操作系统挂了,这时MySQL应该如何保证数据安全?
在落盘之前会记录各种日志,保证重启之后可以找到没有落盘的数据内容。
每创建一张生成一个保存数据的文件称为独立表空间文件。


4.2.页文件头和页文件尾
页文件头和页文件尾中包含的信息,如下图所示:当前页文件的重要信息。

这里我们只关注,上一页页号 和下一页页号 ,通过这两个属性可以把页与页之间链接起来,形成一个双向链表 。通过页号和页大小,可以计算出下一页和上一页在磁盘上的偏移量

4.3.页主体
页主体部分时保存真实数据的主要区域,每当创建一个新页,都会自动分配两个行,一个是页内最小行Infimun,另一个页内最大行Supermun,这两个行并不存储任何真实信息,而是做为数据行链表的头和尾,第一个数据行有一个记录下一行的地址偏移量的区域next_record将页内所有数据行组成了一个单向链表,此时新页的结构如下所示:

当向一个新页插入数据时,将Infimun连接第一个数据行,最后以行真实数据行连接Supermun,这样数据行就构建成了一个单向链表,更多的行数据插入后,会按照主键从小到大的顺序进行链接,如下图所示:

4.4.页目录

创建分组时会在页目录中创建一个槽,槽的数量与分组的数据是一致的,槽会指定对应分组的最后一条记录,同时保存这条记录的主键值。
例如要查找主键为6的行
1.首先要找到这条记录所有的页,
2.在页中找到所在的槽
3.在分组中找到对应的记录
5.数据页头
数据页头记录了当前页保存数据相关的信息,如下图所示:

B+树在MySQL索引中的应用
非叶子节点保存索引数据,叶子节点保存真实数据,如下图所示:

以查找id为5的记录,完整的检索过程如下:
1.首先判断B+树的根节点中的索引记录,此时5<7,应访问左孩子节点,找到索引页2.
2.在索引页2中判断id 的大小,找到与5相等的记录,命中,加载对应的数据页。
以上的IO过程,加载索引页1-->加载索引页2-->加载索引页3 3次IO
3.3计算三层树高的B+树可以存放多少条记录

一个数据页默认16KB,假设一条数据1KB,一页中至多可以存16条数据

索引页中存的是主键值和子节点的引用,也就是下一节点的偏移地址
主键bigint 8Byte,下一页地址6Byte,也就是说一条索引记录占8+6 = 14Byte
一个索引页可以存 16 * 1024/14 =1170
理论上一个三层树高的B+树可以存"1170*1170*16 = 21,902,400条记录
在当前的场景下,表中有21,902,400条记录的情况下,通过三次IO就可以完成数据的查询。
以上的IO过程,加载索引页1-->加载索引页2-->加载索引页3 3次IO
把索引页加载到内存中进行缓存,当查一条没有加载过的数据时,一次真实IO在可以搞定。
3.4索引分类
1.主键索引
当在一个表在定义一个主键PRIMARY KEY时,自动创建索引,索引的值是主键列的值,InnoDB使用它作为聚集索引(聚簇索引)。
推荐为每个表定义一个主键。如果没有逻辑上唯一且非空的列或列集可以使用主键,则添加一个增列。
为了提升查询效率,工作中通常为查询频繁的列创建索引。且列的重复度不高。
2.普通索引
最基本的索引类型,没有唯一性的限制。可以包含一个列也可以包含多个列
可能为多列创建组合索引,成为复合索引或组全索引。(创建索引之后都会生成一个索引树,创建多少索引生成多少棵索引树)
创建索引后,生成的索引树,也是会占用磁盘空间的
创建索引时,要慎重考虑一下需不需要
索引树越多,对增、删、改的效率影响越大。
3.唯一索引
当在一个表上定义一个唯一键UNQUE时,自动创建唯一索引。
与普通索引类似,但区别在于唯一索引的列不允许有重复值。
4.全文索引
基于文本列(CHAR,VARCHAR或TEXT列)上创建,以加快对这些列中包含的数据查询和DML操作。
用于全文搜索,仅MySAM和InnoDB引擎支持。
5.聚集索引
与主键索引同义词
如果没有为表定义PRIMARY KEY,InnoDB使用第一个UNIQUE和NUOT NULL的列作为聚集索引。
聚集索引可以标识数据行的唯一性
如果表中没有PRIMARY KEY 或合适的UNIQUE索引,InnoDB会为新插入的行生成一个行号并用16字节的ROW_ID字段记录,ROW_ID单调递增,并使用ROW_ID做为索引。
ROW_ID时数据行中的一个隐藏列之一。
6.非聚集索引
聚集索引以外的索引称为非聚集索引或二级索引
二级索引中的每条记录都包含着该行的主键列,以及二级索引指定的列。
InnoDB使用这个主键值来搜索聚集索引中的行,这个过程称为回表查询。
7.索引覆盖
当一个select语句使用了普通索引且查询列表中的列刚好是创建普通索引时的所有部分列,这时就可以直接返回数据,而不用回表查询,这样的现象称为所有覆盖。

3.5手动创建索引
3.5.1.主键索引
sql
-- 创建表的时候指定主键索引
CREATE TABLE pk1(
id BIGINT PRIMARY KEY auto_increment,
name VARCHAR(20)
);
desc pk1;
-- 创建表时单独指定主键列
CREATE TABLE pk2(
id BIGINT auto_increment,
name VARCHAR(30),
PRIMARY KEY(id)
);
SHOW INDEX FROM pk2;
-- 修改表中的列为主键索引
CREATE TABLE pk3(
id BIGINT,
name VARCHAR(30)
);
show INDEX from pk3;
-- 修改表中的id 列为主键索引
ALTER TABLE pk3 ADD PRIMARY KEY (id);
ALTER TABLE pk3 MODIFY id BIGINT auto_increment;


3.5.2.唯一索引
sql
-- 创建表时创建唯一键
CREATE table uk1(
id BIGINT PRIMARY KEY auto_increment,
name VARCHAR(30) UNIQUE
);
-- 创建表时单独指定唯一列
CREATE TABLE uk2(
id BIGINT PRIMARY KEY auto_increment,
name VARCHAR(30),
UNIQUE (name)
);
-- 修改表中的列为唯一索引
CREATE TABLE uk3(
id BIGINT PRIMARY KEY auto_increment,
name VARCHAR(20)
);
ALTER TABLE uk3 ADD UNIQUE (name);
3.5.3.普通索引
sql
-- 创建表的时候创建普通索引
CREATE TABLE index1(
id BIGINT PRIMARY KEY auto_increment,
name VARCHAR(30) UNIQUE,
sn VARCHAR(20),
INDEX (sn)
);
-- 修改表中的列为普通索引
CREATE TABLE index2(
id BIGINT PRIMARY KEY auto_increment,
name VARCHAR(30) UNIQUE,
sn VARCHAR(20)
);
ALTER TABLE index2 ADD INDEX(sn);
-- 单独创建索引并指定索引名
CREATE TABLE index3(
id BIGINT PRIMARY KEY auto_increment,
name VARCHAR(30),
sn VARCHAR(30)
);
-- 为name 列建立索引,不指定索引名时失败,必须要指定名字
CREATE INDEX ON INDEX(name);
-- 索引名推荐使用,idx_表名_列名[_列名]..
CREATE INDEX idx_index3_sn ON index3(sn);
ALTER TABLE index3 DROP index idx_index3_sn;

3.5.4.复合索引
sql
-- 创建复合索引
-- 创建表时指定索引列
CREATE TABLE index4(
id BIGINT PRIMARY KEY auto_increment,
name VARCHAR(30),
sn VARCHAR(20),
class_id BIGINT,
INDEX(sn,name)
);
-- 修改表中的列为复合索引
CREATE TABLE index5(
id BIGINT PRIMARY KEY auto_increment,
name VARCHAR(30),
sn VARCHAR(20),
class_id BIGINT
);
ALTER TABLE index5 ADD INDEX (sn,name);
-- 单独创建索引并指定索引名
CREATE TABLE index6(
id BIGINT PRIMARY KEY auto_increment,
name VARCHAR(30),
sn VARCHAR(20),
class_id BIGINT
);
CREATE INDEX idx_index6_sn_name ON index6 (sn,name);
3.5.5.删除索引

sql
-- 删除索引
-- 删除主键索引
ALTER TABLE index6 drop PRIMARY KEY;
-- 如果主键是自增列,先把自增列改成非自增
ALTER TABLE index6 MODIFY id BIGINT;
-- 再次执行删除主键
ALTER TABLE index6 drop PRIMARY KEY;
-- 删除其他索引
ALTER TABLE index6 DROP index idx_index6_sn_name;
3.5.6.查看索引



结束
到这里就结束了,谢谢大家的支持!