目录
索引:
索引的必要与弊端
查看索引
创建索引
删除索引
索引的使用
索引的数据结构:B+树
B+树的特点
B+树的好处
多个索引的B+树
事务:
回滚
事务的使用
事务的特性
隔离性
"脏读"问题
"不可重复读"
"幻读"问题
隔离级别
read uncommitted
read committed
repeatable read
serializable
本质上相当于"书的目录",通过目录,就可以快速找到某个章节的位置.
索引的效果就是为了加快查找的速度,进行数据库操作,无非就是增删查改,在多数场景下,查的概率是比增删改要多很多的,所以,多数情况下,引入索引还是很有必要的.
但是,索引也提高了增删改的开销,此时进行增删改,就需要调整已经创建好的索引了;索引也提高了空间的开销,构造索引,也就需要额外的硬盘空间来保存了.
软件开发里,没有"银弹",没有哪个办法,能够通吃一切问题,总是在取舍,就需要因地制宜的,灵活的应用解决方案.
show index of 表名;
针对学生表来说之前没有创建过索引,但是他还会自带一个索引.
表里如果有主键,主键这一列就会自动创建索引(自带的索引),unique和foreign key也会自带创建索引.
create index 索引名字 on 表名(列名);
如果查询操作,很多时候是按照名字来查询的,此时我们就可以针对名字创建索引.
这个时候我们就有两个索引了.(主键索引)(针对名字的索引)
创建索引,最好在表创建之初就把索引创建好,否则,如果是针对一个有很多记录的表,来创建索引,也是一个危险操作. 这个时候就会吃掉大量的磁盘IO,花很长的时间,在这段时间里,数据库无法被正常使用.
索引是为了加快查询的速度,也不是所有的情况加上索引就一定快,如果名字有重复,索引也是可以加上的,只要重名不是很多,这个时候还是可以大大提高查询速度的.
但是如果针对像性别这样的列加索引,或者针对大学生管理系统的年龄这样的列加索引,是无法提高查询速度的.
drop index 索引名字 on 表名;
和刚才的创建索引类似,删除索引也有可能会吃掉大量的磁盘IO,也是危险操作!
最开始设计数据库的时候,创建表的时候就要规划好,要是已经有大量数据,再进行操作,就要慎重!
把索引创建好了之后,不需要手动使用,直接查询的时候,会自动来走索引.
SQL是通过数据库的执行引擎来进行执行的,这里要涉及到一些"优化"操作,执行引擎会自动评估,哪种方案是成本最低,速度最快的.具体这一次查询,实际上是否在走索引,以及怎么走的,其实是不好预测的.
索引的数据结构是B+树(N叉搜索树的一种),B+树就是为了索引这个场景,量身定做的数据结构.
1.B+树也是一个N叉搜索树,每个节点上可能包含N个key,N个key划分出N个区间,最后一个key就相当于最大值.
2.父元素的key会在子元素中重复出现,并且以最大值的姿态出现.这样的重复出现,导致叶子结点就包含了所有数据的全集,非叶子结点的所有值都会在叶子结点中体现出来.
3.会把叶子结点,用类似链表的方式,首尾相连.
1.作为一个N叉搜索树,高度降低下来,硬盘IO次数就比较少了
2.更适合进行范围查询
3.所有的查询,都是要落在叶子结点上的,无论查询哪个元素,中间的比较的次数是差不多的.
(对于B树来说,可能有的值查的快,有的值查的慢,可能就不均衡了,在根节点,或者深度不深的位置,都查的快,而B+树,无论查哪个元素,都是一样的速度)
4.由于所有的key都会在叶子结点中体现,因此非叶子结点,不必存表的真实记录(不必存数据行)
只要把所有的数据行给放到叶子结点上即可,非叶子结点只需要存索引列的值(比如存个id),数据库里的一条记录,十一行(有很多列,甚至几十列)
由于非叶子结点只存了简单id,没有存一整行,这就意味着,非叶子结点占用的空间,是大大降低的,就有可能在内存中可以放进去缓存,更进一步降低了硬盘IO.(提高查询速度,本质上就是在减少硬盘IO次数!).
对于带有主键的表,就是按照主键索引的B+树来组织的,有的表,不只是主键索引,还有别的非主键列,也有索引,这个情况会构造另一个B+树.B+树非叶子结点里面存的是这一列里面的key(比如一堆学生姓名),而到了叶子结点这一层,不是存完整的数据行,而是主键列的值(比如主键id).
使用主键列来查询,只要查一次B+树就可以了.
如果是使用非主键列的索引来查询,则需要先查一遍索引列的B+树,在查一遍主键列的B+树.("回表")
事务属于是能够把多个SQL给打包到一起,变成一个整体.
上述这些操作,要把多个操作打包成一个整体,要求:要么就全部都执行,要么一个都不执行.
事务中打包成整体这个操作,就称为"原子性",这个是事务最最核心的特性.
执行中间出错了,就让一条都不执行,注意:这里并不是真的一条都没执行,而是自动恢复成执行之前的样子,看起来就像没有执行一样!!这里涉及到一个关键操作,"回滚"(rollback).回滚就是把执行过的操作逆向恢复回去.(类须于电脑上的ctrl+z).
数据库会把执行的每个操作都记录下来,如果某个操作出错,就会把事务中前面的操作进行回滚,根据之前的操作进行逆操作.(前面是插入,现在就删除;前面是删除,现在就插入;前面是修改,现在就改回去).
这些操作是有很大开销的,可以保存,但不可能无限保存,最多就是把正在执行的事务保存下来,额外的东西就不好在保存了.
1.原子性(事务的初心)
2.一致性:事务执行前/执行后,都得是数据合法的状态.
3.持久性:事务产生的修改,都是会写入硬盘的,即使程序重启/主机重启/掉电,事务都是可以正常工作,来保证修改是生效的.
4.隔离性:一个数据库服务器,同时执行多个事务的时候,事务之间的影响程度.
mysql服务器,要同时给多个客户提供服务.
此时多个客户端之间,可能会同时发起事务.尤其是这多个事务在操作同一个数据库的同一个表时,就很有可能引起一些麻烦.
如何权衡隔离程度,如何取舍执行效率和准确性,需要根据实际需求,具体问题具体分析.
mysql给我们提供了不同的挡位(控制隔离性的高低/并发程度的高低/执行效率的高低/数据准确性的高低).
要了解这几个挡位,就必须掌握几个问题:
举例理解脏读问题:有一天,同学A,看到老师正在备课,正在写一个课堂代码,他看到老师的代码里写了个class student....
此时同学是事务A:在读数据
老师是事务B:在写数据
老师和同学读写的是同一份数据.
同学看完之后,就走了,在同学走之后,老师把这个class student给删了.
在这个场景中,同学和老师这两个事务,是完全并发的,没有任何限制,在这个前提下,就会出现脏读问题.
脏读问题在很多时候都很棘手,解决脏读问题的办法,就是降低并发性,提高隔离性.具体来说,就是给这里的写操作"加锁"(老师在写代码的时候,同学不要看,等老师把代码提交到码云,同学再看)
当进行了写操作加锁后,老师在写代码的时候,同学就不能读了,相当于降低了并发程度,提高了隔离性,降低了一定的效率,但是提高了准确性.
现在已经约定了写操作加锁(老师在写代码的过程中,同学不要读,等老师提交了之后,同学在读)
老师这边在写代码,有个同学想看,突然想起老师交代过,要提交了之后,才能看,所以同学只能等,等到老师写完提交.
过了一会,老师写完了,提交了,同学开始读代码.
当同学正在读这个代码的时候,老师突然有了新的想法,于是老师动手修改,重新提交了新版本,导致这个同学读代码读了一半,突然代码自动就变了.
"不可重复读"
在一个事务中,连续两次读到的数据,结果不一致.
如何解决:给读操作也加锁.同学在读代码的时候,老师不能修改.
此时,这两个事务之间的并发程度,进一步降低了,隔离性又进一步提高了,运行速度进一步变慢了,数据的准确性又进一步提高了.
当前已经约定了写操作加锁和读操作加锁,老师写代码的时候,同学不能读,同学读的时候,老师也不能写(注意这里针对的是同一个代码文件)
老师看到同学在读了,老师虽然不能去修改同学正在读的这个文件,但是老师此时可以做别的工作,比如新增/删除一个其他的文件.
这时候,老师又有工作做了,就不用等同学读完.
但是此时,虽然同学读的代码内容没变,但同学发现,文件的数量变了.
这个问题称为"幻读"问题:在同一个事务中,两次读到的结果集不一样.
解决幻读问题:串行化,彻底的舍弃并发.(只要同学在读,老师就只能闲着)
MySQL根据以上问题,提供了四个隔离级别:
不做任何限制,事务之间都是随意并发执行的.并发程度高,隔离性最低.会产生"脏读"+"不可重复读"+"幻读"问题
对写操作加锁了.并发程度降低了,隔离性提高了. 解决了脏读问题,仍然存在不可重复读+幻读问题
对读和写都加锁了.并发程度又降低了,隔离性又提高了. 解决了脏读+不可重复读,可能存在幻读问题
严格串行化.并发程度最低(串行执行的),隔离性最高.解决了脏读+不可重复读+幻读问题,执行速度是最慢的.
mysql的默认挡位是repeatable read,第三挡.
开发中就可以根据当前要解决的问题的需求场景,来决定使用哪个隔离级别.
挡位是通过mysql的配置文件来进行调整的.