问2:磁盘坏了吗?
磁盘坏了 → 介质故障
磁盘没坏 → 排除介质故障
问3:是单个事务出错,还是整个系统停摆?
单个事务 → 事务故障
整个系统停了 → 系统故障
具体案例判断思路
案例1:服务器突然断电
内存数据丢失 → 系统故障
磁盘没坏 → 不是介质故障
重启后需要 REDO+UNDO
✅ 系统故障
案例2:磁盘磁头损坏,数据读不出来
存储介质物理损坏 → 介质故障
内存数据可能还在(如果事务没提交,内存可能没影响) → 不是系统故障(系统没崩)
✅ 介质故障
案例3:某个事务执行时除0,被系统强制终止
单个事务出错 → 事务故障
内存数据还在 → 不是系统故障
磁盘没坏 → 不是介质故障
只需要 UNDO 这个事务
✅ 事务故障
案例4:数据库在写入日志时突然停电
内存数据丢失 → 系统故障
日志文件可能部分写入磁盘,恢复时要用日志做 REDO/UNDO
✅ 系统故障
断电 → 选 系统故障(除非题目说磁盘也坏了)
死锁 → 选 事务故障(虽然死锁由系统解决,但对事务来说是故障)
磁盘坏道 → 选 介质故障
系统崩溃 → 选 系统故障
断点和断电,甭管是不是写错的都是系统故障
各个功能模块用不同语言编写 → 分别编译成目标模块(.obj / .o 文件)。
目标模块还不能直接运行,需要链接(Link)成可执行文件。
链接主要完成:符号解析、地址重定位、模块合并等。
LIKE '王%' → 第一个字是“王”,后面任意字符
LIKE '%王%' → 只要名字中包含“王”字,不管在什么位置
正负0的补码是相同的,正负0的移码是相同的


