Java面试我这样回答MySQL,面试官让我下周来上班!

前言:

求职季在即,技巧千万条,硬实力才是关键,听说今年疫情大环境不好,更要好好准备才行。MySQL是Java程序员面向高级的必备技能,很多朋友在面试时经常在这里折戟沉沙,饮恨不已。熟练掌握MySQL知识,在实践中具有很强的操作性,尤其是在互联网行业,不仅要写好代码、实现功能,而且还要在高并发的情况下能够正常运转。

这篇文章总结了许多关于MySQL方面的知识总结,以及面试多家总结出来的常问面试题,希望对大家有所帮助。另外本人整理收藏了20年多家公司面试知识点整理 ,以及各种Java核心知识点免费分享给大家,想要资料的话请点795983544暗号CSDN。

在这里插入图片描述

在这里插入图片描述

1.Mysql 中有哪几种锁?

(1)表级锁:开销小,加锁快。不会出现死锁,锁定粒度大,发生锁冲突的概率高,并发度低。

(2)行级锁:开销大,加锁慢。会出现死锁,锁定粒度小,发生锁冲突的概率低,并发度高。

(3)页面锁:开销时间、加锁时间、锁定粒度在 表级锁 与 行级锁 之间,会出现死锁,并发度中等。

2.CHAR 与 VARCHAR 的区别?

(1)CHAR 长度不可变,范围 1~255。若存储长度未达到定义的长度,则以 空格 填充。存取速度快,但容易浪费空间。

(2)VARCHAR 长度可变,范围 1~65535。若存储长度未达到定义的长度,则存实际长度数据。存取速度稍慢,但节约空间。

扫描二维码关注公众号,回复: 11594512 查看本文章

3.能说下myisam 和 innodb的区别吗?

myisam引擎是5.1版本之前的默认引擎,支持全文检索、压缩、空间函数等,但是不支持事务和行级锁,所以一般用于有大量查询少量插入的场景来使用,而且myisam不支持外键,并且索引和数据是分开存储的。

innodb是基于聚簇索引建立的,和myisam相反它支持事务、外键,并且通过MVCC来支持高并发,索引和数据存储在一起。

4.你能说下事务的基本特性和隔离级别吗?

事务:数据库中,对数据的一系列操作可以看成一个整体,称为事务。这个整体要么全部执行、要么全部不执行。
ACID 属性的存在确保了 事务的可靠。
(1)Actomicity(原子性):原子性要求 事务中的操作要么全部完成,要么回退成之前未操作的状态。即事务中某个操作失败后,会相当于什么都没发生,不会出现改了部分数据的情况。

(2)Consistency(一致性):一致性要求 事务执行前后,数据库的状态一致,即从一个一致性状态切换到另一个一致性的状态。

(3)Isolation(隔离性):隔离性要求 并发的事务相互隔离、不可见。即一个事务看不见另一个事务内部的操作以及操作的数据。

(4)Durability(持久性):持久性要求 事务对数据库数据的修改是永久的。即数据一旦修改提交后,其状态将永久不变。

5.并发问题 – 脏读、不可重复读、幻读?

对于同时运行的多个事务,若这些事务访问同一数据时,没有采用必要的隔离机制,则会造成如下的并发问题。
(1)脏读:脏读 指的是当一个事务正在访问某数据,并对这个数据进行的修改,且这条数据还未提交到数据库中,此时若另一个事务也访问到这条数据,获取到的是这条被修改的数据,此时得到的数据不对,即脏读。
比如:tom 年龄为 22,事务 A 修改 tom 年龄为 30,此时还未提交到数据库,此时事务 B 获取 tom 年龄,得到的是 30,事务 A 回滚数据,数据库的数据依旧是 22,但事务 B 拿到的数据是 30,这就是脏读,读错了数据。

(2)不可重复读:指一个事务,多次读取同一条数据,在这个事务还未结束时,另一个事务也访问该数据并对其修改,那么可能造成事务多次读取的数据不一致,即不可重复读。
比如:tom 年龄为 22,事务 A 读取 tom 年龄为 22,事务未结束。此时事务 B 修改 tom 年龄为 30,并提交到数据库,当事务 A 再次读取 tom 年龄为 30,事务 A 两次读取的数据不一致,即不可重复读。

