面试总结,4年经验
小伙伴你好,我是田哥。
本文内容是一位星球朋友昨天面试遇到的问题,我把核心的问题整理出来了。
1:Java 层面的锁有用过吗?除了分布式锁以外
是的,Java中提供了多种锁机制来保证并发访问数据的安全性和一致性。常见的Java锁包括:
synchronized
关键字:用于对代码块或方法进行加锁,实现对象级别的互斥访问。ReentrantLock
类:通过构造器传入一个参数来指定是否使用公平锁,可以实现更灵活的线程控制。ReadWriteLock
接口:通过读写锁实现读共享、写独占的机制,可以提高并发读操作的效率。
这些锁机制在不同场景下都有各自的优缺点,需要根据具体的业务需求和性能要求来选择合适的锁机制。另外,还可以结合使用volatile
关键字、Atomic
类等原子操作来实现更高效的并发控制。
2:RocketMQ 消费模式一般有几种?
RocketMQ
消息队列的消费模式一般有两种,即集群消费和广播消费。
集群消费(Clustering)
集群消费是指多个消费者同时消费同一个主题(Topic)的消息,每个消息只能被其中一个消费者处理。当消费者组中的某个消费者挂掉后,其它消费者会自动接管该消费者的所有未确认的消息进行消费。这种消费模式适用于并行处理消息的场景。
广播消费(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 作为数据传输具有以下好处:
可扩展性:HTTP 是一种基于文本的协议,可以在各种平台和编程语言之间进行通信。因此,使用 HTTP 作为数据传输可以轻松地扩展应用程序。
简单性:HTTP 是一种非常简单的协议,易于理解和实现。这使得开发人员可以更快地编写代码,并且更容易维护。
可靠性:HTTP 协议提供了可靠的数据传输机制。如果一个请求失败了,客户端会收到一个错误响应,而不是一个错误的结果。这可以确保数据的完整性。
安全性:HTTP 协议可以与加密技术结合使用,例如 SSL/TLS,以提供安全的数据传输。
高效性:HTTP 协议使用 TCP/IP 协议栈,它是一种高效的协议栈,可以确保数据快速传输。此外,HTTP 还支持缓存机制,可以提高数据传输效率。
总之,选择使用 HTTP 作为数据传输协议可以带来许多好处,包括可扩展性、简单性、可靠性、安全性和高效性。
5:http协议和rpc协议的区别
HTTP 协议和 RPC(Remote Procedure Call)协议的主要区别在于:
技术实现方式不同:HTTP 是基于文本的协议,使用了请求-响应模式,而 RPC 是基于二进制的协议,使用了调用-返回模式。
应用场景不同:HTTP 协议通常用于 Web 应用程序中,用于浏览器与服务器之间的传输数据;而 RPC 协议用于客户端和服务端之间的通信,通常用于分布式系统或微服务架构中。
性能表现不同:RPC 协议相对于 HTTP 协议具有更好的性能表现,因为它使用较小的数据包进行通信,减少了网络延迟和带宽消耗。
接口描述方式不同:HTTP 使用 RESTful 风格的接口描述方式,而 RPC 使用 IDL(Interface Definition Language)来定义服务接口,可以更加明确和规范服务的接口。
语言支持不同:HTTP 协议是一种跨平台、跨语言的协议,支持任何编程语言;而 RPC 协议需要在客户端和服务端使用相同的语言和库才能通信。
总之,虽然 HTTP 协议和 RPC 协议都可以用作网络通信的协议,但它们的技术实现方式、应用场景、性能表现、接口描述方式和语言支持等方面都存在一定的差异,需要根据具体的应用场景来选择合适的协议。
6:在mysql数据库中,比如说在更新的时候会有几种生成的日志格式?
在 MySQL 数据库中,在进行数据更新操作时,会根据事务隔离级别和二进制日志的格式生成不同类型的日志。以下是几种可能的日志格式:
语句格式(statement-based):这种日志格式记录了 SQL 语句本身,也就是在执行 SQL 语句时所使用的具体语句。这种格式适用于非事务性存储引擎,或是事务隔离级别为 READ COMMITTED 的 InnoDB 存储引擎。
行格式(row-based):这种日志格式记录了每一行被修改前后的值,相对于语句格式,这种格式更加详细。这种格式适用于事务性存储引擎,如 InnoDB,以及事务隔离级别为 REPEATABLE READ 或 SERIALIZABLE 的存储引擎。
混合格式(mixed):这种日志格式是上述两种格式的结合。在默认情况下,MySQL 会使用混合格式来记录二进制日志。
可以通过设置系统变量 binlog_format 来切换日志格式,默认值为 MIXED。不同的日志格式有着各自的优缺点,需要根据实际应用场景选择合适的日志格式。
###:7:在MySQL数据库中,什么情况下会触发行锁、什么情况下会触发表锁?在 MySQL 数据库中,行锁和表锁是根据具体的 SQL 操作类型、事务隔离级别以及存储引擎等因素来触发的。
当进行 UPDATE、DELETE、INSERT INTO 等修改性操作时,在满足以下条件的情况下会触发行锁:
该表使用的存储引擎支持行锁(如 InnoDB 存储引擎);
事务隔离级别不是
READ UNCOMMITTED
;当前操作的数据行没有被其他事务占用或锁定;
而在其他情况下,则可能会触发表锁。比较常见的情况有:
当使用 LOCK TABLES 语句显式锁定整个表时;
当进行 SELECT 操作时,如果使用了表锁定读(
LOCK IN SHARE MODE
)或排他锁定读(FOR UPDATE
)。
需要注意的是,MySQL 中的表锁是一种粗粒度的锁定方式,会对整张表进行锁定,所以在高并发场景下可能会出现性能瓶颈。因此,建议尽量使用行锁定方式,同时合理设置事务隔离级别和存储引擎等参数,以达到更好的性能和并发控制效果。
7:说说 MySQL数据库中,悲观锁、乐观锁、表锁、行锁、页锁,最好是用代码来说。
MySQL 数据库中,悲观锁、乐观锁、表锁、行锁、页锁是常见的锁定方式。
悲观锁
悲观锁是一种传统的锁定方式,它的核心思想是“先加锁再操作”,即在每次对数据进行读写操作时,都会先对数据进行锁定,以防止其他并发操作对数据的干扰。悲观锁通常使用数据库的锁机制来实现,比如行锁或表锁等。由于需要频繁地加锁和解锁,在高并发的情况下可能会导致性能问题。
-- 创建测试表
CREATE TABLE `account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`balance` decimal(10,2) NOT 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 语句修改账户余额,最后提交事务。由于在查询过程中已经对该行数据进行了加锁,其他事务在这个时候是无法对该行数据进行读写操作,从而避免了并发问题。
乐观锁
乐观锁是一种基于版本号的锁定方式,它的核心思想是“先操作再判断”,即在每次对数据进行读写操作时,会先获取当前数据的版本号,并将其存储在本地。然后在提交更新之前,会先检查当前数据的版本号是否与本地存储的一致,如果一致,则说明没有其他并发操作对数据进行了修改,可以直接提交更新;如果不一致,则说明有其他并发操作对数据进行了修改,此时需要重新获取数据并重试更新操作。下面是一个示例:
-- 创建测试表
CREATE TABLE `account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`balance` decimal(10,2) NOT NULL,
`version` int(11) NOT 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 语句修改余额和版本号,只有在版本号与查询结果一致的情况下才会更新成功。
表锁
表锁是对整张表进行锁定的方式。在使用表锁时,会对整张表进行加锁,从而保证同时只有一个事务可以对表进行操作。表锁是一种粗粒度的锁定方式,可能会导致性能瓶颈。
以下是一个示例代码,演示如何使用表锁:
-- 获取表锁
LOCK TABLES mytable WRITE;
-- 执行对mytable的修改操作
UPDATE mytable SET column1 = 'new value' WHERE id = 1;
-- 释放表锁
UNLOCK TABLES;
在上面的代码中,LOCK TABLES
语句获取了mytable
的写锁,这意味着其他事务无法对该表进行修改操作,直到该锁被释放。在锁定期间,可以执行对该表的修改操作,例如UPDATE
语句。最后,UNLOCK TABLES
语句释放了表锁,使其他事务可以对该表进行修改操作。
行锁
行锁是对单行数据进行锁定的方式。在使用行锁时,会对要操作的数据行进行加锁,从而保证同时只有一个事务可以对该行数据进行操作。行锁是一种细粒度的锁定方式,可以提高并发性能和吞吐量。
下面是一个简单的代码案例,演示如何使用行锁:
-- 创建测试表
CREATE TABLE `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`age` int(11) NOT 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
的测试表,包含id
、name
和age
三个字段。然后插入了三条测试数据。接下来,我们使用两个事务对同一行数据进行更新操作。在每个事务中,我们使用FOR UPDATE
语句对该行数据加上行锁,防止其他事务修改该行数据。最后,我们提交了两个事务,完成了数据更新操作。
需要注意的是,行锁的使用需要谨慎,如果使用不当可能会导致死锁等问题。因此,在实际开发中,应该根据具体情况来选择合适的锁机制,以保证数据库的性能和数据的一致性。
页锁
页锁是对数据页进行锁定的方式。在使用页锁时,会对要操作的数据所在的页进行加锁,从而保证同时只有一个事务可以对该页数据进行操作。页锁是一种介于行锁和表锁之间的锁定方式,可以根据实际情况选择使用。
在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://woaijava.cc/
回复77 ,获取《面试小抄2.0版》
回复电子书,获取后端必读的200本电子书籍。
文章推荐
中厂,面试就问了4道题,凉了!
分布式问题,你知道几个?
应届生,实力已超6年,太卷了!
手把手教你写简历,包装、优化!
面试不问java,问MySQL,如何破局?