MySQL索引与事务

前言:

本篇主要介绍MySQL中背后的数据结构,和实际我们使用时会涉及到的逻辑情况,当然,我们对于背后的数据结构不要求实现它,只需了解即可。

索引:

我们先来看官方对其的解释:索引是帮助MySQL高效获取数据的数据结构。

当使用select查询的时候,会先遍历表,之后把当前行带入到遍历条件中,看是否成立。如果表很大,这样成本非常高。索引属于针对查询操作引入的优化手段,可以通过索引来加快查询速度,避免针对表的遍历。

1、索引的代价

所以索引是能提高查询速度的,但是也有代价。

  • 占用更多空间,生成索引,是需要一系列的数据结构,以及一系列额外的数据,来存储到硬盘空间中的。
  • 但是可能会降低插入修改删除速度

但是实际开发中,查询的频率比插入删除的频率高很多。

2、索引分类

这里有4种索引(可能会有更多的索引):

索引类型
主键索引
唯一索引
单值索引
符合索引

我们在创建索引中讨论。

3、 索引相关操作

3.1 查看索引

此时我们先创建一张表:

-- 若存在数据库mydb则删除
drop database if exists mydb;
-- 创建数据库mydb
create database mydb;
 
-- 选择数据库mydb
use mydb;
 
-- 创建学生表
create table student (
    id int,
    name varchar(20)
);

查询该表索引语句:

show index from student;

运行结果:

3.2 创建索引

创建索引有两种方式:主动创建 和 被动创建

3.2.1 被动创建

其实当我们在给一个字段添加约束的时候就已经创建了索引,比如此时我们我们在创建一个表的时候为某字段添加了主键约束:

drop table student;

create table student(
    id int primary key,  # 添加主键
    name varchar(20)
);

show index from student; # 查看索引

运行结果:

所以此时如果查询其他列还是逐个遍历。 

此时我们再观察外键约束:

drop table student;

create table class (
    id int primary key,
    name varchar(20)
);

create table student (
    id int primary key,
    name varchar(20),
    classId int,
    foreign key(classId) references class(id)
);

show index from student; # 查看索引

运行结果:

3.2.2 主动创建

此时我们也可以主动创建索引,比如我们对student表中的name字段创建索引:

# 为name字段创建索引
create index idx_student_name on student(name);

运行结果:

当然我们还有其他方式创建索引,但是我们没有必要全部掌握,当我们需要使用时再去查询也是可以的。 

创建索引操作也是危险操作,创建索引的时候,需要针对现有的数据进行大规模的重新整理。 如果当前是一个空表,或者数据不多,创建索引都没啥问题;如果这个表本身就很大,创建索引很容易把数据库服务器给卡住。

一般来说,创建索引都是在创建表的时候就规划好了,如果用了很久的表想增加索引,就需要慎重了。

这里有一个小技巧:另外搞一个机器,部署MySQL服务器,也创建同样的表,并把表上的索引创建好,再把之前的机器上的数据导入到新的MySQL服务器上,此时不能影响原来服务器的运转,当所有数据都导入完毕,就可以使用新的数据库替换旧的数据库了。

3.3 删除索引

手动创建的索引可以手动删除,但是自动生成的索引是不能删除的!

# 语法
drop index 索引名 on 表名;

# 删除name字段的索引
drop index idx_student_name on student;

# 此时尝试删除主动创建的索引
drop index PRIMARY on student; # 删除自动创建的索引

运行结果:

删除索引操作也是危险操作,所以要慎重使用。 

实现索引的数据结构(B树): 

MySQL内部有一个存储引擎,存储引擎模块提供了很多版本实现。Innodb 是当前最常用的 MySQL存储引擎,索引底层数据结构也就是B+树。

但是了解B+树前需要先了解B树,这里作者偷个懒,有一个博主讲解的很好,我们可以看他的视频:B树(B-树) - 来由, 定义, 插入, 构建_哔哩哔哩_bilibili

当然,他也讲解了B+树:数据结构合集 - B+树_哔哩哔哩_bilibili

B+树就是为了方便得到有顺序的数据,这样不需要中序遍历即可得到顺序化数据。

B - 树 不是 B减树,而是连接符,也是B树。

这里来稍微总结一下:

B树也是平衡搜索树,是一颗多叉平衡搜索树。

比如目前是m阶B树,每个节点最多有m个分支,m - 1 个元素。

对于跟节点:最少有一个元素,两个分支。

对于其他节点:最少有 m / 2 个分支(向上取整),每个节点有 m / 2 - 1 个元素。

