数据库面试总结

索引模块

为什么要使用索引?

      使用索引主要是为了在项目中,数据量大,如果一直使用全表扫描,会在很大程度影响程序的执行效率,所以已引入了索引。

数据记录中,有什么样的信息能够成为索引?

      能够把查找范围确立在一定范围内的字段,主键就是一个很好的索引切入点。包括唯一键,普通键等也是可以作为索引存在的。

索引数据结构

  • 建立二叉树进行二分查找
  • 建立B-Tree结构进行查找
  • 建立B+Tree结构进行查找
  • 建立一些Hash结构进行查找

二叉查找树

      每个节点有两个孩子节点,且左孩子的值一定小于父节点的值,右孩子的值一定大于父节点的值。二叉查找树又是平衡二叉树,即左右孩子的深度相差不能大于1.

B-Tree

  • 根节点至少包括两个孩子
  • 树中每个节点的最多含有m个孩子(m>=2)
  • 除根节点和叶子结点以外,其他每个节点至少有ceil(m/2)个孩子 (ceil是取上限,1.2取2)
  • 所有叶子结点都位于同一层
  • 假设每个非终端节点中包含有n个关键字信息,其中
    • Ki(i= 1 … n),为关键字,且关键字按顺序升序排序K(i -1 ) < K i.

    • 关键字的个数n必须满足:[ceil (m / 2) -1] <= n <= m-1;  任意节点的关键字个数上限比他的孩子数上限少1,且对于非叶子节点来说,任何一个节点的关键字个数比他的指向孩子的指针个数少一个

    • 非叶子结点的指针:P[1],P[2],… P[M];其中P[1]指向的关键字小于K[1]的子树,P[M]指向关键字大于K[M-1],其他P[i]指向关键字属于(K[i-1],K[i])的子树;

B+-Tree

密集索引和系数索引

    • 密集索引文件中的每个索引码值对对应一个索引值:叶子节点保存的不仅仅是键值,还保存了位于同一行记录里的其他列的信息,由于密集索引决定了表的物理排列顺序,一个表只能有一个物理排列顺序,所以一个表只能创建一个密集索引。
    • 稀疏索引文件中只为索引码的某些值建立索引项:叶子结点仅保存了键位信息以及改行数据的地址,有的稀疏索引是仅保存了键位信息及其主键。定位到叶子结点之后,任然需要通过地址或者主键信息进一步定位到数据。

对于MySQL的两大主流索引,MyISAM 和InnoDB,MyISAM不论是主键索引,普通索引还是唯一键索引,其索引均数据稀疏键索引,而InnoDB必须是有且仅有一个稠密索引。

InnoDB

  • 如果一个主键被定义,则该主键作为密集索引
  • 若没有主键被定义,则该表的第一个唯一非空索引则作为密集索引
  • 若不满足以上条件,InnoDB内部会生成一个隐藏的主键作为密集索引
  • 非主键索引存储相关键和其对应的主键值,查找时需要两次查找。

主键索引

主键:

  某一个属性组能唯一标识一条记录

  如:学生表(学号,姓名,班级,性别等等),学号时唯一标识的,可以作为主键

特点:

  最常见的索引类型

  确保数据记录的唯一性

  确定特定数据记录在数据库中的位置

实例:

CREATE TABLE `表名`(、

  `GradeID` INT(11)  AUTO_INCREMENT PRIMARY KEY,

  #或 PRIMARY KEY(`GradeID`)

唯一索引

作用:

      避免同一个表中某数据列中的值重复

与主键索引的区别

  主键索引只能有一个

  唯一索引可有多个

实例:

CREATE TABLE `Grade`(、

  `GradeID` INT(11)  AUTO_INCREMENT PRIMARY KEY,

  `GradeName` VARCHAR(32) NOT NULL UNIQUE

  #或 UNIQUE KEY ` GradeID`(`GradeID`)

常规索引

作用:

快速定位特定数据

注意:

index 和 key 关键字都可以设置常规索引

应加在查询条件的字段

不易添加太多常规索引,影响数据的插入,删除和修改操作

实例:

##创建表时添加

CREATE TABLE `result`{

  //省略一些代码

  INDEX / KEY `ind` (`studentNo`,`subjectNo`)

}

##创建后追加

ALTER TABLE `result` ADD  INDEX `ind` (`studentNo`,`subjectNo`);

全文索引

作用:

快速定位特定数据

注意:

只能用于MyISAM类型的数据表

只能用于CHAR ,VARCHAR,TEXT数据列类型

使用大型数据集

实例:

CREATE TABLE `student`(

  #省略一些sql语句

    FULLTEXT(`StudentName`)

)ENDINE=MYISAM;

ALTER TABLE employee ADD FULLTEXT(`first_name`)

密集索引和非密集索引的区别

稀疏索引使用的是B-Tree,各节点存放索引值和索引地址,而稠密索引使用的B+-Tree,树中的非叶子节点当中是不存索引地址的,存的是索引值和指向下一个节点的指针,在树的叶子结点才会真正的存储数据的索引地址或者数据。

如何定位并优化慢查询Sql

  • 根据慢查询日志定位慢查询SQL
  • 使用explain等工具分析SQL
  • 修改SQL或者尽量让SQL走索引

最左匹配原则

MySQL会一直向右匹配直到遇到范围查询(<,>,between,like)就停止匹配,比如,a=3 and b = 4 and c > 5 and d = 6 ,如果建立a,b,c,d顺序的索引,d是用不到索引的,如果建立(a,b,d,c)顺序的索引则都可以用到,a,b,d的顺序顺序可以任意调整。

=和in可以乱序,比如 a= 1 and b = 2  and c = 3 家婚礼(a,b,c)索引可以任意顺序,MySQL的查询优化器会棒你优化成索引可以识别的形式。

联合索引的最左匹配原则的成因

首先会根据复合索引的最左边也就是第一个字段进行排序,在第一个字段排序的基础上在对第二个字段进行排序,类似于order by 第一个字段,在order by 第二个字段,因此,第一个字段是绝对有序的,第二个字段到以后的字段就是无序的了。因此直接使用第二个字段进行条件判断是用不到索引的。

锁模块

MyISAM与InnoDB关于锁方面的区别是什么

  • MyISAM默认使用的表级锁,不支持行级锁
  • InnoDB默认使用的是行级锁,也支持表级锁

 

xxx for update ; 上排他锁

xxx lock in share mode;  行级锁上共享锁

无论是表锁还是行锁都有共享锁和排他锁,其关系如表所示:

如session1对于某一行数据上了一个排他锁,也就是增删改语句,或者select,update的时候都可以上排他锁。与此同时,session2还想对同一行数据上排他锁是不可以的,会出现冲突,直到所得释放,上共享锁也会是不可以的,也是会出现冲突。如果是先对一行数据上了共享锁,在对同一行数据上排他锁也是不可以的,是会有冲突的。但是如果对一行数据上了共享锁,再次的对同一行数据上共享锁,是可以的,他俩是兼容的。

MyISAM适合的场景

  • 频繁执行全表count语句。对于InnoDB来说,是不保存表的具体行数的,执行 select count(*) from 表名 时需要重新扫描统计。MyISAM是用一个变量保存了整个表的行数。
  • 对数据增删改的频率不高,查询非常频繁的操作。增删改设涉及到锁表操作,虽然对于插入操作可以通过一些配置支持从表的尾部插入数据,但是依然会产生很多碎片,依然会很影响性能。如果是纯查询的话效率是非常高的。
  • MyISAM适合不需要事务的操作,它不支持事务

InnoDB适合的场景

  • 数据的增删改查都相当频繁的操作,增删改只是某些行被锁,在大多数情况下避免了阻塞。
  • 可靠性要求高,要求支持事物

数据库事务的四大特性

ACID

      原子性(Atomic),一致性(Consistency),隔离性(Isolation),持久性(Durability)

事务的隔离级别以及各级别下的并发访问问题

脏读,一个线程读取到另一个线程修改但为提交的数据,通过将隔离级别设置为Committed Read就可以解决这一类问题。

不可重复诶,一个线程在读数据,读取到一个数据,之后里一个线程对数据做了修改,然后之前的那个线程再又读了一次,发现与之前的数据不一致了,这就存在很大隐患,对于第一个线程而言,到底那次读取到的数据是真是可靠的。这个问题通过将隔离级别设置为Repeatable Read就可以解决。

幻读,一个线程在对表中数据进行修改或者查询操作时,原来应该是n条数据,可是执行的时候突然出现了n+k条数据或者是n-k 条数据,这就是幻读,这样的问题将数据库的isolation level 设置成serializable就可以解决。

InnoDB可重复读隔离级别下如何避免幻读

  • 表象:快照读(非阻塞读)---伪MVCC
  • 内在:next-key锁(行锁+gap酥锁)

 

当前读:上了锁的增删改,不论是什么锁

当前读,它读取的是记录的最新版本,读取之后还要保证其他的的并发事物不能修改当前记录,对读取的记录加锁,其中除了 lock in share mode 加的是共享锁,其他的语句加的都是排他锁。

快照读:就是简单的select,不加锁,这个不加锁的条件是事务隔离级别不为Serializable的前提下,在Serializable级别下,由于是穿行读,此时的快照读也退化成当前读。

之所以出现快照读是基于提升并发性能的考虑,快照读是基于多版本并发控制即MVCC,可以认为MVCC是行级锁的一个变种,它在很多情况下避免了加锁,开销更低,由于是快照读,所以读到的很可能是不是当前的最新版本,而是历史版本。

在RR级别下,如果是先创建快照,也就是先执行了select,然后就去其他线程中修改数据,然后使用当前读,是能够读到新数据的,但是再次使用快照读,读取到还是历史数据;但是如果之前没有使用select创建快照,而是修改,之后再读,快照读和当前读读到的都是新数据。

RC,RR级别下的InnoDB的非阻塞读如何实现

  • 数据行里的DB_TRX_ID,DB_ROLL,DB_ROW_ID字段

DB_TRX_ID:与事务有关,该字段记录最近一次对本行记录做修改。

DB_ROLL:回滚指针,通过undo日志重建之前的数据信息。

DB_ROW_ID:即行号,包含一个随着新行插入和单调递增的行id。当InnoDB产生聚集索引时,聚集索引会包括这个行id的值,否则这个行id不会出现任何索引中。

  • undo日志

对记录做了变更操作时,就会产生undo记录,undo记录中存储的老版记录,当一个旧的事物需要读取数据时,为了能读取到老版的数据,需要顺着undo列找到满足其可见性的数据列。undolog主要分为两种,insert undolog和update undolog,insert Undolog是事务在insert新纪录产生的Undolog,只在事物回滚时需要,在事物提交后就可以立即丢弃。update Undolog是事务在delete或者update时产生的Undolog,不仅在事物回滚时需要,快照读也需要,不能够随便删除,只有在数据库中所使用的的快照中不涉及改日志记录,对应的日志记录才会被删除。

  • read view 主要是做可见性判断

去执行快照读select的时候,会针对查询的数据创建出一个read view,来决定当前事物能看到的是那个版本的数据,有可能允许看到最新版本的,也有可能看到Undolog中的老版本数据,它遵循一个可见性算法,主要是将要修改的数据的DB_TRX_ID取出来。与系统其他活跃事物id作对比,如果大于或等于这些id,就通过DB_ROLL_PTR指针去取出上次一的Undolog,可以一直向上回溯,直到DB_TRX_ID为0,就是最原始的版本。

next-key锁(行锁+gap锁)

  • 行锁
  • gap锁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值