(3)幻读:指事务并不是独立执行时产生的现象。一个事务修改某个表,涉及表的所有行,同时另一个事务也修改表,比如增加或删除一条数据。此时第一个事务发现多出或者少了一条数据。这种情况就是幻读。
比如:事务 A 查询当前表的数据总数为 11, 此时事务 B 向表中插入一条数据,事务 A 再次查询当前表数据总数为 12,即幻读。

注:
    不可重复读、幻读理解起来有些类似。
    不可重复读是对一条数据操作,重点在于修改某条数据。
    幻读是对表进行操作,重点在于新增或删除某条数据。

6.事务的隔离级别?

数据库系统必须具有隔离并发运行的事务的能力,使各事务间不会相互影响,避免并发问题。
  隔离级别:指的是一个事务与其他事务的隔离程度。隔离级别越高,则并发能力越弱。
(1)Read Uncommitted(读未提交):即读取到 未提交的内容。
  一般不使用。此隔离级别下,查询不会加锁,即可能存在两个事务操作同一个表的情况。可能会导致 “脏读”、“不可重复读”、“幻读”。

(2)Read Committed(读提交):即只能读取到 已提交的内容。
  常用(oracle、SQL Server 默认隔离级别)。此隔离级别下,查询采用 快照读 的机制,即不会读取到未提交的数据,从而避免 “脏读”,但是仍可能导致 “不可重复读”、“幻读”。

(3)Repeatable Read(可重复读)
  常用(mysql 默认隔离级别)。此隔离级别下,查询采用 快照读 的机制,且事务启动后,当前数据不能被修改,从而可以避免 “不可重复读”,但是仍可能导致 “幻读”(新增或删除某条数据)。

(4)Serializable(串行化)
  一般不使用。此隔离级别下,事务会串行化执行(排队执行),执行效率差、开销大。可以避免 “脏读”、“不可重复读”、“幻读“。

【举例:】
select @@transaction_isolation; -- 用于查看当前数据库的隔离级别(8.0版本)
 set session transaction isolation level read committed; --用于设置隔离级别为 read committed


7.说说自增主键、UUID?

(1)自增主键,数据在物理结构上是顺序存储,性能好,占用空间小。可以是 int 和 bigint 类型。int 4字节,bigint 8 字节,项目中理论不应出现 自增主键达到最大值的情况,因为数据太大,效率会大大降低,当出现一定的数据量后,应进行分库分表操作。

(2)UUID,数据在物理结构上是随机存储,性能较差,占用空间大。唯一ID,绝不冲突。

8.mysql 的约束分类?

(1)约束的作用:是一种限制,用于限制表中的数据,为了保证数据的准确性以及可靠性。
(2)约束分类:
  NOT NULL,非空,用于保证某个字段不为空。支持列级约束。
  DEFAULT,默认,用于保证某个字段具有默认值。支持列级约束。
  PRIMARY KEY,主键,用于保证某个字段具有唯一性且非空。支持列级约束以及表级约束。
  UNIQUE,唯一,用于保证某个字段具有唯一性。支持列级约束以及表级约束。
  FORGIEN KEY,外键,用于限制两个表间的关系。支持表级约束。
注:
  列级约束:指的是定义列的同时指定的约束。
  表级约束:指的是列定义之后指定的约束。

外键常用于一对多的关系。即表的某条数据,对应另外一张表的多条数据。
    将 “一” 的一方称为 :主表。将 “多” 的一方称为 :从表。
    通常将 外键 置于从表上,即 从表上增加一列作为外键,并依赖于主表的某列。

【举例:】
员工与部门间的关系。
一个部门可以有多个员工,而一个员工属于一个部门。此时部门与员工间为 一对多 的关系。
部门表为主表,员工表为从表。外键建立在 员工表(从表)上。

CREATE TABLE dept (
    -- 此处的 primary key 为 列级约束。
    deptId int primary key auto_increment,
    deptName varchar(20) not null
);

