1. 数据库:不只是存数据的仓库
很多同学一提到数据库,脑子里蹦出来的可能就是“增删改查”四个字。确实,这是数据库最基础的操作,但面试官想听的远不止这些。他们想考察的是,你是否真正理解了数据是如何被组织、管理和保护的。我当年面试时,就曾被一位老师追问:“如果让你设计一个微博的关注系统,用户表和关注关系表该怎么设计?怎么保证高并发下的读写效率?” 一下子就把我从书本概念拉到了实战场景。
数据库的核心,在我看来,是如何在保证数据正确、安全的前提下,还能让成千上万人同时快速访问。这背后是一整套精密的机制。
1.1 从“三层模式”看数据库的抽象艺术
面试高频题:“说说数据库的三层模式和两级映射,它们有什么用?” 别死记硬背,我给你打个比方。
想象一家大型图书馆(这就是整个数据库系统)。
- 内模式(存储模式):相当于图书馆的仓库管理员视角。他关心书具体怎么放:是按出版年份排架,还是按作者姓氏拼音?用的是铁书架还是木书架?索引卡片怎么做?这些物理存储的细节,普通读者完全不用管。
- 模式(逻辑模式):相当于图书馆的总目录。它定义了有哪些类型的书(比如文学、历史、科学),每类书包含哪些信息(书名、作者、ISBN号)。这是数据库管理员(DBA)设计的全局逻辑结构。
- 外模式(用户模式):相当于某个特定读者能看到的视图。一个研究历史的读者,可能只看到历史区的书目列表;一个借阅小说的读者,看到的又是另一个列表。这就是从总目录中“剪裁”出来的、给特定应用程序或用户使用的局部逻辑结构。
那“两级映射”就是连接这三层的桥梁:
- 外模式/模式映射:当图书馆总目录(模式)调整了,比如新增了“人工智能”分类。管理员只需要修改一下历史读者视图的映射规则,告诉他“人工智能”类的书现在在哪看,而历史读者本身的视图(外模式)可以完全不变。这就是逻辑独立性——底层逻辑变了,上层的应用程序不用改。
- 模式/内模式映射:当图书馆决定把木书架全换成更结实的铁书架(改变存储结构),管理员只需要调整总目录和仓库存储方式的对应关系。而总目录(模式)本身的内容,比如书的分类和字段,可以保持不变。这就是物理独立性——物理存储方式变了,上面的逻辑结构可以不变。
面试点睛:回答这个问题时,一定要点出“独立性”这个关键词。数据库设计这三层,核心目的就是为了让数据存储的物理细节、全局逻辑结构的变化,尽量不影响上层的应用程序和用户视图,极大地提升了系统的可维护性和可扩展性。
1.2 事务与并发控制:多人同时订同一张票怎么办?
这是数据库面试的必考重灾区,也是区分“背课本”和“真理解”的关键。核心就四个字母:ACID。
- A(原子性):事务里的操作,要么全做完,要么全不做。就像银行转账,A扣钱和B加钱必须作为一个整体。实现靠的是日志(Log),特别是Undo Log。如果事务中途失败,系统会根据Undo Log把已经执行的操作“回滚”回去。
- C(一致性):事务执行前后,数据库都必须处于一致的状态。比如转账前后,A和B的账户总额应该不变。这是原子性、隔离性和持久性共同要保证的目标。
- I(隔离性):多个事务同时执行,感觉起来就像是一个一个串行执行的一样。这是最难保证的一点,因为完全串行化性能太差,所以数据库提供了不同的隔离级别(读未提交、读已提交、可重复读、串行化)来在性能和正确性之间做权衡。
- D(持久性):事务一旦提交,它对数据的修改就是永久性的,即使系统崩溃也不会丢失。实现靠的是Redo Log。提交前,先把要做的修改写到Redo Log里(顺序写,很快),哪怕数据还没真正写到磁盘数据库文件里。即使这时宕机,重启后也能根据Redo Log重新执行一遍操作,保证数据不丢。
并发带来的三大经典问题:
- 脏读:事务A读到了事务B修改但未提交的数据,然后B回滚了,A读到的就是无效的“脏数据”。
- 不可重复读:事务A多次读取同一数据,在此期间事务B修改并提交了该数据,导致A多次读取的结果不一致。
- 幻读:事务A按某个条件查询一批数据,事务B插入或删除了符合该条件的新数据并提交,导致A再次查询时,仿佛出现了“幻觉”,多了或少了几行数据。
面试点睛:务必搞清楚这三个问题的区别。简单记:脏读是读到了会“消失”的数据;不可重复读是针对某一行数据的修改;幻读是针对数据行数的增减。常用的锁机制(共享锁S锁、排他锁X锁)和多版本并发控制(MVCC, InnoDB引擎的核心) 就是用来解决这些问题的。你可以说:“MySQL的InnoDB引擎在‘可重复读’隔离级别下,通过MVCC和间隙锁(Next-Key Lock)的组合,基本上解决了幻读问题。”
1.3 SQL优化:你的查询为什么慢?
“我会写SQL”和“我会写高效的SQL”是两码事。面试官给你一个慢查询,问你如何优化,是常规操作。
几个实战中的排查和优化思路:
- 用
EXPLAIN看看执行计划:这是第一步。关注type列(访问类型,从好到坏:system>const>eq_ref>ref>range>index>ALL),key列(实际用到的索引),rows列(预估扫描行数)。如果type是ALL(全表扫描)或者rows巨大,就要警惕了。 - 索引是王道,但别乱用:
- 最左前缀原则:对于复合索引
(a, b, c),查询条件必须包含a,才能用到这个索引。WHERE b=1 AND c=2就用不上。 - 避免对索引列做计算或函数操作:
WHERE YEAR(create_time) = 2023会导致索引失效,应改为WHERE create_time >= '2023-01-01' AND create_time < '2024-01-01'。 - 索引不是越多越好:索引会占用空间,降低写操作(INSERT/UPDATE/DELETE)的速度,因为要维护索引树。
- 最左前缀原则:对于复合索引
- 警惕
SELECT *:只取需要的字段。特别是当表里有TEXT、BLOB大字段时,SELECT *会导致大量不必要的磁盘I/O和网络传输。 - 联表查询的学问:
- 确保联表字段有索引。
- 用小表驱动大表(MySQL的Nested Loop Join机制下,外层循环的表越小越好)。
- 考虑是否能用
JOIN代替子查询,或者反过来,具体要看执行计划。
- 分页查询优化:
LIMIT 100000, 20这种深度分页会非常慢,因为它要先扫描前100000条记录再丢弃。优化方法可以是利用覆盖索引+子查询,或者记录上一页最后一条记录的ID,用WHERE id > last_id LIMIT 20。
面试点睛:结合具体例子说。比如:“我遇到过一张用户表按注册时间分页查询特别慢,用EXPLAIN发现是filesort(文件排序)导致的。后来在register_time和查询条件上建了联合索引,让排序和筛选都能走索引,速度就上来了。”
2. 数据结构:算法的基石,编程的内功
数据结构决定了数据如何组织,算法决定了如何处理这些数据。面试中,数据结构的问题往往和算法题绑定出现。但别慌,面试官更看重的是你思考的过程和对基础数据结构的理解深度,而不是你能否秒杀LeetCode Hard。
2.1 线性结构:链表、栈与队列的妙用
链表逆置是经典考题。原地逆置单链表的代码很短,但体现了指针操作的精华:
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode *prev = NULL, *curr = head, *next = NULL;
while (curr != NULL) {
next = curr->next; // 暂存下一个节点
curr->next = prev; // 当前节点指向前一个
prev = curr; // 前驱指针后移
curr = next; // 当前指针后移
}
return prev; // 新的头节点
}
关键点:需要三个指针prev、curr、next,分别指向前一个、当前、后一个节点。在断开curr->next之前,一定要用next保存好下一个节点,否则链表就断了。
栈和队列:理解它们的特性比死记实现更重要。
- 栈(后进先出,LIFO):函数调用栈、括号匹配、表达式求值(逆波兰表达式)、浏览器的前进后退。
- 队列(先进先出,FIFO):消息队列、线程池任务队列、广度优先搜索(BFS)。
- 循环队列:为什么顺序队列要留一个空位?这是为了区分队列“空”和“满”的状态。当
(rear + 1) % size == front时,我们认为队列满了;当rear == front时,队列为空。这个空位就是哨兵。
2.2 树:从二叉树到多路平衡
二叉排序树(BST):它的中序遍历结果是一个有序序列。这是它的核心性质。查找、插入、删除的平均时间复杂度是O(log n),但最坏情况(树退化成一条链)会变成O(n)。所以面试官常会接着问:“怎么避免退化?” 这就引出了平衡二叉树(AVL树)。
平衡二叉树(AVL树):它通过旋转操作(LL型右旋、RR型左旋、LR型先左后右、RL型先右后左)来保证任意节点的左右子树高度差不超过1。插入节点后,从插入点向上找,第一个不平衡的节点就是“最小不平衡子树”,对它进行旋转。旋转的目的是在保持BST性质的前提下,让树重新平衡。
红黑树:它是工程中更常用的近似平衡二叉搜索树(例如Java的TreeMap, C++的map)。它不像AVL树那样严格平衡,而是通过5条规则来保证从根到叶子的最长路径不会超过最短路径的2倍,从而在维护成本和查询效率之间取得了更好的平衡。规则包括:节点是红或黑;根是黑;叶子(NIL)是黑;红节点的子节点必须是黑;从任一节点到其每个叶子的所有路径包含相同数目的黑节点。
B树与B+树:当数据量大到内存放不下,必须存在磁盘上时,二叉树的深度可能很大,导致多次磁盘I/O,性能急剧下降。B树是一种多路平衡搜索树,一个节点可以有很多孩子(比如几百个),这样树的高度就大大降低了(通常只有3-4层)。B+树是B树的变种,在数据库索引(如MySQL的InnoDB)和文件系统中应用极广。它与B树的主要区别是:
- 非叶子节点只存键,不存数据,这样每个节点能存更多的键,树更矮胖。
- 叶子节点包含了全部键和数据,并且叶子节点之间用指针串联成一个有序链表。这使得范围查询和全表扫描效率极高,因为不需要回溯到上层节点。
面试点睛:谈到数据库索引,一定要能说出B+树的名字,并解释为什么用它而不用哈希表(哈希表不支持范围查询)或二叉树(磁盘I/O次数多)。
2.3 图:如何表示复杂关系?
图的存储就两种主流方式:
- 邻接矩阵:用一个二维数组
matrix[i][j]表示顶点i到j是否有边(或边的权值)。适合稠密图,检查两点间是否有边是O(1),但浪费空间。 - 邻接表:为每个顶点维护一个链表,存储它所有邻接的顶点。适合稀疏图,节省空间,但检查两点间是否有边需要O(degree(V))。
图的遍历:
- 深度优先搜索(DFS):像走迷宫,一条路走到黑,碰壁再回溯。用栈(递归本身就是栈)实现。常用于拓扑排序、寻找连通分量、解决回溯问题。
- 广度优先搜索(BFS):像水波扩散,一层一层向外。用队列实现。常用于求无权图的最短路径、状态搜索(如走迷宫的最少步数)。
最短路径:
- Dijkstra算法:求单源最短路径,权值不能为负。核心是贪心,每次从未确定的点中选一个距离源点最近的,用它去松弛其他点的距离。
- Floyd算法:求任意两点间的最短路径。动态规划思想,三层循环,
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])。
最小生成树:
- Prim算法:从一个点开始,每次找连接“已选点集”和“未选点集”的最小权值边,把该边连接的未选点加入集合。适合稠密图。
- Kruskal算法:把所有边按权值排序,从小到大挑,如果这条边连接的两个点不在同一个连通分量里(用并查集判断),就选中它。适合稀疏图。
面试点睛:手写DFS/BFS的代码是基础。对于算法,要能说清其思想、适用场景和时间复杂度。比如被问到“地图导航怎么实现最短路径?” 你可以说:“在道路权值(时间、距离)非负的情况下,通常用Dijkstra算法;如果需要考虑实时路况(拥堵权重),可能要用更复杂的A*算法。”
3. 操作系统:计算机系统的“大管家”
操作系统管理着所有硬件和软件资源。面试问题往往围绕进程、内存、文件系统这三大核心展开。
3.1 进程与线程:多任务的核心
进程是资源分配的基本单位,线程是CPU调度的基本单位。这是最本质的区别。
- 一个进程崩溃,一般不会影响其他进程,因为各有各的地址空间。但一个线程崩溃,整个进程都可能挂掉,因为它们共享内存。
- 创建进程开销大(要分配独立的内存空间、文件描述符等),线程开销小。
- 进程间通信(IPC)复杂,有管道、消息队列、共享内存、信号量、套接字等方式。线程间通信简单,直接读写共享变量即可,但需要同步机制(互斥锁、条件变量等)来防止竞态条件。
进程状态转换:就绪 -> 运行 -> 阻塞 -> 就绪。调度算法决定了谁从就绪态进入运行态。
- 先来先服务(FCFS):简单,但可能导致短作业等待长作业。
- 短作业优先(SJF):平均等待时间最短,但可能让长作业“饿死”。
- 高响应比优先:响应比 = (等待时间 + 要求服务时间) / 要求服务时间。兼顾了等待时间和作业长度。
- 时间片轮转(RR):公平,响应快,是分时系统的核心。
- 多级反馈队列:综合了以上算法的优点。设置多个优先级不同的队列,新进程进最高优先级队列。每个队列内用RR调度。进程用完一个时间片还没完,就降到下一级队列。这样既能保证短作业快速响应,又不会让长作业完全得不到执行。
死锁:四个必要条件(互斥、占有并等待、不可抢占、循环等待)必须同时成立。解决死锁有预防(破坏条件)、避免(银行家算法)、检测与解除(资源剥夺、撤销进程)几种策略。
3.2 内存管理:虚拟内存的魔法
为什么我们写的程序感觉有“用不完”的内存?这就是虚拟内存的功劳。它让每个进程都以为自己独占整个内存空间,实际物理内存可能只有4GB。
核心机制:
- 分页:把物理内存和进程的虚拟地址空间都切成固定大小的“页”(比如4KB)。操作系统维护一张页表,记录虚拟页号到物理页框号的映射。
- 缺页中断:当程序访问一个不在物理内存中的虚拟页时,CPU会触发缺页中断。操作系统介入,从磁盘的交换区(Swap) 把需要的页调入内存,如果内存满了,还要根据页面置换算法淘汰一个旧页出去。
- 快表(TLB):页表放在内存里,每次地址转换都要多访存一次,太慢。于是在CPU里搞了个小缓存——快表,存放最近用到的页表项。命中TLB就飞快,不命中才去查内存中的页表。
经典的页面置换算法:
- 最佳置换(OPT):淘汰未来最长时间不再被访问的页面。这是理想情况,无法实现,用作评价标准。
- 先进先出(FIFO):淘汰最早进入的页面。可能产生Belady异常(分配的物理页框增多,缺页率反而上升)。
- 最近最久未使用(LRU):淘汰最久没被访问的页面。效果接近OPT,但实现开销大(需要硬件支持记录访问时间戳或维护一个访问栈)。
- 时钟(CLOCK)算法:LRU的近似。给每个页设一个访问位。淘汰时,像时钟指针一样扫描,如果访问位是1,清0并跳过;如果是0,就淘汰它。这是工程中常用的折中方案。
面试点睛:可以结合Linux的free命令输出聊聊。free命令显示的总内存、已用内存、空闲内存、缓冲/缓存内存,以及Swap的使用情况,就是虚拟内存机制在现实系统中的直观体现。缓存(Cache)是为了加速读,缓冲(Buffer)是为了平滑写。
3.3 文件系统与I/O:数据如何持久化?
文件系统解决了如何在磁盘上组织文件和目录,以及如何高效存取的问题。
文件的物理结构(磁盘块怎么组织):
- 连续分配:文件占用的磁盘块是连续的。读写快,支持随机访问,但会产生外部碎片,文件不易增长。
- 链接分配:每个磁盘块里存着下一个块的指针。解决了外部碎片和增长问题,但随机访问慢,可靠性差(一个指针坏了全完)。
- 索引分配:单独拿一个或多个索引块,里面存放文件所有数据块的地址。完美支持随机访问和动态增长。Unix/Linux的inode(索引节点)就是这种思想的体现,它里面存了文件的元数据(权限、所有者、时间戳等)和直接指针、间接指针。
磁盘调度算法:目标是减少磁头移动距离。
- 先来先服务(FCFS):公平,但效率可能很低。
- 最短寻道时间优先(SSTF):找离当前磁头最近的请求。可能产生“饥饿”。
- 扫描算法(电梯算法,SCAN):磁头在一个方向上移动,处理所有请求,到头后再掉头。避免了饥饿。
- 循环扫描(C-SCAN):SCAN的变种,只朝一个方向服务,到头后直接跳到另一端开始,回来路上不服务。提供了更均匀的等待时间。
面试点睛:可以提一下“RAID”(磁盘阵列)。比如RAID 0(条带化,提速但不安全)、RAID 1(镜像,安全但容量减半)、RAID 5(分布式奇偶校验,兼顾性能、容量和安全性)。这是保证数据可靠性和提升I/O性能的常用硬件/软件方案。
4. 计算机网络:让世界连通的协议
计算机网络的知识点非常体系化,从底层物理信号到顶层应用协议,层层封装。面试常从TCP/IP模型或OSI模型切入。
4.1 网络层:IP与路由
IP协议是无连接、不可靠的尽力交付服务。可靠交给上层(TCP)去保证。IPv4地址枯竭的解决方案:
- NAT(网络地址转换):让局域网内大量设备共享一个公网IP。路由器维护一张映射表,把内网设备的(私有IP:端口)映射成(公网IP:端口)。这是目前最主要的过渡技术。
- IPv6:终极方案,地址长度128位,号称“地球上的每粒沙子都能分到一个IP”。除了地址空间巨大,还简化了报文头、支持更好的安全性和移动性。
路由协议:路由器之间如何“打招呼”来知道整个网络的拓扑。
- RIP:基于距离向量,“跳数”作为度量。最大跳数15,只适合小网络。每隔30秒广播整个路由表,收敛慢。
- OSPF:基于链路状态。每个路由器把自己周边的链路状态“广播”给全网所有路由器,这样每个路由器都能有一张完整的网络地图,然后用Dijkstra算法自己算最短路径。收敛快,适合大型网络。
- BGP:边界网关协议,用于不同自治系统(AS)之间。它不是找最短路径,而是找“最好”的路径(策略路由,考虑运营商合约、政治因素等)。
4.2 传输层:TCP与UDP的哲学
这是网络面试的核心中的核心。
TCP vs UDP:
- TCP:面向连接、可靠、字节流、有拥塞控制。像打电话,要先建立连接,保证对方听到,顺序不乱。用于HTTP、FTP、邮件等。
- UDP:无连接、不可靠、数据报、无拥塞控制。像寄明信片,写好地址扔进邮筒,不保证对方收到,也不保证顺序。用于DNS查询、视频直播、语音通话等实时性要求高的场景。
TCP三次握手与四次挥手:
- 三次握手:Client发送
SYN=1, seq=x;Server回复SYN=1, ACK=1, seq=y, ack=x+1;Client再回复ACK=1, seq=x+1, ack=y+1。目的是同步双方的初始序列号,并确认双方的收发能力都正常。为什么不是两次?主要是防止已失效的连接请求报文突然又传到服务器,导致服务器误开连接。 - 四次挥手:双方都可以主动关闭。假设Client先发起:Client发
FIN;Server回ACK;Server发FIN;Client回ACK。为什么需要四次?因为TCP是全双工的,一方发送FIN只表示它没有数据要发了,但还可以收数据。所以关闭需要两个方向分别进行。
TCP的可靠传输与流量控制:
- 可靠:通过序列号、确认应答、超时重传来实现。每个字节都有编号,接收方按序确认,发送方收不到确认就重传。
- 流量控制:接收方通过TCP头部的窗口大小字段,告诉发送方“我还能收多少”。这是为了防止发送方发得太快,把接收方缓冲区撑爆。
- 拥塞控制:防止发送方发得太快,把网络链路撑爆。核心是维护一个拥塞窗口(cwnd)。经典算法包括:慢启动(指数增长)、拥塞避免(线性增长)、快重传(收到3个重复ACK就立刻重传,而不等超时)、快恢复(cwnd减半后直接进入拥塞避免,而不是慢启动)。
面试点睛:务必能画图说明三次握手和四次挥手的状态变迁。被问到“TIME_WAIT状态为什么需要等待2MSL(最大报文段生存时间)?”要能回答:1. 保证最后一个ACK能到达对方(如果丢了,对方会重发FIN)。2. 让本次连接产生的所有报文都在网络中消失,避免影响后续的新连接。
4.3 应用层:我们每天都在用的协议
- HTTP:无状态协议。1.1版本引入了持久连接(一个TCP连接可以传多个请求-响应)和管道化(不必等上一个响应回来就可以发下一个请求)。HTTPS就是在HTTP和TCP之间加了一层SSL/TLS,用于加密和身份认证。
- DNS:域名到IP的映射。采用分布式、层次化的数据库。查询过程:浏览器缓存 -> 系统hosts文件 -> 本地DNS服务器 -> 根DNS -> 顶级域DNS -> 权威DNS。常用UDP协议,端口53。
- 从输入URL到页面显示发生了什么? 这是一个经典的综合问题。涵盖DNS解析、TCP连接、HTTP请求、服务器处理、HTTP响应、浏览器解析HTML、构建DOM树和CSSOM树、合并成渲染树、布局、绘制、显示等全过程。回答时要条理清晰。
5. 计算机组成原理:软件之下的硬件世界
这门课连接了数字电路和操作系统,解释了代码是如何在硬件上跑起来的。
5.1 冯·诺依曼体系结构:现代计算机的蓝图
核心思想就两点:存储程序和二进制。计算机由五大部件组成:运算器、控制器、存储器、输入设备、输出设备。其中,运算器和控制器合起来就是我们现在说的CPU。
程序是如何运行的?
- 取指令:控制器根据程序计数器(PC) 里的地址,从内存中取出指令。
- 译码:控制器分析指令的操作码,知道要做什么操作(比如加法)。
- 执行:控制器发出控制信号,指挥运算器从寄存器或内存中取出操作数,进行运算。
- 回写:将运算结果写回寄存器或内存。
- 更新PC:PC指向下一条指令地址(通常是顺序+1,遇到跳转指令则改变)。
这就是指令周期,它由若干个更小的机器周期(如取指周期、执行周期)组成,而每个机器周期又包含多个时钟周期(CPU的主频倒数)。
5.2 存储器层次结构:为什么要有Cache?
CPU速度极快,而内存速度相对慢很多。为了弥补这个速度鸿沟,计算机采用了金字塔型的存储层次:
- 寄存器:在CPU内部,速度最快,容量最小(以字节计)。
- 高速缓存(Cache):分L1、L2、L3,用SRAM实现,速度快,成本高。
- 主存(内存):用DRAM实现,速度较慢,容量大。
- 外存(磁盘/SSD):速度慢,容量巨大,持久化。
Cache的工作原理基于局部性原理:
- 时间局部性:刚被访问的数据,很可能很快又被访问(比如循环变量)。
- 空间局部性:访问了某个存储单元,其附近的单元也可能很快被访问(比如数组遍历)。
Cache把内存中的数据以“块”为单位拷贝过来。CPU访存时,先看Cache里有没有(Cache命中),有就直接用,没有(Cache缺失)再去内存取,并按照某种策略(如LRU)替换掉Cache里的一块旧数据。Cache和内存的映射方式有直接映射、组相联映射、全相联映射。
5.3 I/O系统:CPU如何与外界对话?
I/O设备千差万别,速度慢,CPU不能一直等着。所以有了几种工作方式:
- 程序查询方式:CPU不断轮询设备状态,“忙等待”,效率极低。
- 程序中断方式:CPU启动I/O后就去干别的,设备准备好后发一个中断信号给CPU,CPU保存当前现场,转去处理I/O,处理完再恢复现场继续工作。提高了CPU利用率。
- DMA方式:对于磁盘这类需要传输大量数据块的设备,中断次数还是太多。于是让DMA控制器这个硬件来负责数据在内存和I/O设备间的直接传输,传输完再通知CPU。整个过程CPU只在开始和结束时介入,进一步解放了CPU。
- 通道方式:可以看作功能更强的DMA,能执行简单的通道程序,管理多个I/O设备。
面试点睛:被问到“CPU和磁盘速度差异这么大,系统怎么优化?”可以提到:1. 使用缓冲区(Buffer)来平滑数据流。2. 使用DMA减少CPU干预。3. 磁盘本身通过磁盘调度算法(如电梯算法)减少寻道时间。4. 在操作系统层面,通过页面缓存(Page Cache)将频繁读写的磁盘数据缓存在内存中。
5万+



被折叠的 条评论
为什么被折叠?



