一面情况

  • 面试时间:2025.12.02;
  • 时长:70min左右。

自我介绍

正常模板。

问题

1. 谈谈你对RAG,AI Agent 和 MCP 的理解,日常在使用 AI 辅助编程的过程中,仅仅是停留在简单的对话模式上吗?

上来先不问技术,而是先谈到前沿技术的使用和理解,比较新颖,但是我回答的不太好,主要当时还是对这三个概念没有主动去了解过。

这里在最后的反问环节,面试官提到了要全面了解AI编程相关的前沿技术,比如MCP,AI Agent,RAG等。

RAG(Retrieval-Augmented Generation,检索增强式生成)

核心思想:模型不要光靠死记硬背的参数,而是先到外部知识库或者代码库里检索相关内容,再在此基础上生成答案。

场景:修改 chat-room 服务的启动流程,我会让CodeBuddy先去读取项目里的代码和配置,比如 server/application/chat-room/main.ccchat-room.conf ,包括里面用到的 muduogRPCLogic 层 HTTP 调用这些上下文。它不是凭空瞎猜,而是先把这些文件内容检索出来作为上下文,再帮我解释HTTP 服务怎么起的、gRPC 怎么监听、房间订阅怎么初始化,然后基于这些真实代码给修改建议,这本质上就是 RAG,检索项目内部知识,再生成答案和代码。

**AI Agent **

核心思想:Agent相比传统的问答,多了两个关键点,目标导向和会自己用工具、分步执行。

场景:比如我希望在Logic服务里新增一个房间历史查询功能,兼顾 MySQL 和 Redis 缓存,CodeBuddy 的 Craft Agent 会根据此目标,做出如下动作。

  • 先列出 server/application/logic 下面的目录结构;

  • 自动去看 logic/main.cc 里 HTTP 路由是如何分发到 api / service 的;

  • 再打开 service/message_serviceRoomService 等相关代码,看当前是怎么查消息、怎么用Kafka和redis的;

  • 在理解现有设计的基础上,帮我完成房间历史查询功能的添加。

这整个过程,其实就是一个 Agent 在工作,它拿到一个业务目标之后,自己规划步骤、自己调用搜索/分析等工具,结合上下文做决策。

MCP(Model Context Protocol)
核心思想:MCP是Anthropic提出的,可以把它理解成一套标准化的模型插件协议,它规定了,像文件系统、数据库、HTTP 服务、命令行这类外部世界,要怎样通过一个统一的协议暴露给大模型,作为上下文资源和可调用工具。模型前面是一个客户端,后面有多个MCP server,每个MCP server都可以暴露自己的 tools、resources,模型就像调用插件一样去用。

场景:在使用CodeBuddy进行AI Coding时,会读取 main.cc 、读取 \*.conf 、搜索特定接口、执行编译或启动命令等。这些能力,在系统内部其实都被抽象成一组标准工具接口,模型通过某种协议去调用,这跟 MCP 的思想是一致的,通过一套协议,把本地代码库、配置、运行时命令,统一接到模型的上下文世界里,让模型不仅能看见代码,还能动手操作。

总结

这里在后面被问到使用AI工具的时候,可以和面试官说是有了解这些比较前沿的概念的,可以说自己已经把 LLM 当成一个会用工具协作开发的同事,在后端项目开发中,它会先通过类似 RAG 的方式读我的配置项和源代码,再以 Agent 的方式调用各种工具(读文件、搜索、执行命令),在调用读文件,搜索,执行命令的过程中,其实就是使用了MCP思路。

2. 介绍一下OSI七层模型,解释一下TCP和UDP的区别,以及TCP是如何保证可靠传输的?

这里回答的比较一般吧,好像TCP的流量控制和拥塞控制忘记了,需要补充一下。

1. TCP和UDP的区别

