Linux

磁盘空间还有,但数据无法写入是什么原因?
  • inode

SQL

练习表
  • 在数据库中执行该sql语句,创建练习表
-- 创建部门表
CREATE TABLE departments (
    department_id INT PRIMARY KEY,
    department_name VARCHAR(100) NOT NULL
);

-- 插入部门数据
INSERT INTO departments VALUES
(1, 'Engineering'),
(2, 'Human Resources'),
(3, 'Marketing'),
(4, 'Finance');

-- 创建员工表
CREATE TABLE employees (
    employee_id INT PRIMARY KEY,
    first_name VARCHAR(50),
    last_name VARCHAR(50),
    gender CHAR(1),
    hire_date DATE,
    department_id INT,
    FOREIGN KEY (department_id) REFERENCES departments(department_id)
);

-- 插入员工数据
INSERT INTO employees VALUES
(101, 'Alice', 'Smith', 'F', '2015-03-01', 1),
(102, 'Bob', 'Johnson', 'M', '2017-07-15', 1),
(103, 'Carol', 'Williams', 'F', '2019-01-10', 2),
(104, 'David', 'Brown', 'M', '2018-11-23', 3),
(105, 'Eve', 'Davis', 'F', '2020-06-17', 4);

-- 创建薪资表
CREATE TABLE salaries (
    salary_id INT PRIMARY KEY,
    employee_id INT,
    salary DECIMAL(10,2),
    from_date DATE,
    to_date DATE,
    FOREIGN KEY (employee_id) REFERENCES employees(employee_id)
);

-- 插入薪资数据
INSERT INTO salaries VALUES
(1, 101, 80000, '2020-01-01', '2021-12-31'),
(2, 101, 85000, '2022-01-01', '9999-12-31'),
(3, 102, 75000, '2019-01-01', '9999-12-31'),
(4, 103, 60000, '2019-01-10', '9999-12-31'),
(5, 104, 72000, '2018-11-23', '9999-12-31'),
(6, 105, 68000, '2020-06-17', '9999-12-31');

-- 创建项目表
CREATE TABLE projects (
    project_id INT PRIMARY KEY,
    project_name VARCHAR(100),
    start_date DATE,
    end_date DATE
);

-- 插入项目数据
INSERT INTO projects VALUES
(1, 'Project Apollo', '2021-01-01', '2021-12-31'),
(2, 'Project Zephyr', '2022-03-01', NULL),
(3, 'Project Orion', '2020-06-01', '2021-06-30');

-- 创建员工-项目关系表(多对多)
CREATE TABLE employee_projects (
    employee_id INT,
    project_id INT,
    role VARCHAR(50),
    PRIMARY KEY (employee_id, project_id),
    FOREIGN KEY (employee_id) REFERENCES employees(employee_id),
    FOREIGN KEY (project_id) REFERENCES projects(project_id)
);

-- 插入员工项目关系数据
INSERT INTO employee_projects VALUES
(101, 1, 'Developer'),
(101, 2, 'Lead Developer'),
(102, 1, 'Tester'),
(103, 2, 'HR Manager'),
(104, 3, 'Marketing Lead'),
(105, 2, 'Accountant');

练习题 1:查询所有员工的姓名及其所在部门名称
  • 查询每位员工的 first_name、last_name 和其对应的 department_name。
SELECT e.first_name, e.last_name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.department_id;

练习题 2:查询当前薪资最高的员工信息
  • 找出当前薪资(to_date = ‘9999-12-31’)最高的员工的姓名和薪资。
SELECT e.first_name, e.last_name, s.salary
FROM salaries s
JOIN employees e ON s.employee_id = e.employee_id
WHERE s.to_date = '9999-12-31'
ORDER BY s.salary DESC
LIMIT 1;

练习题 3:查询每个部门的平均薪资
  • 统计每个部门当前员工的平均薪资。
SELECT d.department_name, AVG(s.salary) AS average_salary
FROM employees e
JOIN departments d ON e.department_id = d.department_id
JOIN salaries s ON e.employee_id = s.employee_id
WHERE s.to_date = '9999-12-31'
GROUP BY d.department_name;

练习题 4:查询参与了“Project Zephyr”的员工及其角色
  • 列出参与了名为“Project Zephyr”的员工的姓名和他们的角色。
SELECT e.first_name, e.last_name, ep.role
FROM employee_projects ep
JOIN employees e ON ep.employee_id = e.employee_id
JOIN projects p ON ep.project_id = p.project_id
WHERE p.project_name = 'Project Zephyr';

练习题 5:查询至少参与了两个项目的员工
  • 列出参与两个或以上项目的员工的姓名和项目数量。
SELECT e.first_name, e.last_name, COUNT(ep.project_id) AS project_count
FROM employee_projects ep
JOIN employees e ON ep.employee_id = e.employee_id
GROUP BY e.employee_id
HAVING COUNT(ep.project_id) >= 2;

练习题 6:查询“Engineering”部门的所有员工当前薪资
  • 显示 Engineering 部门所有员工的姓名及其当前薪资。
