MySQL的索引与事务(面试必考) - 细节狂魔

x33g5p2x  于2022-03-16 转载在 Mysql  
字(7.4k)|赞(0)|评价(0)|浏览(205)

前言

前面讲的sql这些东西都是属于操作层面的,也没有特别难点,可能就多表查询有点难,但都不是问题!多练习就会记住了。

而现在要讲的MySQL的索引与事务,都属于MySQL原理层面的东西。
涉及到一些MySQL内部的一些实现机制,
该内容比较抽象,离咱们比较遥远。毕竟我们不是搞研发数据库的。
MySQL 是一个背后非常庞大的软件。
内部的原理和机制,数不胜数。
至于这里为什么讲这个,纯粹是因为面试要考!

索引

索引的作用 与 概念

索引(index)可以说是一本书的目录(index)。【两者的英文是同一个只是表现的形式不一样。】

索引的优缺点

另外,思考一个问题:树的目录一旦决定了,后续每次对书的内容进行调整时,都可能会印象到目录的准确性,就需要重新调整目录。

数据库的索引也是一样的情况:当我们 对 数据进行“增删改”操作的时候,往往也需要同步调整索引的结构。

索引带来的好处:提高了查询效率
索引带来的坏处:占用了更多的空间,拖慢了增删查改的速度。

从表面来上看,似乎索引的坏处 比 索引带来的好处要多。但!这不必意味着 弊大于利!!
因为在实际需求的场景中,查询操作往往是最高频率的操作。
相对于“增删改” 的使用频率则低的可怜。
因此,查询作为一个高频操作,索引对其来说是不可缺少的,

另外,有了索引之后对于查询的效率的提升使非常巨大的!!!
当MySQL里面的数据量级 达到千万级别的时候(一个表里就有几千万,甚至破亿的数据)再去遍历表,就会非常非常的低效!!!

在另一方面:MySQL在进行数据比较的时候,不是像我们编程那样,一个for循环(这样的想法是错误的)。
编程上的查询是在内存中的比较;MySQL 中的比较是在硬盘上比较。
也就是说:在MySQL中的每一次比较都会涉及到硬盘的 IO 操作。

又因为硬盘的 IO 的 访问速度 比 内存 慢 3 ~ 4 个数量级(几千~万倍)。
所以,如果是查询的操作更是慢上加慢!!!
【这里又一次体现了索引的好处:能使这里的查找效率提高数万倍】

和索引相关的一些 SQL 语句

查看索引

命令格式:show index from 表名;
其作用:查看一个数据表上都有哪些索引

给一个表中的某个列来创建索引

==命令格式:create index 索引名字 on 表名(列名)

需要注意的是:创建索引这件事是一个非常低效的事情,尤其是当前表里面已经有很多数据的时候。
它需要给该列的每一行数据都设置一个索引,因此在数据量庞大的情况下,创建的索引的过程是非常耗时的!
因此,以后我们在工作的时候,看到某个数据库的一个表没有索引,千万不要贸然去创建一个索引。
你啪的一声回车一敲,下一秒数据库就挂了。
一点都不夸张,数据库就是这么娇贵!
因为复杂的东西是最容易出现问题的!!!
越简单的东西越抗造的!
反正总体来说:操作数据库本身就是一个危险操作。必须时刻小心谨慎。

删除某个表中的索引

命令格式:drop index 索引名字 on 表名

删除索引操作和创建同理,都是非常低效的事情,也容易让数据库凉凉。

小结

虽然我们讲了如果创建和删除索引。但是!并不推荐在工作去使用。
最好在创建数据表之前,就想好是否使用索引。
如果使用索引:在创建数据表的时候,利用 主键约束 或者 unique 来 指定 某列。
如果不使用,就不用设置了。
对于 SQL 语句,只要还是使用“增删查改”。
另外,虽然 SQL 也是支持 条件,循环,变量,函数等这些编程语言的普遍机制,但是这些操作一般很少用得到。
如果非要使用 这些机制,通过会通过搭配其他的编程语言进行实现,比如 java 的 JDBC 编程。
在这里我们不多讲。

