面试题-数据库篇
< 返回列表时间: 2019-09-20来源:OSCHINA
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
1. 什么是事务?事务的特性(ACID)?
答:事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么都成功,要么都失败)。 事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所有操作。 事务的特性有:原子性(Atomicity),隔离性(Isolation),一致性( Consistency),持续性(Durability) 原子性:事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做  一致性:事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态。  隔离性 :一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。 持续性 :也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
2.事务的隔离级别有几种?最常用的隔离级别是哪两种(提交读、可重复读)?
答:并发过程中会出现的问题: 丢失更新:提交一个事务时,把其他事务已提交的更新数据覆盖。 脏读:脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。 幻读也叫虚读:一个事务执行两次查询,第二次结果集包含第一次中没有或某些行已经被删除的数据,造成两次结果不一致,只是另一个事务在这两次查询中间插入或删除了数据造成的。幻读是事务非独立执行时发生的一种现象。 不可重复读:一个事务两次读取同一行的数据,结果得到不同状态的结果,中间正好另一个事务更新了该数据,两次结果相异,不可被信任。
事务的四种隔离级别: 未提交读:就是一个事务读取到其他事务未提交的数据,是级别最低的隔离机制。缺点: 会产生脏读、不可重复读、幻读。 提交读:就是一个事务读取到其他事务提交后的数据。Oracle默认隔离级别。缺点:会产生不可重复读、幻读。 可重复读:就是一个事务对同一份数据读取到的相同,不在乎其他事务对数据的修改。MySQL默认的隔离级别。缺点:会产生幻读。 可串行化:事务串行化执行,隔离级别最高,牺牲了系统的并发性。缺点:可以解决并发事务的所有问题。但是效率地下,消耗数据库性能,一般不使用。
3. 什么是索引?
答: 索引其实是一种数据结构,能够帮助我们快速的检索数据库中的数据。
4. 索引具体采用的哪种数据结构呢?
答: 常见的MySQL主要有两种结构:Hash索引和B+ Tree索引,通常使用的是InnoDB引擎,默认的是B+树。当选用memory引擎的时候,就是用的Hash索引。
5. B+ Tree索引和Hash索引区别? 哈希索引适合等值查询,但是无法进行范围查询。 哈希索引没办法利用索引完成排序。 哈希索引不支持多列联合索引的最左匹配规则。 如果有大量重复键值的情况下,哈希索引的效率会很低,因为存在哈希碰撞问题。
6. B+ Tree的叶子节点都可以存哪些东西吗? 他们之间有什么区别?
答:InnoDB的B+ Tree可能存储的是整行数据,也有可能是主键的值。区别:在 InnoDB 里,索引B+ Tree的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。而索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引。
7. 聚簇索引和非聚簇索引,在查询数据的时候有区别吗?
答:聚簇索引查询会更快,因为主键索引树的叶子节点直接就是我们要查询的整行数据了。而非主键索引的叶子节点是主键的值,查到主键的值以后,还需要再通过主键的值再进行一次查询。
8. 主键索引查询只会查一次,而非主键索引需要回表查询多次(这个过程叫做回表)。是所有情况都是这样的吗?非主键索引一定会查询多次吗?
答:覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。当一条查询语句符合覆盖索引条件时,MySQL只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O提高效率。 如,表covering_index_sample中有一个普通索引 idx_key1_key2(key1,key2)。当我们通过SQL语句:select key2 from covering_index_sample where key1 = 'keytest';的时候,就可以通过覆盖索引查询,无需回表。
9. 在创建索引的时候都会考虑哪些因素呢?在创建联合索引的时候,需要做联合索引多个字段之间顺序,这是如何选择的呢?
答:一般对于查询概率比较高,经常作为where条件的字段设置索引。在创建多列索引时,我们根据业务需求,where子句中使用最频繁的一列放在最左边,因为MySQL索引查询会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。 所以当我们创建一个联合索引的时候,如(key1,key2,key3),相当于创建了(key1)、(key1,key2)和(key1,key2,key3)三个索引,这就是最左匹配原则。
10. 那什么情况下会发生明明创建了索引,但是执行的时候并没有通过索引呢?
在一条单表查询语句真正执行之前,MySQL的查询优化器会找出执行该语句所有可能使用的方案,对比之后找出成本最低的方案。这个成本最低的方案就是所谓的执行计划。优化过程大致过程:根据搜索条件,找出所有可能使用的索引;计算全表扫描的代价;计算使用不同索引执行查询的代价;对比各种执行方案的代价,找出成本最低的那一个。
11. 为什么索引结构默认使用B+Tree,而不是Hash,二叉树,红黑树? B+tree是一种多路平衡查询树,节点是天然有序的,非叶子节点包含多个元素,不保存数据,只用来索引,叶子节点包含完整数据和带有指向下一个节点的指针,形成一个有序链表,有助于范围和顺序查找。因为非叶子节点不保存数据,所以同样大小的磁盘页可以容纳更多的元素,同样能数据量的情况下,B+tree相比B-tree高度更低,因此查询时IO会更少。 B-tree不管叶子节点还是非叶子节点,都会保存数据,这样导致在非叶子节点中能保存的指针数量变少(有些资料也称为扇出),指针少的情况下要保存大量数据,只能增加树的高度,导致IO操作变多,查询性能变低; Hash索引底层是基于哈希表,就是以key-value存储数据的结构,多个数据在存储关系上是没有任何顺序关系的。只适合等值查询,不适合范围查询,而且也无法利用索引完成排序,不支持联合索引的最左匹配原则,如果有大量重复键值的情况下,哈希索引效率会很低,因为存在哈希碰撞。 二叉树:树的高度不均匀,不能自平衡,查找效率跟数据有关(树的高度),并且IO代价高。 红黑树:树的高度随着数据量增加而增加,IO代价高。
12. 如何优化MySQL?
答:MySQL优化大致可以分为三部分:索引的优化、SQL语句优化和表的优化 索引优化可以遵循以下几个原则: 联合索引最左前缀匹配原则( 但是这里有一种特殊的情况,如果将一个表中除ID意外的所有字段都组成一个联合索引,那么这个时候就不会遵循最左原则,这个时候无论怎么以哪个字段为where条件,都将用到这个索引。但是当有一个字段没有在这个联合索引索引里面的时候,就会遵循最左原则 ) 尽量把字段长度小的列放在联合索引的最左侧(因为字段越小,一页存储的数据量越大,IO性能也就越好) order by 有多个列排序的,应该建立联合索引 对于频繁的查询优先考虑使用覆盖索引 前导模糊查询不会使用索引,比如说Like '%aaa%'这种,但是可以在mysql中创建全文索引 负向条件不会使用索引,如!=,<>,not like,not in,not exists 索引应该建立在区分度比较高的字段上,一般区分度在80%以上的时候就可以建立索引,区分度可以使用 count(distinct(列名))/count(*) 对于where子句中经常使用的列,最好设置索引 or两边的字段需要索引 SQL语句优化,可以通过explain查看SQL的执行计划,优化语句原则可以有: 在where和order by涉及的列上建立合适的索引,避免全表扫描 任何查询都不要使用select * ,而是用具体的字段列表代替(增加了消耗,e.g CPU,IO等,增加了使用覆盖索引的可能性) 多表连接时,尽量小表驱动大表,即小表join大表 用exists代替in(IN是走rang,就是最差的索引=没有索引,而EXISTS走的ref,已经算是比较好的检索方式了) 尽量避免在where字句中对字段进行函数操作(主要是不会触发索引) SQL语句中in包含的值不应过多(In中的常量全部存储在一个数组中,而且这个数组是排好序的,如果数值较多,产生的消耗也是比较大的) 当只需要一条数据的时候,使用limit 1 如果排序字段没有用到索引,就尽量少排序 尽量用union all代替union(union需要将结果集合并后再进行唯一性过滤操作,会增加大量的CPU运算,加大资源消耗和延迟。当然,前提是union all的前提是两个结果集没有重复的数据) 不要使用Order by rand() in和exists主要是驱动顺序的改变,exists是以外层表为驱动,先被访问;in是先执行子查询。所以in适合于外表大而内表小,exists适合于外表小而内表大的情况, not in和not exists同理 对于分页查询,可以考虑获取前一页的最大的id,用where id > num left join是以左边表为驱动;right join是以右边表为驱动,inner join会自动找出那个数据少的表作为驱动表 数据库表优化 表字段尽可能用not null 字段长度固定表查询会更快 将数据库大表按照时间或者一些标志拆分成小表 水平拆分:将记录散列到不同的表中,每次从分表查询 垂直拆分:将表中的大字段单独拆分到另一张表,形成一对一的关系
热门排行