⽹络协议
百度 Java ⾯试
这次来看看 25 届百度校招开发岗位的薪资情况,拿到 24k 和 26k 的⼈⽐较多,⼤部分都是集中
在这两个 薪资,只有特别优秀的才能拿到 32k 的薪资。

普通 offer :
22k * 16 + 6w 签字费,同学 bg 本科,base 北京
24k * 16 + 6w 签字费,同学 bg 硕⼠ 211 ,base 北京
sp offer :
26k * 16 + 6w 签字费,同学 bg 硕⼠ 211 ,base 北京
29k * 16 + 6w 签字费,同学 bg 硕⼠ 211 ,base 北京
30k * 16 + 6w 签字费,同学 bg 硕⼠ 985 ,base 北京
ssp offer :
32k * 16 + 12w 签字费,同学 bg 硕⼠ 985 ,base 北京
其中签字费也不 是每⼀个⼈都有,签字费是分 2 年才能发完,⽐如签字费 6w ,第⼀年是发 3w ,
剩下的 3w 第⼆年发,如果中途离职就拿不到这个签字费了,所以签字费也是企业留住⼈才的⼀种
⽅式。
这次,来看看 百度校招的 Java 后端开发⾯经,整体上考察的点挺多的。
主要重点考察了⽹络i/o 、⽹络协议、linux 系统、mysql ,Java 问的不多,可能是百度的后端开发
的语⾔不是主要以 Java 为主 ,所以重点看⾯试者的计算机基础是否扎实。
除了这些知识点的拷打之外,还出了1 个⼤数据场景题⽬+1 个算法。
看看 你能应对多少题?

服务器处理并发请求有哪⼏种⽅式?
单线程web 服务器⽅式:web 服务器⼀次处理⼀个请求,结束后读取并处理下⼀个请求,性能⽐
较低,⼀次只能处理⼀个请求。
多进程/多线程web 服务器:web 服务器⽣成多个进程或线程并⾏处理多个⽤⼾请求,进程或线
程可以按需或事先⽣成。有的web 服务器应⽤程序为每个⽤⼾请求⽣成⼀个单独的进程或线程
来进⾏响应,不过,⼀旦并发请求数量达到成千上万 时,多个同时运⾏的进程或线程将会消耗
⼤量的系统资源。(即每个进程只能响应⼀个请求,并且⼀个进程对应⼀个线程)
I/O 多路复⽤web 服务器:web 服务器可以I/O 多路复⽤,达到只⽤⼀个线程就能监听和 处理多个
客⼾端的 i/o 事件。
多路复⽤多线程web 服务器:将多进程和多路复⽤的功能结合起来形成的web 服务器架构 ,其避
免了让⼀个进程服务于过多的⽤⼾请求,并能充分利 ⽤多CPU 主机所提供的计算能⼒。(这种架构可以理解为有多个进程,并且⼀个进程⼜⽣成多个线程,每个线程处理⼀个请求)
说⼀下select ,poll ,epoll 的区别?
select 和 poll 内部都是使⽤「线性结构」来存储进程关注的 Socket 集合。
在使⽤的时候,⾸先需要把关注的 Socket 集合通过 select/poll 系统调⽤从⽤⼾态拷⻉到内核态,
然后由内核检 测事件,当有⽹络事件产⽣时,内核需要遍历进程关注 Socket 集合,找到对应的
Socket ,并设置其状态为可读/可写,然后把整个 Socket 集合从内核态拷⻉到⽤⼾态,⽤⼾态还要
继续遍历整个 Socket 集合找到可读/可写的 Socket ,然后对其处理。
很明显 发现,select 和 poll 的缺陷在于,当客⼾端越多,也就是 Socket 集合越⼤,Socket 集合的
遍历和拷⻉会带来很⼤的开销,因此也很难应对 C10K 。