攻击者(称为“钓鱼者”)会伪装成你信任的人或机构(比如银行、电商平台、同事、政府机构等),通过发送虚假邮件、短信或创建假冒网站,诱导你交出敏感信息(如账号密码、身份证号、信用卡号)或在你的设备上安装恶意软件。
阻塞→运行,是不被允许的
因为现代操作系统为了让系统更稳定、更安全,会把每个进程隔离在各自独立的内存空间里——就像一个房间只住一个人,他们不能直接看到对方屋里的东西。但有时候进程之间又需要协作(比如浏览器进程要把下载的数据交给播放器进程去播放),这时候就需要 IPC 来帮忙“传话”或“递东西”。
就是进程间的电话或者传声筒
根据场景不同(是要传少量控制信息,还是要传大批量数据,或者只是想同步一下动作),IPC 有几种主流实现方式:
- 管道(Pipe)
是什么:一种半双工(单向)的数据流,像一个水管,一头接进程 A,一头接进程 B。
场景:最典型的就是命令行里的 | 符号。比如 ps aux | grep nginx,就是把 ps 进程的输出接到 grep 进程的输入里。
特点:简单,通常用于有亲缘关系的进程(比如父子进程)。 - 消息队列(Message Queue)
是什么:进程可以把数据封装成一个“消息”(有类型、有内容),扔到一个队列里,另一个进程再从队列里取。
场景:适用于需要异步通信的场景,比如服务器接收客户端的请求,先放进队列,慢慢处理。
特点:解耦,消息有边界,不会像管道那样只是字节流。 - 共享内存(Shared Memory)
是什么:划出一块内存区域,让多个进程都能直接读写。这是速度最快的 IPC,因为没有数据复制的开销。
场景:需要大量数据交换的时候,比如视频渲染、数据库缓存。
特点:快,但需要配合信号量之类的同步机制使用,否则多个进程同时写会乱套。 - 信号(Signal)
是什么:一种异步通知机制,一个进程可以给另一个进程发一个简单的“信号”,告诉它发生了某件事(比如 Ctrl+C 就是发 SIGINT 信号)。
场景:用于事件通知、异常处理、进程控制。
特点:携带信息量很少,只能发个编号,不能传大量数据。 - 套接字(Socket)
是什么:本来设计用于网络通信,但也可以在同一台机器的不同进程间通信(Unix Domain Socket)。
场景:最通用的 IPC,特别是跨网络的进程通信,比如 Web 服务器和浏览器之间。
特点:支持双向通信,可用于不同机器,但开销比共享内存大。 - 信号量(Semaphore)
是什么:本质上是一个计数器,用于进程间同步,而不是直接传数据。用来协调多个进程对共享资源的访问。
场景:配合共享内存使用,防止竞争条件。
特点:只用来同步,不传数据。
- 阻塞型同步原语
互斥锁(阻塞版本):例如 pthread_mutex_lock 的默认行为。如果锁已被占用,线程会挂起(进入睡眠),不消耗CPU,直到锁被释放后由内核调度唤醒。
信号量(阻塞版本):当信号量计数值为0时,执行 P 操作的进程会阻塞,加入等待队列,不会轮询。
条件变量:pthread_cond_wait 会将线程阻塞,并释放已持有的互斥锁。当条件满足时,通过 pthread_cond_signal 唤醒线程。整个过程没有忙等待。
读写锁(阻塞版本):类似互斥锁,读/写锁冲突时,线程阻塞。
- 事件/通知机制
事件对象(如Windows的Event、Linux的eventfd):线程可以等待一个事件变为有信号状态,等待期间线程挂起。
信号(Signal):进程可以注册信号处理函数,并调用 pause() 或 sigsuspend() 挂起,信号到来时自动唤醒。
消息队列:接收者尝试从空队列中取消息时,可以选择阻塞直到有消息到达。
Future/Promise 或异步模型:在协程或异步编程中,任务可以挂起(yield),不阻塞线程,但本质上也不是忙等待。
- I/O 操作的阻塞模式
阻塞I/O:当进程调用 read() 从网卡或磁盘读取数据而数据未准备好时,进程会进入睡眠状态,由内核在数据到达后唤醒它。
多路复用(如 select/poll/epoll):这些调用本身可以阻塞,直到监控的某个文件描述符就绪。调用者阻塞在内核中,不忙等待。
- 硬件中断
中断驱动:CPU在等待外部设备完成操作时,可以执行其他任务。设备完成后通过硬件中断通知CPU,CPU暂停当前任务去处理中断。这种方式完全避免了忙等待。
它展示系统中有哪些“东西”(对象、类),以及这些东西之间是什么关系(继承、关联等)。你提到的“静态设计视图”就是指它关注的是结构,而不是动作。
序列图:画的是某一个具体流程的细节。
它展示多个对象之间为了完成一个功能(比如用户登录),是按什么时间顺序来互相发消息的。你提到的“对象生命线”、“控制焦点”都是用来描述这个时间顺序的符号。
部署图:画的是软件跑在什么硬件上。
它展示最终写好的程序(构件)要安装在哪台服务器上(运行处理节点),服务器之间怎么连接。这是物理层面的视图。
状态图:画的是某一个东西的“一生”。
它专门盯着某一个对象(比如一个订单),看它从创建到最后销毁,会经历哪些状态(待支付、已支付、已发货),以及在什么事件(用户点击支付)的触发下发生状态转换。
为什么要把它们统称为 UML?
UML 本身不是一个工具,而是一套标准。它把软件工程历史上各种优秀的建模方法(比如 Booch 方法、OMT 方法、OOSE 方法)统一起来,形成了我们现在看到的这 14 种(最初是 9 种)标准图。
你列出的这四张图,覆盖了 UML 的两大分类:
结构图(描述静态构成):类图、部署图属于这一类。
行为图(描述动态交互):序列图、状态图属于这一类。
页面大小 = 4K
4×1024=4096 字节
计算机中,地址用二进制或十六进制表示,4K 对应2的12次幂 ,所以页内偏移占 12 位(也就是二进制的 12 位)。
页表如下:
页表的作用:告诉你逻辑页号对应的物理块号是多少。
逻辑地址:十六进制 1D16H
我们需要把这个地址转换成物理地址
逻辑地址的结构
在分页系统中,逻辑地址分成两部分:逻辑地址=页号+页内偏移
页号:哪个页面
页内偏移:这个页面里的哪个字节
因为页面大小 4K = 2的12次幂,所以偏移量占 12 位(二进制)。
12 位二进制对应十六进制是 3 位(因为 4 位二进制 = 1 位十六进制,12 ÷ 4 = 3 位十六进制)。
结论:
逻辑地址(十六进制)的低 3 位是页内偏移
剩下的高位就是页号
拆分逻辑地址 1D16H
1D16H 是一个 4 位十六进制数(从右往左数):
低位(右边)3 位:D16 → 这是页内偏移
高位(左边)1 位:1 → 这是页号
验证:
1D16H = 页号 1 + 偏移 D16H
第四步:查页表,找物理块号
页号是 1,查题目给的页表:页号 1 对应物理块号 3。
拼出物理地址
物理地址计算公式:物理地址=物理块号×页面大小+页内偏移
物理块号 = 3
页面大小 = 4096 = 1000H(十六进制)
页内偏移 = D16H
先算 3 × 4096:
3 × 4096 = 12288
12288 转十六进制:
12288 ÷ 16 = 768 余 0
768 ÷ 16 = 48 余 0
48 ÷ 16 = 3 余 0
3 ÷ 16 = 0 余 3
从下往上:3000H
所以 3 × 4096 = 3000H(十六进制)
然后加上偏移 D16H:3000H+D16H=3D16H
为什么这样算?
因为分页系统把内存分成相同大小的“块”(也叫页框),逻辑地址中的页号通过页表映射到物理块号,块内的位置(偏移)保持不变。
公式: 物理块号 × 页面大小 + 偏移 就是为了找到物理内存中的准确位置。
有 \ → 从根开始,绝对路径。
无 \ → 从当前目录开始,相对路径。
所以,flash\ 是相对路径,而 \flash 和 \flash\ 都是绝对路径。
解决问题的思路——隔离级别
如果业务允许少量不一致,追求高并发,可以用读已提交。
如果需要保证同一行数据多次读取一致(如统计、对账),用可重复读。
如果需要绝对精确的范围统计,不允许幻读,用串行化,但要做好性能下降的准备。
总结一下:数据库通过提供不同强度的隔离级别,让你在数据一致性和并发性能之间做权衡。而脏读、不可重复读、幻读就是你要解决的“问题清单”,隔离级别就是你的“工具清单”。
脏读是因为读未提交,不可重复读和幻读,是因为读已提交,读到了并发的其它事务修改的结果。可重复读是先快照然后只看快照的内容,保证数据前后一致解决不可重复读的问题。这些问题都是并发带来的,那么串行化后事务都在排队,虽然阻塞但是并发带来的问题都消失了。一个事务读到了另一个未提交事务的修改(修改随后被回滚了)。
一点补充
关于幻读:在标准SQL中,可重复读级别允许幻读发生。但像MySQL InnoDB这样的引擎,在可重复读级别下使用间隙锁(Gap Lock) 机制,实际上也防止了幻读。所以实践中,MySQL的可重复读已经可以避免幻读。
读已提交:虽然不可重复读和幻读可能发生,但它是许多数据库的默认级别(如Oracle、SQL Server),因为它在性能和一致性之间取得了较好的平衡。
核心区别
不可重复读:同一行的数据内容被改了(UPDATE)。在一个事务内,两次读取同一数据,结果不一致(因为中间被其他事务修改了)。
幻读:一批数据的行数变了(有新插入 INSERT 或删除 DELETE)。
打印机的颜色模式是CMY
多媒体接口标准是MEPG-7
单纠错(SEC)。海明码中,对于m位数据,需要r位校验位,满足:2^r >= m + r + 1。
VLIW 是 Very Long Instruction Word 的缩写,中文意为超长指令字。
+— 0 的编码相同的是补码和移码。
只有相联存储器是按内容访问的,因为它可以同时并行比较。其它的都是按地址访问的。
CPU内部的两大核心组件:控制器和运算器(更常见的术语是算术逻辑单元ALU)
控制器 = PC程序计数器(取指)+ IR指令寄存器(存指)+ ID指令译码器(译指) + 时序控制器/脉冲发生器(定拍)
运算器 = ALU(核心算力)【累加器,算术逻辑单元】+ 通用寄存器(临时存数)+ PW状态寄存器/标志寄存器(记结果)
程序指令平时存放在内存里,在执行时被取到CPU内部的寄存器中。
实体完整性: 怕的是重复和空值(主键不能为空,不能重复)。
参照完整性: 怕的是找不到爹(外键必须在主表里存在,不能凭空捏造)。
用户定义完整性: 怕的是不合逻辑(数据范围、格式要符合现实)。
不需要CPU执行程序指令来传送数据的方式,通常指直接内存访问。DMA控制器接管总线的控制权,直接在I/O设备和内存之间建立数据传输通道,传输过程不需要CPU逐条执行指令(仅在开始和结束时需要CPU干预)。
为什么cache 的命中率不随容量扩大而线性提高?
用一个比喻来理解:
Cache 就像一个办公桌。
你每天常用的文件(工作集)就那么几个(比如笔、笔记本、正在写的稿子)。
当桌子从 1 平米增加到 2 平米时,你可以把常用的东西全摆开,命中率大增。
但当桌子从 10 平米增加到 20 平米时,你常用的东西还是那么几样,多余的空间堆满了不常用的档案,命中率几乎没有变化。
RC5属于对称加密
而它的体系结构,通俗地说,就是这些CPU、内存、磁盘是如何组织在一起协同工作的。不同的组织方式,决定了系统的扩展能力、容错能力和适用场景。
平行数据库主要有以下三种经典的体系结构
平行数据库的体系结构就是回答:为了处理海量数据,我们是怎么把这些硬件资源连接和协调起来的。
现在最流行的云原生和分布式数据库,基本都是采用 无共享 架构,因为它能通过增加普通服务器来实现性能的线性增长,成本效益最高。
1.用户层(你): 发起需求——“我要吃鱼香肉丝”。
2.设备独立性软件(服务员领班): “识别与分发”。
它看了一眼需求,判断出:“这是热菜,不是饮料,应该分给热菜窗口”。
3.驱动程序(热菜窗口的师傅): “逻辑实现与翻译”。
师傅看到单子,知道鱼香肉丝的逻辑是“先切肉、再炒配菜……”。但他不做推理(比如“顾客会不会不喜欢吃辣”),他只是严格按照操作流程,把逻辑变成对灶台的具体操作。
4.中断处理程序(灶台上的感应器/铃铛): “应急响应”。
菜炒好了,灶台发出“叮”的一声。这个铃铛负责捕捉这个信号,然后通知领班:“菜好了,可以上菜了!”
5.硬件(灶台/锅/铲): “物理执行”。
执行最底层的物理动作:发热、翻转、把菜炒熟。
指令本身存放在内存中,它有一个存储地址(由程序计数器指向)
在DMA传输过程中,DMA控制器会向CPU申请总线控制权。
当获得总线控制权后,每传送一个数据(例如一个字或一个字节),DMA控制器就会占用一个总线周期来完成内存与外设之间的数据交换。
这种占用通常是“周期窃取”方式,即DMA控制器在CPU不访问总线时(或强制CPU暂停一个周期)插入一个总线周期来完成数据传输,传送完毕后立即释放总线。
看 IP 地址的第一个8位组(第一个十进制数)。
如果第一个数是 0~127(实际上是1~126),默认就是 A类,掩码固定为 255.0.0.0(/8)。
如果第一个数是 128~191,默认就是 B类,掩码固定为 255.255.0.0(/16)。
如果第一个数是 192~223,默认就是 C类,掩码固定为 255.255.255.0(/24)。
所以A类地址默认掩码是8位,B类地址是16位,C类地址是24位
如何划分子网
以255.255.224.0为例
二进制:11111111.11111111.11100000.00000000
1的个数:8 + 8 + 3 = 19,所以是 /19 掩码。
“划分子网”的前提:必须知道默认掩码
划分子网是在默认掩码的基础上,向主机位“借位”来增加网络位,从而得到更多更小的子网。
默认掩码由IP地址的类别决定:
A类默认 /8(255.0.0.0)
B类默认 /16(255.255.0.0)
C类默认 /24(255.255.255.0)
比默认掩码长还是短?这决定了是“划分子网”还是“超网聚合”。
如果这是一个 B类地址(例如 172.16.0.0)
默认掩码:/16(255.255.0.0)
实际掩码:/19(255.255.224.0)
借了 3 位(19 - 16 = 3)
子网数量 = 2的3次幂=8个
主机位N=32-掩码网络位数
主机IP个数=2的N次幂-2
核心作用
安全隔离:即使 DMZ 中的服务器被攻破,攻击者也难以直接访问内部局域网,因为防火墙严格限制了 DMZ 与内网之间的访问策略。
对外服务:将面向公网的服务集中部署在 DMZ,避免将内部网络直接暴露在互联网中。
为什么 DMA 要用物理地址?
因为 DMA 控制器不经过 CPU 的 MMU(内存管理单元)。
CPU 访问内存时,使用的是虚拟地址,由 MMU 实时转换为物理地址。
DMA 控制器是独立的总线主控设备,它直接连接到内存总线(或通过 IOMMU),它没有内置的 MMU 去翻译虚拟地址。
断点地址和入口地址的区别
当 CPU 响应一个中断时:
1.保存断点
CPU 自动将当前的 程序断点地址(CS:IP 或 RIP)压入堆栈,以便中断处理完成后能返回到被中断的程序继续执行。
2.查表获取入口地址
CPU 根据中断向量号,从中断描述符表(IDT)中找到对应的 门描述符,从中提取出中断处理程序的 段选择子和偏移量,即入口地址。
跳转执行
CPU 跳转到这个入口地址,开始执行中断处理程序。
中断向量表里存的是中断处理程序的入口地址;程序断点地址是被中断时 CPU 自动压栈保存的返回地址。两者在中断处理机制中分工明确,不可混淆。
在经典的进程三态模型中(就绪、运行、阻塞),不会发生的转换主要有两个:
阻塞 → 运行(阻塞就是等待)
进程在阻塞状态下无法被直接调度执行,必须等待事件完成、先转换为就绪态,再由调度器分配 CPU 后才能进入运行态。
就绪 → 阻塞
就绪态的进程尚未获得 CPU,无法执行任何可能引起阻塞的操作(如 I/O 请求、等待信号量等),因此不能直接进入阻塞态。
其他转换如“就绪 → 运行”、“运行 → 就绪”、“运行 → 阻塞”、“阻塞 → 就绪”都是合法的,由操作系统调度或事件驱动触发。
IPC(Inter-Process Communication,进程间通信) 是指操作系统提供的让不同进程之间交换数据或同步状态的机制。常见的 IPC 方法包括:
管道(Pipe) 半双工通信,通常用于父子进程间;命名管道(FIFO)可用于无亲缘关系进程。
消息队列(Message Queue) 以消息块为单位,支持多进程读写,由内核管理,有边界。
共享内存(Shared Memory) 多个进程映射同一块物理内存,速度最快,但需要同步机制(如信号量)配合。
信号量(Semaphore) 主要用于同步与互斥,实现进程间对资源的访问控制。
信号(Signal) 异步通知机制,用于处理异常或简单事件(如 SIGKILL、SIGINT)。
套接字(Socket) 支持本机或网络跨主机通信,适用于客户端/服务器架构。
内存映射文件(Memory-Mapped File) 将文件映射到进程地址空间,实现进程间共享数据。
页面置换(缓存淘汰)算法
- 最优置换(OPT / Optimal)
核心思想:淘汰未来最长时间内不会使用的页面。
特点:理论上缺页率最低,但无法在实际系统中实现(因为无法预知未来)。通常用作衡量其他算法性能的“标尺”。
- 先进先出(FIFO)
核心思想:维护一个队列,淘汰最早进入内存的页面。
特点:实现简单,但可能出现 Belady 异常(分配更多物理页框反而缺页率升高),且不考虑页面访问频率或重要性。
第二次机会(Second Chance)
核心思想:在 FIFO 基础上增加一个“访问位”。如果队首页面被访问过(访问位=1),则将其移到队尾并清零访问位,相当于再给一次机会;否则直接淘汰。
特点:解决了 FIFO 盲目淘汰的问题,是 NRU 的常见实现变体。
时钟(Clock)
核心思想:第二次机会算法的环形链表版本。用一个指针循环扫描页框,检查访问位,类似时钟转动。
特点:开销低,是现代操作系统中最常用的置换算法之一(例如 Linux 的 Page Frame Reclaiming 基于类似思想)。
- 最近最少使用(LRU)
核心思想:淘汰最长时间未被访问的页面。
特点:性能接近 OPT,但硬件支持要求高(需要维护访问历史或时间戳)。纯软件实现需借助链表或堆栈,每次访问都更新,开销较大。
硬件近似:
NRU 就是 LRU 的一种近似(通过定期清零 R 位来区分“近期”是否被使用)。
老化算法(Aging):用多个移位寄存器记录访问历史,模拟 LRU 的时间排序。
- 最不经常使用(LFU)
核心思想:淘汰访问次数最少的页面。
特点:适合长期访问模式,但可能让“刚调入但未来会频繁使用”的页面因初始计数低而被误淘汰。实现需维护计数器。
- 工作集算法(Working Set)
核心思想:基于程序局部性原理,维护每个进程的“工作集”(当前活跃使用的页面集合)。缺页时淘汰不属于工作集的页面。
特点:能有效减少抖动(thrashing),但需要硬件支持访问位和定时器来精确跟踪工作集。
- 随机置换(Random)
核心思想:随机选择一个页面淘汰。
特点:实现极简单,在内存较大时实际性能有时出人意料地不差,避免了复杂算法的开销。
这些算法本质都是在命中率、实现开销、硬件支持之间做权衡。实际系统中常采用混合策略(如 Linux 的 LRU 双链表 + Clock 扫描)。
可以想象一个软件项目的演进过程,不同模型就是不同阶段或不同场景下的选择:
瀑布模型:就像盖房子,先画好图纸,再打地基,再建主体……每一步完成才能下一步。适合需求明确、不会改的项目。
V模型:瀑布的“对称版”。左边是开发(需求→设计→编码),右边是对应的测试(单元测试→集成测试→验收测试)。强调测试与开发一一对应。
原型模型:需求不清楚时,先快速做个“草稿”给用户看,用户说“不对,我要的是这样”,改完再确认,直到满意再正式开发。适合用户自己都说不清需求的场景。
增量模型:先做最核心的功能上线,用户先用起来,再一批批增加功能。适合要抢占市场或核心功能明确,其他待定的项目。
螺旋模型:把项目切成多个小循环,每个循环都做四件事:定目标→识别风险→开发验证→评审规划。适合风险很高的项目(比如新技术、新领域)。
敏捷模型:把大项目拆成多个“冲刺”,每个冲刺1-4周,交付一个可用的版本,持续根据反馈调整。适合需求频繁变化的互联网项目。
数据流图(DFD,Data Flow Diagram) 是一种用于结构化系统分析的图形化工具。它通过描述系统中数据的流动、存储、处理和外部交互,直观地展示系统“做什么”(逻辑模型),而不涉及“怎么做”(物理实现)。涉及怎么做的是流程图。
在软件工程中,它通常用于需求分析阶段,帮助分析师与用户沟通系统的功能范围和数据流向。
数据流图是结构化分析的核心工具,通过“外部实体—过程—数据存储—数据流”四个要素,以分层方式描述系统“做什么”。掌握它需要理解分层思想(上下文图→0层图→子图)和平衡原则,并将其与流程图(控制流)区分开。
顶层图(上下文图)
将整个系统视为一个过程。
只展示系统与外部实体之间的数据流,不显示内部细节。
用于确定系统的边界。
0层图(顶层展开)
将顶层的一个过程分解为主要功能模块(通常3~7个)。
展示这些模块之间的数据流,以及它们与外部实体、数据存储的关系。
子图(逐层分解)
对0层图中的每个过程进一步细化,直到子图足够简单(每个过程的功能单一、清晰)。
父子平衡原则:父图中的一个过程,展开为子图后,子图的输入/输出数据流必须与父图中该过程的输入/输出一致。
六种核心UML图
(1)类图 —— “系统的静态骨架”
画的是类、类的属性、方法,以及类之间的关系(继承、关联、依赖、组合等)。
相当于软件开发中的“建筑设计图”,是面向对象分析设计的核心产物。
(2)用例图 —— “系统给谁用,能干什么”
画的是参与者(人或其他系统)和用例(系统提供的功能)。
只回答“做什么”,不回答“怎么做”。用于需求分析阶段。
(3)顺序图 —— “按时间顺序的交互”
强调时间顺序,从上往下看,消息一个接一个。
用于展示一个场景下,多个对象之间如何协作完成某个功能。
(4)状态机图 —— “一个对象的一生”
画的是状态和状态之间的转移。
适用于有明确状态变化的对象,比如订单(待支付→已支付→已发货→已完成)。
(5)活动图 —— “流程图的升级版”
类似流程图,但可以表达并发和并行分支。
用于描述业务流程、算法逻辑、用例内部的执行步骤。
(6)部署图 —— “软件跑在什么硬件上”
画的是物理节点(服务器、PC、设备)和软件构件的物理部署。
在分布式系统、运维架构设计时使用。
数据模型的组成要素:三个要素共同构成了一个完整的数据模型,其中数据结构是基础,数据操作是基于结构的行为,完整性约束是对结构与操作的规则限制。
直接进行并(∪)、交(∩)、差(-) 运算的前提是:两个关系必须并兼容,具体条件为:
属性个数相同(两个关系都有相同数量的列)。
对应位置上的属性域相同(即第1列的数据类型一致,第2列的数据类型一致,依此类推)。
(注:属性名可以不同,但运算结果通常按第一个关系的属性名命名。)
符合并兼容的例子
关系 R(A,B):A 是整数,B 是字符串
关系 S(C,D):C 是整数,D 是字符串
它们属性个数相同(均为2),对应列域相同(整数→整数,字符串→字符串),因此可以进行并(∪)、交(∩)、差(-) 运算
聚集函数(Aggregate Function,也称聚合函数)是对一组值(通常是一列中的多行数据)进行某种计算,并返回一个单一值的函数。
SQL 的逻辑执行顺序
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
FROM:确定数据来源(表、连接)。
WHERE:逐行筛选,过滤掉不满足条件的行。
GROUP BY:将筛选后的行分组。
HAVING:对分组后的结果进行筛选(此时可以用聚集函数)。
SELECT:选择输出列,可以计算聚集函数。
ORDER BY:排序。
WHERE 执行时,数据还是原始的、未分组的行集合。
聚集函数(如 AVG、SUM)需要先知道分组范围(比如整个表或每个组),才能计算出单一值。
在 WHERE 阶段,分组尚未发生,数据库无法知道“当前行”应该属于哪个组来计算聚集值。
如果允许写 WHERE score > AVG(score),会产生歧义:
AVG(score) 应该是整个表的平均值吗?
还是当前行所在组的平均值?但组还没形成。
为了避免这种语义混乱,SQL 标准直接规定:WHERE 子句中不允许使用聚集函数。
那为什么 HAVING 可以?
因为 HAVING 发生在 GROUP BY 之后,此时分组已经确定,聚集函数可以针对每个组计算,然后筛选分组。
总结一句话
WHERE 是“先过滤,后分组”,而聚集函数需要“先分组,后计算”,顺序冲突,所以不允许直接使用。
触发器的设计初衷就是为了响应数据变更,比如增(INSERT)、删(DELETE)、改(UPDATE)。所以,“增删改”这三件事都能触发触发器,唯独“查”(SELECT)不行
触发器(trigger)是数据库系统提供给程序员和数据分析员来保证数据完整性的一种数据库对象,它是与表事件相关的特殊的存储过程。它的执行不是由程序调用,也不是手工启动,而是由事件触发触发器定义时,可以指定触发的动作(INSERT添加、DELETE删除、UPDATE修改),以及触发的时间(在触发动作发生前、发生后);同时,也可以指定触发器执行类型FOR EACH ROW(行级:每条记录上执行了触发动作,触发器就执行一次)及FOR EACH STATEMENT(语句级:一条SQL如果涉及到多条记录,触发器仅执行一次);对于行级触发器,如果使用了when子句,则当when后的条件为真时,才执行触发器过程体。
三级封锁 = 保护“数据内容”的正确性(脏读、不可重复读)
两段锁 = 保证“并发顺序”的正确性(可串行化)
你可以:严格遵守三级封锁,但可能违反两段锁(比如二级协议读完就释放读锁)。
只有第三级封锁 + 事务结束才释放锁,才同时符合两段锁。
三级封锁就像:
一级:只管改书的人,看书的人随便。防止修改丢失
二级:看书的人每看一次都要“借”,看完立刻还。防止修改丢失和读脏数据
三级:看书的人把书借走,不离馆就不还。防止修改丢失、读脏数据、不可重复读
三个级别的核心区别在于 S锁(读锁)的释放时机:
一级:读不加锁。
二级:读加S锁,但读完马上释放。
三级:读加S锁,并且一直持有到事务结束才释放。
B树索引:当你的查询涉及范围、排序、分组、模糊匹配前缀时,必须用它。它像一把瑞士军刀,万能但稍慢。
散列索引:当你的查询只有精确等值(比如根据用户ID查信息),而且不需要排序,并且数据量大到需要极致速度时,可以用它。它像一把手术刀,专精一刀致命。
一个小秘密:很多数据库(如Oracle、SQL Server)即使你写了等值查询,也可能用B树索引。因为B树在等值查询上也足够快(log n),而散列索引功能太单一,所以默认很少用。
优化SQL查询的方法可以分为多个层次,从最有效的索引优化到查询改写、表结构设计、数据库参数调优等
对于SQL语句
尽可能地减少多表查询或建立物化视图。
以不相关子查询替代相关子查询。
只检索需要的列。
用带IN的条件子句等价替换OR子句。
经常提交COMMIT,以尽早释放锁。
数据库物理设计的主要工作包括:1.确定分布方式 2.确定存储结构 3.确定访问方式
故障与恢复
恢复操作的基本原理:冗余,利用存储在系统其它地方的冗余数据来重建数据库。
数据库系统的故障分为三类:事务故障、系统故障和介质故障。
事务故障:由于程序执行错误而引起事务非预期的、异常终止的故障;包括逻辑错误(溢出、非法输入等)、系统错误(死锁等)。
系统故障:硬件故障、软件(如DBMS、OS或应用程序)漏洞的影响,导致丢失了内存中的信息,影响正在执行的事务。
介质故障:数据库文件的存储介质如硬盘发生故障导致数据丢失。
DBMS对不同类别的故障使用不同的恢复方法:
事务故障和系统故障:由DBMS完成事务级别的恢复,即根据日志文件对未完成的事务进行UNDO操作,对已完成的事务进行REDO操作,使数据库恢复到故障前的一致性状态。
介质故障:需要DBA介入,装载备份文件后交由DBMS进行恢复。
指令周期:取出一条指令并执行这条指令的时间。一般由若干个机器周期组成,是从取指令、分析指令到执行完所需的全部时间,每一阶段完成一个基本操作。
时钟周期:也称为振荡周期,定义为时钟频率的倒数。时钟周期是计算机中最基本的、最小的时间单位。在一个时钟周期内,CPU仅完成一个最基本的动作。更小的时钟周期就意味着更高的工作频率。
总线周期:CPU完成一次访问MEM(内存访问)或I/O端口操作所需要的时间。
CPU周期:完成一个基本操作所需要的时间,也称为机器周期,一般情况下,一个机器周期由若干个时钟周期组成。
存储周期:主存储器两次启动操作之间需要的最小时间间隔,即主存储器周期时间。
I/O接口的核心作用是解决主机(CPU/内存)与外部设备之间在速度、数据格式、信号电平、时序等方面的不匹配问题,从而实现高效、可靠的数据交换。
主要功能可以列举为以下几点:
1.数据缓冲与缓存
利用寄存器或缓冲器,暂存传输中的数据,以协调CPU与外设间巨大的速度差异。CPU可快速地将数据写入接口缓冲,再由接口以适当速度发给外设,反之亦然。
2.信号格式转换
进行电平转换(如TTL与RS-232C之间的转换)、数模/模数转换(A/D, D/A),以及串行/并行数据格式的相互转换(如USB、SATA接口)。
3.地址译码与设备选择
对CPU发出的地址总线信号进行译码,以确定当前要访问哪个具体接口(或接口内部的哪个寄存器),从而选中特定的外设。
4.提供握手信号与状态信息(设备状态检测与反馈)
提供如“数据就绪”“忙/闲”“请求发送”“允许发送”等控制信号,用于主机与外设之间进行通信联络(握手)。接口中的状态寄存器可供CPU查询外设是否空闲、数据是否准备好等。
5.时序协调与同步(控制与定时)
将CPU的快速、随机总线访问时序,转换为外设所需的慢速、有特定时序要求的操作序列,保证双方操作在时间上对齐。
6.中断与DMA管理
中断管理:当外设需要服务时(如数据已准备好),接口可向CPU发送中断请求。接口还需提供中断向量地址、处理中断优先级。
7.DMA管理:在直接存储器访问(DMA)传输中,接口(或DMA控制器)需接管总线控制权,在内存和外设间直接传输数据块,完成后释放总线。
8.错误检测与数据校验
部分接口(如硬盘接口、网络接口)会提供奇偶校验、CRC校验等功能,以检测传输过程中是否发生错误,并可请求重传。
9.提供电气与机械适配
通过标准的物理连接器(如USB Type-C、HDMI、RJ45)和电气规范,保证不同厂商的设备可以物理互连并安全供电。
全协议层级总结如下:
应用层:HTTPS、SSH、PGP、Kerberos、DNSsec(保护特定应用数据)。
会话层/中介层:SOCKS(建立通用代理通道,本身非加密协议)。
传应之间:TLS/SSL(为TCP连接提供加密通道)。
网络层:IPsec(保护整个IP包,用于VPN)。
链路层:MACsec(以太网加密)、WPA2/WPA3(无线加密)。
加密技术:对称加密(DES, AES)、非对称加密(RSA, ECC)、哈希算法(MD5, SHA)。
公钥使用非对称加密,RSA是一种算法,公钥和私钥(密钥对)都是加密算法的产物。
商标权:本质上是一种商业标记权,通过无限续展来实现“永久”保护,只要持续使用并按时缴费即可。期满可以无限次续展,每次续展有效期10年,相当于保护期可以无限长。
专利权:强调“公开换保护”,法律赋予有限时间的垄断权,期限固定且不可续展,期满后技术进入公有领域。保护技术方案本身,期限固定,不可续展 期限20年的保护技术方案,期限15年的保护设计方案,期限10年的保护构造形状。
著作权:为鼓励创作而设,保护期与创作者的生命或作品的发表时间挂钩。作者终生 + 死后50年或者保护期为作品首次发表后第50年的12月31日。作者终生 + 死后50年”:适用于自然人(个人)创作的普通作品(如小说、绘画、歌曲)。保护期与作者的生命绑定,目的是让创作者及其后代在作者去世后仍能受益一段时间。“50年”(自首次发表起算):主要适用于法人或非法人组织作为作者的作品(如软件、公司宣传片、电影、摄影作品),以及匿名/假名作品。因为这些作品没有明确的、自然人的“终生”可以参照,所以从作品首次发表的年底开始计算50年。如果创作后50年内从未发表,则不再保护。
商业秘密:保护模式与其他类型截然不同,其价值在于“不公开”,保护期无固定上限,一旦公之于众,权利也随之终止。保护期长短完全取决于信息是否能持续处于保密状态。
能够不访问页表,快速将虚拟地址映射到物理地址的硬件机制:
TLB转译后备缓冲器(转换检测缓冲区),为CPU的一种缓存器,可介于CPU和CPU缓存之间,或在CPU缓存和主存之间。虚拟内存分页式虚拟存储器:利用硬盘空间来充当内存使用,由硬件和操作系统自动实现存储信息调度和管理的,调度方式有分页式、段式、段页式3种。
多级页表:减少页面表自身占用的内存空间确实是非常有效的。
内存映射:将虚拟地址转换为物理地址,由操作系统和CPU共同完成,操作系统为CPU设置好页表。
线程引入的原因:减少并发执行时的时空开销。线程的属性:轻量实体、独立调度和分派的基本单位,可并发执行(同一进程的多个线程可并发)、共享同一进程资源(PC程序计数器、寄存器、栈)。
两种线程实现方式:
用户级线程(User-Level Threads):不依赖于内核,TCB存在于用户空间,内核不知道其存在。
内核支持级线程(Kernel-Supported Threads):依赖内核,其创建、撤销和切换都由内核实现,存在于内核空间,通过线程控制块TCB控制。
传统防火墙:防护对象是整个网络,保护服务器、电脑等所有设备免受未经授权的访问和网络层攻击(如端口扫描、DDoS攻击的流量型变种、IP欺骗)。它的判断逻辑通常是基于访问控制列表(ACL):允许或拒绝某个IP访问某个端口。工作在网络层和传输层
Web防火墙(WAF):防护对象专门针对Web应用(网站、API接口)。它保护网站免受应用层攻击,比如:SQL注入,跨站脚本攻击(XSS),命令注入,文件包含漏洞,Cookie篡改、会话劫持。工作在应用层。
SQL中的SELECT:默认不去重
实际考虑:SQL操作的对象是表(table),它允许包含重复的行(称为多集或袋)。这是为了性能、灵活性和实用性——大多数业务场景需要保留原始数据的所有细节。
默认行为:SELECT 列名 FROM 表名 会保留所有行,包括重复行。如果想去重,必须显式使用 DISTINCT 关键字。
关系代数中的投影Π:天然去重
数学基础:关系代数中的“关系”被定义为一组元组的集合。集合中的元素是互不相同的,不存在重复。
投影操作(π):从关系中选出指定的列。因为结果仍然要构成一个“关系”(也就是集合),所以必须自动去除重复的行,否则就违反了集合的定义。
存储过程(Procedure)是一组为了完成特定功能的SQL语句集合,经编译后存储在数据库中,用户通过指定存储过程的名称并给出参数来执行。
存储过程正是在服务器端所提供的功能调用,适用于编写更新数据库的事务程序。
存储过程中可以包含逻辑控制语句和数据操纵语句,它可以接受参数、输出参数、返回单个或多个结果集以及返回值,存储过程可以调用其他存储过程。
由于触发器是在表上发生触发事件(添加、删除、修改)时自动执行,所以不需要通过存储过程调用。
X锁(排他锁):加锁的事务可以读,也可以写。其他事务不能读也不能写(不能加任何锁)。
S锁(共享锁):加锁的事务只能读,不能写。其他事务可以读(加S锁),但不能写(不能加X锁)。
乐观锁和悲观锁是两种并发控制的策略,而不是像 X 锁、S 锁那样具体的锁机制。它们的核心区别在于:假设数据会不会发生冲突。
悲观锁 = “我先锁住,再操作”,适合写多读少,强一致性,不会发生数据冲突;乐观锁 = “我先操作,提交时检查”,适合读多写少,无锁阻塞,并发性能高。
行锁(Row Lock) 是锁的粒度概念,而不是一种锁的类型(如 X 锁、S 锁)。它指的是把锁加在表的某一行记录上,而不是整个表。
在数据库的锁机制中,按粒度划分可以分为:
表锁:锁定整张表,并发性差。
行锁:只锁定被操作的一行或多行,其他行仍然可以并发访问。
页锁:介于两者之间(某些数据库如 SQL Server 支持)。
数据库设计的基本步骤需求分析:对数据库应用系统所涉及的内容(数据)与功能(行为)的整理和描述,是以用户的角度来认识系统。
概念结构设计:E-R图。在需求分析的基础上,依照需求分析中的信息要求,对用户信息加以分类、聚集和概括,建立信息模型,反映了用户观点。
逻辑结构设计:逻辑结构设计阶段的主要工作步骤包括确定数据模型、将E-R图转换成为指定的数据模型、确定完整性约束和确定用户视图。
物理结构设计:确定数据库在计算机中的具体存储。
数据库实施:根据逻辑和数据设计的结果,在计算机上建立起实际的数据库结构并装入数据,进行试运行和评价的过程。
数据库运行和维护:①对数据库性能的监测和改善;②数据库的备份及故障恢复;③数据库重组和重构。
数据字典是指对数据的数据项、数据结构、数据流、数据存储、处理逻辑等进行定义和描述,其目的是对数据流图中的各个元素做出详细的说明。在数据库中,数据字典则是系统中各类数据描述的集合。
RISC适合流水线,CISC不适合。RISC是精简指令集系统计算机简称,使用简单的指令。CISC是复杂指令集系统计算机简称,使用复杂的指令。RISC多寄存器寻址,所以会在实现过程中增加通用寄存器,CISC不需要采用很多通用寄存器。RISC采用硬布线逻辑(即组合逻辑控制器)实现,CISC采用微码(即微程序)实现。
RISC采用加载-存储架构:只有Load/Store指令能访问内存,所有运算都必须在寄存器间进行。这迫使RISC需要更多通用寄存器(如ARM有16-32个,RISC-V有32个)来暂存中间结果,减少访存次数。
CISC指令可以直接对内存操作,因此对寄存器数量的依赖较低。早期CISC(如x86)只有8个通用寄存器,确实“不需要很多”。
RISC追求简单指令和高速执行,通常使用硬布线控制器(组合逻辑),直接由硬件产生控制信号,速度快但设计复杂。
CISC指令复杂,用硬布线几乎不可行,因此采用微程序控制器:将每条指令的执行步骤写成微码(存在ROM中),通过执行微程序生成控制信号,便于实现复杂功能,但速度稍慢。
一、事务故障(Transaction Failure)
特点:单个事务逻辑出错或被强制中止,数据库系统本身正常运行。
运算溢出:整型超出范围、小数精度溢出、除零错误。
违反完整性约束:主键重复、外键引用不存在、CHECK 约束失败(如年龄 -5)。
死锁被选中为牺牲品:并发事务形成循环等待,数据库选择回滚其中一个。
应用程序主动回滚:业务逻辑检测到非法状态,执行 ROLLBACK。
资源不足:事务需要的临时表空间、锁、内存等无法分配。
断言失败:用户定义的断言(ASSERTION)不满足。
触发器内错误:触发器执行时发生异常(如除零、违反约束),导致整个事务回滚。
存储过程/函数异常:PL/SQL 或 T-SQL 中未捕获的异常导致事务终止。
二、系统故障(System Failure / Soft Crash)
特点:数据库系统进程崩溃或操作系统故障,但磁盘数据未损坏。重启后数据库可恢复。
数据库软件崩溃:DBMS 进程因 bug 或非法操作退出。
操作系统崩溃:蓝屏、内核 panic。
断电:服务器突然掉电。
硬件错误(非磁盘):内存 ECC 错误、CPU 过热保护、主板故障导致系统重启。
管理员强制终止 DB 进程:kill -9 或 systemctl stop 未正常关闭。
网络故障导致分布式数据库部分节点失联(对于单机数据库而言是系统故障)。
内存泄漏导致 DB 被 OS 杀掉:OOM(Out of Memory)Killer。
看门狗超时重启:系统健康检查超时触发重启。
三、介质故障(Media Failure / Hard Crash)
特点:磁盘数据损坏或丢失,需要从备份恢复。
磁盘坏道:磁头划伤、扇区物理损坏。
磁盘完全损坏:电机烧毁、电路板烧坏。
文件系统损坏:目录结构损坏,文件无法读取。
误删除数据文件:rm -f /var/lib/mysql/ibdata1。
误格式化磁盘:mkfs.ext4 /dev/sda 覆盖了数据库分区。
RAID 卡故障:缓存损坏、多个磁盘同时失效。
SSD 磨损超出寿命:存储单元不可写或读取出错。
病毒或恶意软件加密/删除数据文件(勒索软件)。
磁带或备份介质老化:备份本身损坏,恢复失败。
聚簇是物理结构设计而不是逻辑结构设计
辑结构设计的“规范程度”(通常指关系规范化程度)并非越高越好,需要根据实际应用场景权衡。
规范化(如达到 3NF、BCNF)主要目的是消除数据冗余和更新异常,但过度规范化会带来负面影响:
查询性能下降
高度规范化的模式会将数据拆分成大量小表,很多查询需要执行大量的连接(JOIN)操作,尤其在高并发、复杂查询的场景下,连接开销可能远超冗余带来的存储节省。
实现复杂度增加
业务逻辑中的多表关联、数据整合代码会变得更复杂,开发和维护成本上升。
某些场景需要反规范化
在数据仓库、分析型系统、高读低写场景中,适度的冗余(反规范化)可以大幅提升查询效率,减少连接,代价是可接受的更新异常。
对磁盘碎片进行整理的主要作用是:将分散存储在磁盘上的文件数据块重新连续排列,从而减少磁头移动距离,显著提升文件读写速度,并延长机械硬盘(HDD)的使用寿命。提高访问文件的速度。
盐泄露后,针对某个具体用户的字典攻击效率与不加盐时完全相同;但加盐依然能有效防御跨用户的预计算攻击和彩虹表攻击。
线程实现在“用户空间”或“内核空间”是指线程的管理代码和数据所处的特权级及地址空间范围;而“虚拟空间”是所有线程运行的基础,“物理空间”是硬件层面的细节,这两者不能用来描述线程的实现模型。
打个比方:
讨论“汽车是左舵还是右舵” → 对应用户/内核空间(驾驶方式不同)。
讨论“汽车行驶在公路上还是赛道上” → 对应虚拟/物理空间(场景不同,但不是汽车本身的实现方式)。
进程:拥有独立的地址空间、文件、信号等资源,是资源分配和保护的基本单位。
线程:进程内的执行实体,共享进程资源,是CPU调度和分派的基本单位。真正在CPU上执行指令的是线程。
程序设计语言的基本成分通常包括以下四种:
数据成分:描述程序处理的数据对象,如数据类型(整型、浮点型、字符型、数组、结构体等)和数据结构。
运算成分:定义允许的运算操作,如算术运算、关系运算、逻辑运算、位运算等,通常通过表达式实现。
控制成分:控制程序执行流程,如顺序、选择(if-else、switch)、循环(for、while)、函数调用等。
传输成分:处理程序与外部环境的数据交换,即输入/输出(I/O),如printf、scanf、文件读写等。
有些分类将“抽象与封装”也作为高级语言的成分(如面向对象中的类、模块),但上述四项是几乎所有程序设计语言都具备的核心基础。
常见的耦合度从低到高:非直接耦合、数据耦合、标记耦合、控制耦合、外部耦合、公共耦合、内容耦合。
内容耦合之所以是耦合度最高的,是因为它彻底打破了模块之间的边界,让一个模块直接“钻入”另一个模块的内部,导致修改困难、理解困难、测试困难、复用不可能,是软件设计中最应该避免的结构。 好的设计应追求低耦合(如数据耦合),而非内容耦合。
内聚的准确定义(模块内部元素的结合紧密程度),然后从最低(最差)到最高(最好)列出几个典型等级,比如偶然内聚、逻辑内聚、时间内聚、过程内聚、通信内聚、顺序内聚、功能内聚。
与耦合度相对应,内聚度(Cohesion) 衡量的是一个模块内部各元素(如语句、函数、数据)彼此结合的紧密程度。内聚度越高,模块的独立性、可理解性、可维护性越好。
内聚度衡量模块内部的团结程度——功能内聚(最高)是“一个模块只做一件完整的事”,偶然内聚(最低)是“一堆乱七八糟的东西被扔在一起”。好的软件设计追求“高内聚、低耦合”。