CREATE TABLE emp (
    id int primary key auto_increment,
    name varchar(32),
    age int,
    deptId int,
    -- 此处的 foreign key 为表级约束。
    foreign key(deptId) references dept(deptId)
);

9.drop、delete 与 truncate 的区别:

(1)格式:

drop table 表名;   -- 用于删除数据表。
truncate table 表名;  -- 用于删除数据表的数据,但保留表结构。
delete from 表名 [where 条件];  -- 用于删除数据标的数据,但保留表结构,可回滚。

(2)delete 与 truncate 相比较:

delete 可以添加删除条件,truncate 不可以。
delete 删除后可以回滚,truncate 不可以。
delete 效率较低,truncate 效率较高。
delete 可以返回受影响的行数,truncate 没有返回值。
delete 删除数据后再次插入数据时,标识列从断点处开始,truncate 标识列从 1 开始。

10.隐式事务、显式事务?

隐式事务:事务没有明显的开启与关闭的标志。比如 insert、delete、update等语句会自动提交。
显式事务:事务具有明显的开启与关闭的标志,前提需禁用自动提交功能。

show variables like "autocommit"; -- 用于查看自动提交功能是否打开
set autocommit=1; -- 用于打开自动提交功能
set autocommit=0; -- 用于关闭自动提交功能

【显式事务步骤:】
Step1:开启事务,关闭自动提交功能。
    set autocommit=0;
    
Step2:编写事务语句。
    select、insert、delete、update。
    SAVEPOINT A;  -- 可以设置回滚点
    
Step3:结束事务。
    commit; -- 提交事务
    rollback; -- 回滚事务
    rollback to A;  -- 回滚到回滚点

11.存储过程

(1)存储过程:
  指的是 一组预先编译好的 sql 语句的集合,可以理解成批处理语句。类似于 Java 中的方法,使用时调用方法名即可。

(2)好处:
  提高了代码的重用性。
  简化操作。
  减少了编译次数、与数据库交互的次数,提高了效率。
(3)语法:

【创建存储过程:】
    DELIMITER $
    CREATE PROCEDURE 存储过程名(参数列表)
    BEGIN
        存储过程体(一组合法的 sql 语句)
    END $
    DELIMITER ;
注:
    参数列表分三个部分,分别为 参数模式、参数名、参数类型
    参数模式:IN、OUT、INOUT。
        IN:指该参数可以作为输入,即接收值(默认)。
        OUT:指该参数可以作为输出,即返回值。
       INOUT:指该参数即可作为输入、又可作为输出。
   
    存储过程体中每条语句必须以分号 ; 结尾。
    DELIMITER 用于设置结束标记,用于存储过程末尾,执行到标记处则存储过程结束。
    
【调用存储过程:】
    CALL 存储过程名(参数列表);
    
【删除存储过程:】
    DROP PROCEDURE 存储过程名;

【查看存储过程结构:】
    SHOW CREATE PROCEDURE 存储过程名;

12.你们数据量级多大?分库分表怎么做的?

首先分库分表分为垂直和水平两个方式,一般来说我们拆分的顺序是先垂直后水平。
垂直分库

基于现在微服务拆分来说,都是已经做到了垂直分库了
在这里插入图片描述

垂直分表
如果表字段比较多,将不常用的、数据较大的等等做拆分
在这里插入图片描述
水平分表
首先根据业务场景来决定使用什么字段作为分表字段(sharding_key),比如我们现在日订单1000万,我们大部分的场景来源于C端,我们可以用user_id作为sharding_key,数据查询支持到最近3个月的订单,超过3个月的做归档处理,那么3个月的数据量就是9亿,可以分1024张表,那么每张表的数据大概就在100万左右。

比如用户id为100,那我们都经过hash(100),然后对1024取模,就可以落到对应的表上了。

13.那分表后的ID怎么保证唯一性的呢?