epoll 是解决 C10K 问题的利器, 通过两个 ⽅⾯解决了 select/poll 的问题。
epoll 在内核⾥使⽤「红⿊树」来关注进程所有待检测的 Socket ,红⿊树是个⾼效的数据结构,
增删改⼀般时间复杂度是 O(logn) ,通过对这棵⿊红树的管理,不需要像 select/poll 在每次 操作时都传⼊整个 Socket 集合,减少了内核和⽤⼾空间⼤量的数据拷 ⻉和内存分配。
epoll 使⽤事件驱动的机制,内核⾥维护了⼀个「链表」来记录就绪事件,只将有事件发⽣的
Socket 集合传递给应⽤程序,不需要像 select/poll 那样轮询扫描整个集合(包含有和⽆事件的
Socket ),⼤⼤提⾼了检测的效率。
Java 有⼀种现代的处理⽅式,属于异步I/O ,是什么 ?
redis ,nginx ,netty 是依赖什么 做的这么⾼性能?
主要是依赖Reactor 模式实现了⾼性能⽹络模式,这个是在i/o 多路复⽤接⼝基础上实现的了⽹络模
型。Reactor 翻译过来的意思是「反应堆」,这⾥的反应指的是「对事件反应」,也就是来了⼀个事
件,Reactor 就有相对应的反应/响应。
Reactor 模式主要由 Reactor 和处理资源池这两个 核⼼部分组成,它俩负责 的事情如下:
Reactor 负责 监听和 分发事件,事件类型包含连接事件、读写事件;
处理资源池负责 处理事件,如 read -> 业务逻辑 -> send ;Reactor 模式是灵活多变的,可以应对不同的业务场景,灵活在于:
Reactor 的数量可以只有⼀个,也可以有多个;
处理资源池可以是单个进程 / 线程,也可以是多个进程 /线程;

Redis 6.0 之前使⽤的 Reactor 模型就是单 Reactor 单进程模式。单 Reactor 单进程的⽅案因为全部
⼯作都在同⼀个进程内完成,所以实现起来⽐较简单,不需要考虑进程间通信,也不 ⽤担⼼多进
程竞 争。
但是,这种⽅案存在 2 个缺点:
第⼀个缺点,因为只有⼀个进程,⽆法充分利 ⽤ 多核 CPU 的性能;
第⼆个缺点,Handler 对象在业务处理时,整个进程是⽆法处理其他连接的事件的,如果业务
处理耗时⽐较⻓,那么就造成响应的延迟;
所以,单 Reactor 单进程的⽅案不适⽤计算机密集型的场景,只适⽤于业 务处理⾮常快速的场景。
Redis 是由 C 语⾔实现的,在 Redis 6.0 版本之前采⽤的正是「单 Reactor 单进程」的⽅案,因为
Redis 业务处理主要是在内存中完成,操作的速度是很快 的,性能瓶颈不在 CPU 上,所以 Redis 对
于命令的处理是单进程的⽅案。
Netty 是采⽤了多 Reactor 多线程⽅案,如下图:

多 Reactor 多线程的⽅案优势:
主线程和⼦线程分⼯明确,主线程只负责 接收新 连接,⼦线程负责 完成后续的业务处理。
主线程和⼦线程的交互 很简单,主线程只需要把新连接传给⼦线程,⼦线程⽆须返回数据,直
接就可以在⼦线程将处理结果发送给客⼾端。
Netty 是多 Reactor 多进程⽅案,不过⽅案与标准的多 Reactor 多进程有些差异。

具体差异表现在主进程中仅仅 ⽤来初始化 socket ,并没有创建 mainReactor 来 accept 连接,⽽是
由⼦进程的 Reactor 来 accept 连接,通过锁来控制⼀次只有⼀个⼦进程进⾏ accept (防⽌出现惊
群现象),⼦进程 accept 新连接后就放到⾃⼰的 Reactor 进⾏处理,不会再分配给其他⼦进程。
⽹络协议
https 是如何防范中间⼈的攻击?
主要通过加密和⾝份校验机制来防范中间⼈攻击的。

加密:https 握⼿期间会通过⾮对称加密的⽅式来协商出对称加密密 钥。
⾝份校验:服务器会向证书颁发机构 申请数字证书,证书中 包含了服务器的公钥和其他相关信
息。当客⼾端与服务器建⽴连接时,服务器会将证书发送给客⼾端。客⼾端会验证证 书的合法
性,包括检查证书的有效期、颁发机构 的信任 等。如果验证通过,客⼾端会使 ⽤证书中 的公钥
来加密通信数据,并将加密后的数据发送给服务器, 然后由服务端⽤私钥解密。
中间⼈攻击的关键在于攻击者冒充 服务器与客⼾端建⽴连接,并同时与服务器建⽴连接。但由于
攻击者⽆法获得服务器的私钥,因此⽆法正确解密客 ⼾端发送的加密数据。同时,客⼾端会在建
⽴连接时验证服务器的证书,如果证书验证失败或存在问题,客⼾端会发出警告或中⽌连接。
描述⼀下打开百度⾸⻚后发⽣的⽹络过程