根据多值依赖的定义,若X→→Y 是平凡的多值依赖,则U−X−Y=∅(即Z 为空集),这是平凡多值依赖的标准定义。(王珊教的不对,奈何题目只认这个答案)
系统故障恢复不仅仅是 UNDO,还包括 REDO。(奈何题目遇到CPU故障只认UNDO这个答案)
UNDO:撤销故障发生时尚未提交的事务对数据库的修改(保证原子性)
REDO:重新执行已提交但修改可能尚未写入磁盘的事务的修改(保证持久性)
典型的恢复算法(如 ARIES)通常先分析日志,然后执行 REDO,最后执行 UNDO。
存储过程和触发器的主要区别之一就是执行方式:存储过程需要人为或程序调用,触发器由数据库事件自动激活。
触发器可能引发无限触发链
一个实体可能有多个候选标识符,选定的一个称为主标识符。
计算机科学中“透明性”的通用含义
透明性 = 让某些复杂性对使用者不可见,使用者无需关心其内部细节。
但“某些复杂性”具体指什么,就分出了不同的透明性:
透明性是“隐藏复杂性”这个共同概念,但隐藏的具体内容不同
DMA(直接存储器访问)
目的:解决批量数据的输入/输出问题,实现外设与系统内存直接交换数据,不通过CPU。
速度决定因素:存储器和外设的工作速度。
工作过程:CPU将总线控制权交给DMA控制器(总线进入高阻态),由DMA控制器接管。
DMA控制器必须具备的功能:
向CPU发出系统保持(HOLD)信号,请求总线控制权。
获得允许后,接管总线控制,进入DMA方式。
对存储器寻址并修改地址指针,实现内存读写。
确定本次传送字节数,判断DMA是否结束。
发出DMA结束信号,使CPU恢复正常工作。
单链表存储结构所需空间与结点个数成正比
在表中任意位置插入或删除不用移动元素
表中结点占用存储空间的地址可以不是连续的
单链表中的结点只能通过头结点开始,沿着指针逐个遍历才能访问到,无法像数组那样通过下标直接计算出内存地址进行随机访问。要访问第 i 个结点,必须从头开始依次移动 i 次指针,时间复杂度为 O(n)。
对应用层数据进行过滤的是“应用网关防火墙”(也叫代理防火墙),而不是“Web防火墙”专属。
但为什么容易搞混呢?因为Web防火墙(WAF)也能对应用层数据过滤,只不过它只过滤 HTTP/HTTPS 这一种应用层协议。而“应用网关”可以过滤多种应用层协议(如 FTP、SMTP、Telnet 等)。
进程图中P 代表申请资源(可能阻塞),V 代表释放资源(可能唤醒)。
编译(传统):一次性将源代码 → 目标代码(机器码),生成可执行文件。目标代码是直接给CPU执行的。
解释(传统):逐行读源代码 → 执行,中间可能翻译成某种中间形式(字节码、AST等),但这个中间形式通常不是CPU能直接执行的机器码,需要解释器继续处理。
问:下面哪种耦合度最低?
答:数据耦合(如果选项中有“非直接耦合”,则选它更低,但多数考题会把数据耦合作为最低的常见选项)。
问:模块间传递一个数组作为参数,这属于什么耦合?
答:标记耦合(因为数组是复合数据结构)。
问:通过全局变量共享数据属于什么耦合?
答:公共耦合。
传简单数据 → 数据耦合
传复杂结构(只用一部分) → 标记耦合
传标志位/控制码 → 控制耦合
用全局变量/公共区 → 公共耦合
改对方内部 → 内容耦合
越低越好,数据耦合已经很优秀,非直接耦合是理想。
软件测试开始于需求分析,结束于软件生命周期终结 或 测试活动贯穿始终 都是正确的表述。
对于笛卡尔积,如果两表有相同属性,结果也会保留各自的属性,R有4个属性,S有4个属性,R*S的结果有8个属性。
在大多数主流数据库(如 Oracle、SQL Server、MySQL、PostgreSQL)中,触发器中不能包含事务控制语句。
具体来说,触发器中不允许使用:
COMMIT(提交)
ROLLBACK(回滚)
SAVEPOINT(保存点)
触发器是自动执行的,它依附于某个 DML 语句(INSERT、UPDATE、DELETE)。触发器的执行是外层主事务的一部分。如果触发器内部允许提交或回滚,会破坏整个事务的原子性,导致不可预知的结果。
简单说:触发器不能自己决定“结束事务”,因为事务的控制权属于发起 DML 语句的调用者。
快速判断方法
右部拆成单属性。
去除冗余依赖:对每个依赖 X→Y,去掉它后计算剩余依赖集的闭包,看能否推出 Y。
左部最小化:对每个依赖 X→Y,检查 X 中每个属性是否冗余,即去掉该属性后得到 X',若 X'→Y 能被原依赖集推出,则该属性冗余。
这样得到的就是最小函数依赖集。
动态备份和静态备份的核心区别在于:备份过程中是否允许对数据库进行更新操作。
静态备份:备份时不能写,一致性易得,业务需暂停;动态备份:备份时能正常写,业务不停,但需日志配合保证一致性。
堆栈是一种数据项按序排列的数据结构,只能在一端(称为栈顶top)对数据项进行插入和删除。要点:堆、顺序随意。栈,后进先出(Last-In/First-Out)。为了便于实现多级中断,使用堆栈来保护断点和现场最有效。
报文摘要(Message Digest)的主要目的是:
确保数据完整性:通过哈希函数对原始报文生成固定长度的摘要(如 MD5、SHA-1、SHA-256)。只要原始报文发生任何改动,重新计算的摘要就会完全不同,从而检测出数据是否被篡改。
支持数字签名:对摘要进行签名比直接签名整个长报文更高效,因为摘要长度固定且短小。
快速比对:可用于文件校验、密码存储(如用户密码的哈希值)等场景,避免直接存储或传输敏感原文。
简单说,报文摘要相当于数据的“指纹”,用于验证数据在传输或存储过程中未被修改。
在标准的进程资源图中,从资源实例指向进程的箭头表示已分配(进程占有该资源实例),从进程指向资源实例的箭头表示请求(进程正在等待该资源),而不是释放。释放操作会使分配边消失,但不会用箭头表示释放。因此,指向实例的箭头是请求,不是释放。
即使是多个磁头(例如机械硬盘的每个盘面对应一个磁头),写入操作仍然是串行的。原因在于:
所有磁头通过同一套读写通道和控制器连接到主机,同一时刻只能有一个磁头进行数据传输。
磁头是同时寻道的(由同一个音圈电机驱动),但只能选中其中一个磁头进行读写。
因此,多磁头并不等于并行写入,只是允许在不同磁头间切换。真正的并行写入需要独立的数据通道(如 RAID 0 中的多块磁盘并行)。
使用数据仓库的核心目的是将分散的数据集中管理,并将其转化为可供分析、支持决策的高质量信息。
简单总结一下区别:
业务数据库:为“做业务”设计,像仓库管理员,快速记录每一笔进出货。
数据仓库:为“看业务”设计,像数据分析师,基于历史记录告诉你哪些货好卖、趋势如何。
白盒测试:看代码、走路径、逻辑覆盖要周全
黑盒测试:对需求、测功能、等价边界加场景
数据库中的“短路计算”,指的是程序在计算一个逻辑表达式时,一旦能根据已有信息确定整个表达式的最终结果,就会立即停止,不再继续计算剩余部分的一种优化行为。简单来说,就是“能提前判断结果就立刻停下来”。
表达式为 x and y or not z,根据优先级(not > and > or),等价于 (x and y) or (not z)。短路计算遵循从左到右、能提前确定结果就停止的原则。
当 x 为真时:需要计算 (x and y) 中的 y。
若 y 为真,则 (x and y) 为真,整个 or 表达式为真,无需计算 not z(即无需计算 z)。
若 y 为假,则 (x and y) 为假,整个 or 表达式需要计算 not z 才能确定结果,需要计算 z。
因此,x 为真时,根据 y 的值决定是否需要计算 z
集线器(Hub):物理层设备,作用:从一个端口接收信息,向其他所有端口广播。不解释信息含义,不识别协议数据单元(即不处理数据内容)。集线器端口构成冲突域:同一时刻只能有一个端口发送数据,否则冲突。
网桥:数据链路层设备,能识别数据链路层协议数据单元(帧),根据MAC地址进行转发。
交换机:多端口网桥,任何一对端口之间可同时转发数据。交换机端口构成一个广播域(即广播帧会传播到所有端口),但不是冲突域(即多个端口可同时发送数据而不冲突,因为交换机能隔离冲突域,每个端口独立冲突域)。
职务作品著作权归属分三种情况:
一般职务作品 → 归作者(员工),单位可在业务范围内优先使用。作品完成后两年内,作者不能擅自许可他人以相同方式使用。
特殊职务作品 → 归单位,作者只有署名权。包括:
主要用单位的设备/资金创作的工程设计图、软件等;
媒体单位员工的职务作品;
合同约定归单位的。
合同可以约定归属,但必须公平合理,不能剥夺员工正常的非职务成果。
冗余依赖
正确的判断标准是:去掉该依赖后,左边属性的闭包是否仍然包含右边的属性。
冗余依赖 = 去掉后,左边属性的闭包仍然包含右边属性
事务的四种隔离级别及其特征如下:
Serializable(串行化)
可避免脏读、不可重复读、幻读的发生。
Repeatable read(可重复读)
在开始读取数据(事务开启)时,不再允许修改操作,可避免脏读、不可重复读的发生,但不能避免幻读。
Read committed(读已提交)
一个事务要等另一个事务提交后才能读取数据,可避免脏读的发生。
Read uncommitted(读未提交)
一个事务可以读取另一个未提交事务的数据。最低级别,任何情况都无法保证。





评论