Java面试八股文--MySQL数据库面试题

数据库三大范式

第一范式 列都是不可再分

第二范式 满足第一范式,并且表中非主键列不存在对主键的部分依赖。

第三范式 满足第二范式,非主键列只依赖于主键,不依赖于其他非主键。

数据库索引怎么实现的

索引的实现通常使用B_TREE。B_TREE索引加速了数据访问,因为存储引擎不会再去扫描整张表得到需要的数据;相反,它从根节点开始,根节点保存了子节点的指针,存储引擎会根据指针快速寻找数据。

常见的索引结构

Hash索引

mysql中,只有Memory(Memory表只存在内存中,断电会消失,适用于临时表)存储引擎显示支持Hash索引,是Memory表的默认索引类型,尽管Memory表也可以使用B+Tree索引。Hash索引把数据以hash形式组织起来,因此当查找某一条记录的时候,速度非常快。但是因为hash结构,每个键只对应一个值,而且是散列的方式分布。所以它并不支持范围查找和排序等功能。

B+Tree索引

B+Tree是mysql使用最频繁的一个索引数据结构,是Inodb和Myisam存储引擎模式的索引类型。相对Hash索引,B+Tree在查找单条记录的速度比不上Hash索引,但是因为更适合排序等操作,所以它更受欢迎。毕竟不可能只对数据库进行单条记录的操作。

Mysql常见索引类型

普通索引:最基本的索引,没有任何限制

唯一索引:与"普通索引"类似,不同的就是:索引列的值必须唯一,但允许有空值。

主键索引:它是一种特殊的唯一索引,不允许有空值。

全文索引:仅可用于 MyISAM 表,针对较大的数据,生成全文索引很耗时耗空间。

组合索引:为了更多的提高mysql效率可建立组合索引。

mysql的存储引擎

InnoDb引擎

支持ACID的事务,支持事务的四种隔离级别;

支持行级锁及外键约束:因此可以支持写并发;

不存储总行数;

MyISAM引擎

不支持事务,但是每次查询都是原子的;

支持表级锁,即每次操作是对整个表加锁;

存储表的总行数;

Memory引擎

全表锁,存储在内容中,速度快,但会占用和数据量成正比的内存空间且数据在mysql重启时会丢失,默认使用HASH索引,检索效率非常高,但不适用于精确查找,主要用于那些内容变化不频繁的代码表

简述MyISAM和InnoDB的区别

MyISAM:

● 不⽀持事务,但是每次查询都是原⼦的;

● ⽀持表级锁,即每次操作是对整个表加锁;

● 存储表的总⾏数;

● ⼀个MYISAM表有三个⽂件:索引⽂件、表结构⽂件、数据⽂件;

● 采⽤⾮聚集索引,索引⽂件的数据域存储指向数据⽂件的指针。辅索引与主索引基本⼀致,但是辅索引不⽤保证唯⼀性。

InnoDb:

● ⽀持ACID的事务,⽀持事务的四种隔离级别;

● ⽀持⾏级锁及外键约束:因此可以⽀持写并发;

● 不存储总⾏数;

● ⼀个InnoDb引擎存储在⼀个⽂件空间(共享表空间,表⼤⼩不受操作系统控制,⼀个表可能分布在多个⽂件⾥),也有可能为多个(设置为独⽴表空,表⼤⼩受操作系统⽂件⼤⼩限制,⼀般为2G),受操作系统⽂件⼤⼩的限制;

● 主键索引采⽤聚集索引(索引的数据域存储数据⽂件本身),辅索引的数据域存储主键的值;因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问辅索引;最好使⽤⾃增主键,防⽌插⼊数据时,为维持B+树结构,⽂件的⼤调整。

Mysql聚簇和⾮聚簇索引的区别

聚簇索引:将数据存储与索引放到了⼀块、并且是按照⼀定的顺序组织的,找到索引也就找到了数据,数据的物理存放顺序与索引顺序是⼀致的,即:只要索引是相邻的,那么对应的数据⼀定也是相邻地存放在磁盘上的

⾮聚簇索引:叶⼦节点不存储数据、存储的是数据⾏地址,也就是说根据索引查找到数据⾏的位置再取磁盘查找数据,这个就有点类似⼀本树的⽬录,⽐如我们要找第三章第⼀节,那我们先在这个⽬录⾥⾯找,找到对应的⻚码后再去对应的⻚码看⽂章。