对 URL 进⾏解析,解析出域名、⽅法、资源等,然后⽣成 http 请求报⽂。
对域名进⾏ dns 解析,⾸先会看浏览器和操作系统是否有 dns 解析的缓存,如果没有的话,就
会通过dns 解析,dns 解析过程:
发起DNS 查询:当⽤⼾在浏览器中输⼊⼀个域名(如www.baidu.com )后,操作系统会⾸先检
查本 地的DNS 缓存,如果有 对应的IP 地址 ,则直接返回结果。如果缓存中没有对应的IP 地址 ,操
作系统会向本地DNS 服务器发送⼀个DNS 查询请求。
本地DNS 服务器查询:本地DNS 服务器收到DNS 查询请求后,会先检查⾃⼰的缓存,如果有 对
应的IP 地址 ,则直接返回结果给操作系统。如果本 地DNS 服务器没有缓存对应的IP 地址 ,它会向
根DNS 服务器发送⼀个迭代查询请求。
根DNS 服务器查询:根DNS 服务器是顶级DNS 服务器, 它存 储了全球顶级域名服务器的信息。
当根DNS 服务器收到迭代查询请求后,它会根据请求的顶级域名(如.com )返回对应的顶级域
名服务器的IP 地址 给本地DNS 服务器。
顶级域名服务器查询:本地DNS 服务器收到根DNS 服务器返回的顶级域名服务器的IP 地址 后,
会向顶级域名服务器发送查询请求。顶级域名服务器根据请求的域名(如baidu.com )返回该域
名对应的权威域名服务器的IP 地址 。
权威域名服务器查询:本地DNS 服务器收到顶级域名服务器返回的权威域名服务器的IP 地址
后,会向权威域名服务器发送查询请求。权威域名服务器是负责 管理该域名下所有主机记录的
服务器, 它会根据请求的域名返回对应的主机记录的IP 地址 。
返回结果:本地DNS 服务器收到权威域名服务器返回的主机记录的IP 地址 后,会将结果返回给
操作系统。操作系统将IP 地址 返回给浏览器, 浏览器根据IP 地址 建⽴与服务器的TCP 连接,并发
起HTTP 请求。
建⽴TCP 连接:浏览器使⽤HTTP 协议通过TCP/IP 建⽴与百度服务器的连接。它会向百度服务器
发送⼀个SYN (同步)包,然后等待百度服务器的确认响应。
三次握⼿:百度服务器收到浏览器发送的SYN 包后,会发送⼀个SYN+ACK (同步确认)包给浏
览器, 表⽰接受连接请求。浏览器收到百度服务器的响应后,会发送⼀个ACK (确认)包给服
务器, 完成三次握⼿,建⽴可靠的连接。
发送HTTP 请求:浏览器向百度服务器发送⼀个HTTP 请求,请求百度⾸⻚的HTML ⽂档。请求中
包含了请求⽅法、请求头和其他相关信息。
服务器处理请求:百度服务器接收到浏览器发送的HTTP 请求后,会根据请求的内容进⾏处理。
它可能会读取数据库、执⾏相关的业务逻辑,并⽣成响应数据。
发送HTTP 响应:百度服务器将⽣成的响应数据封装成HTTP 响应报⽂,并发送回浏览器。响应报
⽂中包含了响应状态码、响应头和响 应体等信息。
接收响应和渲染⻚⾯:浏览器接收到百度服务器发送的HTTP 响应后,会解析响应报⽂,提取出
HTML ⽂档和其他相关资源。浏览器会根据HTML ⽂档的结构和CSS 样式,渲染出⻚⾯的可视化
效果。
关闭TCP 连接:当浏览器完成⻚⾯渲染后,会关闭与百度服务器的TCP 连接。它会发送⼀个FIN
(结束)包给服务器, 表⽰关闭连接请求。百度服务器收到请求后,会发送⼀个ACK 包给浏览
器, 表⽰接受关闭连接请求。最终,浏览器和服务器都关闭了TCP 连接。
什么 是ddos 攻击?怎么防范?
分布式拒绝服务(DDoS )攻击是通过⼤规模互联⽹流量淹没⽬标服务器或其周边基础设施,以破
坏⽬标服务器、服务或⽹络正常流量的恶意⾏为。
DDoS 攻击是通过连 接互联⽹的计算机⽹络进⾏的。这些⽹络由计算机和其他设备(例如 IoT 设
备)组成,它们感染了恶意软件,从⽽被攻击者远程控制。这些个 体设备称为机器⼈(或僵⼫),
⼀组机器⼈则称为僵⼫⽹络。