因为我们主键默认都是自增的,那么分表之后的主键在不同表就肯定会有冲突了。有几个办法考虑:

  1. 设定步长,比如1-1024张表我们分别设定1-1024的基础步长,这样主键落到不同的表就不会冲突了。
  2. 分布式ID,自己实现一套分布式ID生成算法或者使用开源的比如雪花算法这种
  3. 分表后不使用主键作为查询依据,而是每张表单独新增一个字段作为唯一主键使用,比如订单表订单号是唯一的,不管最终落在哪张表都基于订单号作为查询依据,更新也一样。

14.那分表后的ID怎么保证唯一性的呢?

因为我们主键默认都是自增的,那么分表之后的主键在不同表就肯定会有冲突了。有几个办法考虑:

  1. 设定步长,比如1-1024张表我们分别设定1-1024的基础步长,这样主键落到不同的表就不会冲突了。
  2. 分布式ID,自己实现一套分布式ID生成算法或者使用开源的比如雪花算法这种
  3. 分表后不使用主键作为查询依据,而是每张表单独新增一个字段作为唯一主键使用,比如订单表订单号是唯一的,不管最终落在哪张表都基于订单号作为查询依据,更新也一样。

15.分表后非sharding_key的查询怎么处理呢?

  1. 可以做一个mapping表,比如这时候商家要查询订单列表怎么办呢?不带user_id查询的话你总不能扫全表吧?所以我们可以做一个映射关系表,保存商家和用户的关系,查询的时候先通过商家查询到用户列表,再通过user_id去查询。
  2. 打宽表,一般而言,商户端对数据实时性要求并不是很高,比如查询订单列表,可以把订单表同步到离线(实时)数仓,再基于数仓去做成一张宽表,再基于其他如es提供查询服务。
  3. 数据量不是很大的话,比如后台的一些查询之类的,也可以通过多线程扫表,然后再聚合结果的方式来做。或者异步的形式也是可以的。
List<Callable<List<User>>> taskList = Lists.newArrayList();
for (int shardingIndex = 0; shardingIndex < 1024; shardingIndex++) {
    taskList.add(() -> (userMapper.getProcessingAccountList(shardingIndex)));
}
List<ThirdAccountInfo> list = null;
try {
    list = taskExecutor.executeTask(taskList);
} catch (Exception e) {
    //do something
}

public class TaskExecutor {
    public <T> List<T> executeTask(Collection<? extends Callable<T>> tasks) throws Exception {
        List<T> result = Lists.newArrayList();
        List<Future<T>> futures = ExecutorUtil.invokeAll(tasks);
        for (Future<T> future : futures) {
            result.add(future.get());
        }
        return result;
    }
}

16.说说mysql主从同步怎么做的吧?

首先先了解mysql主从同步的原理

  1. master提交完事务后,写入binlog
  2. slave连接到master,获取binlog
  3. master创建dump线程,推送binglog到slave
  4. slave启动一个IO线程读取同步过来的master的binlog,记录到relay log中继日志中
  5. slave再开启一个sql线程读取relay log事件并在slave执行,完成同步
  6. slave记录自己的binglog

在这里插入图片描述
由于mysql默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,这样会产生一个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,日志就丢失了。由此产生两个概念。

全同步复制
主库写入binlog后强制同步日志到从库,所有的从库都执行完成后才返回给客户端,但是很显然这个方式的话性能会受到严重影响。
半同步复制

和全同步不同的是,半同步复制的逻辑是这样,从库写入日志成功后返回ACK确认给主库,主库收到至少一个从库的确认就认为写操作完成。

总结:

如今互联网行业用的最多就是 MySQL,然而对于高级 Web 面试者,尤其对于寻找 30k 下工作的求职者,很多 MySQL 相关知识点基本都会涉及,如果面试中,你的相关知识答的模糊和不切要点,基本大多稍好公司的面试官,基本也不会对你有兴趣,毕竟稍微量大的项目,最终都会让你去深入数据库,而且最终的大多瓶颈首先也会出现在数据库。面试中面试官会结合实际工作中遇到的问题来提出,所以面试题只是一部分,主要还是要靠自己的技术。

另外本人整理收藏了20年多家公司面试知识点整理 ,以及各种Java核心知识点免费分享给大家,想要资料的话请点795983544暗号CSDN。

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/banzhuanhu/article/details/108433552