1.索引是一种特殊的文件,包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引,并指定索引的类型,各类索引有各自的数据结构实现。
2.当从数据库中进行查找的时候,例如按照一定的条件来查找,查找可以遍历表,但是遍历操作,比较低效。就需要想尽办法的避免遍历,可以通过一些特殊的数据结构,来表示一些记录的特征,通过这些特征来减少比较次数,加快比较的效率。
索引主要的意义就是进行查找,要提高查找的效率,但是查找的效率是提高了,但同时会付出代价。
以一本书为例
1.书的目录是费纸的,数据库的索引,也需要消耗一定的额外存储空间,数据量越大,索引消耗的额外空间剧越多。
2.书的目录确定了,后序每次对书的内容进行调整,都可能会影响目录的准确性,就需要重新调整目录。数据库的索引也一样,进行增删改的时候,往往也需要同步调整索引的结构。
索引带来的好处,提高了查找的速度
索引带来的坏处,占用了更多的空间,拖慢了增删改的速度。
既然它的缺点比优点更多,那为什么还要用它呢???
索引是一个很常用的东西。实际需求场景中,查找操作往往是最高频的操作。
例如像老师布置作业一样,每节课都要布置作业,每天都要进行查询操作,但是如果某个同学反馈某个作业不合适,就需要进行增删改,可能这些操作几周或一个月才操作一次。
show index from 表名;
查看student表的时候,发现里面已经自带了一个索引,这个自带的索引,就是primary这个主键约束带来的。
查询的时候,如果查询条件指定了根据主键查询,这个时候查询速度就会非常快。
unique 也带索引
创建索引是非常低效的,尤其是当表里有很多数据的时候。
注:后期针对线上的数据库,如果这个数据库没有索引,不要贸然创建索引。
删除索引操作和创建同理,都是非常低效的事。
在创建表的时候,就应该把索引规划好。
索引背后的数据结构要能够加快查找的速度。
此处说的查找,是“按照值”查找,不是按照“下标查找”。
扩展:为什么顺序表按照下标的访问速度快???
顺序表是在连续内存空间上的,内存支持随机访问操作。
二叉树不适合作为索引:当元素多的时候,高度就高了,高度对应着比较次数,对于数据库来说,每次比较都意味着磁盘IO.
哈希表也不适合。虽然哈希表的查找速度很快O(1),但哈希表只能很对“相等”进行判定,不能对“”大于小于”以及范围查找进行判定。
最适合做索引的,还是树形结构,如果使用“多叉搜索树”,高度自然就下降了。
在数据结构中,这样的树称为 B+ 树(这个是数据库索引中最常见的数据结构)。
1.索引是干什么的
2.索引的适用场景,付出的代价
3.索引背后的数据结构
事务诞生的目的就是为了把若干个独立的操作给打包成一个整体。
举个例子
比如说,四十大盗把从阿里巴巴的账户上偷盗了2000元– 阿里巴巴账户减少2000
update accout set money=money-2000 where name = ‘阿里巴巴’;
– 四十大盗账户增加2000
update accout set money=money+2000 where name = ‘四十大盗’;
假如在执行以上第一句SQL时,出现网络错误,或是数据库挂掉了,阿里巴巴的账户会减少2000,但是四十大盗的账户上就没有了增加的金额。
解决方案:使用事务来控制,保证以上两句SQL要么全部执行成功,要么全部执行失败,不存在只执行了第一步,不执行第二步的中间状态。
1.在SQL中,有的复杂的任务需要多个SQL来执行,有的时候,也同样需要打包在一起,前一个SQL是为了给后一个SQL提供支持,如果后一个SQL不执行了或者执行出错了,前一个SQL也就失去意义了。
这就体现了事务的原子性:要么全都执行完,要么一个都不执行,任务不可以在被细分了。
比如A给B转账500元,在转账的过程中,考虑一个极端情况,执行完第一个SQL之后,在执行第二个SQL之前,数据库崩了/程序崩了。显然这种中间状态是不科学的。(事务的原子性就能避免这种中间状态)
2.事务的原子性是怎样保证的呢?
原子性:全都执行/一个都不执行。
在执行第二个SQL之前,我们无法预知这次执行能失败,所以这个时候,还是的先执行第一个SQL,执行完了在执行第二个SQL。
此处的一个都不执行,并不是真正的一个都不执行,该执行还是得执行。当执行失败之后,数据库会自动的执行一些“还原”行的工作(回滚 rollback),来消除前面SQL带来的影响,所以看起来就好像一个都没执行
数据库是如何知道该还原成哪个值的呢?
数据库会把执行的每个操作,给记录下来
那么既然都能还原,是不是就可以放心大胆的删库呢?
数据库要想记录之前的详细操作,要消耗大量的时间和空间
因此这个记录势必不会保存很久的
我们的数据库可能是经过了一年甚至更久的时间沉淀出来的数据,但记录可能只是记录了几天的。
要么全都执行完,要么一个都不执行
在事务执行之前和执行之后,数据库中的数据应该是合理合法的。
例如:在转账之后,不能出现账户为负数的情况。
数据一旦提交了之后,数据就持久化存储起来了(数据就写入硬盘了)
1.隔离性描述的是事务并发执行的时候,产生的情况。
并发编程,是当前最主要的编程方式
当并发执行多个事务,尤其是这多个事务尝试修改/读取同一份数据,这个时候就容易出现一些问题。事务的隔离性,就是在解决这个问题。
2.并发执行事务可能会带来的问题
举例
当老师在写代码的时候,有个同学偷偷的看到了(比如:代码里有一个student类,有一些属性),但是后来,老师把代码修改了,所以这位同学看到的这个数据不是最终版本的数据,而是中间过程。
但是在同学读代码的这个时间内,就一直在等待吗?
如何解决幻读?
彻底串行化执行。
隔离性最高,并发程度最低,速度最慢。
综上:并发(快)和隔离(准)是不可兼得的。
可以根据实际需求来调整数据库的隔离级别,通过不同隔离级别,也就控制了事务之间的隔离性,也就控制了并发性。
3.MySQL中事务的隔离级别
(1)read uncommitted。 允许读取未提交的数据,并发程度最高,隔离性最低,会引入脏读+不可重复读+幻读问题。
(2) read committed。 只允许读取提交之后的数据,相当于写加锁,并发程度降低了一些,隔离性提高了一些,解决了脏读,会引入不可重复读+幻读问题。
(3)repeatable read。 相当于给读和写都加锁,并发程度又降低,隔离性又提高,解决脏读和不可重复读,会引入幻读问题。
(4)serializable。 串行化。并发程度最低(串性执行),隔离性最高,解决了脏读。不可重复读、幻读问题,但是执行效率最低。
可以通过my.ini这个配置文件,来设置当前的隔离级别。
后面要根据实际需求场景,来决定使用哪种隔离级别。