【MySQL系列】MySQL索引事务

☕目录☕

     🍚前言

          🧀一、索引

               🥣🥣1.1 概念

               🍖🍖1.2 为什么要使用索引

               🧇🧇1.3 索引的使用场景

               🥩🥩1.4 索引的使用

                          🥐🥐🥐1.4.1 查看索引

                          🍛🍛🍛1.4.2 创建索引

                          🍣🍣🍣1.4.3 删除索引

          🍱二、事务

               🍜🍜2.1 为什么使用事务

               🥡🥡2.2 事务的概念

               🍔🍔2.3 事务的特性

                          🍤🍤🍤2.3.1 原子性

                          🍲🍲🍲2.3.2 一致性

                          🍨🍨🍨2.3.3 持久性

                          🍗🍗🍗2.3.4 隔离性

               🎂🎂2.4 事务的使用

     🍰后续


前言

这一次,前面的 student表、course表、score表 、classes表 四张表 的数据没有删除~~

咱们现在可以接着用~~

虽然这一篇博客 八股文 所占的比例还是比较重的~~

在这一篇博客中,所要介绍的是 MySQL数据库中的 索引和事务~~

那么现在,正文开始 ......

一、索引

1.1 概念

如果说 "索引" 这个词语 对你们有一点抽象,那么 可以换一个词语来表达 —— "目录"~~

每一本书,都有自己的目录~~

想观看某一章节内容,翻开目录,它就会告诉你 该章节在多少页~~

这就比一页一页的翻 要高效率的多~~

目录 所存在的意义,就是在加快查找的速度~~

想要理解 索引,可以先来理解 目录~~

虽然说 目录是索引的一种,索引不是目录;

但是,可以 "以小见大",通过 学习目录 来学习索引~~

1.2 为什么要使用索引

说白了,索引 实质上是一张描述索引列的列值与原表中记录行之间一 一对应关系的有序表~~

在 MySQL 中,通常有以下两种方式访问数据库表的行数据:

1) 顺序访问

顺序访问是在表中实行全表扫描,从头到尾逐行遍历,直到在无序的行数据中找到符合条件的目标数据~~
顺序访问实现比较简单,但是当表中有大量数据的时候,需要从头到尾进行遍历,效率非常低下~~

2) 索引访问

索引访问是通过 遍历索引 来直接访问表中记录行的方式~~

使用这种方式的前提是 对表建立一个索引,在列上创建了索引之后,查找数据时可以直接根据该列上的索引找到对应记录行的位置,从而快捷地查找到数据~~

比如,在 student表 中,如果基于 student_id 建立了索引,系统就建立了一张 索引列到实际记录的映射表~~

当用户需要查找 student_id 的相关数据时,系统先在 student_id 索引上找到该记录,然后通过映射表直接找到数据行,并且返回该行数据~~

可以这样理解,一个表里面有很多列,一列数据所占用的空间比较大,只能放在磁盘上~~

做索引 常见的情况就是 拿其中的某一个 id 来做索引,单拎出来的 id 可是要比整个行都小多了~~

所以 扫描索引的速度一般远远大于扫描实际数据行的速度,即 采用索引的方式可以大大提高数据库的工作效率~~

简而言之,不使用索引,MySQL 就必须从第一条记录开始读完整个表,直到找出相关的行。表越大,查询数据所花费的时间就越多。如果表中查询的列有一个索引,MySQL 就能快速到达一个位置去搜索数据文件,而不必查看所有数据,这样将会节省很大一部分时间~~

 

1.3 索引的使用场景

"索引" 本质上是通过一些更复杂的数据结构,把所有待查询的记录给组织起来了,从而就能够加快查找的速度!!!

(1)消耗了额外的空间!!!

这一点 可以类似于新华字典,或者是写博客之类的:

新华字典的目录 需要耗费纸张吧,写博客如果加上目录 需要耗费一定的空间吧~~

(2)有了索引,可以加快 查询的速度,但是拖慢了 增删改的速度!!!