⼀旦建⽴了僵⼫⽹络,攻击者就可通过向每个机器⼈发送远程指令来发动攻击。当僵⼫⽹络将受
害者的服务器或⽹络作为⽬标时,每个机器⼈会将请求发送到⽬标的 IP 地址 ,这可能导致服务器
或⽹络不堪重负,从⽽造成对正常流量的拒绝服务。由于每个机器⼈都是合法的互联⽹设备,因
⽽可能很难区分攻击流量与正常流量。
常⻅的DDoS 攻击包括以下⼏类:
⽹络层攻击:⽐较典型的攻击类型是UDP 反射攻击,例如:NTP Flood 攻击,这类攻击主要利⽤
⼤流量拥塞被攻击者的⽹络带宽,导致被攻击者的业务⽆法正常响应客⼾访问。
传输层攻击:⽐较典型的攻击类型包括SYN Flood 攻击、连接数攻 击等,这类攻击通过占⽤服务
器的连接池资源从⽽达到拒绝服务的⽬的。
会话层攻击:⽐较典型的攻击类型是SSL 连接攻击,这类攻击占⽤服务器的SSL 会话资源从⽽达
到拒绝服务的⽬的。
应⽤层攻击:⽐较典型的攻击类型包括DNS flood 攻击、HTTP flood 攻击、游戏假⼈攻击等,这
类攻击占⽤服务器的应⽤处理资源极⼤的消耗服务器处理性能从⽽达到拒绝服务的⽬的。
为了 防范DDoS 攻击,可以采取以下措施:
增强⽹络基础设施:提升⽹络带宽、增加服务器的处理能⼒和承载能⼒,通过增强基础设施的
能⼒来抵御攻击。
使⽤防⽕墙和⼊侵检测系统:配置防⽕墙规则,限制不必要的⽹络流量,阻⽌来⾃可疑IP 地址
的流量。⼊侵检测系统可以帮助及时发现并响应DDoS 攻击。
流量清洗和负载均衡:使⽤专业 的DDoS 防护服务提供商,通过流量清洗技术过滤掉恶意流量,
将合法流量转发给⽬标服务器。负载均衡可以将流量均匀地分发到多台服务器上,减轻单⼀服
务器的压⼒。
配置访问控制策略:限制特定IP 地址 或IP 段的访问,设置访问频率限制,防⽌过多请求集中在单
个IP 上。
Linux 操作系统
进程中通信的⽅式有哪些?

