跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++java算法

字节跳动 Linux C/C++ 后端面试真题解析

字节跳动后端面试的核心知识点,涵盖 HTTP/HTTPS/TCP 网络协议、C++ 指针与内存管理、进程通信与多线程同步、数据库范式、Spring AOP 原理及缓存穿透击穿雪崩等问题。内容包含技术原理详解、代码示例及实战排查经验,旨在帮助候选人深入理解后端开发基础与高并发场景下的解决方案。

奶糖兔发布于 2026/3/23更新于 2026/7/829K 浏览
字节跳动 Linux C/C++ 后端面试真题解析

Part1、Http 请求中有哪些请求方式?

考察重点:HTTP 协议的请求方法语义、适用场景区分,避免仅罗列不说明用途。

回答思路:

按'常用 + 少见'分类,结合后端业务场景说明:

常用方法:

  • GET:查询资源(如商品详情),幂等(多次请求结果一致)、安全(不修改资源),参数拼在 URL(长度有限制);
  • POST:提交资源(如用户注册),非幂等(多次提交可能重复创建),参数放请求体(支持大体积数据、二进制);
  • PUT:全量更新资源(如修改用户信息),幂等(多次更新结果一致),需指定资源唯一标识(如 URL 中的用户 ID);
  • DELETE:删除资源(如删除订单),幂等,需谨慎使用(建议加权限校验);
  • PATCH:部分更新资源(如仅修改用户手机号),非幂等,比 PUT 更灵活(无需传完整资源字段);

少见方法:

  • HEAD:仅返回响应头(无响应体),用于检查资源是否存在(如判断文件是否更新);
  • OPTIONS:获取目标 URL 支持的请求方法(如跨域预检 CORS 时,浏览器自动发送)。

Part2、说一下 Https 是如何保证链接安全的?

考察重点:HTTPS 与 HTTP 的核心差异、安全机制的底层逻辑(身份认证、加密、完整性)。

回答要点:

HTTPS = HTTP + TLS/SSL 协议,通过三层机制保障安全:

身份认证(防伪装):

服务端需配置 CA 机构颁发的证书(含公钥、服务端域名、有效期),客户端请求时会验证证书合法性(如是否过期、域名是否匹配、CA 签名是否有效),避免连接'假服务端';

数据加密(防窃听):

握手阶段用非对称加密协商会话密钥(服务端公钥加密、私钥解密),传输阶段用对称加密(如 AES)加密数据(对称加密效率高,适合大体积数据);

完整性校验(防篡改):

用 MAC(消息认证码)或 HMAC 对数据生成'指纹',客户端接收后验证指纹,若数据被篡改(如中间件拦截修改),指纹不匹配则拒绝接收。

Part3、Https 的加密方式是怎样的?对称还是非对称?

考察重点:HTTPS 加密体系的'混合模式'逻辑,避免混淆对称 / 非对称加密的作用。

回答要点:

HTTPS同时使用对称加密与非对称加密,分工不同:

非对称加密(仅用于握手阶段):

  • 用途:安全传递'会话密钥'(对称加密的密钥),避免密钥在传输中被窃取;
  • 流程:服务端将 CA 证书(含公钥)发给客户端,客户端用公钥加密'预主密钥'并传回,服务端用私钥解密得到预主密钥,双方再基于预主密钥生成会话密钥;

对称加密(用于数据传输阶段):

  • 用途:加密 HTTP 请求 / 响应数据(如表单参数、接口返回值);
  • 原因:非对称加密效率低(1000 次/秒级),对称加密效率高(10 万次/秒级),适合高频数据传输;

补充:TLS1.3 优化了握手流程(仅 1 次往返),但核心加密逻辑仍为'非对称协商密钥 + 对称传输数据'。

Part4、Http 的状态码都有哪些,代表什么意思?

考察重点:状态码的分类逻辑(1xx-5xx)、常见码的实际业务场景(如 304、403、502)。

回答要点:

按 HTTP 标准分类,结合后端场景举例:

1xx(信息性):临时响应,如 100 Continue(客户端需继续发送请求体);

2xx(成功):

  • 200 OK:请求成功(如查询商品返回数据);
  • 204 No Content:成功但无响应体(如删除资源后无返回);

3xx(重定向):

  • 301 永久重定向:资源永久迁移(如旧域名跳新域名,SEO 友好);
  • 302 临时重定向:临时跳转(如登录后跳首页);
  • 304 Not Modified:资源未修改(客户端用本地缓存,减少服务器压力);

4xx(客户端错误):

  • 400 Bad Request:请求参数错误(如少传必填字段);
  • 401 Unauthorized:未授权(如未登录访问需要权限的接口);
  • 403 Forbidden:权限不足(如普通用户访问管理员接口);
  • 404 Not Found:资源不存在(如访问不存在的接口路径);