索引 背后的数据结构 - 面试考点

面试中考察的主要是“索引 背后的数据结构”,前面一般不会考到。
那么,问题来了:什么样的数据结构使用索引能加快查找的速度?
常见的数据结构:
1、顺序表
2、链表
3、二叉搜索树、AVL树、红黑树
4、哈希表

一圈下来,发现没有合适的数据结构能够作为 索引背后的数据结构。
那么,到底使用何种结构,能够在未 索引背后的数据结构呢?
其实,索引背后的数据结构还是树形结构,只是这个树形结构不再是二叉树结构。
而是 N 叉树 结构。

在整个数据量的条件一定的情况下,N叉搜索树的高度 一定比 二叉搜索树要低。
在数据库中使用的这个多叉树,又不太一样,是一个很特殊的树,我们称为 B+ 树。
【B+ 树 是 数据库中最常见的数据结构】
注意!数据库有很多种,每个数据库底层又支持多种存储引擎
这些存储引擎实现了数据库具体按照什么结构来存储的程序。
那么就意味着 每个存储引擎 存储数据的结构 可能都不一样,背后的索引数据结构可能也不同。
所以,这里面可能会有很多种多叉树来去表示这里的数据结构。
只是 B+ 树 是 最常用的一种数据结构。
那么,B+ 树 又是什么样子的?
想要 理解 B+ 树,需要先理解它的前身 B 树( B-树:这个是B树的另一种写法,而不是B减树),

有了B树的基础,我们就可以来了解B+ 树 了。也就是 索引背后的数据结构。
因为B树 是 B+树的前身,那么B+树想对比B树又做出了那些改变?

可能有些朋友会有疑问:为什么B+树这么去构建?
你可以这么认为 B+ 树 就是为了 数据库索引量身打造的!!!!
1、使用B+树进行查找的时候,整体IO次数也是比较少的。
2、所有的查询最终多会落在叶子结点上,每次查询的 IO 次数都是差不多的,故查询的速度是稳定的,相差不大。
3、叶子结点用链表链接之后,非常适合进行范围查找
例如:找到 大于等于5,且小于等于 11 的值。

4、所有的数据存储(数据又称载荷)都是放到叶子结点上的。也就是说非叶子结点中只需要保存key值就可以了。因此非叶子节点整体占用空间较小,甚至可以加载到内存中。(一旦能够全部放在内存里,此时硬盘上的IO次数几乎就为零了)

整个数据库存储就是这个载荷(payload)。
根据这4点,想必大家也不怀疑,B+树就是为数据库创造的!
另外,B树虽然不是很适合数据库,但是它的通用强,不像B+树一样,就是为了数据库而生的。换个场景,可能B+树就不行了。
.主要还是根据实际情况来选择合适的数据结构。

面试主要就是问你:谈谈你对于索引的理解。
1、索引是用来干什么的?
给信息分配一个id,方便能够快速找寻到该数据
2、索引的使用的场景
数据库 的 查询功能,对于海量信息的查询,有这巨大作用。
3、索引的好处与坏处
好处:大幅度提升查找的效率,
坏处:占用额外的存储空间,使得“增删改”操作的执行的速度下降。
4、索引背后的数据结构
B+树,然后给他讲讲B+树是怎么实现 以及它的特点。

事务

事务的概念

事务诞生的目的就是为了把若干个独立的操作给打包成一个整体。
举个例子:
现在我们要去和女神约会。
我们首先第一步要做的就是先去银行取钱。
【不要有疑问,约会不要钱的嘛。当然你能让你女神请你,那当我没举这个例子】
第二步才是去找女神去约会。

但是,这会存在个问题
问题:钱取出来,但女神没有约出来,所以 这钱就是白取的,还不如留在银行吃利息。