SELECT e.first_name, e.last_name, s.salary
FROM employees e
JOIN departments d ON e.department_id = d.department_id
JOIN salaries s ON e.employee_id = s.employee_id
WHERE d.department_name = 'Engineering'
  AND s.to_date = '9999-12-31';

练习题 7:查询所有项目的负责人(Lead Developer 或 Manager)
  • 列出所有项目的名称、员工姓名及他们担任的领导角色(如包含“Lead”或“Manager”的角色)。
SELECT p.project_name, e.first_name, e.last_name, ep.role
FROM employee_projects ep
JOIN employees e ON ep.employee_id = e.employee_id
JOIN projects p ON ep.project_id = p.project_id
WHERE ep.role LIKE '%Lead%' OR ep.role LIKE '%Manager%';

练习题 8:查询未参与任何项目的员工
  • 列出没有参与任何项目的员工。
SELECT e.first_name, e.last_name
FROM employees e
LEFT JOIN employee_projects ep ON e.employee_id = ep.employee_id
WHERE ep.project_id IS NULL;

网络

访问一个网站背后的过程?
  1. 首先通过DNS解析,将域名解析为对应的IP地址,解析顺序依次为 “浏览器缓存” → “本地 hosts 文件” → “递归查询(本地 DNS)” → “迭代查询(根→顶级→权威)”;
  2. 获取到IP地址后,进行TCP三次握手建立连接;
  3. 如果请求的域名是HTTPS的,客户端和服务器还会通过 TLS 握手 建立加密通道,握手过程包括证书验证、密钥协商等;之后所有 HTTP 报文将通过 TLS 进行加密传输。
  4. 之后向服务端发送HTTP请求报文,请求报文中包含方法(GET/POST/PUT/DELETE等)、路径、HTTP版本号、首部字段(一组键值对,如 Host、User-Agent、Accept 等 )、请求体(可选,例如在 POST 请求中携带 JSON 数据);
  5. 服务端收到后,返回HTTP响应报文,其中包含版本号、状态码、短语、首部字段(Content-Type、Server等)、空行、数据实体;

DNS解析流程?
  • 将域名解析为对应的IP地址,解析顺序依次为 “浏览器缓存” → “本地 hosts 文件” → “递归查询(本地 DNS)” → “迭代查询(根→顶级→权威)
  • DNS相关的测试工具有 dig、nslookup

什么是递归查询?什么是迭代查询?
  • 假设宿主机的 DNS 服务器指向了 223.5.5.5(阿里云DNS),本机向这个 DNS 服务器发起的查询就是递归查询;
  • 而这个DNS服务器(223.5.5.5),如果没有命中缓存,它会向根服务器、顶级服务器、权威服务器发起的查询,就叫迭代查询。

迭代查询的细节简述?
  • 访问 www.baidu.com 时,如果本地 DNS 没有缓存,它会向根服务器查询,根服务器返回 .com 的 DNS 地址;
  • 接着去问 .com 顶级域服务器,它告诉你 baidu.com 的权威服务器地址;
  • 最后向权威服务器请求 www.baidu.com 的 IP,获取成功后返回给客户端。这个过程就是迭代查询。

TCP三次握手的过程?
  • 第一次握手(SYN):客户端向服务器发送一个 SYN。此时,客户端进入 SYN_SENT 状态;
  • 第二次握手(SYN-ACK):服务器收到客户端的 SYN 报文后,会发送一个 SYN-ACK(同步确认)报文段作为响应。此时,服务器进入 SYN_RCVD 状态;
  • 第三次握手(ACK):客户端收到服务器的 SYN-ACK 报文后,发送一个 ACK(确认)报文段作为最终确认。此时,客户端进入 ESTABLISHED 状态。服务器收到客户端的 ACK 后也进入 ESTABLISHED 状态,连接正式建立。

TCP和UDP的区别?
  • 连接与可靠性:TCP是面向连接的,会通过三次握手、流量和拥塞控制、重传等机制使传输更可靠 ;而UDP仅提供最基本的数据传输能力,只管发送,不管对方是否收到。
  • 传输速度:UDP要高于TCP。
  • 首部结构:
    • TCP首部最小为20字节,最大为60字节(其中包括源端口、目标端口、序列号、确认号、6个标志位、效验和、滑动窗口、紧急指针、数据实体等)
    • UDP首部为8个字节(其中包括:源端口、目标端口、长度、效验和、数据实体)
  • 通信方式:TCP 仅支持一对一通信(因为只能和一台主机建立三次握手);UDP支持一对一、一对多的方式通信。
  • 应用场景:TCP 适合下载文件等对数据传输可靠性要求高的场景;而 UDP 更多的用于在线视频、语音等对数据实时传输效率要求高的场景,像 DNS、DHCP 协议是基于 UDP 的。

OSI七层模型指的是哪几层?分别有什么协议?
  • xxx