Linux 内核提供了不 少进程间通信的⽅式,其中最简单的⽅式就是管道,管道分为「匿名管道」和
「命名 管道」。
匿名管道顾名思义,它没有名字标识,匿名管道是特殊⽂件只存在于内存,没有存在于⽂件系统
中,shell 命令中的「 | 」竖线就是匿名管道,通信的数据是⽆格式的流并且⼤⼩受限,通信的⽅
式是单向的,数据只能在⼀个⽅向上流动,如果要双向通信,需要创建两个 管道,再来匿名管道
是只能⽤于存在⽗⼦关系的进程间通信,匿名管道的⽣命周 期随着进程创建⽽建⽴,随着进程终
⽌⽽消失。
命名 管道突破了匿名管道只能在亲缘关系进程间的通信限制,因为使⽤命名 管道的前提,需要在
⽂件系统创建⼀个类型为 p 的设备⽂件,那么毫⽆关系的进程就可以通过这 个设备⽂件进⾏通
信。另外,不管是匿名管道还是命名 管道,进程写⼊的数据都是缓存在内核中,另⼀个进程读取
数据时候⾃然也是从内核中获取,同时通信数据都遵循先进先出原则,不⽀持 lseek 之类的⽂件定
位操作。
消息队列克服了管道通 信的数据是⽆格式的字节流的问题,消息队列实际上是保存在内核的「消
息链表」,消息队列的消息体是可以⽤⼾⾃定义的数据类型,发送数据时,会被分成⼀个⼀个独⽴
的消息体,当然接收数 据时,也要与发送⽅发送的消息体的数据类型保持⼀致,这样才能保证读
取的数据是正确的。消息队列通信的速度不是最及时的,毕竟每次 数据的写⼊和读取都需要经过
⽤⼾态与内核态之间的拷⻉过程。
共享内存可以解决消息队列通信中⽤⼾态与内核态之间数据拷 ⻉过程带来的开销,它直接分配⼀
个共享空间,每个进程都可以直接访问,就像访问进程⾃⼰的空间⼀样快捷⽅便,不需要陷⼊内
核态或者系统调⽤,⼤⼤提⾼了通信的速度,享有最快的进程间通信⽅式之名。但是便捷⾼效的
共享内存通信,带来新的问题,多进程竞 争同个共享资源会造成数据的错乱。
那么,就需要信号量来保护共享资源,以确保任何 时刻只能有⼀个进程访问共享资源,这种⽅式
就是互斥访问。信号量不仅 可以实现访问的互斥性,还可以实现进程间的同步,信号量其实是⼀
个计数器, 表⽰的是资源个数,其值可以通过两个 原⼦操作来控制,分别 是 P 操作和 V 操作。
与信号量名字很相似的叫信号,它俩名字虽然相似,但功能⼀点⼉都不⼀样。信号是异步通信机
制,信号可 以在应⽤进程和内核之间直接交互 ,内核也可以利⽤信号来通知⽤⼾空间的进程发⽣
了哪些系统事件,信号事件的来源主要有硬件来源(如键盘 Cltr+C )和软件来源(如 kill 命令),
⼀旦有信号发⽣,进程有三种⽅式响应信号 1. 执⾏默认操作、2. 捕捉 信号、3. 忽略信号。有两个
信号是应⽤进程⽆法捕捉 和忽略的,即 SIGKILL 和 SIGSTOP ,这是为了 ⽅便我们能在任何 时候
结束或停⽌某个进程。
前⾯说到的通信机制,都是⼯作于同⼀台主机,如果要与不 同主机的进程间通信,那么就需要
Socket 通信了。Socket 实际上不仅 ⽤于不 同的主机进程间通信,还可以⽤于本地主机进程间通
信,可根据创建 Socket 的类型不同,分为三 种常⻅的通信⽅式,⼀个是基于 TCP 协议的通信⽅
式,⼀个是基于 UDP 协议的通信⽅式,⼀个是本地进程间通信⽅式。
怎么查看哪个端⼝被哪个进程占⽤?
可以通过 lsof 或者 netstate 命令查看,⽐如查看 80 端⼝。
lsof :
[root@xiaolin ~]# lsof -i :80COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 929 root 6u IPv4 15249 0t0 TCP *:http (LISTEN)
nginx 929 root 7u IPv6 15250 0t0 TCP *:http (LISTEN)
nginx 934 nginx 6u IPv4 15249 0t0 TCP *:http (LISTEN)
nginx 934 nginx 7u IPv6 15250 0t0 TCP *:http (LISTEN)AliYunDun 16507 root 10u IPv4 40212783 0t0 TCP xiaolin:41830->100.100.30.26:htt
netstate :
[root@xiaolin ~]# netstat -napt | grep 80tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 929/nginx: master p
⽤shell 命令替换⼀个⽂件中的字符串
可以使 ⽤ sed 命令。以下是⼀个⽰例:
sed -i 's/旧字符串/新字符串/g' 文件名
在上⾯的命令中, -i 选项表⽰直接在原始⽂件中进⾏修改,⽽不是输出到 标准输出。 s/ 旧字符
串/新字符串 /g 是替换操作的模式,其中 旧字符串 是要替换的字符串, 新字符串 是替换后的新字
符串。 g 表⽰全局替换,即⼀⾏中出现多次的旧字符串都会被替换。
请注意,这将直接修改原始⽂件,如果需要备份原始⽂件,可以在 -i 选项后⾯指定⼀个备份⽂件
的扩展名,例如 -i.bak ,这将在替换前备份原始⽂件。
例如,假设要将⽂件 example.txt 中的字符串 Hello 替换为 Hi ,可以运⾏以下命令:
linux 中有⼀个⽇志⽂件,⽇志⽂件中记录了访问请求的信息,第⼀列是访
问的⽇期,第⼆列是请求的ip ,第三列是请求的耗时,写⼀条shell 命令来查
到请求耗时最⾼的10 条记录
要查找请求耗时最⾼的10 条记录,可以使 ⽤以下Shell 命令:
sort -k3 -nr 日志文件 | head -n 10
在上⾯的命令中, sort -k3 -nr ⽤于按第三列(请求耗时)进⾏倒序排序。 -k3 表⽰按第三列
排序, -n 表⽰按数字排序, -r 表⽰倒序排序。然后,使⽤ head -n 10 来获取排序后的前10
⾏,即耗时最⾼的10 条记录。
将命令中的“⽇志⽂件”替换为实际的⽇志⽂件路径,即可查找到请求耗时最⾼的10 条记录。
假如cpu 跑到100% ,你的解决思路是什么 ?
思路如下:
先通过 top 命令,定位到占⽤ cpu ⾼的进程
然后通过 ps -T -p<> < 进程 ID> 命令找到进程中占⽤⽐较⾼的线程然后通过 jstack 命令去查看该线程的堆栈信息
根据输出的堆栈信息,去项⽬中定位代 码,看是否发⽣了死循环⽽导致cpu 跑到100%
mysql
mysql 有哪⼏种存储引擎,它们的区别是什么 ?

