MySQL索引和视图
索引的概念
MySQL的索引是一种数据结构,索引可以帮助数据库高效地查询 ,索引是通过一定的规则对数据库中的记录进行排序,这样可以加快速度
像新华字典中可以用拼音查字、也可以用笔画、偏旁部首这样的目录进行查询,此时的索引就像这里的目录
但是这个有了索引就会有增加开销,并且当我们使用索引查询才会提高效率(wheret条件关于索引进行查询),反之如果不使用索引进行查询,此时这样普通查询的效率会降低,但是通常情况下索引的利 > 弊
并且在数据库的增删查改中,查的频率是最高的,因此使用索引来提高数据查询的效率
索引的数据结构选择
常见的数据结构有顺序表、栈、队列、优先级队列、二叉树、二叉搜索树(AVL树)、哈希表
首先这里顺序表、栈、队列、优先级队列肯定是不可以的
二叉树 :二叉树的查询最坏的时间复杂度为O(N)(单边树的情况),数据库中的数据都是放到硬盘中,硬盘的访问效率是最低的 ,那使用二叉搜索树(红黑树、AVL树)将其树平衡,此时访问的时间复杂度变成了 O(lonN),但是当数据较多的时候访问硬盘的次数仍然较多,还是不适合用于数据库数据查询
那哈希表查询时间复杂度为O(1)合适吗?
不合适,因为哈希表只可以针对对应的key返回对应的value,只能一对一查询,但是不可以模糊查询,因此不合适
使用N叉树(B树)

此时会将这里进行范围划分,划分之后,新划分的范围也可以继续划分,这样通过范围划分就可以将所有数据表示出来
但是这样仍然不过高效,因此这里就对B树进行了优化
B+树
此时村存放过程中也是和B树类似也是通过范围,并且此时也会将父节点最大值放过来
这样最终所有的都在叶子节点上通过双线链表连接,直接从叶子节点就可以找到数据


B+树的特点
1.是一个N叉搜索树
2.每个节点和B树一样都可以进行范围划分出区间,但是此时新的区间中会把父节点中最大值也放来,最终叶子节点就是完整的结果集合
3.叶子节点通过双向链表连接
B+树和B的对比1.叶子节点就是完整的结果集合并且通过双向链表连接,非常方便进行查询
2.所有的数据都在叶子节点上,非叶子节点只需要存放索引key值 ,这样非叶子节点占据空间小,更适合内存中缓存
3...每次查询时间差不多,因为都要查询到叶子节点才可以找到数据,查询开销相对稳定
4.相对于红黑树 树的高度更低
页
MySQL数据库,是将数据放到.ibd文件中,一个这个文件中有很多页

Page页是内存与硬盘交互的最小单元,页的大小 默认大小是16KB ,因此每次读取至少读取一页的数据,并且其内部地址是连续的
为什么一次读取一页的数据,而不是只读取我们想要的呢?
主要是因为局部性原理 经验规律,访问到一个地方的数据,很大可能访问这个数据临近的文件,这样一次读取一页可以减少硬盘的IO开销,而是从内存中读取这样提高读取速率,提高性能
局部性原理:有时间局部性和空间局部性
时间局部性 :此时这个信息被访问,近期可能还会被访问
空间局部性:将来访问的数据大概率与上一次访问的信息空间上临近的地址
sql
show VARIABLES LIKE 'innodb_page_size';
此时这里页大小默认是16KB,这里也就是 16 * 1024 = 16384个字节,这个大小是可以调节的,一般都使用默认大小

MySQL存在多种类型的页,最常用的就是 索引页 和 数据页
索引页 :B+树中的非叶子节点,保存的是key值和子节点位置
数据页 :具体的数据行
虽然有多种类型,但是每一种类型都有 页头(File Header) 和 页尾(File Trailer)
页头里面有上一页页号和下一页页号,可以通过这个将页和页之间练习起来,最终形成一个双向链表
B+树在索引中的应用
如果找到对应的数据页
叶子节点才存放数据,非叶子节点只存放对应key和子节点信息

使用B+树查找 id为5的记录
此时 5 < 7,向左孩子查找,找到了索引页2,在索引页2中找id = 5相等的记录,找到后就进行加载数据页
计算三层树高可以存放多少条记录
非叶子节点只存放对应key + 子节点位置 (索引页)
叶子节点才会存放数据(数据页)
假设 此时一条数据大小占1KB,一个页的大小是16KB,忽略数据页中其他自身属性占用的空间,因此一个页可以存放16条数据
索引页 一条记录大小为 key + 子节点位置(主键 + 下一页的地址),假设是bigint是8Byte , 下一个页地址占 6Byte 此时也就是 14Byte字节
16KB = 16384字节 16384 / 14 = 1170条数据

索引分类
1.主键索引
primary key
创建主键的时候,数据库会自动创建
如果有主键那必然是有主键索引,但是如果没有数据库也会自动创建一个"隐藏列"(唯一且非空的列或者列集)作为主键,围绕隐藏列建立索引
2.普通索引
最基本的索引类型,没有唯一性限制,可以是一个列,也可以是多个列创建的复合索引
3.唯一索引
一个表的列定义了一个UNIQUE唯一键,此时数据会主动创建唯一索引
和普通索引类似,只不过这里唯一索引的列值不可以相同
4.全文索引
基于⽂本列(CHAR、VARCHAR或TEXT列)上创建,针对字符串进行创建的,前面的都是根据整数这种类型创建的索引用于比较整个key,但是字符串这种可能存在局部比较(模糊查询),因此就使用全文索引
5聚集索引和非聚集索引
聚集索引:主键索引就是聚集索引
如果没有定义 primary key,InnoDB使用第一个UNIQUE 和 NOT NULL的列作为聚集索引,如果既没有定义,也没有合适的UNIQUE和 NOT NULL列,此时InnoDB会心插入的行生成一个行号用6字节的ROW_ID记录,递增的,ROW_ID作为索引
非聚集索引 :
聚集索引以外的索引称为非聚集索引或二级索引
并且其每条记录都包含该行的主键列,以及二级索引所指向的列
使用这个二级索引的列进行查询的时候,称为回表