事务:

事务是一组被认为是单个逻辑工作单元的SQL操作,他们要么全部成功执行,要么全部不执行。事务可以包含一个或多个数据库(如:插入、更新、删除等),这些操作要么全部生效,要么全部回滚,以保证数据的一致性和完整性。

这里举个栗子:比如转账的时候,银行现在卡里扣钱,之后在另外一个账户里赚钱。这是两个步骤,如果此时执行完第一步程序崩溃,最终你的卡里钱扣了,另一个账户并没有收到钱,事务就是为了避免这个情况。

注意:回滚用日志的方式记录事务中的关键操作(将内容打印到文件中去),即使主机掉电也不影响(回滚的日志已经在文件中了),一旦重新启动,就会发现回滚日志中有一些需要进行回滚的操作,这样就完成了回滚。

1、使用事务:

我们先来了解事物的语法:

# 开启事务
begin    
# 或者
start transaction

# 提交事务
commit

# 回滚事务
rollback

这里我们就模拟上面的转账的场景,来模拟事物的场景:

李四向张三借了1000元,张三原来有3000元,李四原来有1000元。

create table account (
    id int primary KEY auto_increment,
    name varchar(20) not null,
    money decimal(7,2)
);

# 插入数据
insert into account(name,money) values('张三',3000);
insert into account(name,money) values('李四',1000);

# 此时开启事务
start transaction;

# 更新账户
update account set money = money - 1000 where name = '张三';

出错了
update account set money = money - 1000 where name = '李四'  

# 回滚
rollback;

此时我们如果没有出错,就可以提交事务,就是使用commit进行提交。

所以回滚就是回滚到距离最近的上一次commit的步骤或者 start transaction ,这样就保证了一件实务不会进行到一半崩溃而数据也只保存了一半。

2、事务的隔离级别:

MySQL支持多种事务隔离级别,用于控制事务之间的隔离程度。包括以下几种:

1.读未提交(Read Uncommitted):这是最低的级别。在这个级别下,一个事务可以读取另一个事务未提交的数据。这意味着可能会出现脏读的情况。

举个栗子:当我在写文件的时候,另一个人在抄我的文件,我写完了(不算晚全写完,我又检查了一下)以后他抄完了,但是此时我们发现我写错了于是开始修改,但是另一个人并没有修改文件。

2. 读已提交(Read Committed):这个级别确保了一个事物只能读取到其他事务已经提交的数据,从而避免了脏读的问题。这个级别下,当一个事务正在修改的时候,其他事务不能读取到未提交的数据。但可能出现重复读的问题。

3.可重复读(Repeatable Read):这个级别确保了在同一个事务内多次读取同一个数据时,结果是一样的。它通过在读取某行数据时加锁来实现,这样其他事务无法在此期间修改这一行数据,从而避免了不可重复读。但可能出现幻读的问题。

4.串行化(Serializable):这是最高的隔离级别。它通过强制事务串行执行来避免并发任何问题,确保了数据的一致性。然而,由于事务必须按顺序执行,可能会导致性能下降。

解释:

  • 脏读(Dirty Read):这就是我上面举的那个例子(抄文件),在隔离级别为“读未提交”可能出现的问题。
  • 重复读(Non-repeatable Read):这是隔离级别为“读已提交”时可能出现的问题。在一个事务中多次读取同一行数据的过程中,另一个事物也对这个事务也对这部分数据进行了修改。如果此时事务再次读取这一行数据,那么它将读到与第一次读取时不同的数据。这就是不可重复读。
  • 幻读(Phantom Read):这是隔离级别为“可重复读”时可能出现的问题。在一个事务多次读取表中的数据过程中,另一个事务插入了新的行。如果此时第一个事务再次读取表中的所有数据,那么它将看到一些原本不存在的行,这就是幻读。

不可重复读:同一事务中,多次读取同一条记录时,数据可能发生了修改。比如第一次是X,第二次是Y。这里的重点是同一条记录的数据被修改了。

幻读:是指在同一个事务中,多次执行相同的查询语句时,结果集的行数发生了变化。例如,第一次查询某个条件下的记录集有10条,另一个事务插入了几条符合该条件的新记录并提交,第二次查询时,结果集变成了12条。这里的重点是结果集的行数发生了变化,因为有新的记录插入或删除。

注意:默认级别为可重复读。

总结:

这些知识都不难,主要还是我们要多去练习,有其他错误请大佬在评论区指正,制作不易,大家可以点点赞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值