java面试:数据库

Posted 乐之者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java面试:数据库相关的知识,希望对你有一定的参考价值。

Web后端数据库一般用mysql

数据库基础

Q:数据库三范式是什么?
第一范式:列不可再分
第二范式:行可以唯一区分,主键约束
第三范式:表的非主属性不能依赖与其他表的非主属性 外键约束
且三大范式是一级一级依赖的,第二范式建立在第一范式上,第三范式建立第一第二范式上。

Q:数据库引擎有哪些?他们有什么区别?
区别:

  1. InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务;
  2. InnoDB支持外键,而MyISAM不支持。对一个包含外键的InnoDB表转为MYISAM会失败;
  3. InnoDB是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而MyISAM是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
  4. InnoDB不保存表的具体行数,执行select count(*) from table时需要全表扫描。而MyISAM用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
  5. Innodb不支持全文索引,而MyISAM支持全文索引,查询效率上MyISAM要高;

Q:int(5)跟int(4)有什么区别?
int(M),其中的M表示长度。

int类型占用4个字节,1个字节 8 bit(比特)。存储空间为 2^(4*8),也就是2的32次幂,因此取值范围是-2147483648~2147483647,也就是最大长度是10

其实当我们在选择使用int的类型的时候,不论是int(3)还是int(11),它在数据库里面存储的都是4个字节的长度,在使用int(3)的时候如果你输入的是10,会默认给你存储位010,也就是说这个3代表的是默认的一个长度,当你不足3位时,会帮你补全,当你超过3位时,就没有任何的影响。

对于存储和计算来说,int(10)和int(11)没有区别,10代表数字长度,也就是最大长度是10,如果写成int(11), 插入11位数的时候是插入不进去的,mysql会默认int最大10位(2147483647),2147483648也将插入不了。所以int(1)和int(20)是没有区别的,但是写int(20)会认为对mysql字段类型理解不到位。

另外,TINYINT和INT区别就大了,TINYINT占用1个字节,也就是取值是-128~127。所以尽可能取占用空间小的整数类型。

合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。

Q:mysql中char与varchar的区别?
CHAR的长度是固定的,而VARCHAR2的长度是可以变化的,比如,存储字符串“abc”,对于CHAR(10),表示你存储的字符将占10个字节(包括7个空字符),而同样的VARCHAR2(10)则只占用3个字节的长度,10只是最大值,当你存储的字符小于10时,按实际长度储存。

事务

Q:数据库事务是什么?
多条sql语句,要么全部成功,要么全部失败。

Q:数据库事务有哪些特性?
数据库事务特性:原子性(Atomic)、一致性(Consistency)、隔离性(Isolation)、持久性(Durabiliy)。简称ACID。
原子性:组成一个事务的多个数据库操作是一个不可分割的原子单元,只有所有操作都成功,整个事务才会提交。任何一个操作失败,已经执行的任何操作都必须撤销,让数据库返回初始状态。
一致性:事务操作成功后,数据库所处的状态和它的业务规则是一致的。即数据不会被破坏。如A转账100元给B,不管操作是否成功,A和B的账户总额是不变的。
隔离性:在并发数据操作时,不同的事务拥有各自的数据空间,它们的操作不会对彼此产生干扰
持久性:一旦事务提交成功,事务中的所有操作都必须持久化到数据库中。

Q:数据库的隔离级别有哪些?
读未提交(read-uncommitted)读提交(read-committed) 可重复读(repeatable-read)串行化(serializable)。

Q:mysql默认的数据库隔离级别是什么?
可重复读(repeatable-read)

Q:mysql的可重复读,是怎么实现的?
使用的的一种叫MVCC的控制方式 ,即Mutil-Version Concurrency Control,多版本并发控制,类似于乐观锁的一种实现方式。

Q:MVCC的实现方式是怎样的?
InnoDB在每行记录后面保存了隐藏的列,其中包括版本号,当数据被修改时,版本号加1。
在读取事务开始时,系统会给当前读事务一个版本号,事务会读取版本号<=当前版本号的数据。
此时如果其他写事务修改了这条数据,那么这条数据的版本号就会加1,从而比当前读事务的版本号高,读事务自然而然的就读不到更新后的数据了。