而事务就是这两件事打包在一起,形成一个整体。
什么意思呢?
要么就是把两个操作都执行完;
要么就是一个操作不执行;
不需要存在 只执行第一步,不执行第二步的中间状态。
直白来说:要做就做完所有事,不做就碰都别碰,这就是事务的概念。
结合MySQL来说:
在SQL中,有的复杂的任务需要多个SQL 执行。
有的时候,也需要同样打包在一起,前一个SQL 就是为了给后一个SQL 提供支持。
如果后面的SQL不执行了,或者出错了。那么前面的SQL也就失去了意义。
这样的情况,我们就能借助 事务 来进行。
总之,事务就是把很多东西打包在一起,要做就全做完,不做就一个也别做。

事务的原子性

像MySQL中事务这样的特点【要做就全做完,不做就一个也别做。(换个说法:任务不能被细分,必须按照一个整体任务来看待)】,我们称其称为原子性。
至于为什么将事务的特点称为原子性?
这是因为:在过去科技不发达的时候,人们认为-》原子 就是不可拆分的最小单位了。
所以,原子就保留了原子不可查分的含义。而事务的特点就是具有原子含义的一种特点
但是,在如今的我们都知道:物质是通过分子构成的,分子通过原子构成的,原子是由原子核和核外电子构成,原子核由质子和中子构成。

再举一个列子,来让大家加深对原子性的理解

抛出一个问题:事务的原子性是如何保证的?

前面硕大事务的原子性就是:要么全部执行成功,要么就一个都不执行!
实际上我们在执行第二个SQL之前,我们并不能预知这次执行是否会失败。
那么,我们该怎么去做?
没有办法,只能还是去执行第一个SQL,执行完了之后,在执行第二个SQL,试试看、
成功就最好,没成功再想办法。
也就是说:实际上 在处理事务的时候,并不像它说的那样【要么就全部执行成功,要么就一个都不执行】,也就是执行失败的时候,执行了SQL语句。
原因就是我们并不能预知突发事件的发生,所以不管能否执行成功,我们都需要去执行一下(该执行还是要执行一下)。

解决方法:当出现执行失败之后,由数据库自动执行一些“还原”性的操作。来消除前面的SQL带来的影响。让我们从外面来看就好像一个都没有执行一样。
(其实它是执行了的,正因为它执行了。所以它才知道出错了。你的操作没有效果就是最好的证明)

在打个比方:手机翻新
现在我们去买一个手机,对于一个萌新用户(就是不懂手机的),他是无法区分 这个手机是新机,还是翻新机。
所谓的翻新机:就是将一个别人用了很久的手机,做一系列清洗工作,外加筛机,再重新包装一下。让这个旧手机看起来跟新的一摸一样。这就是翻新机。
简单来说 翻新机 就是将以前用户的使用痕迹抹除,让其看起来像是一个新机。
在MySQL中,这种“还原”操作 被称为 “回滚”(rollback)。
回到那转账的问题上。

事务的使用

开启事务:start transaction;

简单来说
1、当我们将要执行多句SQL时,先输入 start transaction;
2、之后,便是执行多行 sql 语句。
3、最后 执行完之后,我们要以 rollback / commit 作为返回,
rollback 代表执行失败,进行“回滚”,而 commit 就是执行成功提交的意思。

start transaction;
// transaction 与 commit 之间,就是事务中的若干操作
-- 阿里巴巴账户减少2000
update accout set money=money-2000 where name = '阿里巴巴';
-- 四十大盗账户增加2000
update accout set money=money+2000 where name = '四十大盗';
commit;

事务相关的面试题

谈谈事务的基本特性

四个基本特性:
1、原子性:将操作进行合并看作一个整体,要么全做,要么一个都不做)
2、一致性:在事务执行之前 和 执行之后,数据库中的数据都必须合理合法。就比如说 转完钱,你余额不可能为负数,这就叫做合理合法
3、持久性:事务一旦提交了之后,数据就会持久化存储起来。也就是数据存储到硬盘上了。
4、隔离性:非常麻烦
隔离性描述的是:事务并发执行时候,产生的状况。

