有没有了解过http1.0、http1.1、http1.2、http1.3这些?
了解。
HTTP/1.0 默认是短连接,一个 TCP 连接通常处理一个请求;HTTP/1.1 默认支持长连接,并增加了 分块传输等功能,但多个请求基本还是串行处理,容易产生队头阻塞。
HTTP/2 使用二进制分帧、头部压缩和多路复用,多个请求可以共享一个 TCP 连接,但 TCP 丢包仍可能造成队头阻塞。
HTTP/3 基于 QUIC 和 UDP,不同流之间相互独立,可以进一步减少队头阻塞,还支持连接迁移。整体演进过程是:短连接、长连接,再到多路复用和 QUIC。
http1和http1.1的长连接解决的是什么问题?
HTTP/1.0 默认是短连接,每次请求通常都要建立并关闭 TCP 连接,反复握手和断开会增加延迟及资源开销。
HTTP/1.1 默认使用长连接,让多个请求复用同一条 TCP 连接,减少重复建立连接的成本,提高传输效率。
假如一个团队给你分配角色,你觉得你至少需要哪些人来帮助你对这个项目进行开发?
我需要产品或业务人员明确需求,其他开发人员共同完成模块,测试人员进行验证;项目上线时还需要运维配合。
为什么选择C++开发方向?
我最开始在了解开发方向的时候,看到 C++ 在游戏、基础架构以及一些高性能场景里都有比较重要、甚至不可替代的作用。而且我当时觉得 C++ 本身涉及的知识比较底层,把 C++ 学扎实以后,再去学习其他语言相对也会更容易一些。
所以我一开始就选择了 C++,后面学的过程中,确实发现难,但不至于完全学不懂,学到网络部分时候,和C++联系起来发现,挺有趣的,就一直沿着这个方向深入学习下来了。
数组和链表有什么区别?两个各有什么优势呃?
数组和链表的核心区别是内存布局不同:
- 数组 :元素存放在连续内存中,支持下标随机访问,访问复杂度为 O(1) ,缓存友好、额外空间开销小;但中间插入或删除需要移动后续元素,复杂度通常为 O(n),扩容也可能涉及搬迁。
- 链表 :节点分散存储,通过指针连接,访问第
i个元素需要遍历,复杂度为 O(n) ;但如果已经找到插入或删除位置,修改指针即可,复杂度为 O(1),并且大小扩展灵活。
所以,需要频繁随机访问时选择数组或 vector;需要频繁在已知位置插入删除时,可以考虑链表。不过链表有额外指针开销,缓存局部性也较差。
进程和线程区别
进程是资源分配和保护的基本单位 ,线程是CPU 调度和执行的基本单位。
- 进程拥有独立的虚拟地址空间、文件描述符等资源,彼此隔离,创建和切换开销较大。
- 线程属于进程,同一进程内的线程共享代码段、全局数据、堆和文件描述符,但各自拥有栈、寄存器和程序计数器,创建和切换开销较小。
- 进程间通信通常需要 IPC;线程间可以通过共享内存通信,但需要加锁等同步机制。
因此,进程隔离性强,线程并发效率高。
线程的流转状态?
线程常见有五种状态:
- 新建:线程对象已创建,但还未参与调度;
- 就绪:具备运行条件,等待 CPU 调度;
- 运行:正在占用 CPU 执行;
- 阻塞或等待:等待 I/O、锁、条件变量或其他事件;
- 终止:线程执行完毕或被退出。
常见流转是:
- 新建 → 就绪;
- 就绪 → 运行;
- 运行 → 就绪:时间片用完或被抢占;
- 运行 → 阻塞:等待资源或事件;
- 阻塞 → 就绪:等待条件满足;
- 运行 → 终止:执行结束。
volatile关键字有什么作用?
volatile 告诉编译器:该变量的值可能被程序外部因素随时修改,因此每次访问都必须保留,不能随意优化或缓存到寄存器中。 如下,如果不加volatile,多线程修改成false,while仍在死循环
cpp
bool stop = true;
while (stop) {
// 等待外部事件修改 stop
}
数据库索引什么情况下会失效?
- 对索引列做函数、计算或表达式 ,例如
YEAR(time)、id + 1; - 联合索引不满足最左匹配原则;
LIKE使用前导%,例如LIKE '%abc';- 发生隐式类型转换。
- 即使索引可用,如果查询结果占比很大或选择性很低,优化器也可能选择全表扫描。**实际要以
EXPLAIN为准。
B树和B+树的区别?
B 树和 B+ 树都是多路平衡搜索树,主要区别有:
- 数据存储位置:B 树的非叶子节点和叶子节点都可以存储数据;B+ 树只有叶子节点存储完整数据,非叶子节点只存索引键。
- 叶子节点连接:B+ 树的叶子节点通过链表连接,便于范围查询和顺序遍历;B 树通常没有。
- 查询效率:B+ 树非叶子节点存储内容少,因此每个节点能存更多索引,树高度更低,磁盘 I/O 更少。
- 查询路径:B 树可能在中间节点直接找到数据;B+ 树所有查询最终都要到叶子节点,查询性能更稳定。
因此,数据库索引通常采用 B+ 树。
索引的实现方式以及底层原理?
索引本质上是一种辅助查找结构,用索引列和数据位置建立映射,减少全表扫描。
常见实现方式有:
- B+ 树索引 :最常用。非叶子节点存储索引键和子节点指针,叶子节点存储索引值和数据位置,并通过链表连接。查询从根节点逐层定位到叶子节点,时间复杂度约为
O(logN),同时支持范围查询和排序。 - 哈希索引 :通过哈希函数定位数据,等值查询效率高,平均接近
O(1),但不支持范围查询和排序。
以 InnoDB 为例,主键索引的叶子节点存储完整数据;二级索引叶子节点存储索引列和主键值,查询完整记录时还需要根据主键回表。
数据库事务有四个特性,简称 ACID:
- 原子性(Atomicity):事务中的操作要么全部完成,要么全部回滚,不会只完成一部分。
- 一致性(Consistency):事务执行前后,数据库都满足完整性约束和业务规则,例如转账前后总金额不变。
- 隔离性(Isolation):控制并发事务之间的相互影响,具体隔离程度由隔离级别决定。
- 持久性(Durability):事务一旦提交,修改就会被持久保存,即使数据库崩溃也不会丢失。
I/O多路复用了解过吗?
了解。I/O 多路复用是指一个线程同时监听多个文件描述符,当某个 fd 就绪时,内核通知应用程序,再由应用执行读写操作,从而避免"一个连接对应一个线程"。
常见实现有:
- select :每次遍历所有 fd,时间复杂度为
O(n),存在 fd 数量限制。 - poll:同样需要遍历所有 fd,但没有固定的 fd 数量限制。
- epoll:内核维护就绪事件,避免每次遍历全部 fd,适合大量并发连接;支持水平触发和边缘触发,边缘触发通常配合非阻塞 fd 使用。
它常用于网络服务器的 Reactor 事件循环模型。
Redis缓存穿透、击穿、雪崩是什么意思?
-
缓存穿透:请求查询一个缓存和数据库中都不存在的数据,缓存一直无法命中,请求持续打到数据库。
-
缓存击穿:某个热点数据过期的瞬间,大量并发请求同时访问数据库。
-
缓存雪崩 :大量缓存 key 同时过期,或者缓存服务整体故障,导致大量请求同时访问数据库。
解决:过期时间加随机值、缓存预热、 Redis 集群和多级缓存,以及限流、熔断和降级。
TCP三次握手和四次挥手的过程?
三次握手用于建立 TCP 连接:
- 客户端发送
SYN=1,seq=x,进入SYN_SENT。 - 服务端回复
SYN=1,ACK=1,seq=y,ack=x+1,进入SYN_RECV。 - 客户端回复
ACK=1,ack=y+1,双方进入ESTABLISHED。
这样可以确认双方的发送和接收能力正常,也能同步初始序列号。
四次挥手用于断开 TCP 连接:
- 主动关闭方发送
FIN,表示自己没有数据要发送。 - 对方回复
ACK,此时只能确认收到 FIN,连接仍可能继续发送数据。 - 对方数据发送完后再发送
FIN。 - 主动关闭方回复
ACK,进入TIME_WAIT,等待2MSL后关闭。
TCP 是全双工通信,两个方向需要分别关闭,因此通常需要四次挥手。
HTTP状态码的分类含义?
- 1xx:信息响应 ,表示请求已收到,服务器正在处理,例如
100 Continue。 - 2xx:成功 ,表示请求已成功处理,例如
200 OK、201 Created。 - 3xx:重定向 ,表示需要进一步操作,例如
301永久重定向、302临时重定向、304使用缓存。 - 4xx:客户端错误 ,表示请求本身有问题,例如
400请求错误、401未认证、403禁止访问、404资源不存在。 - 5xx:服务器错误 ,表示服务器处理请求失败,例如
500内部错误、502网关错误、503服务不可用。