Q:为什么mysql要将隔离级别设置为可重复读?为什么mysql不像oracle一样,像事务隔离级别设置为读提交?
读提交的隔离级别,会出现不可重复读。
在读提交的隔离级别下,比如当事务A查询帐户余额为100,事务B将余额取出50并提交,那么事务A再次查询账户时余额为50,这个就是不可重复读。

那既然如此,为什么oracle要把事务隔离级别设置为读提交呢?
mysql为什么要将隔离级别设置为可重复读,更深层的原因见:https://blog.csdn.net/m0_37774696/article/details/88951846

Q:不同的数据库隔离级别会导致哪些问题?
数据库中的数据可能同时被多个事务访问,如果没有采取必要的隔离措施,就会导致各种并发问题,破坏数据的完整性。
1.脏读:A事务读取B事务尚未提交的更改数据,并在这个数据的基础上进行操作。如果恰巧B事务回滚,那么A事务读取到的数据是根本不被承认的。
读未提交的隔离级别,会出现脏读。
2.不可重复读:A事务读取了B事务已经提交的更改数据。(注意,是更改)
读提交的隔离级别,会出现不可重复读。
3.幻读:A事务读取了B事务已经提交的新增数据。(注意,是新增)
可重复读的隔离级别,会出现幻读。
概念讲解:https://www.cnblogs.com/expiator/p/9626123.html
操作示例:https://juejin.im/post/5d8b2a9c518825091471fe2c

sql

Q:union和union all的区别是什么?
union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序;
union all:对两个结果集进行并集操作,包括重复行,不进行排序;

Q:讲一下各种join的区别。
JOIN: 如果表中有至少一个匹配,则返回行
INNER JOIN 与 JOIN 是相同的。
LEFT JOIN: 即使右表中没有匹配,也从左表返回所有的行
RIGHT JOIN: 即使左表中没有匹配,也从右表返回所有的行
FULL JOIN: 只要其中一个表中存在匹配,就返回行

Q:讲一下explain。
它可以对 SELECT 语句进行分析,并输出 SELECT 执行的详细信息,方便针对性地优化。
查询结果的字段如下:
select_type: SELECT 查询的类型。包括SIMPLE、PRIMARY、UNION、UNION RESULT等
table: 查询的是哪个表
partitions: 匹配的分区
type: 类型。type值为all,表示全表扫描。type值为const,说明使用了主键索引。
不同的 type 类型的性能关系如下:
ALL < index < range ~ index_merge < ref < eq_ref < const < system。
possible_keys: 此次查询中可能选用的索引
key: 此次查询中确切使用到的索引.
ref: 哪个字段或常数与 key 一起被使用
rows: 显示此查询一共扫描了多少行. 这个是一个估计值.
filtered: 表示此查询条件所过滤的数据的百分比
extra: 额外的信息
参考资料:https://segmentfault.com/a/1190000008131735

sql优化

Q:平常使用过哪些sql优化的手段?

1、查询语句中不要使用select  *
2、尽量减少子查询,使用关联查询(left join,right join,inner  join)替代
3、减少使用IN或者NOT IN ,使用exists,not exists或者关联查询语句替代
4、or 的查询尽量用 union或者union all 代替(在确认没有重复数据或者不用剔除重复数据时,union all会更好)
5、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
6、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0

索引

Q:有哪些数据结构的索引?
B+ Tree索引、哈希索引(Hash Index)、空间数据索引(R-Tree)、全文索引(Full Text)

Q:有哪些类型的索引?
普通索引,唯一索引,主键索引与组合索引
普通索引:仅加速查询
唯一索引:加速查询 + 列值唯一(可以有null)
主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
全文索引:对文本的内容进行分词,进行搜索

Q:唯一索引和主键有什么区别?
a.主键是一种约束,唯一索引是一种索引;
b.一张表只能有一个主键,但可以创建多个唯一索引;
c.主键创建后一定包含一个唯一索引,唯一索引并不一定是主键;
d.主键不能为null,唯一索引可以为null;
e.主键可以做为外键,唯一索引不行;

Q:索引的数据结构。
B+树。Mysql支持Hash索引和B+树索引两种。

Q:B+树和B树的区别有哪些?
B+ Tree 和 B Tree 不同,B+ Tree 中,只能将数据存储在叶子结点中,内部节点将只包含指针,而 B Tree 可以将数据存储在内部的叶节点中。因此 B+ Tree 的关键优势是中间节点不包含数据,因此 B+ Tree 的大小远小于 B Tree,并且可以将更多数据存储到存储器中。另外,B+ Tree 的每一个叶子节点包含了到相邻的节点的链接,这样可以快速地进行范围遍历。