5xx(服务端错误):

  • 500 Internal Server Error:服务端未知错误(如代码抛异常未捕获);
  • 502 Bad Gateway:网关错误(如 Nginx 转发到的后端服务宕机);
  • 503 Service Unavailable:服务暂不可用(如高峰期限流、服务重启);
  • 504 Gateway Timeout:网关超时(如后端服务处理时间过长)。

Part5、TCP 是如何实现可靠传输的呢?

考察重点:TCP 可靠传输的核心机制(避免丢失、乱序、重复),底层原理与业务场景结合。

回答要点:

TCP 通过六大机制保障可靠传输,对应不同问题:

  • 确认应答(ACK):客户端发送数据后,服务端需返回 ACK 确认(含确认号),未收到 ACK 则重传(解决'数据丢失');
  • 超时重传:发送方若超过超时时间未收到 ACK,自动重传数据(超时时间会动态调整,避免网络延迟导致误重传);
  • 序号与确认号:每个 TCP 段都有序号,服务端按序号重组数据(解决'数据乱序'),确认号表示'已收到到该序号前的所有数据'(解决'数据重复');
  • 滑动窗口(流量控制):服务端告知客户端'当前可接收的最大数据量'(窗口大小),客户端按需发送(避免服务端接收能力不足导致数据丢失);
  • 拥塞控制:通过'慢开始 - 拥塞避免 - 快重传 - 快恢复'调整发送速率(解决'网络拥堵',如刚开始慢发送,避免加剧拥堵);
  • 数据校验:TCP 段头部含校验和,接收方校验数据完整性,校验失败则丢弃并要求重传(解决'数据篡改')。

Part6、在浏览器中输入 url 后会发生哪些事情?

考察重点:完整网络请求链路(从 URL 解析到页面渲染),各层协议协同逻辑。

回答要点:

分 8 个核心步骤,覆盖'网络 + 浏览器'全流程:

  • URL 解析:拆分 URL 为'协议(http/https)、域名(如 www.baidu.com)、端口(默认 80/443)、路径(/index)、参数(?id=1)';
  • DNS 域名解析:将域名转为 IP(如 www.baidu.com→180.101.49.12),流程:本地 DNS 缓存→路由器缓存→ISP DNS→根 DNS→顶级域 DNS→权威 DNS;
  • 建立 TCP 连接:基于 IP 建立三次握手(客户端发 SYN→服务端回 SYN+ACK→客户端回 ACK),HTTPS 需额外进行 TLS 握手(协商密钥、验证证书);
  • 发送 HTTP 请求:客户端构造请求(请求行:GET /index HTTP/1.1;请求头:Host、Cookie、User-Agent;请求体:POST 参数等),通过 TCP 发送到服务端;
  • 服务端处理请求:服务端(如 Nginx→后端服务→数据库)处理请求(查数据、业务逻辑),构造响应(响应行、响应头、响应体);
  • 关闭 TCP 连接:若 HTTP/1.1 无 keep-alive,进行四次挥手(客户端发 FIN→服务端回 ACK→服务端发 FIN→客户端回 ACK);有 keep-alive 则复用连接;
  • 浏览器解析响应:
    • HTML 解析为 DOM 树,CSS 解析为 CSSOM 树,两者合成'渲染树';
    • 布局(Layout):计算元素位置 / 大小;绘制(Paint):将元素画到屏幕;
  • 执行 JavaScript:通过 JS 引擎(如 V8)执行脚本,操作 DOM/CSSOM(可能触发回流 / 重绘),绑定事件(如点击事件)。

Part7、C++ 指针和引用的差别是什么?

考察重点:C++ 内存模型理解、指针与引用的语法 / 语义差异,实际编码选择逻辑。

回答要点:

从 6 个核心维度对比,结合场景举例:

维度指针引用
定义与初始化变量(存地址),可空 / 未初始化变量别名,必须初始化且绑定对象
内存占用占 4/8 字节(依平台)不占独立内存(编译器优化为指针)
可修改性可重指向其他对象(如 int* p=&a; p=&b;)绑定后不可更改(如 int& r=a; r=b 是赋值,非重绑定)
操作方式需解引用(*p)/ 取地址(&p)直接使用(如 r=1 等价于 a=1)
多态支持可指向基类 / 派生类对象(如 Base* p=new Derived;)可绑定基类 / 派生类对象(如 Base& r=Derived ();)
风险点野指针(未初始化)、空指针(nullptr)无空引用,但需避免绑定局部变量(生命周期问题)
场景举例函数参数传递时,需修改实参用指针 / 引用;返回对象时,用引用避免拷贝(如 vector& getVec ()),但不可返回局部变量引用。