由事务的第四个特性引出一个关键字:并发

并发编程 是当下最主要的一个编程方式。
写出来的代码,是并发式的执行的。
什么意思呢?
就是所谓的一心二用:吃着饭,看这剧。嘴 和 眼睛 各干各的。
像这种情况:就是并发式执行。
虽然我们人脑并不擅长并发,但是计算机特别擅长。

有的人可能会有疑问:为什么计算机擅长开发?
其实计算机也是迫不得已。

但是 光刻机这种东西非常难搞。我们国家的水平还没有达到那么高的水平。
毕竟 CPU 是 我们人类科技的天花板之一。(能与之相提并论的只有氢弹)
要向提高算力,就必须要让单个门电路模块尽量做得很小。
意思就是:你单个电路做得越小,那么意味着同样的一块板子,你能比别人多刻几个电路,自然你的CPU的算力就比别人的高。
那么可以做到无线小嘛?不行的!!!
当物体小到一定程度的时候,经典力学就已经起不了作用了,量子力学就开始接管了~!!
而 量子计算机 就是 量子力学接管后的产物。(虽然还在试验阶段,并没有普及)

一般谈到的“并发”这个词,是一个‘广义’的概念,包含了并行 和 狭义 的 并发
比如说:并发 属于单核,并行 属于多核。
虽然这句话,它没错。
但是从宏观的角度去看:并发 和 并行 是一样的。
既然计算机是支持并发的,那么我们党的数据库也是需要具备相应的功能的。

数据库的并发执行

那么,问题就来了:在数据库中,并发执行多个事务的时候,修改 / 读取的 收据,是同一个数据的时候,就会出现一些问题。
而事务的隔离性就是解决上述问题。
所以我们这里事务的隔离性,主要就是针对并发执行事务的场景。
因此要想理解并发执行事故,我们就必须要理解什么是并发,并发:就是同时执行多个任务。
那么我们这里的并发执行事故:一个数据库服务器同时执行多个事务。

并发执行事故所带来的问题
脏读问题

比如说:我们此时坐在同一个考场进行笔试。我们在写一到编程题。
当我们写代码的过程中,我们创建一个表,参数(…)。
就在我们写的时候,有个人偷偷在瞄我们的代码。
他就看到我们是怎么去创建这个表的。然后他就咔咔飞速的写起来。
但是呢!我们觉得这个表不合适,把它擦掉改了。
等交的时候,他一看我们的代码怎么改了??
然后他就GG了。

也就是说这个人,他瞄到的数据 不是我们的最终结果,而是我们中间过程的数据。
这个中间过程的数据是能会被改的。

此时的情况,就是脏读。
放在数据库中:事务A在对某个数据进行修改的同时,事务B 去 读取了 这个 数据。
此时,事务B 读到的很可能是一个“脏数据”(这个数据是一个临时的结果,而不是最终的结果)
再举一个例子:测体温,填体温表。
我们测完体温之后,去填体温表。
不知道哪根筋断了,把 36.7, 写成了 46.7。
此时被后方的同志看到了,立马就去打报告了。
等我们将体温改回来的时候。
发现此时,我们被人围住。。。
结果可想而知,不管你是不是真的填错了,都要抓你去隔离了。

此时的 46.7 就是一个“脏数据”

出现脏读问题的原因:就是事务与事务之间没有进行任何的隔离。
其实加上一些约束限制,就可以有效的避免脏读问题。

脏读问题 的处理方法

就是给 写操作 加上锁。
意思就是 在 写的过程中,别人看不到的。(加锁的状态)
写完之后,别人看到了。(解除枷锁)