这一点 可以类比于目录,如果 张三同学 写了一本书,此时 书的内容已经写好了,并且目录已经写好了;但是,此时 张三同学 突然又想到了 某些好点子,或者又觉得书上的某些内容不好,或者是又想修改某一部分内容~~

那么,此时 张三同学 不仅仅是要修改书的内容了,书的目录也是要修改的,那肯定是拖慢了速度~~

即 索引也不是尽善尽美的,主要是看场景是否合适:

  1. 对于空间不紧张,对于时间更敏感,可以使用 索引(以空间换时间)~~
  2. 查询频繁,增删改不频繁,可以使用索引(其实,这个也是大多数的情况,比如说 教务系统)~~

所以说,满足了上述条件,就可以考虑 对表中的字段 创建索引,以提高查询效率~~

反之,如果非条件查询列,或经常做插入、修改操作,或磁盘空间不足时,不考虑创建索引~~

1.4 索引的使用

1.4.1 查看索引

show index from 表名;

虽然后面有许多,但是 现在主要关注的是 Key_name 和 Colum_name

一开始,咋们是没有创建索引的~~

但是,虽然前面没有给这个 student表 手动创建索引,

却可以看到它自带了一个索引~~ 

我们需要注意的是,

一个表的主键列会自动带上索引,unique以及外键约束的列,也会自动带上索引~~

像 主键 这种,需要保证 记录不重复,

每次插入新纪录,都需要查询一下旧记录,看看新记录是否已经存在,判断是否重复,插入是否成功~~

此时,就需要进行频繁的查询~~

当然,unique 和 外键约束 也是如此,需要进行频繁的查询~~


1.4.2 创建索引

create index 索引名 on 表名(列名);

我们可以创建索引~~

先看看 student表 的结构:

现在,我们可以创建一个索引:

现在,我们就可以查看刚刚创建的索引:


1.4.3 删除索引

drop index 索引名 on 表名;

此时,我们可以删除 刚刚创建的 idx_student_name索引:

此时,索引删除成功~~

创建索引 和 删除索引,也是危险操作!!!

尤其是针对一个已经包含很大数据量的表 进行操作的时候!!!

如果是针对一个大表,创建索引,就会导致大规模磁盘IO,直接把主机的磁盘IO吃满!!!

主机就卡了,无法对线上服务进行响应~~


 

二、事务

2.1 为什么使用事务

在实际操作中,有些操作,希望是一个整体~~

假设,张三同学 谈了一个对象,那么 我们把中间某些流程简化一下,张三同学 肯定是希望把 给彩礼 => 领证 => 办酒席 这些环节 希望是一个整体~~

如果 中间的某些环节出现了问题,那么 希望能够把这些问题的影响降到最低~~

如果彩礼给完了,领证的时候,女方发现张三同学是一个渣男,后悔了 不想结婚了~~

女方想要反悔就得退回彩礼~~

如果彩礼给完了,证也领了,想反悔~~

那就需要先把彩礼退了,然后还得去民政局办个离婚证~~

这三个操作希望能够一气呵成,当做一个整体来进行!!!

如果执行中间过程中,出现异常,就需要把前面已经进行过的操作 进行回退/恢复~~

恢复成好像完全没有操作过的样子~~

此时,把这三个环节(一个整体),就可以视为一个 "事务"!!! 

2.2 事务的概念

事务就是把多个步骤,多个操作,打包成一个步骤,一个操作~~

其中任意一个步骤执行失败,都会进行回退,使这里的影响被降到最低~~

2.3 事务的特性

2.3.1 原子性

在数据库上涉及到很多和事务相关的操作!!!

最典型的:转账~~

李四 给 王五 转账转500,

涉及到的操作:

  1. 给 李四 -500    => SQL1
  2. 给 王五 +500   => SQL2

如果在执行SQL1之后,再执行SQL2的过程中,数据库出现问题了(宕机了),那么 李四就会减少500,而王五却并没有增加500,显然这个是非常不科学的~~

按正常的情况,要么两个都执行完,要么就一个都不执行!!!