Part8、说一下动态链接和静态链接是什么,以及各自的优缺点

考察重点:程序编译链接机制,静态 / 动态链接的底层差异与场景适配。

回答要点:

先定义核心概念,再对比优缺点:

静态链接:

  • 定义:编译时将依赖的库代码(如 libxxx.a)全部拷贝到可执行文件中;
  • 优点:运行时不依赖外部库,启动快(无需加载库),部署简单;
  • 缺点:可执行文件体积大(如链接 Boost 后体积翻倍),库更新需重新编译(如 libxxx.a 修复 bug,所有依赖它的程序都要重编);

动态链接:

  • 定义:编译时仅记录库引用(如 libxxx.so),运行时加载共享库到内存(多个程序可共享同一份库);
  • 优点:可执行文件体积小,库更新无需重编(替换.so 文件即可),内存占用低(共享库);
  • 缺点:运行时依赖共享库(缺失.so 会报错'cannot open shared object file'),启动稍慢(需加载库);

场景选择:工具类程序(如命令行工具)用静态链接(部署方便);后端服务(如 API 服务)用动态链接(便于更新库、节省内存)。

Part9、说一下深拷贝和浅拷贝的区别

考察重点:C++ 对象拷贝的内存管理,浅拷贝的风险与深拷贝的实现逻辑。

回答要点:

核心差异在'动态内存成员的处理',结合例子说明:

浅拷贝:

  • 逻辑:仅拷贝对象的成员变量值,若成员是指针(如 char*),仅拷贝指针地址(不拷贝指向的内存);
  • 特点:不额外分配内存,拷贝效率高,但两个对象共享同一块动态内存;
  • 风险:对象析构时会'双重释放'(如两个对象的 char * 指向同一块内存,析构时两次 delete),导致程序崩溃;

深拷贝:

  • 逻辑:拷贝成员变量时,若成员是指针,会重新分配内存并拷贝指针指向的内容;
  • 特点:两个对象内存独立(修改一个不影响另一个),无双重释放风险,但拷贝效率低(需分配内存);

实现举例:

// 浅拷贝(默认拷贝构造)
class String {
public:
    char* data;
    String(const char* s) { 
        data = new char[strlen(s)+1]; 
        strcpy(data, s); 
    } 
    ~String() { 
        delete[] data; 
    }
}; // 浅拷贝时会双重释放

// 深拷贝(重写拷贝构造与赋值运算符)
String::String(const String& other) {
    data = new char[strlen(other.data)+1];
    strcpy(data, other.data); // 拷贝指针指向的内容
}

String& String::operator=(const String& other) {
    if (this != &other) {
        delete[] data; // 先释放自身内存
        data = new char[strlen(other.data)+1];
        strcpy(data, other.data);
    }
    return *this;
}

Part10、进程通信的解耦机制?

考察重点:进程通信的架构设计思维,如何降低进程间依赖(避免紧耦合)。

回答要点:

解耦核心是'减少直接交互,通过中间层 / 协议隔离',5 类常见机制:

1)、消息队列(如 Linux msgqueue、RabbitMQ):

  • 逻辑:进程 A 发送消息到队列,进程 B 从队列接收,双方无需知道对方存在;
  • 解耦点:异步通信(发送方无需等待接收方处理)、数据格式统一(队列定义消息结构);
  • 场景:日志收集(业务进程发日志到队列,日志进程消费);

2)、发布 - 订阅模式(如 Redis Pub/Sub、Kafka):

  • 逻辑:发布者发消息到'主题',订阅者订阅主题接收消息,多对多通信;
  • 解耦点:完全解耦(发布者不知订阅者,订阅者不知发布者);
  • 场景:实时通知(如订单状态变更,多个服务订阅'订单主题');

3)、共享内存 + 信号量:

  • 逻辑:共享内存存数据(高吞吐),信号量负责同步(避免竞争);
  • 解耦点:数据存储与同步分离(共享内存只管存,信号量只管锁);
  • 场景:大数据量传输(如视频处理进程间传帧数据);

4)、中间件代理(如 RPC 服务发现):

  • 逻辑:进程通过注册中心(如 Nacos、Eureka)发现服务,无需硬编码对方地址;
  • 解耦点:地址解耦(服务下线 / 上线不影响调用方);
  • 场景:微服务调用(如订单服务调用支付服务,通过注册中心找地址);

5)、管道 / 命名管道(半解耦):

  • 逻辑:匿名管道(父子进程)、命名管道(无亲缘进程),通过管道传递数据;
  • 解耦点:比直接共享内存松,但需知道管道路径(解耦程度低于消息队列);
  • 场景:简单数据交互(如两个服务传递配置)。

