数据库之索引和事务

本文主要讲述了数据库的学习中,涉及到的索引以及事务的知识点,以下是我个人的一些所得。


一、索引

1.1、基本概念

索引是一种特殊的文件,包含着对数据表里所有记录的引用指针。
简单来说,索引就好类似于一本书的目录部分,它存在的最大意义就是,加快数据的查找速度。(省略了每次获取数据都得重新遍历一遍)

1.2 索引的操作

1.2.1 查看索引

SQL格式:
show index 索引名 from 表名;

例如创建一张学生表Student,以student_id 为自增主键;此时这个自增主键就是默认的一个表索引
create table student (student_id int primary key auto_increment,name varchar(20));

在这里插入图片描述

1.2.2 创建索引

SQL格式:
create index 索引名 on 表名(列名);

右下图可以看到,这里以 name 字段重新生成了另一个索引。
在这里插入图片描述
注意!!!
创建索引操作可能会很危险,如果表中的数据是非常庞大的,那么建立索引的开销也是十分巨大的;比较推荐的做法是:创建表的时候就把索引设定好;如果表里已经有很多数据了,那么索引就按照现在的,不要去新增或者删除了。

1.2.3 删除索引

SQL格式:
drop index 索引名 on 表名;

如下图,name 字段的索引成功被删除了。
在这里插入图片描述
注意!!!
和创建索引一样,在数据量庞大的情况下,删除索引也是非常危险的!!

1.3 索引背后的数据结构

1.3.1 引入

要操作索引相对是比较简单的,但是比较复杂的是,理解其背后的数据结构(即数据以何种形式来进行存储的)

首先,我们学习索引的**初心**是可以加快查找数据的效率。那么问题来了,哪些数据结构可以用来加快查找呢??
我的第一反应就是可以使用 “二叉搜索树” 、“哈希表”,让我们来简单分析一下这两种数据结构是否适合作为 MySQL 背后组织数据的数据结构。

二叉搜索树 : 由于二叉搜索树是一种特殊的二叉树,有着特定的数据组织方式; 但是二叉搜索树每个节点只保存一个 key 的值;如果要存储的元素多了,会导致树的高度增加,树越高相当于是查询的时候,比较次数就越高;对于数据库来说,则是 IO 访问次数增加,因为数据库中的数据都是保存在硬盘中的。
哈希表:哈希表计算哈希值去查找元素,虽然查找效率快,但是也存在缺陷的地方;比如它就不支持 数据库基本操作中的 “范围查询“ 和 “模糊查询”。

综上所述:最终 MySQL 所采取的数据结构是 B+ 树!!

1.3.2 B 树

首先,我们先来简单认识一下 B树。
如下图所示,这就是一颗 B树(也称为 B- 树):
在这里插入图片描述

B树特点:
一个节点中包含了多个 key 值,N 个 key 值将节点数据分成了 N+1 个区间。
当节点的子树多了,节点上保存的 key 多了,意味着在同样的 key 数量情况下,树的高度相对于二叉搜索树就降低很多了。【树的高度越高,比较次数越多,IO 访问磁盘的次数就越多】

1.3.3 B+ 树

认识了 B 树,再来看一下 B+ 树。B+ 树是在 B 树的基础上,做了改进!!
【也是相当于一棵 N 叉搜索树】
如下图,是一棵 B+ 树:
在这里插入图片描述

B+ 树特点:
1.一个节点可以存储 N 个key, N 个 key 划分 N 个区间。(而不是 N+1 个)
2.父节点中的 key 值,都会在子节点中出现。(同时该 key 值也是子节点中 的最大值)
3.B+ 树的叶子结点是首尾相连,类似于一个链表。
4.由于叶子结点是完整的数据集合,只在叶子结点中存储数据库中每一行的数据,而非叶子结点只存储数据中的 key 值即可。

B+ 树的优点有以下几个方面:

1.当前一个节点中保存的 key 值更多,最终构建的树的高度就较低了;对于数据库,IO 访问磁盘的次数就减少了。【这点和 B树一样】
2.由于最终的完整数据都是在叶子结点上,那么对于每一个数据,查找的次数都一样,IO 访问次数一样。
注意!! 这样很稳定,稳定对于程序猿来说很重要,能够更准确的评估程序的执行效率!!
3.B+ 树的所有数据构成链表,更加方便进行 “范围查询” 和 “模糊查询”。
4.由于非叶子结点中存储的并不是完整的数据,而是对应的 key 值,那么就会占用空间较少;这些非叶子结点可能就在内存中缓存,不需要再去磁盘中读取,又进一步减少了 IO访问。

1.4小结

以上所述的 B+ 树就是 MYSQL 背后组织数据所使用的 数据结构。当你在MySQL 中看到一张 “表” 的时候,实际上,这个表不一定按照 “表格” 这样的结构存储在硬盘上的,也有可能是按照这样的 B+树 来进行存储的。

二、事务

2.1 基本概念

什么是事务呢?
简单来说,事务的本质就是把多条 sql 语句进行打包成一个整体,执行的过程中,要么都执行成功,要么一个也不执行;而不会出现执行到一半这样的中间状态。
注意一个小细节 此处的一个也不执行,并不代表真正意义上的,一条语句也不执行;而是 “看起来没执行一样”,如果执行到一半出错了,选择恢复现场,把数据恢复到未执行前的状态;这样的恢复数据的操作,称为 “回滚”

2.2 特性

事务有四个特性

2.2.1 原子性

原子性是事务最核心的性质。所谓原子,就是不可再划分了,即一个事务是一个整体。

2.2.2 一致性

事务执行前后,数据是靠谱的。

