MySQL秘密武器:索引与事务
ok,今天来分享下MySQL中的索引与事务。
当我们进行查询的时候,数据咻的一下就查询出来,速度很快噢。
那么这里呢,就有了索引的功劳了。
所以,先来介绍下索引是什么?
索引
索引是数据库中的一种数据结构。但我们可以理解为像是一本书中的目录,而我们更早接触到类似的,比如我们学习的数组的下标。
由前面的引入,显然,索引的引入是为了加快查询速度的。
一枚硬币既有反面也有正面。显然这个索引也是带来一些缺点(代价)
1.引入索引会消耗一些存储空间
2.索引的引入,确实能提高查询速度,但不总是能提高增删改的速度。
体现在:加快、拖慢、没什么变化
加快:比如删除操作:delete from student where id=5;这里就涉及了查询。
拖慢:比如进行增删改的时候,需要同步更新维护索引。
所以,这么看来索引可谓是有利也有弊。所以大家要辩证的去使用它。
那么讲到这,索引如何去使用呢?
索引使用
1.查看索引
语法:show index from 表名
这里举个例子
这里有两张表:cla、student。
所以我查询两张表的索引:
这里cla查询出来是空的,student是存在一个索引的
为什么呢?
回到展示表结构那个图片。
可以发现,cla中那key列中,没有任何信息。
所以查询出来的时候就没有索引信息。
所以就此得出了,primary key是会自带索引的。
同时也值得注意的是,这个primary key索引是不用我们手动添加的,指定主键后就会自动添加。
那么就只有这一个?
当然不是
还有unique 和 foreign key
unique
foreign key
还有一些注意的点,你对那个列进行索引创建,那么查询的时候以这个列为条件,查询就快。
同时一张表中可以存在多个索引,但是主键不能存在多个噢。
2.创建索引。
语法:create index 索引名 on 表名(列名)
还是举个例子
创建一个班级表,为id列创建索引,索引名为index_id
这个操作具有一定危险性,
当前表的数据量好说,一旦涉及到千万级别的表数据,那么这个操作就会触发大量的硬盘IO,可能导致机器垮掉。
因为,索引创建中,有一步就是要进行全表扫描,搜集所有列的值并构建索引机构,这样是产生磁盘的大量读写操作。
所以,创建索引的过程,一般也是事先商量好的,或者规划好的。
3.删除索引
语法:drop index 索引名 on 表名
举个例子
刚刚为class创建了个index_id索引,现在删除掉。
但值得注意的是,像是primary key 和 foreign key这些创建之后,数据库自动生成的不能删除。
那么讲到这里,索引一些操作讲完了。
接下来就要讲讲另外一个了,
前面说到索引是一种数据库中的数据结构。
那么这种数据结构是什么呢?
索引背后的数据结构
是我们接触数据结构时的那些?
比如顺序表、链表、栈、队列?
还是二叉树搜索树、哈希表?
顺序表、链表、栈、队列这些是可以pass掉了,毕竟有些能做其一,不能做其二,要么就是插入耗资源,要么就是查找耗资源。
那么二叉搜索树和哈希表呢?
其实也不然,二叉搜索树,重点在二叉上,当我们插入很多数据时,树的高度很高了,再去插入,对硬盘也是一个不小的挑战。
哈希表呢?
查询速度是很快,但不能进行模糊查询,进行的是精确性的查找。
那么到底是什么适合呢?
答案揭晓:
B+树
当然介绍这个之前,先来浅浅介绍下B树
B树是一棵n叉搜索树,每个节点都能分出多个子树。
这么一说,发现,这样就是可以降低树的高度咯。
那么举一个实例图咯
由图可以得知,一层中保存了n个key节点,可以分出n+1个子树来。
虽然,这里插入还是需要多次比较,才能走下一个。
但是比二叉搜索树优势很大,起码高度降低不少,且一层一层节点中的数据全部一次硬盘IO就行,不在需要多次IO了。
那么B+树呢?
B+树是B树的进一步优化的数据结构来的,同时它也是一个n叉搜索树。
还是给个图片看看
这是一个B+树的简单示意图。
在第一层节点中16,就是整个树的最大值。
对比B树,发现是少一个区间的。
即N个key,就是有N个区间。
同时还发现了,父节点的中的最大值会下放到子区间中,作为最大值,比如第一层中的8,作为父节点,下放到左边的区间是作为最大值的。
最后,在叶子节点中,所有的值会呈现数据的全集,就像是1-16全部的集合
通过链表的形式存储下来。
同时值得注意的是,这些非叶子节点是不存储数据行,叶子节点才存储,所以也得出了,我们查询数据都是落到叶子节点进行的。
所以当我们进行范围查询的时候,这个就特别有用。
ok,可以来简单总结下B+树的优势有什么
优势:
1.N叉搜索树,高度较低,磁盘IO较少。
2.叶子节点是表示数据范围的全集,并用链表进行连接,非常便于进行查询
3.B+树所有的查询,最终落到叶子节点下,任何的一次查询,我们的磁盘IO次数和比较次数是不会相差太大,,所以查询开销较为稳定。
4.同时B+树中,由于非叶子节点不存储“数据行”,存储索引列的key值,叶子节点才存储“数据行”,所以占用的空间少。
ok,到这里,分享完第一个秘密武器了。
接下来到事务了。
事务
咋一听这词有点陌生呢。
这是我们不常接触的话,就有点陌生咯。
事务是什么呢?
事务是一组作为单个逻辑单元的执行的SQL语句。
简单来说,就是解决一些特定场景的问题的。
举个例子。
我们平时用的较多的转账。
比如有两个这样的账户:
1:1000
2:500
那么现在1对2进行转账500元
一般的操作流程就是先对1账户进行减去500元
再对2账户进行增加500元;
不出意外的,一切都顺利成章,那么账户1就是有500,账户2就有1000.
但意外总是无处不在的,万一执行第一个操作之后,突然出现程序崩溃、主机崩溃啥的,没能执行第二个语句,那么数据库内容就会出错。那客户不得更银行急眼。
所以为了解决这个问题,引入了事务。
把这些语句打包成一个整体,这个整体执行中,要么全部执行完,要不都不执行。
但值得注意的是,都不执行这里不是说一个sql语句都不执行。
而是执行到一半的时候,发现错误的时候,数据库会自动进行“还原操作”,把之前执行语句给“撤销”,达到什么都没有做的效果。
这个“还原操作”在数据库中就成为回滚(rollback)。
那么数据库是如何知道,怎么回滚?怎么把之前的操作复原的呢?
这里呢归功于数据的库中强大的日志体系
这一系列的日志体系,会把我们开启事务的时候,执行的每一个sql语句,以及对数据进行哪些修改,都会记录下来。
这样当我们主机断电,程序错误且需要回滚的时候,就可以根据记录的信息,进行还原操作了。
但是我们要注意的的是,回滚只在事务中进行,还有,事务中的sql语句也是不能够太多的。
诶,上诉这里就涉及到了事务的一个特性——原子性。
那么讲到了事务的特性,那么事务是特性有什么呢?
特性
1.原子性
刚刚上面讲诉过了
2.一致性
事务执行前执行后,数据库中的数据都是“合法的状态”,不会出现非法临时结果性的状态
3.持久性 事务执行完毕后,就会修改硬盘上的数据,而且这些修改是持久生效的。
4.隔离性
这个呢,解释起来,就是说,描述多个事务并发执行的时候,相互之间产生的影响是怎样的。
诶,并发执行又是怎么样呢?
比如说,MySQL中提供的多个客户端,且每个客户端提交了多个事务给到服务端,与此同时,服务端收到多个事务需要同时执行的时候,这个时候就是并发执行。
都在执行的话,或多或少会产生一些问题的。
比如,当同时执行这些事务的时候,恰好针对一个表,且进行了一些增删改查。
那么我们可能会引入这样的问题:
1.脏读
2.不可重复度
3.幻读
那我们来一一解释下
脏读:
比如现在有两个事务A和B并发执行
其中事务A对某个表进行数据修改。与此同时,B对这个表进行读取,B读完后,A呢就把数据进行修改完成了
这样导致了B读的数据,不是A最终改为的数据,而是临时性的“脏数据”,也可以称之为“过期了”。
通俗点,当我进行对我的白月光写一篇“小作文”的时候,我的朋友偷看到了,然后并没有等我写完,就去告诉了我的白月光,可是我写到一半,发现不对,重写了再次发给白月光,那么白月光收到的信息和我朋友传达的信息就不一致了。
解决办法也挺简单的,就是约束一下,我写的时候,我的朋友不能读,等我写完提交给我的白月光了,你再来读。
不可重复读:
这里呢涉及到了三个事务了,
比如A事务执行修改操作,然后把数据提交了,然后B事务进行读取A提交的数据的时候,突然来个C事务进行把刚刚A提交的事务进行修改了,然后当B再次读取时候,就会发现,第一次读取和第二次读取,出现数据不一致。当然,这里更多指的是针对一个事务来讲,比如B事务
通俗点解释
还是那我给白月光写“小作文”的例子,这次呢,我写完了,我本人没做修改,同时我的朋友也是完完全全读取到了,然后我的朋友屁颠屁颠的去我的白月光那边再想看一次,这时候,我发现写的不好,我教我的“文豪"朋友来帮我把之前的”小作文“修改一下,然后再次发给我的”白月光“。此时当我那个朋友去到白月光那边进行读的时候,发现第一次读和第二次读,发现不一致了。
如何解决呢?
还是约定一下,当我那个朋友在我白月光那边进行读取的时候,就不能修改正在读的文章了
幻读:
比如,有一个事务A在读取数据的时候,读的过程中,另外一个事务B进行增删一些数据,此时,在A的角度上看,当进行结果集查询的时候,第二次查询出现了第一次不存在的记录。
那么如何去解决呢?
这里我们直接一些,当事务A读的时候,B事务不能进行任何操作,有点线性化那样。这样的操作称之为串行化,就是一个个排队执行完事务。
那以上三个问题和隔离性有什么关系呢?
诶,这里的MySQL隔离提供了四个级别,通过一些配置文件来设置当前服务器的隔离级别是哪个级别,从而影响到三个问题出现的情况。
那么有哪四个呢?
1.read uncommitted 读未提交
这样的情况下,一个事务是可以读取另一个事务未提交的数据,那么及其容易出现脏读、不可重复读、幻读的问题。
但此时事务并发执行的速率是最高的,执行速度最快!
2.read committed 读已提交。
这样的情况下,一个事务只能读取已经提交的数据,可能会产生不可重复读或者幻读问题。但脏读解决了。
此时,并发程度降低,速度也降低,但隔离性提高了,也就是说,事务间的相互影响较小,数据更准确些了。
3.repeatable read 可重复读。
这样的情况下,就是进行了一些写和读操作的加锁,当这个事务正在执行的时候,不能对此内容进行读和写,解决了不可重复读和脏读问题,可能会出现幻读问题。同时速度降低,并发程度进一步降低,但事务间的隔离性进一步提高!
4.串行化
这个时候,事务是在服务器上一个个的执行,就像排队一样,这时候脏读、幻读、不可重复读也解决了。此时,速度最低,并发程度也是最低的,但是隔离性是最好的,数据最准确!
当然,这里提到的一些速度和准确性是相对业务场景而言,什么时候需要快,什么时候需要准确,
都是对业务需求分析后而定夺的。
那么,这个事务和索引,就浅浅的分享到这啦~