Part11、Linux 进程通信的几种方式以及各自的应用场景

考察重点:Linux IPC 机制的分类与场景适配,避免仅罗列不说明用途。

回答要点:

7 类核心 IPC 方式,按'数据传递 / 同步'分类:

方式核心逻辑适用场景
匿名管道父子 / 兄弟进程,半双工,基于文件描述符shell 命令管道(如 ls)
命名管道(FIFO)无亲缘进程,半双工,文件系统可见不同服务间简单通信(如服务 A→服务 B 传状态)
消息队列无亲缘进程,异步,按类型接收消息高并发异步通信(如日志收集、订单通知)
共享内存进程直接访问同一块内存,最快 IPC大数据量传输(如视频帧、传感器数据)
信号量同步互斥,不传递数据,控制并发数保护共享资源(如限制 3 个进程读写共享内存)
信号异步通知,传递简单信号(如 SIGKILL)异常处理(如 kill -9 终止进程)、事件触发(子进程退出通知父进程)
Socket(套接字)跨主机 / 本地进程,基于 TCP/UDP网络服务(如客户端 - 服务端通信,Nginx 接收请求)、本地进程通信(如 Unix 域套接字)

Part12、说一下数据库的范式

考察重点:数据库设计规范,范式的作用(减少冗余、避免异常)与实际权衡。

回答要点:

按'1NF→3NF→BCNF'讲解(常用范式),结合反例说明:

1NF(第一范式):列不可再分(原子性);

  • 反例:'联系方式'列存'13800138000,[email protected]'(拆分为'电话''邮箱');
  • 作用:保证数据可正常查询(如按电话筛选);

2NF(第二范式):满足 1NF,且非主键列'完全依赖'主键(消除部分依赖);

  • 反例:订单表(订单 ID,商品 ID,商品名称),商品名称依赖商品 ID(部分依赖主键'订单 ID + 商品 ID');
  • 优化:拆为'订单表(订单 ID,商品 ID)''商品表(商品 ID,商品名称)';
  • 作用:避免插入异常(新增商品无需先有订单);

3NF(第三范式):满足 2NF,且非主键列'不传递依赖'主键(消除传递依赖);

  • 反例:用户表(用户 ID,用户名,地区 ID,地区名称),地区名称依赖地区 ID(传递依赖主键);
  • 优化:拆为'用户表(用户 ID,用户名,地区 ID)''地区表(地区 ID,地区名称)';
  • 作用:避免更新异常(修改地区名称只需改地区表,无需改所有用户);

BCNF(巴斯 - 科德范式):满足 3NF,且主键列'不传递依赖'非主键列(解决主属性传递依赖);

  • 反例:学生选课表(学生 ID,课程 ID,教师 ID),若'一门课对应一个教师',教师 ID 依赖课程 ID(主属性传递依赖);
  • 优化:拆为'选课表(学生 ID,课程 ID)''课程教师表(课程 ID,教师 ID)';
  • 作用:避免删除异常(删除学生不会丢失课程 - 教师关系);

关键提醒:范式不是越高越好,过度范式会增加表连接(如 5 张表 join,查询效率低),实际设计需'反范式'权衡(如热点数据冗余存储,减少 join)。

Part13、说一下多线程死锁的原因吧

考察重点:死锁的底层逻辑(必要条件)、实际开发中的资源竞争场景,避免仅罗列理论不结合代码。

回答要点:

多线程死锁是'多个线程互相等待对方持有的资源,无法继续执行'的状态,需同时满足四个必要条件,结合 C++ 场景举例说明:

互斥条件:资源只能被一个线程占用(如 std::mutex 加锁后,其他线程需等待解锁);

  • 示例:线程 A 加锁 mutex1,线程 B 尝试加锁 mutex1 时会阻塞;

持有并等待条件:线程持有已获得的资源,同时等待其他线程的资源;

  • 示例:线程 A 持有 mutex1,等待线程 B 的 mutex2;线程 B 持有 mutex2,等待线程 A 的 mutex1;

不可剥夺条件:线程持有的资源不能被强制剥夺(如已加锁的 mutex,只能由持有线程主动解锁);

  • 示例:线程 A 不释放 mutex1,线程 B 无法强制获取 mutex1,只能一直等待;

循环等待条件:多个线程形成'资源等待循环链'(线程 A→线程 B→线程 C→线程 A);

示例(典型死锁代码):

std::mutex mutex1, mutex2;
// 线程 A
void threadA() {
    mutex1.lock(); // 持有 mutex1
    std::this_thread::sleep_for(100ms); // 让线程 B 先持有 mutex2
    mutex2.lock(); // 等待 mutex2,此时线程 B 已持有 mutex2 并等待 mutex1,形成死锁
    // 业务逻辑...
    mutex2.unlock();
    mutex1.unlock();
}