举个例子来说:比如 A 给 B 进行转账操作,B 账户原来有1000元,此时A 给 B 转了 1000,此处转账就是执行一次事务,如果保证一致性,那么 B 的账户里的余额应该是 2000元;如果 B 的账户不是 2000,那么此时就不叫做事务具有 一致性。

2.2.3 持久性

事务修改的内容是写到硬盘上的,持久存在的,重启也不会丢失。

还是 A 给 B 转账的例子,当 A 转了1000 给B,B 这一次的账户余额确实是 2000,当下次打开余额的时候,应该还是 2000,而不是其他余额。

2.2.4 隔离性

隔离性是为了解决并发处理事务产生的问题。

首先,何为并发?
一个餐馆 (服务器) 在营业的时候,是给多个顾客 (客户端) 提供就餐的,那么这些顾客是一个接着一个来呢?还是一起涌进来点餐呢?两种都有可能!!
此时,服务器就要同时处理多个客户端的请求,就称为 “并发”(齐头并进的感觉);
那么,数据库也是个服务器,也会有很多客户端同时发请求,数据库就要并发处理多个事务了。

那么问题来了,如果这些事务尝试的是 修改不同的表/不同的数据,此时不会有啥影响;但是如果现在多个事务在修改同一个表/同一个数据,那么就会带来一定的问题~~
事务的隔离性,就是数据库在并发处理多事务的时候,不会出现上述这个问题的。

并发处理事务,会出现哪些问题?并且出现这些问题,事务的隔离性是如何来解决的?

2.2.4.1 脏读问题

先举个例子简单了解一下脏读是个啥哈。
假如,我在写代码,房间门没锁,突然有个同学从我后面走过去,他偷看了一眼我写的代码,然后就走了,很有可能,他走了之后,我又接着修改我的代码了~此时,他读到的代码就不是最终的代码了。

一个事务A 正在对数据进行修改,还没提交,另外一个事务 B,读取了同一个数据,此时事务B 的操作就是 “脏读”,事务B 读取到数据,就是一个 “脏数据”,脏就是无效的意思。

那么如何解决 脏读问题 呢?
很简单,MySQL 为了解决这样的问题,引入了 “写加锁” 的机制。 意味着就是,在我写代码前,和同学约定好,我写的时候,他不能看,当我写完提交到github上,再去看。

当我写的时候,意味着我的 写操作 和同学的 读操作 不能并发了(不能同时进行),这个给写加锁操作,就降低了程序执行效率,但是提高了隔离性。(提高了数据的准确性)

2.2.4.2 不可重复读

前面约定好了 写加锁 操作,解决好了 脏读 的问题。那么接下来还会出现什么问题呢?
还是我在写代码,写之前已经和同学约定好了,我写的时候,不能去读。当我写完提交到github后,同学就去开始读取代码,读到的是版本1,但是此时,我又打开了这个代码,重新修改了这个代码,重新提交了版本2,那么此时同学读着读着,突然代码变了,出现这样的问题就是 不可重复读。

事务A 已经提交了数据,事务B开始读取数据,但是此时事务3,针对提交到数据,重新修改提交新的数据,此时意味着事务2多次读取数据,读出来的数据不一样(预期是多次读取数据,结果是一样的),这个问题就叫做 不可重复读

那么,又如何解决 不可重复读 问题呢?
仔细想想并不难,找到出现 不可重复读 的问题。同学知道他这个问题后,知道是他在读代码的时候,我又改了代码,于是他又跑来找我约定,在他读代码的时候,我不能修改~

刚才的约定是,写的时候,不能读,即 写加锁;
现在约定,读的时候,不能写,即 读加锁。
通过这个读加锁,又进一步降低了事务的并发处理能力(降低了执行效率),但有提高了隔离性(t提高了事务的准确性)

2.2.4.3 幻读

有了前面的 脏读 和 不可重复读 的限制,即 写加锁 和 读加锁 了。
此时,如果同学读提交上去的代码,此时我不能修改这个代码,但是,我闲着没事,可以修改其他的代码啊;假设这里同学正在读 student.java 文件,我去修改了 teacher.java 的文件,不影响同学正在读的内容,但是这个时候,同学可能就会发现,读着读着,莫名多了个 teacher.java 文件出来,有些时候就接受不了~~

在写加锁 和 读加锁 的前提下,一个事务两次读取同一个事务,读到的结果是一致的,但是结果集是不一样的(第一次读到的只是 student.java 文件,第二次读取到了student.java 和 teacher.java文件),这种就被称为幻读

那么,幻读 问题又该怎么解决呢?
方式很简单,彻底 串行化!! 相当于同学在读代码的时候,我彻底啥也不干,就搁在那里摸鱼了~~

数据库使用 串行化 的方式解决幻读问题,彻底放弃并发处理事务,一个接一个的串行处理事务,这样做,并发程度是最低的(效率是最慢的),但是隔离性是最高的。数据的准确性最高)

2.2.5 小结

对于上述三个问题,就是数据库处理并发问题典型方式:
脏读 => 给写加锁
不可重复读=>给读加锁
幻读 => 彻底串行化

另外,对于上述问题,MySQL 提供了四种隔离级别,就对应上述几种情况:

read uncommitted 没有进行任何加锁 ,并发效率最高,隔离性最差 (数据准确性最差)
read committed 写加锁,并发程度降低,隔离性提高(数据准确性提高)
repeatable read 写和读加锁,并发程度又降低,隔离性又提高 (数据准确性又提高)
serializable 彻底串行化,并发程度最低,隔离性最高 (数据准确性最高)

三、总结

关于《数据库的索引和事务》就讲到这,欢迎大佬们的留言以及批评指正,如果文章对您有帮助或者觉得作者写的还不错的可以点一下关注、点赞,收藏支持一下。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值