这个就是事务最核心的特性:原子性!

(事务的原子性指的是,事务中包含的程序作为数据库的逻辑工作单位,它所做的对数据改操作要全部执行,要么全部不执行;这种特性称为原子性 —— 是事务诞生的初衷)~~

注意:此处的 "一个都不执行",不是真的没执行~

           而是通过恢复的方式,把之前造成的影响给还原了~~

           这个还原的过程,称为 "回滚"(rollback)~~

数据库的事务回滚,是如何做到的?

数据库里的每个操作,在内部都有记录~~

尤其是事务内部的操作~~

如果事务中间出现问题,就可以根据之前的记录,来进行恢复了~~ 


 

2.3.2 一致性

事务的一致性,指在一个事务执行之前和执行之后数据库都必须处于一致性状态~~

这种特性称为事务的一致性~~

简单的来说,就是 一致数据是对的,没有纰漏~~

就比如上面的转账的例子,李四 给 王五 转账转500,李四 -500,那么王五就得要 +500;不能说 李四 -500 而王五 +400 ~~


2.3.3 持久性

持久性,即 存储在磁盘上,事务进行的操作都会写在磁盘上,只要事务执行成功,造成的修改,就是永久化保存的了;哪怕重启主机,这样的改变也存在~~

磁盘严格来说和硬盘不是一个东西~~

虽然都是外存~~

但是此处也就直接混用了~~


2.3.4 隔离性

事务的隔离性:这个是 描述多个事务并发执行的时候,所产生的情况~~

正好 现在正处于疫情期间,相信大家对于 "隔离" 这个词语已经很熟悉了~~

其实,事务的隔离性 和 现在疫情的隔离意思差不多的~~

疫情的隔离,有几种级别:

  1. 定点医院隔离(隔离效果最好,出现交叉感染的概率最小,成本最高!!!)
  2. 指定酒店隔离(隔离效果比较好,成本比较高)
  3. 居家隔离/在学校隔离(隔离效果一般,成本比较低)
  4. 不隔离 (没啥隔离效果)

根据不同的隔离级别,严格情况不同,所以效果也是不同的~~

隔离的目的是:为了避免相互之间产生影响~~

而数据库中的事务 彼此之间也是可能会相互影响的,

事务的隔离性也就是在描述事务执行过程中,影响能接受到啥程度~~

如果事务是一个一个执行的,那么还好;

但是如果事务是并发执行的,那么相互之间可能会造成影响~~

并发执行,可以简单粗暴的理解成,两个事物在同时执行!!!

在之前介绍过,一个数据库服务器 可以给多个客户端提供服务,

这个时候就可能会涉及到 说多个客户端同时尝试操作一个表,

就可能会产生这种并发事务的情况~~

比如说,现在这里有一张售票表(剩余票数1),

现在 客户端可能要执行 "买票事务":

  1. 先检查剩余的票数是否 > 0
  2. 如果 > 0 就修改收票表里的计数,让计数 -1

如果两个客户端不加限制,同时进行,那么就会带来麻烦:

客户端1 和 客户端2 都执行了 -1 操作,那么 实际上票数就变成了 -1~~

这种情况叫做 "超卖"!!!

上述问题,就是两个事务之间产生了干扰和影响,

为了避免相互干扰,就引入了 "隔离性",通过隔离性来降低上述的影响~~

为啥要并发执行?

目标是为了提高执行效率!!! 

提高隔离性,带来的问题就是 数据更准确了,但是效率更低了~~

为了解决并发执行事务带来的问题,MySQL等数据库引入了 "隔离级别",可以让用户自行选择一个 适合自己当前业务场景的级别~~

先研究一下,并发执行事务的时候 还会有哪些问题?

(1)脏读问题(数据被污染了,不准了)

举个例子:

张三同学 在写代码,写了一个 Student类,写了一些属性和方法~~

在他写的过程中,李四走过来看了一会儿,就走了~~

在李四走了之后,张三同学 又把 Student类 里的实现又改了~~