// 线程 B
void threadB() {
    mutex2.lock(); // 持有 mutex2
    std::this_thread::sleep_for(100ms); // 让线程 A 先持有 mutex1
    mutex1.lock(); // 等待 mutex1,死锁发生
    // 业务逻辑...
    mutex1.unlock();
    mutex2.unlock();
}

常见场景:数据库连接池(线程 A 持有连接 1 等待连接 2,线程 B 持有连接 2 等待连接 1)、车载系统资源竞争(线程 A 持有传感器 1 等待传感器 2,线程 B 相反)。

Part14、如何避免死锁呢?

**考察重点:**针对死锁四个必要条件的解决方案,工程化的编码规范与工具应用,需结合 C++ 实践。

回答要点:

核心思路是'破坏死锁的任一必要条件',分 5 类具体措施,附代码示例:

**1.破坏'持有并等待'条件:**一次性申请所有资源,不部分持有;

实现:封装资源申请函数,同时申请所有需要的锁,失败则全部放弃;

示例:

// 一次性申请 mutex1 和 mutex2,用 std::lock 避免部分持有
void safeLock() {
    std::lock(mutex1, mutex2); // 原子操作,要么同时获取,要么同时等待
    std::lock_guard<std::mutex> lock1(mutex1, std::adopt_lock); // 接管已加锁的 mutex1
    std::lock_guard<std::mutex> lock2(mutex2, std::adopt_lock); // 接管已加锁的 mutex2
    // 业务逻辑...
}

**2.破坏'循环等待'条件:**按固定顺序申请资源(如按资源 ID 升序);

规则:约定所有线程申请锁时,先申请 ID 小的锁,再申请 ID 大的锁;

示例:

const int MUTEX1_ID = 1, MUTEX2_ID = 2;
// 线程 A、B 都按'小 ID→大 ID'申请
void threadA() {
    if (MUTEX1_ID < MUTEX2_ID) { // 先申请小 ID 的 mutex1
        mutex1.lock();
        mutex2.lock();
    } else {
        mutex2.lock();
        mutex1.lock();
    }
    // 业务逻辑...
    mutex2.unlock();
    mutex1.unlock();
}

void threadB() {
    // 与线程 A 申请顺序一致,避免循环等待
    if (MUTEX1_ID < MUTEX2_ID) {
        mutex1.lock();
        mutex2.lock();
    } else {
        mutex2.lock();
        mutex1.lock();
    }
    // 业务逻辑...
    mutex2.unlock();
    mutex1.unlock();
}

**3.破坏'不可剥夺'条件:**允许超时释放资源(用带超时的锁申请);

实现:用 std::unique_lock 的 try_lock_for,超时未获取资源则释放已持有资源;

示例:

void threadA() {
    std::unique_lock<std::mutex> lock1(mutex1);
    // 尝试获取 mutex2,超时 100ms 未成功则放弃
    if (!lock2.try_lock_for(100ms)) {
        lock1.unlock(); // 释放已持有的 mutex1
        return; // 退出或重试
    }
    // 业务逻辑...
    lock2.unlock();
    lock1.unlock();
}

**4.破坏'互斥'条件:**用无锁数据结构(如 std::atomic)或共享资源(如读写锁的读共享);

场景:读多写少的场景,用 std::shared_mutex 让多线程同时读,避免互斥;

示例:

std::shared_mutex rwMutex;
std::string data;

// 读线程(共享访问,不互斥)
void readThread() {
    std::shared_lock<std::shared_mutex> lock(rwMutex);
    std::cout << data << std::endl;
}

// 写线程(独占访问,互斥)
void writeThread() {
    std::unique_lock<std::shared_mutex> lock(rwMutex);
    data = "new data";
}

**5.工程化工具检测:**提前发现死锁风险;

工具:C++ 用 pstack(查看线程调用栈)、valgrind --tool=helgrind(检测数据竞争与死锁);

流程:测试环境运行服务,用工具监控线程状态,若发现'循环等待'调用栈则优化代码。

Part15、C++ 是如何保证线程安全的呢?

考察重点:C++ 线程安全的实现手段,不同机制的适用场景(如原子操作 vs 锁)。

回答要点:

分 6 类核心手段,结合后端场景举例:

互斥同步(阻塞式):

  • std::mutex(互斥锁):保护临界区(如多线程读写共享变量),用 lock_guard 自动加解锁;
  • std::shared_mutex(读写锁):读共享、写独占(适合读多写少,如缓存查询);
  • std::spinlock(自旋锁):线程等待时不阻塞(循环查锁),适合临界区执行时间短(如传感器数据读取);

