分享

面试总结,4年经验

 田维常 2023-04-28 发布于广东

小伙伴你好,我是田哥。

本文内容是一位星球朋友昨天面试遇到的问题,我把核心的问题整理出来了。

1:Java 层面的锁有用过吗?除了分布式锁以外

是的,Java中提供了多种锁机制来保证并发访问数据的安全性和一致性。常见的Java锁包括:

  1. synchronized 关键字:用于对代码块或方法进行加锁,实现对象级别的互斥访问。
  2. ReentrantLock 类:通过构造器传入一个参数来指定是否使用公平锁,可以实现更灵活的线程控制。
  3. ReadWriteLock 接口:通过读写锁实现读共享、写独占的机制,可以提高并发读操作的效率。

这些锁机制在不同场景下都有各自的优缺点,需要根据具体的业务需求和性能要求来选择合适的锁机制。另外,还可以结合使用volatile关键字、Atomic类等原子操作来实现更高效的并发控制。

2:RocketMQ 消费模式一般有几种?

RocketMQ 消息队列的消费模式一般有两种,即集群消费和广播消费。

  1. 集群消费(Clustering)

集群消费是指多个消费者同时消费同一个主题(Topic)的消息,每个消息只能被其中一个消费者处理。当消费者组中的某个消费者挂掉后,其它消费者会自动接管该消费者的所有未确认的消息进行消费。这种消费模式适用于并行处理消息的场景。

  1. 广播消费(Broadcasting)

广播消费是指多个消费者同时消费同一个主题(Topic)的消息,每个消费者都会消费一遍所有的消息,而不是共同消费所有消息。这种消费模式适用于需要将消息推送给所有消费者的场景,如系统通知。

在实际场景中,可以根据不同的业务需求选择不同的消费模式。

3:openFegin底层内部使用什么通信协议?

OpenFeign 是 Spring Cloud 组件之一,用于声明式的 REST 客户端调用。它底层使用了 Netflix 的 Ribbon 作为负载均衡器,并使用了 Feign 自己的编码器和解码器来支持五种不同的注解。而在具体的通信协议方面,OpenFeign 并没有限定使用哪种协议,它可以支持 HTTP、TCP 等传输协议的请求。

通常情况下,OpenFeign 基于 HTTP 协议进行通信,因为 RESTful API 是使用 HTTP 来完成状态转移的一种方式,而 OpenFeign 正是用于调用 RESTful 服务的工具。所以,在大多数场景下,OpenFeign 底层使用的是 HTTP 协议来进行通信。

4:选http 作为数据传输有什么好处吗?

使用 HTTP 作为数据传输具有以下好处:

  1. 可扩展性:HTTP 是一种基于文本的协议,可以在各种平台和编程语言之间进行通信。因此,使用 HTTP 作为数据传输可以轻松地扩展应用程序。

  2. 简单性:HTTP 是一种非常简单的协议,易于理解和实现。这使得开发人员可以更快地编写代码,并且更容易维护。

  3. 可靠性:HTTP 协议提供了可靠的数据传输机制。如果一个请求失败了,客户端会收到一个错误响应,而不是一个错误的结果。这可以确保数据的完整性。

  4. 安全性:HTTP 协议可以与加密技术结合使用,例如 SSL/TLS,以提供安全的数据传输。

  5. 高效性:HTTP 协议使用 TCP/IP 协议栈,它是一种高效的协议栈,可以确保数据快速传输。此外,HTTP 还支持缓存机制,可以提高数据传输效率。

总之,选择使用 HTTP 作为数据传输协议可以带来许多好处,包括可扩展性、简单性、可靠性、安全性和高效性。

5:http协议和rpc协议的区别

HTTP 协议和 RPC(Remote Procedure Call)协议的主要区别在于:

  1. 技术实现方式不同:HTTP 是基于文本的协议,使用了请求-响应模式,而 RPC 是基于二进制的协议,使用了调用-返回模式。

  2. 应用场景不同:HTTP 协议通常用于 Web 应用程序中,用于浏览器与服务器之间的传输数据;而 RPC 协议用于客户端和服务端之间的通信,通常用于分布式系统或微服务架构中。

  3. 性能表现不同:RPC 协议相对于 HTTP 协议具有更好的性能表现,因为它使用较小的数据包进行通信,减少了网络延迟和带宽消耗。

  4. 接口描述方式不同:HTTP 使用 RESTful 风格的接口描述方式,而 RPC 使用 IDL(Interface Definition Language)来定义服务接口,可以更加明确和规范服务的接口。

  5. 语言支持不同:HTTP 协议是一种跨平台、跨语言的协议,支持任何编程语言;而 RPC 协议需要在客户端和服务端使用相同的语言和库才能通信。

总之,虽然 HTTP 协议和 RPC 协议都可以用作网络通信的协议,但它们的技术实现方式、应用场景、性能表现、接口描述方式和语言支持等方面都存在一定的差异,需要根据具体的应用场景来选择合适的协议。

6:在mysql数据库中,比如说在更新的时候会有几种生成的日志格式?