最左前缀原则

当⼀个SQL想要利⽤索引是,就⼀定要提供该索引所对应的字段中最左边的字段,也就是排在最前⾯的字段,⽐如针对a,b,c三个字段建⽴了⼀个联合索引,那么在写⼀个sql时就⼀定要提供a字段的条件,这样才能⽤到联合索引,这是由于在建⽴a,b,c三个字段的联合索引时,底层的B+树是按照a,b,c三个字段从左往右去⽐较⼤⼩进⾏排序的,所以如果想要利⽤B+树进⾏快速查找也得符合这个规则

B树和B+树的区别

B树的特点

1. 节点排序,⼀个节点了可以存多个元素,多个元素也排序

2. 所有的叶子节点在同一层,每个节点上都存储的是key和他对应的数据

B+树的特点

1. 拥有B树的特点,是在BTree基础上的一种优化

2. 叶⼦节点之间有指针连接

3. 非叶子节点存储key,叶子节点存储key和数据,也就是叶⼦节点中存储了所有的元素,并且排好顺序

数据库事务的隔离级别?

(一)可读取未提交(Read uncommitted)

写事务阻止其他写事务,避免了更新遗失。但是没有阻止其他读事务。

存在的问题:脏读。即读取到不正确的数据,因为另一个事务可能还没提交最终数据,这个读事务就读取了中途的数据,这个数据可能是不正确的。可能会导致脏读、幻读或不可重复读

解决办法就是下面的“可读取确认”。

(二)可读已提交(Read committed)

Sql Server , Oracle的默认隔离级别

写事务会阻止其他读写事务。读事务不会阻止其他任何事务。

存在的问题:不可重复读。即在一次事务之间,进行了两次读取,但是结果不一样,可能第一次id为1的人叫“李三”,第二次读id为1的人就叫了“李四”。因为读取操作不会阻止其他事务。

解决办法就是下面的“可重复读”。可以阻止脏读,但是幻读或不可重复读仍有可能发生

(三)可重复读(Repeatable read)

MySQL的默认隔离级别

读事务会阻止其他写事务,但是不会阻止其他读事务。

存在的问题:幻读。可重复读阻止的写事务包括update和delete(只给存在的表加上了锁),但是不包括insert(新行不存在,所以没有办法加锁),所以一个事务第一次读取可能读取到了10条记录,但是第二次可能读取到11条,这就是幻读。

解决办法就是下面的“串行化”。可以阻止脏读和不可重复读,但幻读仍有可能发生

(四)可串行化(Serializable)

可避免幻读。读加共享锁,写加排他锁。这样读取事务可以并发,但是读写,写写事务之间都是互斥的,基本上就是一个个执行事务。//最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

脏读:也就是当数据库的一个事务A正在使用一个数据但还没有提交,另外一个事务B也访问到了这个数据,还使用了这个数据,这就会导致事务B使用了事务A没有提交之前的数据。

不可重复读:在一个事务A中多次操作一个数据,在这两次或多次访问这个数据的中间,事务B也操作此数据,并使其值发生了改变,这就导致同一个事务A在两次操作这个数据的时候值不一样,这就是不可重复读。

幻读:是指事务不独立执行产生的一种现象。事务A读取与搜索条件相匹配的若干行。事务B以插入或删除行等方式来修改事务A的结果集,然后再提交。这样就会导致当A本来执行的结果包含B执行的结果,这两个本来是不相关的,对于A来说就相当于产生了“幻觉”。

Mysql锁有哪些,如何理解

按锁粒度分类:

1. ⾏锁:锁某⾏数据,锁粒度最⼩,并发度⾼

2. 表锁:锁整张表,锁粒度最⼤,并发度低

3. 间隙锁:锁的是⼀个区间

还可以分为:

1. 共享锁:也就是读锁,⼀个事务给某⾏数据加了读锁,其他事务也可以读,但是不能写

2. 排它锁:也就是写锁,⼀个事务给某⾏数据加了写锁,其他事务不能读,也不能写

还可以分为:

1. 乐观锁:并不会真正的去锁某⾏记录,⽽是通过⼀个版本号来实现的