原子操作(无锁式):

  • std::atomic(如 atomic_int、atomic_bool):底层基于 CPU 原子指令(如 x86 的 cmpxchg),无需锁,效率高;
  • 场景:多线程计数器(如请求量统计)、标志位(如线程停止信号);

线程局部存储(TLS):

  • thread_local 关键字:每个线程有独立的变量副本,避免共享;
  • 场景:线程私有缓存、日志 ID(如每个线程记录自己的请求 ID);

避免共享状态:

  • 设计无状态函数(不依赖全局 / 静态变量),如纯函数(输入决定输出);
  • 场景:工具函数(如字符串处理函数);

内存屏障:

  • std::memory_order(如 memory_order_acquire/release):解决指令重排序与内存可见性问题;
  • 场景:多线程下变量修改后,确保其他线程能立即看到(如线程 A 改 flag=true,线程 B 能及时读取);

同步工具:

  • std::condition_variable(条件变量):线程间通信(如生产者 - 消费者模型,生产者唤醒消费者);
  • std::semaphore(信号量):限制并发数(如限制 5 个线程同时访问数据库)。

Part16、说一下 C++ 里面的容器是如何保证线程安全的呢?

考察重点:C++ 标准容器的线程安全特性,实际使用中的同步策略(避免踩坑)。

回答要点:

先明确核心前提,再讲保障手段:

前提:C++ 标准库容器(vector、map、queue 等)本身不保证线程安全(标准未强制,避免性能开销),单个操作(如 push_back)也可能不是原子的;

保障线程安全的 3 种方式:

外部全局锁:

  • 对容器的所有操作(读 / 写)加锁(如 std::mutex),适合低并发;
  • 示例:
std::mutex mtx;
std::vector<int> vec;

// 写操作
void push(int val) {
    std::lock_guard<std::mutex> lock(mtx);
    vec.push_back(val);
}

// 读操作
int get(int idx) {
    std::lock_guard<std::mutex> lock(mtx);
    return vec[idx];
}

细粒度锁(分段锁):

  • 对容器分段加锁(如 hash_map 按桶加锁),不同段的操作可并发,提高效率;
  • 场景:高并发哈希表(如自定义线程安全 hash_map);

使用线程安全容器(第三方):

  • 标准库无,需依赖第三方库:如 Boost 的 boost::thread_safe_queue、Intel TBB 的 tbb::concurrent_vector;
  • 优势:内部实现同步,无需外部加锁(如 tbb::concurrent_vector 支持多线程 push_back);

关键注意事项:

即使单个操作(如 vec.push_back)是原子的,复合操作仍需同步(如'判断非空→取值':if (!vec.empty ()) { val=vec[0]; },empty () 和 [] 之间可能被其他线程打断,导致取到空值)。

Part17、AOP 在 Spring 中是怎么实现的呢?

考察重点:Spring AOP 的底层原理(动态代理)、核心概念与实际应用。

回答要点:

先讲核心概念,再拆实现流程,最后讲场景:

AOP 核心概念:

  • 切面(Aspect):封装横切逻辑的类(如日志切面、事务切面),用 @Aspect 注解;
  • 通知(Advice):切面的具体逻辑(@Before 前置、@After 后置、@Around 环绕、@AfterReturning 返回后、@AfterThrowing 异常后);
  • 切入点(Pointcut):定义'哪些方法要被增强'(如 execution (* com.xxx.service..(..)));
  • 目标对象(Target):被代理的原始对象(如 UserService);
  • 代理(Proxy):Spring 生成的代理对象(增强后的对象);

底层实现:

动态代理:Spring AOP 基于动态代理,分两种方式,按需选择:

JDK 动态代理:

  • 前提:目标类必须实现接口(如 UserService implements IUserService);
  • 原理:通过 java.lang.reflect.Proxy 生成代理对象,InvocationHandler 接口处理增强逻辑;
  • 示例:
IUserService proxy = (IUserService) Proxy.newProxyInstance(
    classLoader,
    new Class[]{IUserService.class},
    (proxy, method, args) -> {
        // 前置通知:日志记录
        System.out.println("method " + method.getName() + " start");
        Object result = method.invoke(target, args); // 执行目标方法
        // 后置通知
        System.out.println("method " + method.getName() + " end");
        return result;
    }
);

CGLIB 动态代理:

  • 前提:目标类无需实现接口(通过继承生成子类);
  • 原理:通过字节码生成工具(ASM)生成目标类的子类,重写目标方法实现增强;
  • 限制:不能代理 final 类 / 方法(无法继承);
  • Spring 选择逻辑:Spring Boot 2.x 后默认 CGLIB(无论是否有接口),可通过配置切换为 JDK 代理;