Q:InnoDB一棵B+树可以存放多少行数据?
几千万。
B+树的深度一般是2~3层,所以假设B+树有三层,且子节点都被填满,每个节点就是一个页,页的大小是16K。而且假设每一行的数据是1K,那么一页就能够存放16行。然后通过一通计算,得出了B+树可以存放的行数是两千万。

Q:使用索引为什么可以加快数据库的检索速度啊?
将无序的数据变成有序(相对)。没有用索引我们是需要遍历双向链表来定位对应的页,现在通过“目录”就可以很快地定位到对应的页上了。
类比翻字典。。查了索引,知道是在第几页,直接去第几页找就行了。不要从头开始翻。

Q:为什么说索引会降低插入、删除、修改等维护任务的速度。
B+树是一颗平衡树,如果我们对这颗树增删改的话,那肯定会破坏它的原有结构。
要维持平衡树,就必须做额外的工作。正因为这些额外的工作开销?导致索引会降低增删改的速度

Q:索引的最左匹配原则指的是什么?
Q:Hash索引和B+树索引有什么区别?主流的使用哪一个比较多?InnoDB存储都支持吗?
Q:组合索引是怎么存储数据的?
组合索引也是一棵B+树,不同的是组合索引的键值不是1个,而是大于等于2个。

Q:组合索引有哪些使用规则?

Q:聚簇索引和非聚簇索引有什么区别?
聚簇索引:将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据
非聚簇索引:将数据与索引分开存储,索引结构的叶子节点指向了数据对应的位置

区别:
聚集索引在叶子节点存储的是表中的数据。
非聚集索引在叶子节点存储的是主键和索引列。
使用非聚集索引查询出数据时,拿到叶子上的主键再去查到想要查找的数据。(拿到主键再查找这个过程叫做回表)

Q:聚簇索引和主键有什么关系?
InnoDB聚集索引的叶子节点存储行记录,因此, InnoDB必须要有,且只有一个聚集索引。
(1)如果表定义了主键,则PK就是聚集索引;
(2)如果表没有定义主键,则第一个非空唯一索引(not NULL unique)列是聚集索引;
(3)否则,InnoDB会创建一个隐藏的row-id作为聚集索引;

Q:什么叫回表?
如果索引的列在 select 所需获得的列中(因为在 mysql 中索引是根据索引列的值进行排序的,所以索引节点中存在该列中的部分值)或者根据一次索引查询就能获得记录就不需要回表,如果 select 所需获得列中有大量的非索引列,索引就需要到表中找到相应的列的信息,这就叫回表。

使用聚集索引(主键或第一个唯一索引)就不会回表,普通索引就会回表。

Q:什么叫覆盖索引?
所谓覆盖索引就是指索引中包含了查询中的所有字段,这种情况下就不需要再进行回表查询了

Q:什么叫索引下推?
索引下推(index condition pushdown )简称ICP,

在不使用ICP的情况下,在使用非主键索引(又叫普通索引或者二级索引)进行查询时,存储引擎通过索引检索到数据,然后返回给MySQL服务器,服务器然后判断数据是否符合条件 。

在使用ICP的情况下,如果存在某些被索引的列的判断条件时,MySQL服务器将这一部分判断条件传递给存储引擎,然后由存储引擎通过判断索引是否符合MySQL服务器传递的条件,只有当索引符合条件时才会将数据检索出来返回给MySQL服务器 。

索引条件下推优化可以减少存储引擎查询基础表的次数,也可以减少MySQL服务器从存储引擎接收数据的次数。

参考链接:https://www.jianshu.com/p/d0d3de6832b9

Q:索引的最左前缀原则是什么?
如果不是按照索引的最左列开始查找,则无法使用索引;
不能跳过联合索引中的某些列;
如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找;

Q:索引什么时候不生效?
Q:索引优化策略。

1.最左前缀匹配原则。
2.主键外键一定要建索引
3. 对 where,on,group by,order by 中出现的列使用索引
4.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0
5.对较小的数据列使用索引,这样会使索引文件更小,同时内存中也可以装载更多的索引键
6.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);
7.为较长的字符串使用前缀索引
8.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可
9.不要过多创建索引, 权衡索引个数与DML之间关系,DML也就是插入、删除数据操作。这里需要权衡一个问题,建立索引的目的是为了提高查询效率的,但建立的索引过多,会影响插入、删除数据的速度,因为我们修改的表数据,索引也需要进行调整重建
10.对于like查询,”%”不要放在前面。 
    SELECT * FROM  t_order  WHERE uname LIKE \'编程%\' -- 走索引 
    SELECT * FROM  t_order  WHERE uname LIKE  \'%编程%\' -- 不走索引
