⭐️写在前面
- 这里是温文艾尔的学习之路
- 如果对你有帮助,给博主一个免费的点赞以示鼓励把QAQ
- 博客主页 温文艾尔的学习小屋
- ⭐️更多文章请关注温文艾尔主页
- 文章发布日期:2022.03.01
- java学习之路!
- 欢迎各位点赞评论收藏⭐️
- 冲冲冲
- ⭐️上一篇内容:【备战面试】每日10道面试题打卡——Java基础篇(二)
索引用来快速寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表,我们字典中的用来寻找字的笔画,拼音就是索引
索引的原理:就是把无序的数据变成有序的查询
无论是聚簇还是非聚簇索引,都是B+树的数据结构
优势:
1.查询通过聚簇索引可以直接获取数据,相比非聚簇索引需要第二次查询(非覆盖索引的情况下)效率要高
2.聚簇索引对于范围查询的效率很高,因为其数据是按照大小排列的
3.聚簇索引适合用在排序的场合,非聚簇索引不适合
劣势:
1.维护索引很昂贵,特别是插入新行或者主键被更新导致要分页(page split)的时候,建议在大量插入新行后,选在负载较低的时间端,通过OPTIMIZE TABLE优化表,因为必须被移动的行数据可能造成碎片,使用独享表空间可以弱化碎片
2.表因为使用UUID(随机ID)作为主键,使数据存储稀疏,这就会出现聚簇索引有可能有比全表扫描更慢,所以建议使用int的auto_increment作为主键
3.如果主键比较大的话,那辅助索引将会变的更大,因为辅助索引的叶子存储的是主键值;过长的主键值,会导致非叶子节点占用占用更多的物理空间
InnoDB
中一定有主键,主键一定是聚簇索引,不手动设置,则会使用unique索引,没有unique索引则会使用数据库内部的一个行的隐藏id来当作主键索引,在聚簇索引纸上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次寻找,非聚簇索引都是辅助索引,像复合索引,前缀索引,唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值
MyISM
使用的是非聚簇索引,没有聚簇索引,非聚簇索引的两颗B+树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助索引B+树存储了辅助键。表结构存储在独立的地方,这两颗B+树的叶子结点都是用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别,由于索引树是独立的,通过辅助键检索无需访问主键的索引树
如果涉及到大数据量的排序,全表扫描,count之类的操作的话,还是MyISAM占优势些,因为索引所占空间小,这些操作是需要在内存中完成的
索引的数据结构和具体存储引擎的实现有关,在MYSQL中使用较多的索引有Hash索引
,B+树索引
等,InnoDB存储引擎
的默认索引实现为:B+树索引
。对于哈希索引来说,底层的数据结构就是哈希表
,因此在绝大多数需求为单条记录查询的时候可以选择哈希索引,查询性能最快;紫御大部分场景,建议选择BTree索引
B+树:
B+树是一个平衡的多叉树
,从根节点到每个叶子节点的高度差不超过1,而且同层级的节点间有指针相互链接。在B+数上的常规检索,从根节点到叶子节点的搜索效率基本相当,不会出现大幅波动,而且基于索引的顺序扫描时,也可以利用双向指针快速左右移动,效率非常高。因此,B+树索引被广泛应用于数据库,文件系统等场景
哈希索引就是采用一定的哈希算法
,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子结点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快
如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;前提是键值都是唯一的,如果键值不唯一,那么先找到该键所在位置,再根据链表向后扫描寻找数据
如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索
查询更快、占用空间更小
where子句
中的列,或者连接子句中指定的列基数较小
的表(表的数据不多),索引效果较差,没有必要在此列建立索引短索引
,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间,如果搜索词超过索引前缀长度,则使用索引排除不匹配的行,然后检查其余行是否可能匹配不要过度索引
,索引需要额外的磁盘空间,并降低写操作的性能,在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长,所以只需要保持需要的索引有利于查询即可。外键
的数据列一定要建立索引不能有效区分数据的列
不适合做索引列(比如性别,男女未知,最多也就三种,区分度实在太低)扩展索引
,不要新建索引
。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可基于锁的属性分类:共享锁
、排它锁
基于锁的粒度分类:行级锁(INNODB)
、表级锁(INNODB、MYISAM)
、页级锁(BDB引擎)
、记录锁
、间隙锁
、临键锁
基于锁的状态分类:意向共享锁
、意向排它锁
共享锁(Share Lock)
共享锁又称读锁,简称S锁,当一个事物为数据加上读锁之后,其他事物只能对该数据加读锁,而不能对数据加写锁,直到所有的读锁释放之后其他事物才能对其进行加持写锁,共享锁的特性主要是为了支持并发的读取数据,读取数据的时候不支持修改,避免出现重复读的问题
排它锁(Exclusive Lock)
排它锁又称写锁,简称X锁;当一个事务为数据加上写锁时,其他请求将不能再为数据加任何锁,直到该锁释放之后,其他事务才能对数据进行加锁,排它锁的目的是在数据修改时候,不允许其他人同时修改,也不允许其他人读取,避免了出现脏数据和脏读的问题
表锁
表锁是指上锁的时候锁住的是整个表,当下一个事物访问该表的时候,必须等前一个事务释放了锁才能进行对表进行访问:
特点:粒度大、加锁简单、容易冲突
行锁
行锁是指上锁的时候锁住的是表的某一行或多行记录,其他事物访问同一张表时,只有被锁住的记录不能访问,其他的记录可正常访问
特点:粒度小,加锁比表锁麻烦,不容易冲突,相比表锁支持的并发要高
记录锁(Record Lock)
记录锁也属于行锁中的一种,只不过记录锁的范围只是表中的某一条记录(和行锁不同,行锁有可能锁住多行),记录锁是说事务在加锁后锁住的只是表的某一条记录
精准条件命中,并且命中的条件字段是唯一索引
加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题,也避免了在修改的事务未提交前被其他事物读取的脏读问题
页锁
页级锁是MYSQL中锁定粒度介于行级锁和表级锁中间的一种锁,表级锁速度快,但冲突多,航迹冲突少,但速度慢,所以取了折中的页级,一次锁定相邻的一组记录
特点:开销和加锁时间介于表锁和行锁之间,会出现死锁,锁定粒度介于表锁和行锁之间,并发度一般
间隙锁(Gap Lock)
属于行锁的一种,间隙锁是在事务加锁后锁住的是表中的一个区间,当表的相邻ID之间出现空隙则会形成一个区间,遵循左开右闭原则
范围查询并且查询未命中记录,查询条件必须命中索引,间隙锁只会出现在REPEATABLE_READ(重复读)的事务级别中
触发条件:防止幻读问题,事务并发的时候,如果没有间隙锁,就会发生同一个事务中,A事务的两次查询出的结果会不一样
比如表里面的数据ID,为1,4,5,7,10,那么会形成以下几个间隙区间,-n-1区间,1-4区间,7-10区间,10-n区间(-n代表负无穷大,n代表正无穷大)
临键锁(Next-Key Lock)
属于行锁的一种,并且它是INNODB的行锁默认算法,总结来说它就是记录锁和间隙锁的组合,临键锁会把查询出来的记录锁住,同时也会把该范围查询内的所有间隙空间也会锁住,再之它会把相邻的下一个区间也会锁住
触发条件:范围查询并命中,查询命中了索引
结合记录锁和间隙锁的特性,临键锁避免了在范围查询时出现脏读、重复读、幻读问题,加了临键锁以后,在范围区间内数据不允许被修改和插入
如果当事务A加锁成功之后就设置一个状态告诉后面的人,已经有人对表里的行加了一个排它锁了,你们不能对整个表加共享锁或排它锁了,那么后面需要对整个表加锁的人只需要获取这个状态就知道自己是不是可以对表加锁,避免了对整个索引树的每个节点扫描是否加锁,而这个状态就是意向锁
意向共享锁
当一个事务试图对整个表进行加共享锁之前,首先需要获得这个表的意向共享锁
意向排它锁
当一个事务试图对整个表进行加排它锁之前,首先需要获得这个表的意向排它锁
意向共享锁和意向排它锁提高了加锁的效率
执行计划就是sql的执行查询的顺序
,以及如何使用所以查询,返回的结果集的行数
我们执行以下sql语句
EXPLAIN SELECT * FROM A WHERE X=? AND Y=?
SIMPLE
:表示此查询不包括UNION查询或子查询PRIMARY
:表示此查询是最外层的查询(包含子查询)SUBQUERY
:子查询中的第一个SELECTUNION
:表示此查询是UNION的第二或随后的查询DEPENDENT UNION
:UNION中的第二个或后面的查询语句,取决于外面的查询UNION RESULT
:UNION的结果DEPENDENT SUBQUERY
:子查询中的第一个SELECT,取决于外面的查询,即子查询依赖于外层查询的结果DERIVED
:衍生,表示导出表的SELECT(FROM子句的子查询)const
:通过索引一次命中,匹配一行数据system
:表中只有一行记录,相当于系统表eq_ref
:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配ref
:非唯一性索引扫描,返回匹配某个值的所有range
:只检索给定范围的行,使用一个索引来选择行,一般用于between、<、>index
:只遍历索引树ALL
:表示全表扫描,这个类型的查询时性能最差的查询之一。那么基本就是随着表的数量增多,执行效率越慢using filesort
:表示mysql对结果集进行外部排序,不能通过索引顺序达到排序效果。一般有using filesort都建议优化去掉,因为这样的查询cpu资源消耗大,延时大using index
:覆盖索引扫描,表示查询在索引树中就可查找所需数据,不用扫描表数据文件,往往说明性能不错using temporary
:查询有使用临时表,一般出现于排序、分组和多表join的情况,查询效率不高,建议优化using where
:sql使用了where过滤,效率较高事务基本特性ACID分别是:
原子性指的是一个事务中的操作要么全部成功,要么全部失败。
一致性指的是数据库总是从一个一致性的状态转换到另外一个一致性的状态,我们事务要达成的目的就是一致性,为了保证事物的一致性,我们需要原子性,隔离性和持久性三种操作
隔离性指的是一个事务的修改在最终提交前,对其他事物是不可见的。
持久性指的是一旦事务提交,所做的修改就会永久保存到数据库中。
我们利用转账的例子来理解一下这几种基本特性
比如A向B转账,A有500元,B有100元,A要向B转账100元,这就代表A要-100,B要+100,A-100和B+100的操作是一个整体,不能说只做一半,
A要-100,B要+100,要么一起成功,要么一起失败,最终的结果一定要达到A=400,B=200才保证了事物的原子性,而数据从A=500,B=100变成了A=400,B=200状态的转变是事物的一致性,如果当A转钱成功要将200写入B的时候,C也在向B转钱,同时也要把200写入B,C应该等200被真正写入B时才能进行转账操作,否则就违反了事物的隔离性,当B=200确定之后,这个值应该是固定的,不能说因为某些原因(例如宕机)就丢失数据,否则违反持久性
隔离性有4个隔离级别,分别是:
read uncommit
读未提交,可能会读到其他事物未提交的数据,也叫作脏读
read commit
读已提交,两次读取结果不一致,叫做不可重复读
repeatable read
可重复读,这是mysql的默认级别,就是每次读取结果都一样,但是有可能产生幻读serializable
串行:一般是不会使用的,他会给每一行读取的数据加锁,会导致大量超时和锁竞争的问题关于脏读、不可重复读、幻读读通俗解释可以看这篇博客,讲的通俗易懂
关于脏读、不可重复读、幻读读通俗解释可以看这篇博客,讲的通俗易懂
https://www.cnblogs.com/wangenxian/p/11014504.html
脏读(Drity Read)
:某个事务已更新一份数据,另一个事务在此时读取了同一份数据,因为某些原因,前一个回滚了操作,则后一个实际上读取了一个不存在的数据
不可重复读(Non-repeatable read)
:在一个事务的两次查询之中数据不一致,这可能是两次查询过程中插入了一个事务更新的原有的数据
幻读(Phantom Read)
:在一个事物的两次查询中不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的
上面的面试题是博主通过牛客面经,博文,资料加上我自己的理解总结而成的,小温自己也在积极准备面试,所以文章中出现的关于面试题的错误请在评论区指出,我再进行改正优化,如果文章对你有所帮助,请给博主一个免费的三连吧,感谢大家