执行流程:

  • 扫描 @Aspect 类,解析切入点与通知;
  • 对目标类创建代理对象(JDK/CGLIB);
  • 调用代理对象方法时,先执行通知逻辑,再执行目标方法(@Around 可完全控制方法执行,如修改参数、捕获异常);

应用场景:日志记录、事务管理(Spring 声明式事务基于 AOP)、权限校验、性能监控。

Part18、说一下缓存穿透、击穿、雪崩

考察重点:缓存常见问题的根因与解决方案,结合后端高并发场景(如电商)。

回答要点:

分三类问题,每类讲'原因 + 解决方案 + 示例':

问题原因解决方案示例(Redis 缓存)
缓存穿透请求不存在的数据(如恶意查 item:999999),缓存 / 数据库都无,直击数据库1. 缓存空值(设短期过期,如 5 分钟);2. 布隆过滤器(存所有存在的 key);3. 接口限流缓存 item:999999 为 null,expire 300
缓存击穿热点 key 过期(如 item:1001,百万用户同时查),缓存失效后直击数据库1. 互斥锁(查缓存失效后加锁查库,其他线程等);2. 热点 key 永不过期(后台更新);3. 熔断降级用 SETNX lock:item:1001 加锁,查库后 del 锁
缓存雪崩大量 key 同时过期 / 缓存服务宕机,所有请求直击数据库1. 过期时间随机化(如 1h+0-30min);2. 多级缓存(本地缓存 + Caffeine+Redis);3. 缓存集群(主从 + 哨兵);4. 服务熔断给 key 加随机过期:expire item:1001 3600+rand()%1800

Part19、写的项目有没有上线过,有没有用户大规模使用,缓存穿透这些问题是怎么遇到的?

考察重点:项目落地经验、问题排查能力,避免空泛回答(需具体场景 + 数据)。

回答要点:

按'项目背景→问题现象→排查过程'结构回答,举例参考:

'我参与开发的'电商商品详情页'项目(后端用 Java+Redis+MySQL)已上线,峰值 QPS 5000+,日活用户 10 万 +。缓存穿透是上线后第 2 周遇到的:

现象:监控显示 Redis 命中率从 95% 骤降到 12%,MySQL CPU 使用率飙升到 90%,接口响应时间从 50ms 涨到 500ms,部分请求超时;

排查过程:

  • 查 Redis 监控:发现大量 key(如 item:999999、item:1000000)的'未命中'记录,且这些 key 在数据库中无对应商品;
  • 查 Nginx 访问日志:有 IP 批量发送'itemId=999999'的请求(每秒 100+次),判断是恶意攻击;
  • 定位根因:恶意请求查不存在的商品,Redis 未缓存空值,所有请求直击 MySQL,导致数据库过载;

解决措施:给不存在的商品 key 缓存空值(expire 300 秒),同时用布隆过滤器预加载所有存在的商品 ID,拦截无效请求,1 小时后 Redis 命中率回升到 93%,MySQL CPU 降到 35%。'

Part20、你是怎么模拟这些过程的呢?

考察重点:测试能力(本地模拟、压测、灰度验证),验证解决方案有效性的逻辑。

回答要点:

结合题 19 的缓存穿透场景,分 4 步讲模拟流程:

本地环境模拟:

  • 工具:JMeter 构造请求,参数设为不存在的商品 ID(如 item:100000-200000),并发数 1000;
  • 观察:Redis 命中率从 95% 降到 10%,MySQL CPU 从 20% 升到 80%,复现穿透场景;

压测环境验证方案:

  • 搭建与生产一致的集群(Redis 3 主 3 从、MySQL 主从),用 Gatling 模拟 5000 QPS 混合请求(80% 正常 ID,20% 无效 ID);
  • 验证'缓存空值'方案:给无效 ID 缓存 5 分钟空值,模拟后 Redis 命中率回升到 92%,MySQL CPU 降到 30%;

单元测试覆盖:

  • 用 JUnit+Mockito 模拟 Redis 缓存未命中,测试布隆过滤器逻辑:
// 预加载存在的商品 ID 到布隆过滤器
bloomFilter.add(1001);
bloomFilter.add(1002);

// 测试无效 ID(100000)被拦截
assertFalse(bloomFilter.contains(100000));

// 测试有效 ID(1001)通过
assertTrue(bloomFilter.contains(1001));

线上灰度验证:

  • 先对 10% 流量开启'缓存空值 + 布隆过滤器'方案,观察监控(Redis 命中率、MySQL 负载)1 小时,无异常后全量上线;
  • 效果:全量后未再出现穿透,接口超时率从 5% 降到 0.1%。