覆盖索引
当⼀个select语句使⽤了普通索引且查询列表中的列刚好是创建普通索引时的所有或部分列,这时就可以直接返回数据,⽽不⽤回表查询,这样的现象称为索引覆盖
使用索引
主键索引
sql
//构建表时候进行创建
create table test(
id int primary key auto_increment,
name varchar(20));
create table test1(
id int,
name varchar(20));
create table test2(
id int,
name varchar(20));
//给test1添加一个主键
alter table test1 add primary key(id);
//给test2中id列修改成自增主键
alter table test2 modify id bigint primary key auto_increment;
show index from test;
show index from test1;
show index from test2;
test索引情况


这里的BTREE表示B+树
唯一索引
sql
create table test(
id int unique,
name varchar(20));
show index from test;


普通索引
sql
create table test(
id int,
name varchar(20),
index(id));


复合索引
sql
create table test(
id int,
name varchar(20),
class_id int,
index(id,class_id));
show index from test;
此时这里显示索引就会显示两行,


删除索引
主键索引
sql
alter table 表名 drop primary key;//此时要注意带自增属性的主键
sql
create table test(
id int primary key ,
name varchar(20));
//删除主键索引
alter table test drop primary key;
此时如果是自增主键需要先将其转化成主键,让后进行删除
sql
create table test(
id int primary key auto_increment,
name varchar(20));
//先删除自增属性
//才可以删除索引
alter table test modify id int;
alter table test drop primary key;

普通索引
sql
alter table 表名 drop index 索引名;
drop index 索引名 on 表名;//这个也可以
sql
create table test(
id int,
name varchar(20),
index(id));
//下面这两种方式都可以删除
alter table test drop index id;
drop index id on test;

复合索引删除
给复合索引起了名称,可以将复合索引名称看成简单索引进行删除
sql
create table test(
id int,
name varchar(20),
class_id int,
index test_id_calss_id(id,class_id));
show index from test;
//删除,根据复合索引名称删除
alter table test drop index test_id_calss_id;
drop index test_id_calss_id on test;


但是如果没有起索引名呢,此时数据库会将其自动命名为id
sql
create table test(
id int,
name varchar(20),
class_id int,
index(id,class_id));
//这里复合索引的名称为id
alter table test drop index id;
drop index id on test;

索引的注意事项
sql
1.索引应建立在高频查询的列上
2.索引会占据额外空间
3.索引应该在创建表的时候进行合理规划和谨慎选择
4.索引也会影响表的插入、修改、删除操作
视图
视图是一个虚拟的表,基于一个表或多个表的查询结果集 ,视图本身并不占存储空间,而是查询动态生成数据。视图并不占用物理存储空间,相当于一个查询逻辑表示
sql
# 创建视图
CREATE VIEW view_name [(column_list)] AS select_statement
sql
# 创建学生表
create table student(
id int,
name varchar(20));
insert into student VALUES (1,'张三'),(2,'李四');

sql
drop view v_student;#创建一个视图
# 这个视图存的是学生表的信息
create view v_student as select id , name from student;
select id , name from v_student;
这里创建视图里面存放的是student表中内容
此时根据视图进行查询,先执行视图对应的SQL,把视图对应数据拿到,然后再基于视图进行查询

此时对原student修改,可能也会影响到其对应视图
例如此时对学生表中插入信息,查看其视图是否变化
sql
insert into student VALUES(3,'张六');
select id , name from v_student;
果然发生了变化

此时对视图修改也会影响到原student表
sql
insert into v_student VALUES(4,'王五');
select id , name from student;

update操作同理
sql
#对原表进行更新
update student set name = '张张三三' where id = 1;
select id , name from v_student;

sql
# 对视图进行更新
update v_student set name = '张三' where id = 1;
select id , name from student;

当然也可以将视图和原表进行联合查询
sql
select * from student v,v_student s where v.id = s.id;

当然这里也可以指定列进行创建视图,并不一定需要所有列
sql
create view v2 as select id from student;
select id from v2;

删除视图
sql
drop view view_name;
注意事项
修改真是表会影响视图,修改视图也会影响真实表
但是并不是所有视图都可以进行更新
1.创建视图使用聚合函数的视图
2.使用DISTINCT
3.使用GROUP BY以及HAVING子句
4.使用UNION 或者 UNION ALL
5.查询列表使用子查询
6.在FROM⼦句中引⽤不可更新视图
这些都不可以进行更新视图
视图的优点
1.简单性 :将一个复杂查询封装成一个简单的,针对复杂多表连接查询,可以先创建视图,通过视图进行查询
2.安全性 :可以将一些隐蔽一些数据
3.逻辑数据独立性 :即使原表发生改变(增加列,拆分等),只需修改视图定义即可,无需修改视图对应的代码
4.重命名列:视图中允许用户重命名列名,增加可读性