在 MySQL 数据库中,在进行数据更新操作时,会根据事务隔离级别和二进制日志的格式生成不同类型的日志。以下是几种可能的日志格式:

  1. 语句格式(statement-based):这种日志格式记录了 SQL 语句本身,也就是在执行 SQL 语句时所使用的具体语句。这种格式适用于非事务性存储引擎,或是事务隔离级别为 READ COMMITTED 的 InnoDB 存储引擎。

  2. 行格式(row-based):这种日志格式记录了每一行被修改前后的值,相对于语句格式,这种格式更加详细。这种格式适用于事务性存储引擎,如 InnoDB,以及事务隔离级别为 REPEATABLE READ 或 SERIALIZABLE 的存储引擎。

  3. 混合格式(mixed):这种日志格式是上述两种格式的结合。在默认情况下,MySQL 会使用混合格式来记录二进制日志。

可以通过设置系统变量 binlog_format 来切换日志格式,默认值为 MIXED。不同的日志格式有着各自的优缺点,需要根据实际应用场景选择合适的日志格式。

###:7:在MySQL数据库中,什么情况下会触发行锁、什么情况下会触发表锁?在 MySQL 数据库中,行锁和表锁是根据具体的 SQL 操作类型、事务隔离级别以及存储引擎等因素来触发的。

当进行 UPDATE、DELETE、INSERT INTO 等修改性操作时,在满足以下条件的情况下会触发行锁:

  1. 该表使用的存储引擎支持行锁(如 InnoDB 存储引擎);
  2. 事务隔离级别不是 READ UNCOMMITTED
  3. 当前操作的数据行没有被其他事务占用或锁定;

而在其他情况下,则可能会触发表锁。比较常见的情况有:

  1. 当使用 LOCK TABLES 语句显式锁定整个表时;
  2. 当进行 SELECT 操作时,如果使用了表锁定读(LOCK IN SHARE MODE)或排他锁定读(FOR UPDATE)。

需要注意的是,MySQL 中的表锁是一种粗粒度的锁定方式,会对整张表进行锁定,所以在高并发场景下可能会出现性能瓶颈。因此,建议尽量使用行锁定方式,同时合理设置事务隔离级别和存储引擎等参数,以达到更好的性能和并发控制效果。

7:说说 MySQL数据库中,悲观锁、乐观锁、表锁、行锁、页锁,最好是用代码来说。

MySQL 数据库中,悲观锁、乐观锁、表锁、行锁、页锁是常见的锁定方式。

  1. 悲观锁

悲观锁是一种传统的锁定方式,它的核心思想是“先加锁再操作”,即在每次对数据进行读写操作时,都会先对数据进行锁定,以防止其他并发操作对数据的干扰。悲观锁通常使用数据库的锁机制来实现,比如行锁或表锁等。由于需要频繁地加锁和解锁,在高并发的情况下可能会导致性能问题。