Part21、你的 Linux 主要是用来干嘛的呢?

考察重点:Linux 实操能力,与后端开发的结合度(开发、部署、调试)。

回答要点:

分 4 类核心场景,结合后端实际操作举例:

开发环境搭建:

  • 安装工具链(gcc、g++、jdk、cmake)、版本控制(git clone/commit/push)、编辑器(vim/VS Code);
  • 举例:用 cmake 编译 C++ 后端服务,git 管理代码分支(feature 分支开发,merge 到 main);

服务部署与运维:

  • 部署中间件(Nginx、Redis、MySQL),用 systemd 管理服务(systemctl start/stop redis);
  • 编写 shell 脚本(如服务启动脚本、日志切割脚本:每天 0 点压缩 Nginx 日志);
  • Docker 容器化(docker run Redis,docker-compose 编排多服务:Redis+MySQL + 后端服务);

问题排查与监控:

  • 日志查看:tail -f app.log(实时日志)、grep "error" app.log(查错误);
  • 进程管理:ps aux | grep java(找后端服务 PID)、top/htop(看 CPU / 内存占用);
  • 网络调试:netstat -tulpn | grep 8080(查端口占用)、telnet 127.0.0.1 8080(测端口通断);
  • 性能分析:vmstat(系统资源)、perf top -p PID(分析 CPU 高占用进程的函数);

日常操作:

  • 文件管理:ls、cd、cp、rm(如 rm -rf 清理日志)、chmod 755 设权限;
  • 远程操作:ssh 登录服务器、scp 传文件(如 scp local.log user@ip:/home/log/);
  • 压缩解压:tar -zcvf app.tar.gz app/(压缩)、tar -zxvf app.tar.gz(解压)。

目录

  1. Part1、Http 请求中有哪些请求方式?
  2. Part2、说一下 Https 是如何保证链接安全的?
  3. Part3、Https 的加密方式是怎样的?对称还是非对称?
  4. Part4、Http 的状态码都有哪些,代表什么意思?
  5. Part5、TCP 是如何实现可靠传输的呢?
  6. Part6、在浏览器中输入 url 后会发生哪些事情?
  7. Part7、C++ 指针和引用的差别是什么?
  8. Part8、说一下动态链接和静态链接是什么,以及各自的优缺点
  9. Part9、说一下深拷贝和浅拷贝的区别
  10. Part10、进程通信的解耦机制?
  11. Part11、Linux 进程通信的几种方式以及各自的应用场景
  12. Part12、说一下数据库的范式
  13. Part13、说一下多线程死锁的原因吧
  14. Part14、如何避免死锁呢?
  15. Part15、C++ 是如何保证线程安全的呢?
  16. Part16、说一下 C++ 里面的容器是如何保证线程安全的呢?
  17. Part17、AOP 在 Spring 中是怎么实现的呢?
  18. Part18、说一下缓存穿透、击穿、雪崩
  19. Part19、写的项目有没有上线过,有没有用户大规模使用,缓存穿透这些问题是怎么遇到的?
  20. Part20、你是怎么模拟这些过程的呢?
  21. Part21、你的 Linux 主要是用来干嘛的呢?
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 本地部署 AI 大模型:Ollama 安装与 WebUI 配置教程
  • TRAE 与 VSCode Git 版本管理实战
  • DeepSeek 深度使用指南与高效提示词技巧
  • Python 爬虫实战:获取中国农药信息网登记数据
  • 1D-CNN 算法原理与 PyTorch 多步预测实现
  • 语义化 AI 驱动器:提示词工程未来图景与技术演进
  • macOS 安装 Theos 开发环境完整教程
  • GitLab 个人访问令牌(Token)创建与使用指南
  • AI 大模型本地离线部署指南:GPT4All、LM Studio 与 Ollama 方案
  • ERNIE-4.5-0.3B 轻量模型部署与性能测试指南
  • C++ 高并发内存池:ObjectPool 构造与实现
  • Claude Code 结合 Figma:AI 辅助设计原型工作流实战
  • Whisper-CTranslate2 高效语音识别与 AI 翻译工具
  • 前端实现 Web 视频画中画功能 - 主窗口与小窗同步控制
  • AI 绘画动画快速上手指南:让静态涂鸦动起来
  • YOLO11 基于 DroneVehicle 数据集的无人机视角车辆目标检测
  • AI 产品经理核心能力体系与学习路径指南
  • CarelessWhisper: 将非因果 Whisper 改造为低延迟流式模型
  • 数据库迁移 TCO 分析:MySQL 替代隐性成本与工具链实测
  • 大语言模型(LLM)学习路线与初学者入门指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online