2. 悲观锁:上⾯所的⾏锁、表锁等都是悲观锁

数据库优化了解那些

1. 当只要一行数据时使用LIMIT1 ,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。

2. 避免出现SELECT * FROM table 语句,要明确查出的字段。

3. 在一个SQL语句中,如果一个where条件过滤的数据库记录越多,定位越准确,则该where条件越应该前移。

4. 查询时尽可能使用索引覆盖。即对SELECT的字段建立复合索引,这样查询时只进行索引扫描,不读取数据块。

5. 在判断有无符合条件的记录时建议不要用SELECT COUNT (*)和select top 1 语句。

6. 尽量减少子查询,使用关联查询(left join,right join,inner join)替代

7. 应绝对避免在order by子句中使用表达式。

8. 如果需要从关联表读数据,关联的表一般不要超过7个。

9. 小心使用 IN 和 OR,需要注意In集合中的数据量。建议集合中的数据不超过200个。

10. <> 用 < 、 > 代替,>用>=代替,<用<=代替,这样可以有效的利用索引。

11. 在查询时尽量减少对多余数据的读取包括多余的列与多余的行。

Mysql慢查询该如何优化?

1. 检查是否⾛了索引,如果没有则优化SQL利⽤索引

2. 检查所利⽤的索引,是否是最优索引

3. 检查所查字段是否都是必须的,是否查询了过多字段,查出了多余数据

4. 检查表中数据是否过多,是否应该进⾏分库分表了

5. 检查数据库实例所在机器的性能配置,是否太低,是否可以适当增加资源

什么是 MVCC

多版本并发控制:读取数据时通过⼀种类似快照的⽅式将数据保存下来,这样读锁就和写锁不冲突了,不同的事务session会看到⾃⼰特定版本的数据,版本链

分库分表

垂直拆分:拆出来的每个表,每个表的表结构不同,表数据不同,每个表的字段至少有一列是交集,所有数据就是这些表的并集

水平拆分:拆出来的每个表,结构是相同的,数据是不同的,

要选择合适的策略:大部分数据是和用户相关联的,所以我们用户id是常用的分表字段,假设我们有order表,由于order表记录条数太多,将被拆分成256张表。拆分的记录根据user_id%256取得对应的表进行存储,

分表能够解决单表数据量过大带来的查询效率下降的问题,但是,却无法给数据库的并发处理能力带来质的提升。面对高并发的读写访问,当数据库服务器就会无法承载写操作压力,我们必须换一种思路,对数据库进行拆分,从而提高数据库写入能力,这就是所谓的分库!与分表策略相似,分库可以采用通过一个关键字取模的方式,来对数据访问进行路由。还是之前的订单表,假设user_id 字段的值为258,将原有的单库分为256个库,那么应用程序对数据库的访问请求将被路由到第二个库(258%256 = 2)。

有时数据库可能既面临着高并发访问的压力,又需要面对海量数据的存储问题,这时需要对数据库既采用分表策略,又采用分库策略,以便同时扩展系统的并发处理能力,以及提升单表的查询性能,这就是所谓的分库分表。

分库分表的策略比前面的仅分库或者仅分表的策略要更为复杂,一种分库分表的路由策略如下:

◆中间变量 = user_id % (分库数量 * 每个库的表数量)

◆库 = 取整数 (中间变量 / 每个库的表数量)

◆表 = 中间变量 % 每个库的表数量

​ 同样采用user_id作为路由字段,首先使用user_id 对库数量*每个库表的数量取模,得到一个中间变量;然后使用中间变量除以每个库表的数量,取整,便得到对应的库;而中间变量对每个库表的数量取模,即得到对应的表。

什么是内联接、左外联接、右外联接?

内联接(Inner Join):匹配2张表中相关联的记录。

左外联接(Left Outer Join):除了匹配2张表中相关联的记录外,还会匹配左表中剩余的记录,右表中未匹配到的字段用NULL表示。

右外联接(Right Outer Join):除了匹配2张表中相关联的记录外,还会匹配右表中剩余的记录,左表中未匹配到的字段用NULL表示。在判定左表和右表时,要根据表名出现在Outer Join的左右位置关系。

你可能感兴趣的:(Java面试,java,面试,开发语言,mysql)