可以用instr代替左模糊。
instr(title,\'name\')>0  相当于  title like \'%name%\' 
instr(title,\'name\')=1  相当于  title like \'name%\' 
instr(title,\'name\')=0  相当于  title not like \'%name%\' 
11.查询where条件数据类型不匹配也无法使用索引。字符串与数字比较不使用索引; 

Q:有一个性别字段,男和女,这个字段需要加索引吗?
离散度低的字段,没有必要加索引。

Q:讲一下悲观锁、乐观锁。
悲观锁是指总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。具有独占性和排他性。比如,行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁不是数据库层面上的锁,是需要自己手动去加的锁。一般我们添加一个版本字段version来实现。
Q:讲一下共享锁、排它锁。
共享锁:指的就是对于多个不同的事务,对同一个资源共享同一个锁。相当于对于同一把门,它拥有多个钥匙一样。
在执行语句后面加上lock in share mode就代表对某些资源加上共享锁了。

排它锁:排它锁,就是指对于多个不同的事务,对同一个资源只能有一把锁。
在需要执行的语句后面加上for update就可以了。

binlog

Q:讲一下binlog、redoLog、undoLog
二进制日志(binlog)。用于记录用户对数据库操作的SQL语句((除了数据查询语句)信息。

作用:用于复制,在主从复制中,从库利用主库上的binlog进行重播,实现主从同步。

回滚日志(undo log)。作用:保存了事务发生之前的数据的一个版本,可以用于回滚。

使用undolog来实现原子性,如果事务执行过程中出错或者用户执行了rollback,系统通过undolog日志返回事务开始的状态。

redo log是InnoDB存储引擎层的日志,又称重做日志文件,用于记录事务操作的变化,记录的是数据修改之后的值,不管事务是否提交都会记录下来。

使用redolog来实现持久性,只要redolog日志持久化了,当系统崩溃,即可通过redolog把数据恢复。

Q:binlog有几种格式?
statement:记录的是修改SQL语句。
row:记录的是每行实际数据的变更。
mixed:statement和row模式的混合。

主从架构

Q:为什么要做主从架构?
读写分离,主机写,从机读。提高数据库性能,扛更高的并发。
Q:讲一下主从复制。
主库创建一个binlog dump thread线程,把binlog的内容发送到从库。
从库创建一个I/O线程,读取主库传过来的binlog内容并写入到relay log。
从库还会创建一个SQL线程,从relay log里面读取内容,并将更新内容写入到slave的db(也就是重新在从库里跑一次insert/update语句)。
参考资料:https://blog.csdn.net/darkangel1228/article/details/80003967
Q:主从复制延迟怎么解决?
如果只是部分业务出现延迟,可以直接用主库去读。这个是比较粗暴的方法。

其他的方法:
a.分库,降低并发量。
即对于分库,则根据业务内聚性,拆分为几个相对独立的子服务,各个子服务使用独立的数据库分开部署,则可以将写操作分散到各个数据库,各个数据库的复制流量相对较小,从而通过分而治之的方法来降低整体的延迟。

b.打开Mysql的并行复制,多库并行复制。

分库分表

Q:分库分表怎么分?
水平分表,垂直分表
Q:分库分表有哪些中间件?
ShardingJdbc、MyCat
Q:分表策略有哪些?
Hash分表:对某个字段进行hash分表。比如user表可以对user_id值进行hash后拆分成user_1,user_2。。查询后根据id的hash值找到对应的表就可以了。
待补充。

参考资料:
数据库面试题--开发者必看
数据库要点--索引和锁

以上是关于java面试:数据库的主要内容,如果未能解决你的问题,请参考以下文章

面试常用的代码片段

面向面试编程代码片段之GC

经验总结:Java高级工程师面试题-字节跳动,成功跳槽阿里!

前端面试题之手写promise

一道经典面试题:字符串在Java中如何通过“引用”传递

准备两个月,面试五分钟,Java中高级岗面试为何越来越难?