TCP UDP
是否面向连接 面向连接:三次握手建立连接,四次挥手断开 无连接:直接发送
传输可靠性 可靠:有确认、重传、顺序控制、校验和等 尽力而为:不保证到达、不保证顺序、不负责重传
数据传输方式 字节流(Stream),无边界概念 数据报(Datagram),有消息边界
是否保证有序 保证按发送顺序交付 不保证,可能乱序、丢失、重复
流量控制 有:基于接收窗口(rwnd)的滑动窗口流量控制 无,发送端按自己节奏发
拥塞控制 有:慢启动、拥塞避免、快重传、快恢复等算法,这里有拥塞窗口的概念 无,协议层不关心网络拥塞
头部开销 较大,至少 20 字节 较小,固定 8 字节
适用场景 要求可靠、有序的场景:HTTP/HTTPS、数据库、文件传输等 要求实时、对少量丢包可容忍:语音/视频通话、直播、游戏等
连接状态维护 维护连接状态(发送/接收缓冲区、窗口、序号等) 无连接状态,协议层无状态
广播/多播支持 只支持一对一通信 支持广播、多播

2. TCP如何保证可靠传输?

从数据正确性 ==> 交付顺序 ==> 丢包处理 ==> 流量/拥塞来宏观记忆。

保证数据正确

  • 校验和(Checksum):保证数据未损坏

    • TCP 头部有 16 位校验和字段,覆盖TCP头 & 数据 & 伪首部;
    • 解决的问题:比特错误 / 数据损坏
  • 序列号 & ACK 确认机制

    • 每个 TCP 连接有一个 32 位序列号空间;

    • 发送方为每个字节编号,报文段的 seq 表示本段数据的起始字节序号;

    • 接收方用 ACK(确认号告诉发送方,我已经按序收到从起始到 N−1 的所有字节,下一个期望是 N;

    • 特点:累计确认,ACK=N 表示“之前所有字节都收到了,可以减少 ACK 数量;接收方只对按序到达的最后一个连续字节发送 ACK。

    • 解决的问题:知道哪些数据到了、哪些没到。

丢包处理

  • 超时重传

    • 发送方对每个未确认的报文段设置一个重传计时器(RTO);

    • 若在RTO内没有收到相应ACK,则认为该报文段丢失,触发重传;

    • RTO不是固定值,而是依据往返时延RTT动态估计,典型算法是使用 加权移动平均 + 偏差估计 来计算RTO,使其略大于RTT;

    • 解决的问题:丢包能被发现并重传。

  • 滑动窗口协议

    • TCP 使用滑动窗口实现流水线传输和流量控制;

    • 发送窗口:允许已发送但未确认的数据上限;

    • 接收窗口:接收方还能接收的缓冲空间大小;

    • 发送方在窗口范围内可以连续发送多个报文段,不必每个都等 ACK;

    • 一旦收到 ACK,窗口右移,允许发送新的数据。

    • 解决的问题:将ARQ(自动重传请求)从停等协议升级为连续ARQ,大幅提高吞吐量,为后面的流量控制提供基础(接收窗口控制发送方速率)。

  • 有序交付与乱序缓存

    • 网络中转发可能导致报文段乱序到达;

    • 解决的问题:TCP 接收端会根据序列号将乱序段暂存于缓存,保证应用层始终是连续、有序的字节流。

3. TCP流量控制(Flow Control)

  • 目标:不要把接收方冲爆;

  • 接收方通过 ACK 中的窗口字段(Advertised Window,rwnd)告诉发送方,当前还能接收多少字节的数据;

  • 发送方的实际有效发送窗口 = min(rwnd, cwnd)

  • 当接收方缓冲快满时,会把 rwnd 调小甚至设置为 0:发送方必须减速或暂停发送。

4. TCP 拥塞控制(Congestion Control)

  • 目标:不要把网络冲爆;

  • 经典拥塞控制算法

    • cwnd(Congestion Window,拥塞窗口):发送方根据对网络拥塞的估计得出的窗口大小;

    • ssthresh(Slow Start Threshold,慢启动阈值):控制何时从慢启动切换到拥塞避免。

  • 发送方实际发送窗口为: min(rwnd, cwnd)

慢启动(Slow Start)

  • 目标:从小速率开始探测网络容量;

  • 连接建立或发生超时重传触发慢启动算法:初始 cwnd = 1 MSSssthresh 通常设为一个较大的值或上次的一半;每收到一个 ACK:cwnd += 1 MSS

  • 特点:指数增长:1, 2, 4, 8, … MSS;当 cwnd >= ssthresh 时,进入拥塞避免阶段。

拥塞避免(Congestion Avoidance,AIMD)

  • 目标:在可能接近网络极限的区域,线性增加发送速率,这避免了指数爆炸,减小了引发拥塞的概率。

超时&快重传&快恢复

丢包通常被看作网络发生了拥塞,需要降低发送速率。

  • 情况 A:超时重传(Timeout)

    • 最严重的拥塞信号;

    • 处理方式:ssthresh = cwnd / 2cwnd重置为1MSS ,重新进入慢启动阶段。

  • 情况 B:收到 3 个重复 ACK

    • 3 个相同ACK说明,有一个中间段丢了,但之后的段还在抵达,说明链路并未完全堵死;

    • 快重传(Fast Retransmit):不等RTO超时,立即重传丢失的报文段;

    • 快恢复(Fast Recovery):认为网络只是轻度拥塞,处理方式为ssthresh = cwnd / 2cwnd = ssthresh + 3 * MSS,每再收到一个重复 ACK: cwnd += 1 MSS

5. 一些计网简称回顾

RTT(Round-Trip Time,往返时间)

MSL(Max Segment Lifetime,最大报文段生存时间)

MSS(Maximum Segment Size,最大报文段长度)

RTO(Retransmission Timeout,重传超时时间)

3. 如何判断TCP报文段网络传输的稳定性,日常有抓包过吗,使用过什么抓包工具?

忘记了自己其实使用过Wireshark这样的抓包工具,其实拓展一下,可以说自己曾经开发过基于dpdk的用户态TCP/IP协议栈,这种网络模式对比linux内核实现的TCP/IP协议栈,具有更快的通信效率,而且可操作性高,但是编程难度较大,需要自己解析和组织TCP报文段。(如果有时间,可以复盘一下之前的dpdk程序)。

1. 如何判断 TCP 报文段网络传输的稳定性?

从抓包和指标两个维度来判断

  • RTT时长和抖动情况

    • 看三次握手的 RTT、后续数据和ACK的往返时间是否稳定;

    • 如果RTT波动很小、没有明显尖刺,说明网络时延比较平滑;RTT 经常突然拉高,说明链路有时拥塞或排队很严重。

  • 重传率和重复ACK

    • 在 Wireshark 里可以直接看到 TCP RetransmissionFast RetransmissionDup ACK 等指标;
    • 偶尔有重传可以接受;如果连续超时重传、重复 ACK 非常多,就说明链路丢包、拥塞严重,传输明显不稳定。
  • 接收窗口变化情况

    • 看接收窗口是否频繁收缩,甚至出现“零窗口(Zero Window)”情况;

    • 接收端窗口频繁被打满,说明对端处理不过来,从应用视角看会表现为卡顿和吞吐掉下来。

2. 一些抓包工具

  • 在本机或开发环境中,使用Wireshark;
  • 在服务器上更多用:tcpdump 指令:例如: tcpdump -i eth0 tcp port 8090 -w logic.pcap ,然后在本地用 Wireshark 打开分析。

3. dpdk用户态TCP/IP协议栈开发流程回忆

以收包,协议处理,发包三步骤为场景。

  • 初始化 & 接管网卡

    • DPDK EAL 初始化,绑定网卡到 DPDK 驱动,配置 RX/TX 队列、内存池(mbuf pool);

    • 这一层把「网卡 → 内核协议栈」的路径改成「网卡 → 用户态进程」。

  • 轮询收包(L2/L3/L4 解析)

    • 用户态线程用 rte_eth_rx_burst() 从 RX 队列轮询收包(忙轮询,无中断);

      • 对每个 mbuf 解析以太网头、IP 头、TCP 头,做基本校验和过滤(目标 IP/端口等)。
  • TCP/IP 状态机处理 & 用户态socket接口

    • 在用户态维护 ARP 表、路由表、TCP 连接控制块(TCB);
    • 实现简化版的 TCP 状态机:三次握手、四次挥手、序列号/ACK、重传、滑动窗口等;
    • 对上提供类似 connect()/listen()/send()/recv() 的接口或回调,业务代码直接调用这套用户态 API。
  • 发送路径:构造报文 & 发到网卡

    • 上层调用用户态 send(),协议栈组装 TCP 段、填 IP/以太网头、计算校验和;

    • 将报文放入 mbuf,调用 rte_eth_tx_burst() 发送出去(零拷贝 DMA 到网卡)。

  • 定时器 & 重传管理

    • 用户态定时器周期扫描所有连接:处理超时重传、keepalive、连接超时回收等;

    • 简单可以模仿 Reno/CUBIC 的 cwnd/ssthresh,或者根据场景自定义策略。

4. 用户态与内核态TCP/IP协议栈

维度 用户态 TCP/IP 协议栈(基于 DPDK) Linux 内核 TCP/IP 协议栈
协议栈 用户态进程 内核态
数据路径 网卡 → DPDK 驱动 → 用户态协议栈 → 业务 网卡 → 内核驱动 → 内核协议栈 → socket → 用户态
收包方式 轮询(poll-mode),忙轮询 RX 队列,无中断 中断驱动为主,结合 NAPI,软中断 + 内核线程处理
拷贝次数 可以做到 零拷贝:网卡 DMA → 用户态 mbuf 通常存在 内核 ↔ 用户态 拷贝(recv/send 时)
上下文切换 协议处理在用户态,无内核切换;少数系统调用 频繁在 用户态 ↔ 内核态 切换(系统调用、协议栈处理)
延迟 & 抖动 延迟极低、抖动小; 延迟略高且抖动较大,但对普通业务足够,性能平衡更好
安全性 & 稳定性 很大程度取决于自己实现;bug 可能导致进程崩溃或协议不兼容 经多年验证,稳定性、安全性、兼容性都非常成熟
开发 & 维护成本 非常高:要自己维护完整协议栈、状态机、定时器、容错、安全 由内核社区维护,业务方只需使用 socket API
适用场景 极端高性能场景:HFT、L4/L7 负载均衡器、自研网关、SDN 数据平面 绝大多数普通业务:Web 服务、数据库连接、微服务等

5. 总结

我会用Wireshark / tcpdump等抓包工具来判断TCP连接的稳定性,看 RTT、重传、DupACK、窗口变化等指标;另外,我也尝试基于 DPDK 写用户态 TCP/IP 协议栈,对 TCP 的状态机、重传和拥塞控制有比较深入的理解(下次被问到可以直接这样回答)。

4. 30TB的数据,如何在16GB的内存上进行排序?

这个题没回答出来很炸裂啊,多路归并了解的太不熟悉了,要复习一下多路归并的具体流程了,但其实在面试过程中也把基本思路答出来了一点点。

以30TB数据为例子,每次可读取16GB数据到内存中排序,多路归并的思路如下:

第一步:先将30TB数据分成30TB/16GB,也就是1920份数据,将这1920份数据分别加入到内存中,进行快排,使其局部有序;

第二步:局部有序之后,进行多路归并,假设我们将16GB分成:15GB为输入缓冲区,1GB为输出缓冲区,我们可以对输入缓冲区15GB拆分成150份,每一份100MB,也就是做150路归并(每次从磁盘中拿150个文件,1920份可以拿13次),所以会生成13份文件;

第三步:基于第二步的13份文件,继续做13路归并,生成一份已排序的文件,完成归并。

这里需要深入思考归并过程

第一步,一定要拆分成1920份,因为尽量大的局部空间有序;

第二步:其实150路归并可以自定义,也不一定是150路,可以是其它多路,150路归并的意思是,每次将1920份中的150个局部有序的文件进行排序,所以一共可以生成13份有序的大局部文件;

第三步:做13路归并即可,很好理解,因为只有13个文件了。

为什么不使用二路归并?

如果使用二路归并,第一次1920份数据,执行一次二路归并后,生成960份,960份又进行二路归并,如此重复,会造成多次磁盘的IO,所以不合适,在IO上面非常耗时,而使用多路归并,只需要一共3次磁盘的IO。

5. 你知道bitmap这个数据结构吗,它的应用场景在哪里?

拿到这个问题比较抽象啊,我当时猜的回答应用场景是磁盘的文件存储,分为顺序存储了,链式存储和索引存储,可以使用位图标识磁盘中哪些位置是已经存储数据的(为1),哪些是不存储数据的(为0),不知道对不对。

面试的时候,回答应该是没问题的,猜对了,这里做一下小总结。

1. 什么是bitmap?

Bitmap(位图)就是一块连续的比特数组,每一个bit表示一个元素的状态(比如占用/空闲、存在/不存在等)。

2. 位图的特点

  • 空间极省:一个元素只占1bit;
  • 操作快:位运算可以非常高效地做
    • 查询(某个bit是否为1);
    • 统计(统计1的个数);
    • 集合运算(按位与/或,实现交集/并集)。

3. 位图的应用

  • 文件系统 / 内存管理中的资源分配,记录每一个磁盘块或者物理页框是否被使用;

  • 大规模ID / 用户集合的存在性判断、去重

    • 比如有上亿个用户 ID(数字型),需要快速判断“某 ID 是否出现过”,或者做去重;
    • 用set/hash占内存很大,而用bitmap,只要按ID做偏移,一个 bit 表示“是否出现过”即可(一个bit位表示一个用户ID);
    • 典型用法:日志去重、UV统计、黑名单/白名单标记等。
  • 权限、特性开关、标签集合

    • 一组权限feature,第0位表示是否是管理员,第1位表示是否有读权限,第2位表示是否有写权限等;
    • 用一个整数就能表示一个用户的一整套权限集合,存储紧凑,判断也只需要按位与运算。

6. MySQL即使用了索引,为什么查询还是很慢的原因?

也是非常经典的问题,只有知道了查询很慢的原因,我们才能对其进行优化,这里面试的时候只说了回表查询,面试官显然不是很满意,说还有其它的原因吗,需要系统的总结一下。

这里分两种情况回答,比较有思路一点

情况一:只是偶尔sql语句执行很慢

  • 数据库在刷新脏页,在数据库运行的内存中,存放了redo log,当我们执行一条更新语句的时候,数据库会在内存中把对应字段的数据更新了,但是更新之后,这些更新的字段并不会马上同步持久化到磁盘中去,而是把这些更新的记录写入到redo log日记中去,文件当数据库存在大量的更新时,redo log一下就满了,数据库会全身心来把数据同步到磁盘中去的,而这个时候,就会导致我们平时正常的SQL语句突然执行的很慢,所以说,数据库在在同步数据到磁盘的时候,就有可能导致我们的SQL语句执行的很慢;
  • 当前执行的sql语句涉及到的表或者行被加锁了,只有等其它事务执行完释放锁了,当前sql语句才能继续执行下去,可以使用show processlist 命令查看是否是真的等待锁。

情况二:sql语句一直执行很慢,那可能需要考虑我的sql语句书写问题了

  • 字段没有索引,数据库只能全表扫描;
  • 字段有索引,但是没有用上,一般是对索引字段进行了运算、函数操作或者隐式类型转换时,索引会失效,也只能全表扫描;
  • 查询语句查询了索引以外的字段,会导致回表查询;
  • 查询条件的字段有索引,数据库本身不使用索引查询,而是使用全表查询
    • 因为系统会判断索引区分度(基数,也就是索引不同值的个数)大小,如果索引基数很小,那么数据库会选择全表扫描。

7. 你说到了回表查询的问题,那么如何避免呢?

这里当时面试的时候好像说了不要查询非主键索引以外的字段,就不会产生回表查询,大差不差吧,可以再多说一点。

回表查询的本质就是,因为我们的非主键(聚簇)索引在B+树的叶子节点中只存储了索引值和主键值,如果需要拿到非主键索引以外的值,就需要使用主键索引去拿到行数据,这个时候就导致了会出现回表查询,解决方法如下:

  • 最根本的方法就是不要使用非聚簇索引去查询索引以外的字段,避免回表查询;
  • 直接使用主键索引查询;
  • 对于查询的字段建立复合索引。

拓展:使用 EXPLAIN 检查sql语句的执行计划,查看是否真的使用了覆盖索引或回表查询。

8. MySQL事务的隔离机制有哪四种机制?

面试的时候直接说不知道,没办法当时还没复习到,也是非常经典的MySQL问题。

1. 四种隔离级别

READ UNCOMMITTED (读未提交)

  • 特点:一个事务可以看到其他事务未提交的数据;
  • 问题:会出现 脏读、不可重复读、幻读,基本不建议使用。

READ COMMITTED (读已提交)

  • 特点:只允许读到已经提交的数据,不会读到未提交的中间状态;
  • 解决:避免脏读;
  • 仍可能出现:不可重复读、幻读;
  • 一些数据库(如 Oracle)的默认级别就是它。

REPEATABLE READ (可重复读,InnoDB默认隔离级别)

  • 特点:一个事务内,多次读取同一行,看到的数据是一致的快照(同一版本);
  • 解决:避免脏读和不可重复读;
  • InnoDB通过MVCC和间隙锁,在大多数场景下也能避免幻读。

SERIALIZABLE (可串行化)

  • 特点:最高隔离级别,理论上相当于所有事务加锁串行执行;
  • 解决:脏读 & 不可重复读 & 幻读全部避免;
  • 代价:并发度极低,锁冲突严重,很少在高并发业务场景使用。

2. 拓展

  • 脏读:读到了别人没提交的修改;

  • 不可重复读:同一行,多次读,结果不一样;

  • 幻读:同一条件,多次查,行的“数量 or 集合”变了,多了或少了几行。

3. 什么是MySQL的MVCC(多版本并发控制)?

一句话解释MVCC就是,给每一行保留多个版本和生成事务自己的快照视图,让大多数读操作不用加锁,就能读到一致的数据。

  • MVCC出现背景?==> 没有 MVCC 时,要想实现可重复读,最直接的办法是:读的时候给行加读锁,别人不能改;写的时候给行加写锁,别人不能读,这会导致系统并发度极差,大家互相锁来锁去,吞吐量很低;
  • MVCC 解决什么问题?==> 让读和写尽量互不阻塞,写,照常更新当前版本;读,读自己那个时间点看到的旧版本(快照),而不是强行锁住最新那一份。

4. InnoDB怎么实现多版本的?

  • InnoDB 每一行都有两个隐藏字段(简化版理解)

    • trx_id :最后一次修改这个行的事务 ID;

    • roll_pointer :指向undo log的指针,通过它能找到这个行旧版本的信息。

  • 执行UPDATE语句

    • InnoDB在undo log记录旧值;

    • 把数据页里这一行更新为新值,把trx_id改成当前事务ID,把roll_pointer指向刚写入的undo记录;

    • 数据页里的当前行是新版本,undo链表里串着历史旧版本。

多版本存在于当前页和undo log链中。

5. 快照读是怎么挑版本的(Read View)?

当一个事务开始做快照读(普通 SELECT ,不带 FOR UPDATE )时,InnoDB 会为这个事务生成Read View,主要包含:

  • 当前系统里活跃未提交事务的ID列表;
  • 当前系统分配过的最大事务ID等。

读某一行,InnoDB会沿着当前版本和undo链往回走,找到对这个事务来说可见的那个版本

  • REPEATABLE READ(可重复读,InnoDB 默认)
  • 一个事务在执行过程中,多次SELECT用的是同一个 Read View;
  • REPEATABLE READ看到的是事务开始那一刻的快照,即使别的事务已经提交了新的修改,它也看不到,从而避免不可重复读。
  • 对于READ COMMITTED(读已提交)
    • 每次SELECT都会生成新的Read View;
  • READ COMMITTED看到的是别人已经提交的新版本,不会看到未提交的,解决了脏读,但允许不可重复读。

MVCC实现了同一条物理数据,不同事务、不同隔离级别下,看到的是不同版本的本质。

6. MVCC 和锁的关系

  • 普通 SELECT(快照读),利用MVCC不加行锁可以读到一致性视图,读写之间的冲突减少,提高数据库并发量;
  • 当前读(SELECT … FOR UPDATE / UPDATE / DELETE),操作当前最新版本的数据,会加行锁、间隙锁等,MVCC主要优化读体验,写操作需要加锁。

9. 还问了索引的问题,在查询条件where a=x and b=x;中,对a和b分别建立的索引,优先使用哪一个索引?

应该是听到了我说的最左匹配原则(完全不对啊,最左匹配原则是在复合索引下的概念),面试官深入引申了一下,后面我猜回答说是使用a。

情况一:有联合索引,不管是(a, b)还是(b, a),都优先使用联合索引,而不是单列索引

CREATE INDEX idx_a_b ON t(a, b);
-- 或
CREATE INDEX idx_b_a ON t(b, a);

情况二:只有单列索引

  • 会根据索引的区分度大小(基数),选择区分度高的索引进行查询;
  • 也有可能两个单列索引都使用,然后将结果做交集。

注:选择区分度(基数)大的索引进行查询之后,再进行回表查询,然后根据另一个字段的where条件过滤结果行(如果没有使用两个索引做交集,不会两个索引都使用)。

最左匹配原则

对于一个联合索引,比如 (a, b, c) ,MySQL 在进行查询匹配时,必须严格按照从左到右的顺序来使用,不能跳过中间的列。只要查询条件从最左边的列开始并且是连续的,就能利用到索引;一旦中间断开,或者遇到了范围查询,后面的列就无法再通过索引进行高效匹配了。

10. 继续上述问题,一定会使用a吗,什么情况下不使用a,而是使用b?

这里我直接说不会了,面试官进行了简单的拓展,说是如果索引a失效了,就会使用b,索引a失效的情况, 比如对索引使用了数学运算或者函数操作等?

不一定会使用索引a,以下情况会使用索引b

  • 在a和b都建立了单列索引前提下,MySQL优化器计算出索引b的区分度(基数)更大,会选择使用索引b而不是索引a;
  • 在索引a中使用了函数操作、数学运算或者隐式类型转换,导致索引a失效了,这个时候只能使用索引b;
  • b 索引是覆盖索引;
  • ORDER BY/GROUP BY更契合索引b,WHERE a = x AND b = x ORDER BY b ,可能用 idx_b 能一次完成过滤和排序,整体代价更低。

总结

MySQL,Redis,Kafka这样的中间件,如果仅仅是在使用的程度上,是不行的,需要去深入了解一些常见后端开发需要用到的知识点,只有这样,才能对整个后端的程序进行针对性的性能优化,针对面试过程也能和面试官聊起来。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