InnoDB :是MySQL 的默认存储引擎,⽀持事务、⾏级锁定和外键约束等特性。它采⽤了聚集索
引的⽅式来组织 数据,具有较好的并发性能和数据完整性,适⽤于⼤多数应⽤场景。
MyISAM :是MySQL 早期的存储引擎,不⽀持事务和⾏级锁定,但具有较⾼的插⼊和查询速
度。它采⽤了表级锁定的⽅式,适合于读密集型应⽤,如数据仓库、⽇志等。
Memory :也称为Heap 存储引擎,将数据存储在内存中,读写速度⾮常快,但数据在服务器关
闭时会丢失。适⽤于临 时表、缓存等需要快速读写的场景。
mysql 的隔离级别分 为哪⼏种类型?
读未提交,指⼀个事 务还没提交时,它做的变更就能被其他事 务看到;
读提交,指⼀个事 务提交之 后,它做的变更才能被其他事 务看到;
可重复读,指⼀个事 务执⾏过程中看到的数据,⼀直跟这个事 务启动时看到的数据是⼀致的,
MySQL InnoDB 引擎的默认隔离级别;
串⾏化;会对记录加上读写锁,在多个事 务对这条记录进⾏读写操作时,如果发⽣了读写冲 突
的时候,后访问的事务必须等前⼀个事 务执⾏完成,才能继续执⾏;
接下来,举个 具体的例⼦来说明这四种隔离级别,有⼀张账⼾余额表,⾥⾯有⼀条账⼾余额为 100
万的记录。然后有两个 并发的事务,事务 A 只负责 查询余额,事务 B 则会将我的余额改成 200
万,下⾯是按照时间顺序执⾏两个事 务的⾏为:

在不同隔离级别下,事务 A 执⾏过程中查询到的余额可能会不同:
在「读未提交」隔离级别下,事务 B 修改余额后,虽然没有提交事 务,但是此时的余额已经可
以被事务 A 看⻅了,于是事务 A 中余额 V1 查询的值是 200 万,余额 V2 、V3 ⾃然也是 200 万了;
在「读提交」隔离级别下,事务 B 修改余额后,因为没有提交事 务,所以事务 A 中余额 V1 的
值还是 100 万,等事务 B 提交完后,最新的余额数据才能被事务 A 看⻅,因此额 V2 、V3 都是
200 万;
在「可重复读」隔离级别下,事务 A 只能看⻅启动事务时的数据,所以余 额 V1 、余额 V2 的值
都是 100 万,当事务 A 提交事 务后,就能看⻅最新的余额数据了,所以余 额 V3 的值是 200
万;
在「串⾏化」隔离级别下,事务 B 在执⾏将余额 100 万修改为 200 万时,由于此前事务 A 执⾏
了读操作,这样就发⽣了读写冲 突,于是就会被锁住,直到事务 A 提交后,事务 B 才可以继续
执⾏,所以从 A 的⻆度看,余额 V1 、V2 的值是 100 万,余额 V3 的值是 200 万。
这四种隔离级别具体是如何实现的呢?
对于「读未提交」隔离级别的事务来说,因为可以读到未提交事 务修改的数据,所以直接读取
最新的数据就好了;
对于「串⾏化」隔离级别的事务来说,通过加读写锁的⽅式来避免并⾏访问;
对于「读提交」和「可重复读」隔离级别的事务来说,它们是通过 Read View 来实现的,它们
的区别在于创建 Read View 的时机不同,「读提交」隔离级别是在「每个语句执⾏前」都会重新
⽣成⼀个 Read View ,⽽「可重复读」隔离级别是「启动事务时」⽣成⼀个 Read View ,然后
整个事 务期间都在⽤这个 Read View 。
慢查询是如何调试解决的?
确认慢查询:⾸先,通过MySQL 的慢查询⽇志或性能监控⼯具,确认哪些SQL 查询较慢,需要
进⾏调优。
分析执⾏计划:通过使⽤ EXPLAIN 关键字,可以获取SQL 查询的执⾏计划。执⾏计划可以告诉
您MySQL 是如何执⾏查询的,包括使⽤的索引、连接⽅式等。分析执⾏计划可帮助您理解查询
的性能瓶颈所在。
优化查询语句:根据执⾏计划的分析结果,考虑对查询语句进⾏优化。例如,添加合适的索
引、调整查询条件、避免全 表扫描等。优化查询语句可以提⾼查询性能。
优化数据库结构:有时,慢查询的性能问题可能与数据库结构有 关。考虑对表结构进⾏优化,
例如拆分⼤表、规范化设计 等。合理的数据库结构可以提⾼查询性能。
缓存和查询缓存:对于⼀些频繁查询的结果,可以考虑使⽤缓存机制,避免重复的查询操作。
mysql 的explain 有什么 作⽤
explain 是查看 sql 的执⾏计划,主要⽤来分析 sql 语句的执⾏过程,⽐如有没有⾛索引,有没有
外部排序,有没有索引覆盖等等 。
如下图,就是⼀个没有使⽤索引,并且是⼀个全表扫描的查询语句。