常见服务的端口号
  • 未特别说明的,均为TCP,但有些服务,既支持UDP,又支持TCP
    PortService
    20ftp-data
    21ftp
    22ssh
    23telnet
    25smtp
    53/udpdns
    67/udpdhcp
    68/udpdhcp-client
    69/udptftp
    80http
    110pop3
    119nntp
    123/udpntp
    443https
    465smtps
    514/udpsyslog
    873rsync
    995pop3s
    1521oracle
    2049nfs
    3306mysql
    3389rdp
    5432postgresql
    5672RabbitMQ
    6379redis
    8080tomcat
    9092Kafka
    9200elasticsearch
    11211memcached

监控与告警

如何设计一个有效的监控系统?
  • 全面覆盖:监控系统应覆盖所有关键组件,包括服务器、网络设备、数据库、应用层等。
  • 多维度监控:不仅要监控硬件资源(如CPU、内存、磁盘),还要监控应用性能(如请求延迟、错误率)和服务健康状态(如服务可用性)。
  • 实时性:监控数据应尽可能实时更新,以便及时发现问题。
  • 告警机制:设置合理的告警阈值和通知渠道(如邮件、短信、Slack等),确保问题能够被及时发现和处理。
  • 可视化:通过仪表盘展示关键指标,便于快速了解系统状态。
  • 历史数据分析:保留历史数据,便于分析趋势和排查问题。

如何设置合理的告警阈值?
  • 基于历史数据:分析历史数据,了解系统的正常运行范围,确定合适的阈值。
  • 区分紧急和非紧急告警:对于影响用户体验的关键指标(如请求失败率),设置较为严格的阈值;对于次要指标(如磁盘使用率),可以适当放宽。
  • 动态调整:随着系统负载的变化,定期调整告警阈值,确保其始终处于合理范围内。
  • 避免误报:设置合理的触发条件,避免频繁的误报。例如,可以通过多次采样确认异常后再触发告警。

CI/CD

容器

容器与虚拟机的区别?
  • 容器使用宿主机的内核,而每个虚拟机都有自己独立的内核。
  • 容器更加轻量,迁移和部署都比虚拟机更加方便,并且启动速度也要比虚拟机快。
  • 容器的隔离性不如虚拟机,因为容器是公用宿主机的内核 + 通过内核的 namespace 实现隔离。

namespace 有哪些?
  • PID namespace:隔离进程 ID(每个 namespace 中的进程看到的 PID 是独立的)
  • Network namespace:隔离网络资源(如网卡、IP 地址、端口等)
  • Mount namespace:隔离文件系统挂载点
  • UTS namespace:隔离主机名和域名
  • User namespace:隔离用户和组 ID
  • IPC namespace:隔离进程间通信(如信号量、消息队列等)

Dockerfile 的指令有哪些?
  • FROM 指定基础镜像。
  • LABEL 给镜像打标签。
  • COPY 将宿主机文件。
  • COPY 和 ADD,都可以将宿主机的文件拷贝至容器,但 ADD 还可以将压缩包解压缩拷贝,以及从 URL 下载文件拷贝至镜像。
  • RUN 指定镜像构建时执行的shell命令,每个 RUN 指令都会在镜像中新增一层。
  • ARG 和 ENV,ARG 是 “构建时变量”,ENV 是 “运行时变量”。
  • USER 指定容器运行时的用户。
  • WORKDIR 为后续的RUN、COPY、ADD、CMD、ENTRYPOINT等指令设置工作目录”(构建和运行阶段均生效)
  • EXPOSE 声明容器运行时 “监听的端口”(仅为元数据说明,不实际映射端口)。
  • VOLUME 定义容器中的 “匿名卷”(持久化数据的目录),避免容器内的数据随容器删除而丢失。
  • ONBUILD 定义 “触发器”,当当前镜像被用作其他镜像的基础镜像时,自动执行ONBUILD后的指令。
  • STOPSIGNAL 指定容器停止时发送的系统信号(默认是SIGTERM),用于优雅关闭应用。
  • CMD 和 ENTRYPOINT,都是指定容器启动时的命令,但 ENTRYPOINT 不可变。CMD 通常作为容器启动时的参数,可以被覆盖。

Dockerfile 中的 COPY 和 ADD 有什么区别?
  • COPY 和 ADD,都可以将宿主机的文件拷贝至容器,但 ADD 还可以将压缩包解压缩拷贝,以及从 URL 下载文件拷贝至镜像。

Dockerfile 中的 CMD 和 ENTRYPOINT 有什么区别?
  • CMD 和 ENTRYPOINT,都是指定容器启动时的命令,但 ENTRYPOINT 不可变。CMD 通常作为容器启动时的参数,可以被覆盖。

使用 Dockerfile 制作镜像时,如何减少镜像的大小?
  • 使用轻量级镜像,如 alpine。
  • 清理无用的缓存文件。
  • 尽量减少 RUN 指令的使用,以减少镜像分层。
  • 使用多阶段构建,比如第一阶段使用nodejs镜像构建前端代码,第二阶段仅把前端构建结果拷贝至nginx镜像。