举个例子:
还是前面的例子,只不过改一下。笔试偷窥不太好。
假设 我们和自己的朋友一起学习代码,遇到他们一道题他们不会,想借鉴一下我们的代码思维。
但是我们和朋友不要急,等我写完,你们再来看。
免得你们看的都是错误的,
现在我们在写代码的时候,就不会有人回过来看。
等我们写完了,才有人来看我们代码。
此时我们就避免了脏读的发生。别人看到的是最终结果,而不是中间数据。

放在数据库中,事务A 在 访问 一个数据的时候,将这个数据上锁,一旦上锁之后,其它事务就不能访问这个数据了,意味着事务之间的隔离性提高了。
并发发生的概率就降低了。【简称并发性降低】

疑问:当我们给数据上锁了,那么我们的事务就没有问题了吗?
答案:不是的!

第二个问题:不可重复读

不可重复读问题的处理方法

有了脏读的处理经验,不可重复读的问题也就很好处理。
即:当读者进行阅读博客的时候,身为作者的我们不能改动博客。
反过来说:当读者看完了我们的博客,我们才能对其进行改动。
综合来讲:给 读操作也上锁。
也就是说:现在的 情况就是 我们写博客的时候,读者不能阅读;读者阅读的时候,我们不能改博客。
意味着我们必须得等到读者读完了,才能进行修改。
因此通过给读操作也加上锁,就可以解决不可重复读的问题。

小结

根据前面的问题,相比大家已经进一步认识到 并发性 与 隔离性。
简单来说:并发就是多个事务一起运行。
          隔离就是 处理完一个事务后,再处理它的事务(就是一个个来)。

你以为就完了?不存在的!还存在一个问题:幻读

幻读问题的解决办法

想要解决幻读问题的办法至于一个,彻底 串行化 执行。
也就是说:读者在堵我们的博客的时候,叫我们摸鱼。不要写任何博客了!让我们来卷一卷。
简单来说,就是一个个来,出完一个事务,再处理一下事务,以此类推。
(期间不能做任何其他的事务操作。)
这种执行方式,隔离性最高,并发性最低,数据最可靠,速度最慢。(强制摸鱼)

隔离性 总结

以上我所讲的这些,就是关于隔离性相关问题。
并发执行的速度很快;隔离执行的数据最为准确,但是两者是不可以兼得的。
需要根据实际情况进行调整隔离的级别。
通过不同的隔离级别,就控制了事务之间的隔离性,也就是控制了并发程度。
从而在 快与 准之间,找到一个平衡点。

MySQL中事务的隔离级别

1、read uncommitted :允许读取未提交的数据,并发程度最高,隔离最低,会带有脏读 + 不可重复读 + 幻读问题
2、read committed:只允许读取 提交之后的数据,相当于写加锁。并发性降低,隔离性提高。解决脏读,但带有 不可重复读 + 幻读问题
3、repeatable read:相当于给读和写操作都上锁了,并发性进一步降低,隔离性进一步提高。解决脏读、不可重复读,但带有 幻读问题。
4、serializable:串行化,并发性降到最低(串行执行),隔离程度最高,解决了脏读、不可重复读、幻读问题。但是运行的速度是最低的。
MySQL 可以通过修改 配置文件(my.ini) 来进行设置当前的隔离级别。
这样就可以根据实际情况,来决定使用哪种隔离级别。

注意!以上这些关于事务的都是考点。

拓展:如何查询当前服务器数据库的隔离级别 与 设置当前的隔离级别的指令

//设置read uncommitted级别:
set session transaction isolation level read uncommitted;
中文意思:设置 会话事务 的隔离级别为 read uncommitted

//设置read committed级别:
set session transaction isolation level read committed;
中文意思:设置 会话事务 的隔离级别为 read committed

//设置repeatable read级别:
set session transaction isolation level repeatable read;
中文意思:设置 会话事务 的隔离级别为 repeatable read

//设置serializable级别:
set session transaction isolation level serializable;
中文意思:设置 会话事务 的隔离级别为 serializable

下一篇:数据库中的JDBC编程(现在还没写完,写完了这里就是蓝色链接字体)

相关文章

微信公众号

最新文章

更多