此时,李四 读到的数据就是 "脏数据",上述的观察过程,就是 "脏读"~~

用数据库的术语来说,

一个事务 A,在执行过程当中,对数据进行了一系列的修改。在提交到数据库之前(完成事务之前),另一个事务 B,读取了对应的数据;

此时 这个 B 读到的数据都是一些临时结果,后续可能马上就被 A 给改了,此时 B 的读取行为就是 "脏读"~~

这个问题好解决:

张三同学 和 李四 约定好,在提交到数据库之前,李四不要去看该代码,等到提交到数据库之后,再去看看!!!

给读操作加锁(提交之前 不允许随便去读了啊,如果要想读,那就要去向 张三同学/A 申请)~~

相当于降低了并发程度,降低了效率,提高了隔离性~~

(2)不可重复读

即 事务A提交了之后,事务B才开始读(读是加锁了),然后在B的执行过程中,A又开始了一次,修改了数据;​​​​​​​

此时,B执行中,两次读取操作,结果可能不一致,这个就叫做 "不可重复读"~~

 

如何解决上述问题: 

来进行重新约定:

  1. 张三同学提交之前,李四不要去读(之前的加锁)
  2. 李四读的时候,张三同学不要去改代码

 

因此,隔离性又提高了,并发性又降低了,数据更准确了,效率又更低了~~

(3)幻读

闲着也是闲着,张三同学 在 李四 读代码的时候,又开始改别的代码;

李四 在读 student类的代码,张三就在改 teacher类的代码~~

但是,这里又出现了一个问题,如果 teacher类 本来就存在,也就罢了;

如果是在李四读的过程中,张三新增了一个 teacher类,或者是删除了一个已有的内容,就会产生 "幻读" 问题~~

事务B读取过程中,事务A进行了修改,没有直接修改B读取的数据,但是 却影响到了B读取的结果集~~

事务B两次读取到的结果集不一样~~

这个就叫做 "幻读",相当于是 "不可重复读"的一种特殊情况~~

解决 "幻读"问题,核心思路就是 "串行化",

严格要求,李四 在读代码的时候,张三不要去修改任何东西,保证读和写操作都是严格串行执行的(串行:一个执行完,才能执行另一个)~~

隔离性最高,并发程度最低,数据的准确性最好,同时效率最慢~~

 

为啥要并发 —— 效率!!!

为啥要隔离 —— 准确!!!

两者一般情况下是相悖的~~

实际使用的时候,就需要根据实际场景,来决定如何选择档位(看场景对于 性能 比较敏感,还是对于 准确性 比较敏感) ~~

如:转账操作,一分钱都不能差,慢点没事,一定要稳~~

       B站之类的,大博主 浏览数/点赞数......这些对于准确性不是很高~~

MySQL 里提供了 4 个档位,可供自由选择:

  1. read uncommitted               并发能力最强,隔离性最弱
  2. read committed                   只能读取提交之后的数据,解决了 "脏读"问题
  3. repeatable read                  针对读和写都限制了,解决了不可重复读问题
  4. serializable                          严格的串行执行,解决了 "幻读"问题 

1 —> 4,并发能力逐渐减弱,隔离性逐渐增强~~

我们可以根据实际需要,在配置文件里面,修改数据库的隔离级别(配置文件是之前演示过的 MySQL修改字符集的那个 my.ini 里)~~

默认的是第3个隔离级别,解决了 "不可重复读"问题~~  

 

2.4 事务的使用

  1. 开启事务:start transaction;
  2. 执行多条SQL语句
  3. 回滚或提交: rollback/commit;
说明: rollback 即是全部失败, commit 即是全部成功。
在实际开发的时候,很少通过SQL代码来写事务,更多的是借助其他编程语言~~

后续

这就是MySQL数据库 索引和事务的内容啦~~

下一篇就是 MySQL数据库 JDBC编程 部分的内容了~~

如果这篇博客给你带来了收获~~

可以留下一颗小小的赞吗~~

 

 

  • 48
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 45
    评论
评论 45
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哎呀是小张啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值