亚信安全AI面试
一、I/O多路复用
1 什么是I/O多路复用?它解决了什么问题?相比传统的阻塞I/O有什么优势?
定义: I/O 多路复用(I/O Multiplexing)是一种 允许单个线程或进程同时监听多个文件描述符(如 socket) 的技术。当其中某个描述符就绪(如有数据可读/可写)时,就通知应用程序进行相应操作,而不需要为每个连接单独创建线程或进程。
它解决的问题:
- 在传统阻塞 I/O 模型中,每个连接都要占用一个线程或进程;大量连接时会导致线程数爆炸、上下文切换频繁。
- I/O 多路复用允许使用少量线程同时管理大量连接(如数万长连接)。
优势:
| 对比项 | 阻塞 I/O | I/O 多路复用 |
|---|---|---|
| 线程/进程 | 每连接一个 | 少量线程监听多个连接 |
| 资源消耗 | 高(线程多) | 低(少量线程) |
| 适用场景 | 少连接场景 | 高并发连接场景 |
| 响应效率 | 阻塞等待 | 事件驱动,及时响应 |
典型应用:Nginx、Redis、Netty、libevent、libuv。
2 I/O多路复用是如何实现同时监听多个文件描述符的?它的底层机制是什么?
- 操作系统内核为每个文件描述符维护一个事件状态(如是否可读/可写)。
- 程序通过系统调用(如
select、poll、epoll)向内核注册要监听的多个 fd。 - 内核在某个 fd 状态变化时(比如数据可读),将其放入就绪队列。
- 系统调用返回就绪的 fd 列表,应用程序只需对就绪的 fd 进行 I/O 操作。
底层机制核心:
- 利用操作系统内核的事件通知机制(如 epoll 的事件回调机制)。
- 不需要轮询所有 fd,提高性能。
- 事件驱动模型避免无效等待和资源浪费。
3 select/poll/epoll的区别?
| 特性 | select | poll | epoll |
|---|---|---|---|
| 出现时间 | 最早 | 改进版 select | 高效版本 |
| FD 上限 | 有(一般 1024) | 无明显限制 | 无明显限制 |
| 内核态实现 | 轮询 | 轮询 | 事件驱动 |
| 时间复杂度 | O(n) | O(n) | O(1) |
| 使用方式 | 每次调用传入全部 fd | 每次传入全部 fd | 通过 epoll_ctl 注册一次 |
| 性能 | 低 | 一般 | 高 |
| 边缘触发 | 不支持 | 不支持 | 支持 ET、LT |
epoll 在大量连接且活跃连接较少时性能优势明显。
4 epoll是否适用于所有场景?
不完全适用。
适用场景:
- 高并发连接,活跃连接较少(如 IM、消息推送)。
- I/O 密集型应用(如 Web 服务器、网关、代理)。
不适用场景:
- 连接数少但活跃度高,select/poll 也能满足。
- 业务逻辑复杂时,epoll 的事件驱动编程模型维护成本较高。
- Windows 平台不支持 epoll(Windows 用 IOCP)。
二、ArrayList和LinkedList
1 ArrayList和LinkedList的区别?
| 对比项 | ArrayList | LinkedList |
|---|---|---|
| 底层结构 | 动态数组 | 双向链表 |
| 随机访问 | O(1) | O(n) |
| 插入/删除 | 可能 O(n),需移动元素 | O(1),不需移动元素 |
| 占用内存 | 较少 | 较多(需存储前后节点引用) |
| 是否线程安全 | 否 | 否 |
2 在高并发场景下,选择ArrayList还是LinkedList?
一般选择 ArrayList:
- ArrayList 的查询效率高。
- LinkedList 在高并发环境下频繁修改结构,GC 压力较大。
如果是频繁插入删除、链表特性更适用,但在高并发下都不推荐直接使用非线程安全的集合类。
3 在高并发场景下使用,如何保证线程安全?
方式:
-
使用
Collections.synchronizedList()包装:List<String> list = Collections.synchronizedList(new ArrayList<>()); -
使用
CopyOnWriteArrayList替代:- 适合 读多写少 的场景。
-
自行加锁(如
ReentrantLock或synchronized)。 -
使用并发集合框架(如
ConcurrentLinkedQueue/ConcurrentSkipListMap)。
4 CopyOnWriteList
CopyOnWriteArrayList 是一种 写时复制 的线程安全 List 实现。
写操作时会拷贝原数组,新数组写入后再替换。
读操作无需加锁,性能高。
适合读多写少的场景,比如缓存、白名单列表。
三、Fluentd
1 Fluentd的主要作用?
Fluentd 是一个开源的 日志收集与分发系统。
统一收集不同来源的日志,结构化处理后再发送到目标系统(如 Elasticsearch、Kafka、S3)。
优点:
- 插件丰富(input/output/filter)。
- 易于扩展和管理。
- 支持高可用和容错机制。
2 分布式系统中,日志格式和来源非常多样化,Fluentd如何处理这些差异?
- 多输入插件(Input Plugin):支持 tail、syslog、http、kafka 等。
- Parser 机制:可通过正则、JSON、LTSV、nginx/apache 格式解析。
- Filter 插件:标准化字段、增加标签、结构化数据。
- Tag + Route:根据日志类型灵活路由到不同输出目标。
👉 这样即便来源格式不同,也能实现统一处理与转发。
3 如果日志量激增,Fluentd在传输过程中出现延迟和丢失,如何应对?
常见手段:
- 增加 buffer(缓冲):
- Fluentd 支持内存/文件 buffer。
- 使用 file buffer 可以避免丢失。
- 集群部署 Fluentd:
- 多节点负载均衡,分摊压力。
- 调优 flush_interval / retry_wait 等配置。
- 上游限流:
- 防止 Fluentd 过载。
- 采用 Fluent Bit 作为边车(sidecar):
- Fluent Bit 更轻量,适合高吞吐场景,再转发给 Fluentd。
四、MySQL索引
1 什么是聚簇索引和非聚簇索引?在InnoDB存储引擎中,主键索引是哪一种?
- 聚簇索引(Clustered Index):
- 数据和索引存储在一起。
- 叶子节点存储的是数据本身。
- InnoDB 的主键索引就是聚簇索引。
- 优点:查询效率高,范围查询快。
- 非聚簇索引(Secondary Index):
- 叶子节点存储的是主键值,而不是数据。
- 查询时需要“回表”查真实数据。
👉 例如:
select * from user where id=1; -- 主键查询,不回表
select * from user where name='张三'; -- 非主键查询,要先查索引,再回表
2 如果一个表没有定义主键,会发生什么?InnoDB如何处理?
InnoDB 会自动选择:
- 第一个非空唯一索引 作为聚簇索引;
- 如果没有唯一索引,会 自动生成一个隐藏的 RowID 作为聚簇索引。
这种情况不利于数据管理和性能调优。 ✅ 建议总是为表定义主键。
五、加签验签
SHA256和RSA算法
- SHA256:
- 是一种 不可逆 的哈希算法。
- 常用于数据摘要和签名时的摘要部分。
- 例如对请求体计算 SHA256 哈希,保证数据完整性。
- RSA:
- 是一种 非对称加密算法。
- 分为公钥和私钥。
- 加签时用 私钥签名,验签时用 公钥验证。
- 常与 SHA256 配合使用(如
SHA256withRSA):- 先对数据做 SHA256 摘要。
- 再用 RSA 私钥对摘要加密生成签名。
- 验签时用公钥解密签名,比对摘要一致性。
👉 这种方式确保:
- 防篡改(数据一致性)
- 身份认证(只有私钥持有者能签名)
- 不可否认性(签名不可伪造)