1.百度秋招面试题
1.2024 百度提前批Java面试
一面
1.1 算法题:一个长度为n的数组中找出m个最大的数。
思路:将数组排序,然后创建一个长度为m的数组,将原数组下标n-m-1到n-1的数组复制到长度到m的新数组中。
public class FindMaxM { public static int[] findMaxM(int[] nums, int m){ if (nums == null || nums.length == 1 || m <= 0 || m > nums.length){ return new int[0]; } Arrays.sort(nums); int[] arr = new int[m]; //复制原数组后m个数给新数组 System.arraycopy(nums, nums.length-m, arr, 0, m); return arr; } public static void main(String[] args) { int[] nums = {1, 3, 2, 5, 4}; int[] maxM = findMaxM(nums, 3); for (int val: maxM) { System.out.print(val + " "); } } }
测试结果:
3 4 5
1.2 说出给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url的思路
该问题设计大数据处理和算法优化知识点。由于内存限制是4G,文件a和b共占用空间超过该限制,因此无法将所有URL加载到内存中比较。为解决该问题,可以使用哈希算法将文件a和b的URL分成多个小文件,再将每个小文件加载到内存中比较。具体流程为:
- 对文件a和b的每个URL计算hashcode,并根据hashcode将URL分配到不同的小文件中;
- 接着将每个小文件加载到内存中,使用hashset或布隆过滤器存储URL,加快查询
- 一旦所有的URL都处理完毕,并存储在数据结构中,可以将文件b逐个加载到内存中,同时对每个URL进行哈希计算,并在数据结构中查询是否存在相同的URL。
- 如果存在相同的URL,即找到共同的URL之一,可以记录下来或进行其他处理。
- 重复执行步骤4,直到文件b中的所有URL都处理完毕,返回最后结果。
1.2.1 布隆过滤器
布隆过滤器(Bloom Filter)是一种空间效率非常高的概率型数据结构,用于判断某个元素是否存在于一个集合中。它通过使用位数组和多个哈希函数来判断元素的存在性,具有高效的插入和查询操作,并且相对于传统的数据结构可以节省大量的内存空间。
原理:
- 初始化时,创建一个位数组,长度为m,并设置所有位的值为0。
- 选择k个独立的哈希函数,每个哈希函数可以将元素映射到位数组中的一个位置。
- 针对要插入的元素,依次使用k个哈希函数计算哈希值,并将对应的位数组位置设置为1。
- 对于查询操作,同样使用k个哈希函数计算待查询元素的哈希值,并检查对应的位数组位置是否全为1。如果有任何一个位数组位置为0,则判定该元素一定不存在;如果所有位数组位置都为1,则判定该元素可能存在。
布隆过滤器的使用场景如下:
唯一性判断:在数据库或缓存中,可以使用布隆过滤器判断某个元素是否已经存在,避免重复插入或查询;
URL去重:对于大规模的URL集合,可以将URL放入布隆过滤器中以过滤重复的URL,减少存储和计算资源的消耗;
黑名单过滤:可以将黑名单中的元素放入布隆过滤器,从而高效地判断某个元素是否在黑名单中;
防止缓存穿透:通过布隆过滤器判断请求的数据是否在缓存中不存在,从而减轻数据库或后端存储的压力。
1.3 缓存雪崩、缓存击穿、缓存穿透
为保证缓存数据与数据库数据的一致性,会给 Redis 里的数据设置过期时间,当缓存数据过期后,用户访问的数据如果不在缓存里,业务系统需要重新生成缓存,因此就会访问数据库,并将数据更新到 Redis 里,这样后续请求都可以直接命中缓存。
1.3.1 缓存穿透
缓存穿透:当用户访问数据时,数据既不在缓存中,也不在数据库中。当有大量的用户请求数据时,数据库压力剧增。
发生原因:1)业务误操作导致缓存和数据库中数据全部被删除;2)恶意黑客攻击,大量访问某些读取不到的数据的业务。
解决方案:
- 非法请求限制,在API接口处判断参数合理性,避免访问缓存和数据库;
- 缓存null值或默认值,避免查询数据库;
- 使用布隆过滤器快速判断数据是否存在,避免查询数据库数据是否存在,来应对缓存穿透。
缓存穿透中布隆过滤器的工作原理:
布隆过滤器由「初始值都为 0 的位图数组」和「 N 个哈希函数」两部分组成。当我们在写入数据库数据时,在布隆过滤器里做个标记,这样下次查询数据是否在数据库时,只需要查询布隆过滤器,如果查询到数据没有被标记,说明不在数据库中。
布隆过滤器会执行下面三个操作完成标记:
- 第一步,使用 N 个哈希函数分别对数据做哈希计算,得到 N 个哈希值;
- 第二步,将第一步得到的 N 个哈希值对位图数组的长度取模,得到每个哈希值在位图数组的对应位置。
- 第三步,将每个哈希值在位图数组的对应位置的值设置为 1;
举个例子,假设有一个位图数组长度为 8,哈希函数 3 个的布隆过滤器。
在数据库写入数据 x 后,把数据 x 标记在布隆过滤器时,数据 x 会被 3 个哈希函数分别计算出 3 个哈希值,然后在对这 3 个哈希值对 8 取模,假设取模的结果为 1、4、6,然后把位图数组的第 1、4、6 位置的值设置为 1。当应用要查询数据 x 是否数据库时,通过布隆过滤器只要查到位图数组的第 1、4、6 位置的值是否全为 1,只要有一个为 0,就认为数据 x 不在数据库中。
布隆过滤器由于是基于哈希函数实现查找的,高效查找的同时存在哈希冲突的可能性,比如数据 x 和数据 y 可能都落在第 1、4、6 位置,而事实上,可能数据库中并不存在数据 y,存在误判的情况。所以,查询布隆过滤器说数据存在,并不一定证明数据库中存在这个数据,但是查询到数据不存在,数据库中一定就不存在这个数据。
1.3.2 缓存雪崩
缓存雪崩是指用户访问时,大量缓存数据同时失效或Redis故障宕机;当大量用户发起访问请求时,这些请求会直接访问数据库,造成数据库压力剧增,甚至宕机。
发生原因:1)大量缓存同时失效,大量用户请求访问缓存中失效数据;2)Redis故障宕机。
解决方案:
1.针对大量缓存同时失效引起的缓存雪崩,可采取以下方法:
- 均匀设置过期时间:在设置缓存过期时间时,给过期时间添加一个随机数,保证数据不会在同一时间过期。
- 互斥锁:加个互斥锁,保证同一时间内只有一个请求来构建缓存(从数据库读取数据,再将数据更新到 Redis 里),当缓存构建完成后,再释放锁。未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。实现互斥锁时,最好设置超时时间,避免其他请求长时间无法获取锁导致系统无响应。
- 后台更新缓存:1)后台线程不仅负责定时更新缓存,而且也负责频繁地检测缓存是否有效(轮询),检测到缓存失效了,原因可能是系统紧张而被淘汰的,于是就要马上从数据库读取数据,并更新到缓存。这种方式的检测时间间隔不能太长,太长也导致用户获取的数据是一个空值而不是真正的数据,所以检测的间隔最好是毫秒级的,但是总归是有个间隔时间,用户体验一般。
2)在业务线程发现缓存数据失效后(缓存数据被淘汰),通过消息队列发送一条消息通知后台线程更新缓存(异步通知),后台线程收到消息后,在更新缓存前可以判断缓存是否存在,存在就不执行更新缓存操作;不存在就读取数据库数据,并将数据加载到缓存。该方式相比前一种方式缓存的更新会更及时,用户体验也比较好。
在业务刚上线时,我们最好提前把数据缓起来,而不是等待用户访问才来触发缓存构建,这就是所谓的缓存预热,后台更新缓存的机制适合该场景。
1.3.3 缓存击穿
缓存击穿是指缓存中的某个热点数据过期,当大量请求访问时,无法从缓存中读取,请求数据库,造成数据库压力剧增,甚至宕机。
发生原因:缓存中某个热点数据过期,大量用户请求访问给数据。
解决方法:
- 互斥锁:保证同一时间只有一个业务线程更新缓存,未获取锁的请求线程,要么等待锁释放后重新读取缓存,要么直接返回默认值或null值。
- 不给热点数据设置过期时间:由后台异步更新缓存,或者在热点数据准备要过期前,提前通知后台线程更新缓存以及重新设置过期时间。
其实从缓存击穿和缓存雪崩,可以看出缓存击穿是属于缓存雪崩的子集,一个是缓存数据,一个是缓存中热点数据。
1.4 服务器处理并发请求有哪几种方式?(如tomcat,nginx)
- 多线程处理(应用层):服务器可以使用线程池来处理并发请求。每个请求都分配一个线程来处理,可以同时处理多个请求。该方式可以充分利用服务器的多核资源,提高并发处理能力。但是线程资源开销较大,需要对线程池大小进行适当配置,避免线程过多导致服务器负载过高。
- 异步处理(限流):服务器可以使用异步处理方式来处理并发请求。不需要为每个请求分配一个线程,而是将请求交给专门的线程池进行处理。异步处理可以通过非阻塞I/O等技术实现,提高服务器的吞吐能力和并发处理性能。
- 集群部署:可以使用服务器集群的方式来处理并发请求。将请求分配到不同的服务器节点进行处理,可以通过负载均衡来实现请求的均衡分发。集群部署方式可以提供更高的并发容量和可用性,减轻单个服务器的压力。
- 缓存技术(快速响应):可以使用缓存技术来降低服务器负载和提高并发性能。将一些计算结果、数据库查询结果等保存在缓存中,可以减少对后端资源的请求,提高响应速度和并发能力。
- 静态资源分离(分离):可以将静态资源(如图片、CSS、JS文件)分离到独立的服务器或CDN上进行处理,减少对应用服务器的压力,提高并发处理能力。
1.4 谈谈IO模型以及select,poll,epoll吗
1.4.1 何为IO?
I/O即输入输出,从计算机结构角度来看,就是计算机系统与外部设备之间的通信。
从应用角度来看,应用程序对操作系统发起了IO调用(系统调用),操作系统负责的内核执行具体的IO操作。
1.4.2 常见的IO模型
1.4.2.1 BIO(同步阻塞IO模型)
同步阻塞 IO 模型中,应用程序发起 read 调用后,会一直阻塞,直到内核把数据拷贝到用户空间。
该模型在并发量低的情况下可以很好处理请求,一旦并发请求达到十万甚至百万的时候,模型无法承受。
1.4.2.2 NIO
Java 中的 NIO 于 Java 1.4 中引入,对应 java.nio
包,提供了 Channel
, Selector
,Buffer
等抽象。NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它是支持面向缓冲的,基于通道的 I/O 操作方法。 对于高负载、高并发的(网络)应用,应使用 NIO 。Java 中的 NIO 可以看作是 I/O 多路复用模型。
NIO模型中,应用程序会一直发起 read 调用,等待数据从内核空间拷贝到用户空间的这段时间里,线程依然是阻塞的,直到在内核把数据拷贝到用户空间。相比于BIO 模型,通过轮询操作,避免了一直阻塞。
NIO核心组件
NIO主要包含Buffer、Channel、Selector三个组件:
- Buffer(缓冲区):NIO 读写数据都是通过缓冲区进行操作的。读操作的时候将 Channel 中的数据填充到 Buffer 中,而写操作时将 Buffer 中的数据写入到 Channel 中。
- Channel(通道):Channel 是一个双向的、可读可写的数据传输通道,NIO 通过 Channel 来实现数据的输入输出。通道是一个抽象的概念,它可以代表文件、套接字或者其他数据源之间的连接。
- Selector(选择器):允许一个线程处理多个 Channel,基于事件驱动的 I/O 多路复用模型。所有的 Channel 都可以注册到 Selector 上,由 Selector 来分配线程来处理事件。
IO 多路复用模型,通过减少无效的系统调用,减少了对 CPU 资源的消耗。Java 中的 NIO ,有一个非常重要的选择器 ( Selector ) 的概念,也可以被称为 多路复用器。通过它,只需要一个线程便可以管理多个客户端连接。当客户端数据到了之后,才会为其服务。
注意:该NIO模型存在的问题是应用程序不断进行 I/O 系统调用轮询数据是否已经准备好的过程是十分消耗 CPU 资源。
为解决上述问题,IO多路复用产生了作用:IO 多路复用模型中,线程首先发起 select(支持所有操作系统) 调用,询问内核数据是否准备就绪,等内核把数据准备好了,用户线程再发起 read 调用。read 调用的过程(数据从内核空间 -> 用户空间)还是阻塞的。
1.4.2.3 AIO
AIO 即 NIO 2。Java 7 中引入了 NIO 的改进版 NIO 2,它是异步 IO 模型。AIO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
小结:
- BIO是应用程序发起read调用后,会一直阻塞,知道数据从内核空间拷贝到用户空间;
- NIO是应用程序会一直发起read调用,等待数据从内核空间拷贝到用户空间的时间内,现成是阻塞的;
- I/O多路复用中,会通过select调用,询问内核数据是否准备就绪,等内核数据准备就绪后,应用程序发起read调用;
- AIO是基于事件和回调的,应用程序发起read调用后会立即返回,等后台程序处理完请求后,操作系统会通知响应的线程进行后续操作。
1.4.4 select、poll、epoll
select、poll和epoll是I/O多路复用的技术,用于实现高效的事件驱动网络编程。它们的作用是监听多个文件描述符(sockets、文件、设备等)的I/O事件,并在事件就绪时通知应用程序进行处理。
- select:
- 原理:使用轮询方式检测文件描述符的状态,包括可读、可写和错误等事件。当一个或多个描述符就绪时,将其通知给应用程序进行处理。
- 作用:通过单个线程同时处理多个I/O事件,提高应用程序的并发性能。
- 应用场景:适用于连接数较少或者单个进程需要处理大量低并发连接的场景。
- poll:
- 原理:与select类似,也是采用轮询的方式监听文件描述符的状态。与select的区别在于使用了链表来保存文件描述符,解决了select中文件描述符数量有限的问题。
- 作用:提供了更高效的I/O多路复用机制,适用于连接数较多的场景。
- 应用场景:适用于连接数相对较多的情况,但是由于遍历链表的开销,对大量连接仍有性能瓶颈。
- epoll:
- 原理:通过内核事件表(event table)来保存和管理文件描述符的事件。应用通过epoll_ctl向内核注册感兴趣的事件,内核则将就绪的事件通知给应用程序。
- 作用:通过事件通知的方式来处理I/O事件,避免了遍历等待事件就绪的开销,提高了并发处理性能。
- 应用场景:适用于连接数非常多和并发连接性能要求较高的场景,比如高性能服务器、实时通信系统等。
小结:它们三种都是I/O多路复用技术,都可以监听文件描述符的I/O事件,在事件就绪时通知应用程序处理事件。select和poll都是以轮询的方式监听文件描述符的状态,前者只适用于低并发的时间处理,后者由于使用链表保存文件描述符,可解决前者文件描述符数量有限的问题。epoll通过内核事件表保存和管理文件描述符的事件,以事件通知的方式处理I/O事件,避免遍历循环时间就绪开销,适用于高并发场景的时间处理。
1.5 redis,nginx,netty这些超高性能的中间件,是依赖什么做的这么高性能
- I/O多路复用:这些中间件通过使用事件循环和多路复用技术,实现了高并发的I/O操作,避免了线程切换和同步等开销,提高了系统的响应性能和吞吐量。
- 内存管理:中间件如Redis和Netty会更加有效地利用内存进行数据的存储和处理。Redis作为内存数据库,使用高效的数据结构和算法,减少了内存访问的开销。Netty作为网络编程框架,采用内存池技术来管理网络缓冲区,减少了内存分配和释放的开销。
- 多线程支持:这些中间件通常会采用多线程的方式来处理并发请求。通过充分利用多核处理器,将不同的请求分发到不同的线程进行处理,实现了并发和并行执行。同时,通过线程池等技术可以避免线程的频繁创建和销毁,提高了系统的效率和稳定性。
- 零拷贝技术:中间件如Nginx和Netty使用了零拷贝技术来减少数据的复制和拷贝。通过在内核态中直接进行数据传输,避免了数据从内核缓冲区到应用程序缓冲区的拷贝,减少了CPU的拷贝及上下文切换消耗和数据的延迟。
1.6 https是如何防范中间人的攻击
中间人攻击(Man-in-the-Middle Attack,简称MITM攻击)是一种常见的网络安全攻击方法。在中间人攻击中,攻击者置身于通信双方之间,伪装成合法的通信方与双方进行通信。攻击者可以监听、窃取、篡改或伪造通信数据,而通信双方都不会意识到中间有第三方存在。
1.6.1 中间人攻击的几种方式
- 直接抓取报文获得明文信息
- 非法中间加密代理,窃取明文信息
- 留存密文,如果对称密钥泄露,解密历史报文
1.6.2 Https访问的三个阶段
认证站点
客户端向站点发起HTTPS请求,站点返回数字证书。客户端通过数字证书验证所访问的站点是真实的目标站点。
协商密钥
客户端与站点服务器协商此次会话的对称密钥,用于下一阶段的加密传输。
加密传输
客户端与站点直接使用已协商的对称加密密钥传输数据。
对于中间人攻击,Https通过三种方法来防范:
- 使用混合加密,在通信前使用非对称加密,在通信后使用对称加密;保证传输内容不被窃听;
- 发送方和接收方各自采用摘要算法对传输内容计算哈希值,如果相等,则传输内容未被篡改;但不能确认中间人是否偷换传输内容和哈希值,因此通过非对称加密算法(公钥解密,私钥加密)加密内容的哈希值,保证消息的可靠性;
- 由于存在公钥被伪造情况,因此发送端向数字证书认证机构注册公钥,再通过数组证书认证机构私钥加密发送者公钥,从而保证发送端公钥的安全性。
1.7 描述一下打开百度首页后发生的网络过程
- 首先我们会在浏览器输入访问的URL,浏览器会解析URL为请求的Web服务器和文件名。DNS服务器先从本地缓存中查询服务器对应的IP地址,如果找不到就递归迭代地从根服务器找到顶级服务器,再从顶级服务器找到权威服务器,直到找到对应的网址对应的IP地址,然后将网址解析为IP地址;
- 在传输层,TCP中的源端口和目标端口明确将数据传输到哪个应用,在传输消息之前需要建立三次握手保证发送方和接收方的可靠通信;
- 在网络接口层,需要源IP地址和目标IP地址才能进行数据传输;在第一步中解析出了目标IP,现在需要计算的源IP地址判断源IP地址由客户端哪一个网卡发出。IP层根据路由 规则将目标IP与每个网卡的子网掩码进行位于运算得到IP地址,与该网卡的Destination比较。如果相同,则将该网卡的IP地址作为IP包头的源地址。
- 在MAC层,需要发送方的MAC地址和目标MAC地址,MAC地址只需要读取网卡的ROM就可获得。在第三步中我们已确定源IP由哪一个网卡发出,因此可获取源MAC地址;想要获取目标MAC地址,首先根据路由表将发送的包通过网关发送给接收方的IP,然后通过ARP广播(先查ARP缓存,存在返回缓存中IP对应的MAC地址,反之广播)的形式找到接收方IP对应的网卡,再从该网卡中获取其MAC地址。
- 在网卡层,在准备传输的二进制数据后头部添加起始帧标记符标记包起始位置,在尾部添加FCS校验包是否损坏;然后将封装后的二进制数据由数字信号转换为电信号在网线中传输;
- 在交换机层,交换机将电信号转换为数字信号,然后校验包的FCS确认包是否损坏,如果数据没有损坏将数据存入其缓存区,根据MAC地址表查询目标MAC地址对应的IP地址,然后将报数据发送到相应端口。
- 在路由器中,路由器具有MAC地址和IP地址。首先会根据MAC地址将包数据传输到目标端口所在的以太网,再通过IP地址确定接收方的IP地址。
1.8 什么是ddos攻击,怎么防范
DDoS攻击(分布式拒绝服务攻击)是一种恶意行为,攻击者试图通过占用目标系统的资源,使其无法正常提供服务。DDoS攻击通常通过使用大量的计算机或设备来向目标系统发送垃圾流量(类似于秒杀系统中的黄牛刷单),超过目标系统的处理能力,导致系统瘫痪或服务不可用。
- 流量过滤和防火墙:使用流量过滤器和防火墙来识别和过滤掉来自潜在攻击者的恶意流量。这可以有效减轻攻击的影响。
- 限制公共资源:对公共资源(如API、登录接口、表单提交等)进行访问限制,如设置访问频率限制、验证码、身份验证等,以减少恶意流量的影响。
- 负载均衡:使用负载均衡技术来分散流量,将流量分发到多个服务器上,使攻击难以集中于单一目标。
- CDN(内容分发网络):使用CDN将流量分发到各地的边缘节点,以减轻服务器的负担,提高服务的可用性。
1.9 进程和线程通信的方式有哪些
1.9.1 进程通信
进程通信是指不同进程之间交换数据和信息的机制。
- 管道(Pipe):管道是一种半双工的通信方式,它可以在父进程和子进程之间传递数据。管道通常用于具有亲缘关系的进程之间的通信。
- 命名管道(Named Pipe):命名管道是一种允许不相关进程之间通信的机制,它使用一个具有唯一名称的文件作为通信的中介。
- 信号(Signal):信号是一种异步通信方式,用于在进程之间传递简单的消息或通知。
- 消息队列(Message Queue):消息队列是一种用于在进程之间传递数据的机制,它允许按照特定规则在队列中存储消息,并由接收方按照特定顺序接收。
- 共享内存(Shared Memory):共享内存是一种在不同进程之间共享数据的高效方式,它允许多个进程访问相同的物理内存区域,从而实现数据共享。
- 信号量(Semaphore):信号量是一种用于进程间同步和互斥的机制,用于管理对共享资源的访问。
- 套接字(Socket):套接字是一种网络通信的机制,它允许不同计算机上的进程进行通信。
1.9.2 线程通信
- 共享内存:多个线程可以通过共享内存区域来实现数据的共享和通信。每个线程可以访问相同的内存地址,通过读写该地址上的数据进行通信。
- 互斥锁:互斥锁是一种同步机制,用于确保同一时间只有一个线程可以访问某个共享资源。当线程需要访问共享资源时,首先尝试获取互斥锁。如果互斥锁已被其他线程占用,则当前线程会被阻塞,直到互斥锁被释放。
- 条件变量:条件变量用于在一个线程等待特定条件满足时被阻塞,直到其他线程通知条件满足,当前线程才会被唤醒。条件变量通常与互斥锁一起使用,以确保线程在等待和通知过程中的互斥性。
- 信号量:信号量是一种计数器,用于管理多个线程对共享资源的访问。线程在访问资源之前,必须通过信号量进行申请。如果信号量的计数器大于0,则线程可以继续访问资源,并将计数器减一;如果计数器为0,则线程将被阻塞,直到其他线程释放资源并增加计数器。
- 管道和队列:管道和队列是数据在多个线程之间传递的一种方式。管道可用于实现单向的、固定大小的数据传输,而队列则更适用于多个生产者和消费者之间的数据传输,支持多个数据项的缓存。
- RPC(远程过程调用):RPC是一种实现远程通信的方法,让程序在不同线程或不同计算机上的进程之间进行函数调用和数据传输。通过RPC,线程可以直接调用其他线程或进程中的函数,并传递参数和获取返回结果。
1.10 linux中有一个日志文件,日志文件中记录了访问请求的信息,第一列是访问的日期,第二列是请求的ip,第三列是请求的耗时,写一条shell命令来查到请求耗时最高的10条记录
假设日志名称为access.log
sort -k3 -nr access.log | head -n 10
sort -k3 -nr
:按照第三列(请求的耗时)进行逆序排序,-n
表示按照数值进行排序,-r
表示逆序排序
head -n 10
:获取结果的前10行,即耗时最高的10条记录
1.11 怎么查看哪个端口被哪个进程占用
1.11.1 Windows查看端口占用
在Windows中通过netstat- ano查看端口被进程占用情况
查找目标端口类似Linux命令的grep命令的findstr:netstat -ano | findstr 端口号
Windows中也有类似于Linux命令psde tasklist:tasklist | findstr 进程ID
1.11.2 Linux查看端口占用
端口占用:netstat -ntulp | grep 8080
查看进程号:ps -ef | grep 进程ID
1.12 用shell命令替换一个文件中的字符串
在shell命令中,替换一个文件中的字符串,使用sed命令
sed -i 's/old_string/new_string/g' filename
-i
:表示直接在原文件中进行替换操作。s/old_string/new_string/g
:是sed
命令的替换表达式,其中s
表示替换操作的开始,g
表示全局替换(一行中的所有匹配都会被替换)。filename
:是目标文件的名称,将在该文件中进行字符串替换。
1.13 使用过git吗,在一次commit后,如果想再进行一次commit并且和并之前的commit,一共只产生一条commit,该如何操作
在Git中,要将多个连续的commit合并成一条commit,你可以使用Git的交互式重新基于(Interactive Rebase)功能。
- 确保你当前在要操作的分支上,使用以下命令切换到该分支:
- 运行以下命令启动交互式重新基于操作:
<n>
是希望合并的commit数量,如上述问题希望合并为一条。
其中, - Git会打开一个文本编辑器,展示你要重新基于的提交历史列表。在编辑器中,将你想要合并的commit前面的
pick
改为squash
(或简写为s
)。这告诉Git将这些commit合并到前一个commit。 - 保存并关闭编辑器。Git会合并选定的commit,并要求你提供一个新的commit消息。
- 编辑器将打开一个新的文件,其中包含由合并生成的commit消息。根据需要,对commit消息进行编辑,然后保存并关闭编辑器。
- Git会创建一条新的commit,其中包含之前选定的提交的更改,并关闭交互式重新基于。
1.14 mysql有哪几种存储引擎,它们的区别是什么
mysql的存储引擎有innoDB、MySlam
- InnoDB:
- InnoDB是MySQL的默认存储引擎,它支持事务、行级锁以及外键约束,提供高并发性能和可靠性。
- InnoDB适合用于处理大量写操作的应用,如高并发的事务处理、OLTP(联机事务处理)等场景。
- MyISAM:
- MyISAM是MySQL的早期存储引擎,不支持事务和行级锁,但提供了表级锁定。
- MyISAM适合用于读密集型应用,如查询频繁但写操作较少的简单数据仓库、博客等
1.15 mysql的隔离级别分为哪几种类型
1.15.1 并发事务引起的问题
其中并发事务会引起的问题有脏读、不可重复读、幻读。
脏读:一个事务读取了另一个事务未提交修改的事务
不可重复读:一个事务多次读取同一数据,出现数据不一致情况
幻读:一个事务多次查询符合条件的数据,发现读取的数据记录数量不一致
1.15.2 mysql隔离级别
mysql的隔离级别有四种,分别是读未提交、读已提交、可重复读、串行化
读未提交:一个事务未提交时,他做的更改能被其他事务看到;
读已提交:一个事务提交后,他做的更新才能被其他事务看到;
可重复读:一个事务执行过程中看到的数据,与该事物启动时看到的数据一致;
串行化:在多个事务对记录进行读写操作时,会加上锁,发生读写冲突时,后访问的事务只能等先访问的事务执行完后,才能继续执行。
读已提交和可重复读都是以MVCC机制解决并发事务中的某些问题,其中读未提交解决了脏读问题,可重复读解决了脏读和不可重复读问题,串行化解决了并发事务的所有问题。
读已提交是在每次读取数据时都会生成一个Read View,而可重复读是在启动事务时生成一个视图,在执行过程中一直使用该视图。
1.15.3 Read View
Read View中存在四个重要字段:
- m_ids:创建Read View时,当前活跃事务(启动类但未提交的事务)的事务id列表;
- min_trx_id:创建Read View时,当前活跃事务中最小的事务id;
- max_trx_id:创建Read View中,当前活跃事务中最大的事务id+1;
- creator_trx_id:创建Read View事务的事务id;
1.15.4 聚簇索引记录
聚簇索引记录中包含两个隐藏列:
- trx_id:当一个事务对某条聚簇索引记录进行改动时,就会将该事务的事务id记录在trx_id隐藏列中;
- roll_pointer:每次对某条聚簇索引记录进行改动时,就会将旧版本记录记录在undo日志中,通过roll_pointer指向每个旧版本记录,通过它找到修改前的记录。
事务访问记录:
- 如果记录的 trx_id 值小于 Read View 中的
min_trx_id
值,表示这个版本的记录是在创建 Read View 前已经提交的事务生成的,所以该版本的记录对当前事务可见。 - 如果记录的 trx_id 值大于等于 Read View 中的
max_trx_id
值,表示这个版本的记录是在创建 Read View 后才启动的事务生成的,所以该版本的记录对当前事务不可见。 - 如果记录的 trx_id 值在 Read View 的
min_trx_id
和max_trx_id
之间,需要判断 trx_id 是否在 m_ids 列表中:- 如果记录的 trx_id 在
m_ids
列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见。 - 如果记录的 trx_id 不在
m_ids
列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见。
- 如果记录的 trx_id 在
1.16 慢查询是如何调试解决的
1.16.1 慢查询和慢查询日志
我们可以通过show variable like "%query%"语句查看慢查询的三个参数:
- long_query_time:慢查询时间阈值,超过则定义为慢查询语句,反之则不是;
- slow_query_log:是否开启慢查询日志;
- slow_query_log_file:慢查询日志文件
1.16.2 查看慢查询日志情况
- 使用日志分析工具mysqldumpslow来分析慢查询日志。
- 使用show processlist命令显示用户正在运行的线程。
- 在查询语句前加上explain关键字查看SQL语句的执行计划。
1.16.3 优化慢查询
通过排查定位到慢SQL后,在查询语句前面加上explain分析该SQL的执行计划进行优化:
- 如果没有走索引,要为表建立合适的索引:
- 如果是因为数据表太大,即使走了索引也依然很慢,这时要考虑分表
1.17 mysql的explain有什么作用
mysql中的explain可以用于查看一条sql查询的执行计划,我们可以执行计划中的type字段确定SQL语句查询是否走索引来提高查询效率,另外,还通过extra字段查看查询是否走覆盖索引减少查询次数,从而提升查询效率。
1.18 java中有哪些常用的锁,在什么场景下使用
Java中存在synchronized关键、ReentrantLock以及CAS等锁机制;
应用场景:
- synchronized锁可以用来修饰代码块、方法,它是一种轻量级锁、非公平锁、可重入锁。可防止多线程竞争同一资源引起的数据不同步问题,但是会造成死锁情况;
- lock是一种重量级锁,非公平锁,但可设置为公平锁,可防止多线程竞争统一资源引起的数据不同步问题。相较于synchronized,它是可中断锁,可使用tryLock方法避免死锁阻塞共享资源。他会尝试获取锁,如果获取不到就中断,不会像synchronized那样获取不到资源,就一直阻塞;
- CAS是一种无锁的体现,它利用一直尝试的机制获取共享资源,直到获取。
1.19 什么是反射,反射在java中有哪些使用场景
1.19.1 概念
反射是指在运行时分析类,执行类中方法的机制;反射可以获取任意一个类的所有属性和方法,并调用其属性和方法
1.19.2 使用场景
- 反射可以用于动态代理的实现,其中动态代理中的invoke方法,就使用到Method获取运行时被代理类的方法信息;
- 反射还在Spring的AOP中有着广泛应用,在切面的连接中会通过反射获取运行时方法的信息,进行方法的增强和拓展。
1.20 假如cpu跑到100%,你的解决思路是什么
当服务器的CPU爆满时,通过以下三个步骤排查。
- 执行top命令,查看所有进程占用系统CPU的排序
- 执行top -Hp PID查看进程下所有线程占CPU情况
- 由于jvm的进程快照中线程显示是16进制的,所以需要将PID转换为16进制,查看具体CPU爆满信息
2.参考面试题目链接
1.2024百度提前批Java面经_牛客网 (nowcoder.com)
什么是缓存雪崩、击穿、穿透? | 小林coding (xiaolincoding.com)
Java NIO 核心知识总结 | JavaGuide(Java面试 + 学习指南)
清晰图解https如何防范中间人攻击_https 防止中间人攻击的过程-CSDN博客