-- 创建测试表
CREATE TABLE `account` (
  `id` int(11NOT NULL AUTO_INCREMENT,
  `name` varchar(50NOT NULL,
  `balance` decimal(10,2NOT NULL,
  PRIMARY KEY (`id`)
ENGINE=InnoDB;

-- 插入测试数据
INSERT INTO `account` VALUES (1,'张三',1000.00),(2,'李四',1000.00);

-- 开始事务
START TRANSACTION;

-- 查询账户余额并加锁
SELECT `balance` FROM `account` WHERE `name` = '张三' FOR UPDATE;

-- 修改账户余额
UPDATE `account` SET `balance` = `balance` - 500 WHERE `name` = '张三';

-- 提交事务
COMMIT;

上述示例中,我们先使用 SELECT ... FOR UPDATE 语句查询账户余额并加锁,然后再执行 UPDATE 语句修改账户余额,最后提交事务。由于在查询过程中已经对该行数据进行了加锁,其他事务在这个时候是无法对该行数据进行读写操作,从而避免了并发问题。

  1. 乐观锁

乐观锁是一种基于版本号的锁定方式,它的核心思想是“先操作再判断”,即在每次对数据进行读写操作时,会先获取当前数据的版本号,并将其存储在本地。然后在提交更新之前,会先检查当前数据的版本号是否与本地存储的一致,如果一致,则说明没有其他并发操作对数据进行了修改,可以直接提交更新;如果不一致,则说明有其他并发操作对数据进行了修改,此时需要重新获取数据并重试更新操作。下面是一个示例:

-- 创建测试表
CREATE TABLE `account` (
  `id` int(11NOT NULL AUTO_INCREMENT,
  `name` varchar(50NOT NULL,
  `balance` decimal(10,2NOT NULL,
  `version` int(11NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
ENGINE=InnoDB;

-- 插入测试数据
INSERT INTO `account` VALUES (1,'张三',1000.00,1),(2,'李四',1000.00,1);

-- 开始事务
START TRANSACTION;

-- 查询账户余额和版本号
SELECT `balance``version` FROM `account` WHERE `name` = '张三';

-- 修改账户余额和版本号
UPDATE `account` SET `balance` = `balance` - 500`version` = `version` + 1 WHERE `name` = '张三' AND `version` = 1;

-- 提交事务
COMMIT;

上述示例中,我们在表中添加了一个 VERSION 字段,并在查询和更新语句中使用它来实现乐观锁。在更新时,我们先查询该行数据的版本号,然后再执行 UPDATE 语句修改余额和版本号,只有在版本号与查询结果一致的情况下才会更新成功。

  1. 表锁

表锁是对整张表进行锁定的方式。在使用表锁时,会对整张表进行加锁,从而保证同时只有一个事务可以对表进行操作。表锁是一种粗粒度的锁定方式,可能会导致性能瓶颈。

以下是一个示例代码,演示如何使用表锁:

-- 获取表锁
LOCK TABLES mytable WRITE;

-- 执行对mytable的修改操作
UPDATE mytable SET column1 = 'new value' WHERE id = 1;

-- 释放表锁
UNLOCK TABLES;

在上面的代码中,LOCK TABLES语句获取了mytable的写锁,这意味着其他事务无法对该表进行修改操作,直到该锁被释放。在锁定期间,可以执行对该表的修改操作,例如UPDATE语句。最后,UNLOCK TABLES语句释放了表锁,使其他事务可以对该表进行修改操作。

  1. 行锁

行锁是对单行数据进行锁定的方式。在使用行锁时,会对要操作的数据行进行加锁,从而保证同时只有一个事务可以对该行数据进行操作。行锁是一种细粒度的锁定方式,可以提高并发性能和吞吐量

下面是一个简单的代码案例,演示如何使用行锁:

-- 创建测试表
CREATE TABLE `test` (
  `id` int(11NOT NULL AUTO_INCREMENT,
  `name` varchar(50NOT NULL,
  `age` int(11NOT NULL,
  PRIMARY KEY (`id`)
ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 插入测试数据
INSERT INTO `test` (`name``age`VALUES ('张三'20);
INSERT INTO `test` (`name``age`VALUES ('李四'22);
INSERT INTO `test` (`name``age`VALUES ('王五'25);

-- 执行事务1,使用行锁更新数据
START TRANSACTION;
SELECT * FROM `test` WHERE `id`=1 FOR UPDATE;
UPDATE `test` SET `age`=21 WHERE `id`=1;
COMMIT;

-- 执行事务2,使用行锁更新数据
START TRANSACTION;
SELECT * FROM `test` WHERE `id`=1 FOR UPDATE;
UPDATE `test` SET `age`=22 WHERE `id`=1;
COMMIT;

在上面的例子中,我们首先创建了一个名为test的测试表,包含idnameage三个字段。然后插入了三条测试数据。接下来,我们使用两个事务对同一行数据进行更新操作。在每个事务中,我们使用FOR UPDATE语句对该行数据加上行锁,防止其他事务修改该行数据。最后,我们提交了两个事务,完成了数据更新操作。

需要注意的是,行锁的使用需要谨慎,如果使用不当可能会导致死锁等问题。因此,在实际开发中,应该根据具体情况来选择合适的锁机制,以保证数据库的性能和数据的一致性。

  1. 页锁

页锁是对数据页进行锁定的方式。在使用页锁时,会对要操作的数据所在的页进行加锁,从而保证同时只有一个事务可以对该页数据进行操作。页锁是一种介于行锁和表锁之间的锁定方式,可以根据实际情况选择使用。

在MySQL中,可以使用SELECT ... FOR UPDATE语句来获取页锁,例如:

-- 事务A获取页锁
BEGIN;
SELECT * FROM user WHERE age >= 18 AND age <= 25 FOR UPDATE;

-- 事务B尝试获取页锁,会被阻塞
BEGIN;
UPDATE user SET age = 20 WHERE age >= 18 AND age <= 25;

在上述代码中,事务A获取了age在18到25岁之间的所有行的页锁,事务B在尝试更新这些行时,由于这些行已经被事务A持有页锁,所以需要等待事务A释放锁才能执行更新操作。

需要注意的是,页锁只能保证页级别的互斥操作,并不能保证行级别的互斥操作。如果多个事务同时对同一行数据进行修改,即使这些行属于同一个页,也不会被页锁阻塞,因为页锁只限制对整页的修改。

另外,使用页锁可能会导致死锁问题,因为如果两个事务都要修改同一页数据,但是获取锁的顺序不同,就会发生死锁。因此,需要谨慎使用页锁。

除了页锁外,MySQL还有行锁和表锁两种锁机制,需要根据具体情况选择适当的锁。

最后

从文章中大家也可以发现,现在面试中,MySQL会占很大的比例,所以希望大家多多留意MySQL数据相关的问题。

好了,今天就分享这么多,我们下期再见,记得点赞收藏

题外话:如果有需要简历修改、简历优化、简历包装、面试辅导、模拟面试、技术辅导、技术支持等,欢迎加我微tj20120622

我的个人技术博客:http:///

回复77 ,获取《面试小抄2.0版》

回复电子书,获取后端必读的200本电子书籍。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多