对于执⾏计划,参数有:
possible_keys 字段表⽰可能⽤到的索引;
key 字段表⽰实际⽤的索引,如果这⼀项为 NULL ,说明没有使⽤索引;
key_len 表⽰索引的⻓度;rows 表⽰扫描的数据⾏数。
type 表⽰数据扫描类型,我们需要重点看这个。
type 字段就是描述了找到所需数据时使⽤的扫描⽅式是什么 ,常⻅扫描类型的执⾏效率从低到⾼
的顺序为:
All (全表扫描);
index (全索引扫描);
range (索引范围扫描);
ref (⾮唯⼀索引扫描);
eq_ref (唯⼀索引扫描);const (结果只有⼀条的主键或唯⼀索引扫描)。
在这些情况⾥,all 是最坏的情况,因为采⽤了全表扫描的⽅式。index 和 all 差不多,只不过
index 对索引表进⾏全扫描,这样做的好处 是不再需要对数据进⾏排序,但是开销依然很⼤。所
以,要尽量避免全 表扫描和全索引扫描。
range 表⽰采⽤了索引范围扫描,⼀般在 where ⼦句中使⽤ < 、>、in 、between 等关键词,只检索给定范围的⾏,属于范围查找。从这⼀级别开始,索引的作⽤会越来越明显 ,因此我们需要尽
量让 SQL 查询可以使 ⽤到 range 这⼀级别及以上的 type 访问⽅式。
ref 类型表⽰采⽤了⾮唯⼀索引,或者是唯⼀索引的⾮唯⼀性前缀,返回数据返回可能是多条。因
为虽然使⽤了索引,但该索引列的值并不唯⼀,有重复。这样即使使 ⽤索引快 速查找到了第⼀条
数据,仍然不能停⽌,要进⾏⽬标值附近的⼩范围扫描。但它的好处 是它并不需要扫全表,因为
索引是有序的,即便有重复值,也是在⼀个⾮常⼩的范围内扫描。
eq_ref 类型是使⽤主键或唯⼀索引时产⽣的访问⽅式,通常使⽤在多表联查中。⽐如,对两张表
进⾏联查,关联条件是两张表的 user_id 相等,且 user_id 是唯⼀索引,那么使⽤ EXPLAIN 进⾏执
⾏计划查看的时候,type 就会显⽰ eq_ref 。
const 类型表⽰使⽤了主 键或者唯⼀索引与常量值进⾏⽐较,⽐如 select name from product where id=1 。需要说明的是 const 类型和 eq_ref 都使⽤了主 键或唯⼀索引,不过这 两个 类型有所区别,const 是
与常量进⾏⽐较,查询效率会更快,⽽ eq_ref 通常⽤于多表联查中。extra 显⽰的结果,这⾥说⼏个重要的参考指标:
Using filesort :当查询语句中包含 group by 操作,⽽且⽆法利⽤索引完成排序操作的时候,
这时不得不选择相应的排序算法进⾏,甚⾄可能会通过⽂件排序,效率是很低的,所以要避免
这种问题的出现。
Using temporary :使了⽤临时表保存中间结果,MySQL 在对查询结果排序时使⽤临时表,常
⻅于排序 order by 和分组查询 group by 。效率低,要避免这种问题的出现。
Using index :所需数据只需在索引即可全部获得,不须要再到表中取数据,也就是使⽤了覆盖
索引,避免了回表操作,效率不错。
Java
Java 中有哪些常⽤的锁,在什么 场景下使⽤?
synchronized :是Java 内置的关键字,⽤于实现互斥锁。在多线程环境下,通过对代码块或⽅法
添加synchronized 关键字,可以确保同⼀时刻只有⼀个线程执⾏该代码块或⽅法。适⽤于对共
享资源的访问进⾏同步控制的场景。
ReentrantLock :是Java.util.concurrent 包提供的可重⼊锁实现。与synchronized 相⽐,
ReentrantLock 提供了更灵活的锁定⽅式,例如可以⼿动控制锁的获取和释放、⽀持公平锁等。
适⽤于需要更⾼级别控制的场景。
ReadWriteLock :是Java.util.concurrent 包提供的读写锁接⼝。读写锁允许多个线程同时读共享
资源,但在写操作时需要独占锁。适⽤于读多写少的场景,可以提⾼并发性能。
StampedLock :是Java.util.concurrent 包提供的乐观读写锁。与ReadWriteLock 相⽐,
StampedLock 提供了更⾼的并发性能,但使 ⽤⽅式较为复杂。适⽤于读多写少、读操作频繁的
场景。
AtomicInteger :是Java.util.concurrent.atomic 包提供的原⼦整数 类。通过使⽤AtomicInteger
类,可以实现在多线程环境下对整数 值的原⼦操作,避免线程安全问题。适⽤于对整数 值进⾏
原⼦操作的场景。
什么 是反射?有哪些使⽤场景?
Java 反射机制是在运⾏状态中,对于任意⼀个类,都能够知道这个类中的所有属性和⽅法,对于
任意⼀个对象,都能够调⽤它的任意⼀个⽅法和属性;这种动态获取的信息以及动态调⽤对象的
⽅法的功能称为 Java 语⾔的反射机制。
应⽤场景:
逆向代码,例如反编译
与注解相结合的框架,如 Retrofit
单纯的反射机制应⽤框架,例如 EventBus (事件总线)
动态⽣成类框架 例如Gson
场景题⽬
给定a、b两个 ⽂件,各存放50 亿个 url ,每个url 各占64 字节,内存限制是
4G ,让你找出a、b⽂件共同的url
⽅法:分治+hashmap
遍历⽂件a,对每个url 求取hash(url)%1000 ,然后根据所取得的值将url 分别 存储到1000 个⼩⽂件(记为a0,a1,…,a999 )中。这样每个⼩⽂件的⼤约为300M 。
遍历⽂件b,采取和a相同的⽅式将url 分别 存储到1000 ⼩⽂件(记为b0,b1,…,b999 )。这样处理
后,所有可能相同的url 都在对应的⼩⽂件(a0-b0 , a1-b1,…,a999-b999 )中,不对应的⼩⽂件
不可能有相同的url 。然后我们只要求出1000 对⼩⽂件中相同的url 即可。
求每对⼩⽂件中相同的url 时,可以把其中⼀个⼩⽂件的url 存储到hash_set 中。然后遍历另 ⼀个
⼩⽂件的每个url ,看其是否在刚才构建的hash_set 中,如果是,那么就是共同的url ,存到⽂件⾥⾯就可以了。
