作者: HalfSugar

  • 一些工具链/中间件的面经(可能算是面经)(纯原创哦)

    有部分排版问题,懒得改了,这是 Markdown 复制过来的。

    Kafka & NATS

    Kafka:高吞吐、持久化的消息系统

    端口:9092

    通俗来说干嘛的?把各种服务的消息丢进去的日志,然后提供接口给分析系统分析数据。(不是 Kafka 里的,是开发者写的)

    Apache Kafka 是一个分布式、高吞吐、持久化日志系统,常用于日志收集、实时流处理、大数据管道。

    Kafka 是什么?

    • Kafka 是由 Apache 基金会开发的 Java 程序
    • 运行 Kafka 需要:
    • 启动 Kafka 服务器(kafka-server-start.sh
    • 依赖 ZooKeeper(新版已支持移除)
    • 你的程序(用 Go、Java、Python 都可以)通过 Kafka 客户端 SDK 与 Kafka 服务交互

    Kafka 是一个独立运行的服务进程

    Go 程序只是客户端(通过 confluent-kafka-go 等包)

    模块

    模块说明
    Producer负责向 Kafka 集群发送消息,支持异步、分区路由、压缩等
    BrokerKafka 服务端组件,接收消息、保存日志、处理消费者请求
    Topic & Partition逻辑主题划分单位,Partition 是并发和负载均衡的核心
    Consumer订阅并消费消息,支持 Consumer Group 机制实现负载均衡
    Zookeeper / Kafka ControllerKafka 的控制面组件(早期依赖 ZK,现在逐步用 Raft 替代)
    Storage Layer (Log)顺序写入磁盘、段文件、索引文件、高效日志存储
    Replication分区副本机制,用于高可用,支持 ISR(in-sync replica)机制

    特点

    • 持久化强:数据写入磁盘,可保留几天或几周
    • 高吞吐:适合处理大量数据(TB 级别)
    • 可分区/可复制:天生支持分布式
    • 消费者分组:支持一个 topic 多个消费者组并发消费

    使用场景

    • 日志收集(ELK)
    • 实时指标分析
    • 流式数据处理(如 Spark、Flink)
    • 解耦微服务的数据依赖

    Kafka 怎么保证消息不丢失

    Kafka 消息可靠性保证主要依赖以下机制:

    1. 磁盘顺序写(持久化)
      消息写入后立即追加到分区日志文件(log segment),并调用 fsync 或定期刷盘,防止进程崩溃或重启导致数据丢失。
    2. 多副本机制(Replication)
      每个分区有多个副本(replica),分布在不同 broker 上,一个 leader 负责读写,follower 从 leader 拉取数据。
    3. ACK 确认机制
      生产者发送消息时可设置 acks 参数:
    • acks=0:不等待确认(可能丢失)
    • acks=1:leader 写入成功即返回(follower 可能还没同步)
    • acks=-1 / all:leader 和 ISR(in-sync replicas)中所有副本写入成功才返回,可靠性最高
    1. ISR(同步副本集合)
      Kafka 维护一个与 leader 数据完全同步的 follower 列表,只有 ISR 中的副本才参与 acks=-1 的确认,保证数据一致性。

    NATS:轻量、低延迟的消息系统

    端口:4222

    通俗来说干嘛的?微服务之间快速传递消息、实时反应。它可以把一个服务的消息转发给多个服务,例如下单服务把信息发给风控、通知、日志。

    NATS 是一个轻量级、高性能、低延迟的消息系统,强调简洁与速度,使用 Go 实现。

    NATS 是什么?

    • NATS 是一个用 Go 写的消息服务器程序
    • 核心组件是 nats-server,你需要像运行 Redis 一样运行它
      nats-server -p 4222
    • 客户端用各种语言 SDK 连接(如 github.com/nats-io/nats.go

    NATS 也是一个独立运行的服务进程

    你写的 Go 程序用 SDK 与它通信,不嵌入服务逻辑。

    特点:

    • 低延迟:通信延迟非常小(微秒级)
    • 轻量简洁:安装快,配置少
    • 适合内网微服务通信:默认不做持久化(JetStream 扩展支持持久化)
    • 支持 Pub/Sub、Request/Reply

    使用场景:

    • 微服务之间通信
    • 实时事件广播(如游戏、IoT)
    • 替代 RPC 通信

    对比

    特性KafkaNATS
    持久化强,磁盘存储,适合大数据默认无(JetStream 才支持)
    吞吐非常高高,但主要强调低延迟
    延迟毫秒级微秒级
    安装复杂度较复杂非常轻量
    编程模型Topic + 分区 + 消费者组Pub/Sub、Req/Reply、Queue
    使用语言Java 生态(客户端支持广)原生 Go 实现(跨语言支持)
    典型使用场景日志处理、大数据管道微服务通信、事件通知

    Kafka 和 NATS 都是独立运行的程序(Server)

    就像 MySQL 一样,它们是需要单独部署和运行的服务进程,而不是 Go 的普通库。

    Redis – Remote Dictionary Server

    端口:6379、6380(TLS)

    Redis 是一个巨大的 Map,存储 key-value。


    〇、为什么快

    1. 内存数据库
    2. 单线程避免上下文切换
    3. I/O 多路复用,同时监听大量连接,实现高并发处理。
    4. 高效的数据结构
    5. 避免磁盘随机 I/O。绝大部分操作在内存中完成,持久化是异步写磁盘,减少阻塞。
    6. 命令简单且优化到位

    一、数据结构

    核心类型

    1. String:最基础的类型,支持整数、浮点、位操作等。
    2. List:双向链表,支持队列、栈操作。
    3. Set:无序集合,元素唯一。
    4. Hash:键值对集合,适合表示对象。
    5. ZSet(Sorted Set):有序集合,基于分数排序。
    6. Bitmap:位图,节省空间进行布尔标记。
    7. HyperLogLog:用于基数估算,节省内存。
    8. Geo:地理空间索引。
    9. Stream:可持久化的消息队列结构。

    二、底层实现

    Set

    • intset:元素全为整数,数量少时使用。
    • hashtable:元素多或包含非整数时自动转换。

    ZSet

    • 跳表(skiplist):按分数有序,用于范围查询、排名。
    • 哈希表:存储成员到分数的映射。
    1. ZSet 的使用场景:
    • 实时排行榜(按分数排序)
    • 延时队列(score 为时间戳)
    • 社交平台点赞、活跃度排序
    • 分布式优先级任务队列
    • 电商按权重排序推荐
    1. 分数相同时的排序规则:
    • Redis 在分数相同情况下,按成员名的字典序排序

    三、跳表机制

    跳表简介

    跳表是一种分层链表结构,每一层都是下层的索引。它以概率方式构建索引,平均查找、插入、删除 时间复杂度为对数级别。跳表的节点指针较少,适合内存存储。

    在 Redis 中,ZSet 的排序部分由跳表实现,支持按分数范围查询、按排名获取等操作。

    复杂度

    • 查找:O(log n)
    • 插入:O(log n)
    • 删除:O(log n)
    • 区间范围查找:O(log n + m),m 为结果集大小

    还有哪些查找数据结构复杂度为 O(log n)

    • 平衡二叉搜索树(AVL 树、红黑树)
    • B 树 / B+ 树(数据库索引结构)
    • 二分查找(在有序数组上)
    • 堆(heap)查找最大/最小值是 O(1),插入/删除是 O(log n)
    • Treap(随机平衡树)

    为什么不用二叉树而用跳表

    • 二叉树需要复杂的旋转维持平衡,实现和维护难度大;跳表实现简单,插入删除无需全局重平衡。
    • 链表结构天然支持范围扫描,跳表的多级索引能直接跳过不相关区间,非常适合范围查询(ZREVRANGEBYSCORE 等)。
    • 内存利用率和局部性:跳表是分层链表结构,插入删除局部修改;二叉树在大量插入删除下可能退化(或需要频繁旋转)。
    • Redis 压缩结构优化:跳表在节点数量小的时候可以用压缩列表节省空间。

    最大高度

    Redis 跳表默认最大高度是 32 层,由常量定义。实际构建过程中,大多数节点只参与低层索引。

    建立层数算法

    Redis 使用随机算法决定层数,采用几何分布(概率为 1/4)。具体算法是:

    • 初始为 1 层
    • 每次以概率 1/4 决定是否增加一层
    • 最终不超过最大高度 32 层

    这个设计保证了跳表整体平衡,同时插入开销较小。


    四、持久化机制 AOF 与 RDB

    AOF(Append Only File):每次写操作都追加一条命令到日志,恢复时可以按顺序重放。

    RDB(Redis Database Backup):定时将内存快照保存为二进制文件。

    项目RDBAOF
    落盘机制周期性快照每次写操作记录为命令
    文件格式二进制压缩快照文本命令日志(可读)
    典型内容紧凑、序列化后的 key-value原始命令,例如:SET x 123
    恢复速度快,加载快照慢,重放命令
    数据安全可能丢失几分钟丢失几秒甚至 0
    占用空间小,可压缩大,命令多,冗余
    使用场景快速恢复、冷备实时恢复、热备

    推荐方式

    • AOF + RDB 混合模式
    • 配置 appendfsync 策略控制写频率

    五、分布式与一致性问题

    分布式 + 主从

    Redis 是内存数据库,如果数据太大存不下怎么办?

    分布式存储 + 哈希槽。

    存不下就做多个 Redis 节点。数据通过哈希计算,落入某个哈希槽,每个分布式节点负责一部分 哈希槽。每个节点都记录了其它节点负责的哈希槽。

    哈希槽在这里是一种中间件,负责逻辑上存储一个数据,然后被一个节点负责存储。当添加节点,我们只需要把部分哈希槽挪进新的节点即可。

    当客户端查询 Redis,它可能访问了错误的节点,这个节点没有对应数据。但由于每个节点都存储了其它节点负责的哈希槽,所以它会返回一个 error,并告诉客户端,到哪里去找正确的数据。这叫 重定向

    如果某节点正在迁移入数据,此时客户端来查找数据,该节点并不知道这个数据是不存在还是未迁入。它通过重定向,让客户端访问数据迁出的节点,即可查到或知道数据不存在。

    每个 Redis 节点可以设置多个从节点,当主节点失效,使用从节点替代。

    主从的哨兵机制

    Redis 的哨兵机制是 Redis 官方提供的高可用组件。它独立于 Redis 服务器进程,负责实时监控主从节点的健康状态。一旦主节点宕机,会自动完成主从切换,并通知客户端更新连接配置,从而实现自动故障转移和服务可用性保障。

    哨兵的三个核心功能:

    1. 监控(Monitoring)
      哨兵不断 ping 主从 Redis 节点,检测其是否宕机。
    2. 通知(Notification)
      当发现某个 Redis 实例出现问题,会通知管理员或系统。
    3. 自动故障转移(Automatic failover)
      当主节点不可用,哨兵会自动将某个从节点升级为主节点,并更新其他从节点的主节点指向。

    注意事项

    • 哨兵本身也是一个独立的进程,需要部署多个实例(通常奇数个)以实现多数投票机制。
    • 客户端通常要支持哨兵协议,或借助中间件实现连接重定向。

    一致性

    1. Redis 不是强一致性的,你下发通知可能会失败的;
    2. 修改全局内存涉及并发安全。
    3. 关于 Redis 不强一致性的问题:
      • Redis 的 Pub/Sub 是 “至多送达”模型,如果订阅者挂了或重启、网络抖动,可能 漏掉消息
      • 所以可以采用如下改进方案:
        • 使用 可靠消息队列 替代(如 Kafka、NATS)
        • 或者在 Redis 里再维护一份 “修改版本号 + 数据快照”
        • 每次订阅后拉取当前版本,与本地版本比对是否同步,不一致则全量拉取

    Redis 的 Pub/Sub 不保证强一致

    • 采用“至多送达”,可能丢失消息
    • 改进方案:
    • Kafka、NATS 等消息队列替代
    • 增加版本号和快照机制进行补偿

    替代中间件(配置中心)

    • Nacos / Apollo / etcd / Consul
    • 特性:
    • Watch 监听变更
    • 版本控制、回滚
    • 多副本一致性(Raft)

    六、MySQL 为主,Redis 为 cache 的正确做法:Cache Aside 模式

    一般缓存的顺序:用户请求->HTTP 缓存->CDN 缓存->代理服务器缓存->进程内缓存->分布式缓存->数据库

    本地缓存(Local Cache) 和 分布式缓存(Distributed Cache)。本地缓存更注重 访问速度,而分布式缓存则关注 数据一致性和扩展性。

    写流程

    1. 客户端写数据 → 写入 MySQL
    2. 写成功后 → 删除对应 Redis 缓存
    3. 下次查询会从 MySQL 读,然后写入 Redis(懒加载)

    读流程

    1. 查询先查 Redis(命中 → 返回)
    2. 未命中 → 查 MySQL → 回写 Redis

    为什么写后要删 Redis?

    • 避免读到旧值(写 MySQL 后 Redis 还留着旧缓存)

    延迟双删策略

    SET MySQL
    DELETE Redis
    sleep 50ms
    DELETE Redis again

    问题背景:并发写入场景下的数据不一致

    我们来举一个具体的例子:

    假设两个请求并发发生:

    1. 请求 A:查询数据
    • 查 Redis,没有命中 → 查数据库 → 读到旧值 → 写入 Redis 缓存
    1. 请求 B:更新数据
    • 写 MySQL 成功
    • 立即删除 Redis 缓存

    这时出现问题了:

    • 请求 B 删除了缓存
    • 但请求 A 随后写入了旧数据到 Redis
    • 最终 Redis 缓存反而是“脏的”,与数据库不一致!

    延迟双删就是为了解决这个时序问题。

    缓存穿透

    突发大流量查询,Redis 中没有缓存,MySQL 中也没有。造成大量无效查询。这是一种恶意攻击。可能造成数据库宕机。如果 key 变化不大,就把这个 key 放进 Redis,注意设置过期时间,避免大量占用 Redis。

    其它方法:

    • 接口验证筛掉明显不合理的查询
    • 针对 IP 或者用户限流
    • 布隆过滤器:用于判断:“某个元素可能在集合中”或“一定不在集合中”。

    缓存击穿

    大流量对小部分 key 查询,Redis 可以扛住,但如果 Redis 中的缓存突然失效(超时),大量流量就会进入数据库,造成宕机。

    解决方案:

    • 设置热点数据永不过期或者热点时期不过期。
    • 加互斥锁,保证只有少量的请求到达数据库。

    缓存雪崩

    大流量对大量数据查询,由于某些原因,大量的 key 几乎同时过期。原因比如不合理的过期时间,或者 Redis 失效。这样会导致数据库压力大甚至死机。

    解决方案:

    • 保证 Redis 可用。使用 Redis 集群、主从。持久化,重启后自动加载数据。
    • 多级缓存,使流量不会全部涌入数据库。
    • 设置随机的失效时间,避免大量数据同时过期。
    • 提前预热,热 key 提前加载。
    • 数据库限流,限制大量流量进入数据库。

    七、常见问题与优化建议

    1. 避免大 key 和 big value

    • 大 key 占内存高,迁移慢,删除慢,可能导致阻塞。
    • big value 影响网络传输效率,压垮带宽。
    • 建议 key 控制在几十字节以内,value 不超过几 KB。

    2. 避免慢查询(slowlog)

    • 单条命令执行时间超 1 毫秒可视为慢查询。
    • 常见慢命令:KEYS、SCAN 大量遍历,SORT、ZRANGE 大范围排序。
    • 使用 slowlog 命令排查问题。

    3. 合理设置过期时间

    • 防止缓存击穿、雪崩。
    • 为所有缓存 key 设置 TTL,避免内存泄露。
    • 使用随机过期时间,防止同时过期造成突发流量。
       EXPIRE key $((300 + RANDOM % 60)) # 随机过期时间

    4. 谨慎使用危险命令

    • KEYS:全库扫描,易阻塞
    • FLUSHALL:清空库
    • EVAL:脚本执行时间长可能阻塞

    5. 连接数限制

    • 默认支持的最大连接数有限(一般 10000 左右),建议使用连接池。
    • 服务端设置 maxclients 限制连接总数。

    6. 限制内存使用,防止 OOM

    • 设置 maxmemory 和 maxmemory-policy(如 volatile-lru、allkeys-lru)
    • 设置淘汰策略防止写满内存导致 Redis crash

    7. 持久化配置建议

    • AOF 可靠但体积大,写频繁时会影响性能。
    • RDB 性能高但可能丢数据。
    • 推荐 AOF + RDB 混合模式,设置合适的 appendfsync 策略。

    8. 单线程阻塞问题

    • Redis 单线程处理命令,阻塞命令会拖慢所有请求
    • 保证每条命令都轻量快速,必要时拆分任务逻辑。

    9. 安全性建议

    • Redis 默认无密码,不能暴露在公网
    • 使用 bind、requirepass、rename-command 等限制访问。
    • 使用防火墙或内网隔离。

    10. 事务和原子性

    Redis 的事务采用 Lua 脚本来完成。

    优点:

    • 原子性强,天然保证“要么都成功,要么都失败”
    • 脚本里可以自由组合对任意 key 和数据结构的操作(支持 string、hash、list、zset、set)
    • 简单、高效、业务侧无感

    适用场景:

    • key 数量可控(Redis 限制 8000 key 内)
    • 不依赖外部状态(Lua 脚本无法调用网络或持久化)

    其实也用(MULTI / EXEC)语句来做事务,但

    • 仅保证命令按顺序执行,不支持失败自动回滚
    • 一旦某个命令失败,其他命令不会自动撤销

    11. 扩展能力

    • 使用 Redis Cluster 进行分片
    • 使用 Codis、Twemproxy 做代理分布式扩展

    12. Redis 热 key 如何优化?

    热 key 问题指某个 key 被高频访问或更新,导致 Redis 单线程被拖慢或热点迁移时压力集中。

    优化方案:

    1. 加本地缓存,如使用 Go 的 sync.Map 或 LRU 缓存,避免频繁请求 Redis
    2. 用 Nginx 或业务层加缓存 TTL 分散访问压力
    3. 使用分片 key(如给 key 加随机后缀),读时做合并处理
    4. 利用 Redis Cluster,将热 key 分散到不同 slot(但不能 hash 到同一 slot)
    5. 对频繁写的热 key,引入消息队列异步落入 Redis
    6. 使用 Lua 脚本或 pipeline 合并访问

    八、大 key 删除方法

    Redis 删除大 key(比如:包含百万元素的 list、set、hash、zset)时,不能直接使用 DEL key,否则可能阻塞 Redis 主线程,导致服务卡顿甚至宕机

    Redis 是单线程模型,任何慢操作都会阻塞整个服务,包括大 key 的删除。下面是应对这种情况的几种推荐做法。

    为什么不能直接 DEL 大 key?

    • DEL key 是同步删除
    • 会立刻释放该 key 占用的内存
    • 如果 key 很大(如 set 有 1 百万个元素),这个释放过程耗时可能达数百毫秒甚至几秒
    • Redis 是单线程,这期间其他命令无法响应

    正确做法:使用非阻塞删除机制

    1. 使用 Redis 4.0+ 的 UNLINK 命令(推荐)
        UNLINK mybigkey
    - **非阻塞删除**
    - Redis 会将 key 从主线程移除,然后在后台线程中异步释放内存
    - 非常适合删除大 key
    - 和 DEL 命令相比,UNLINK 不会阻塞主线程
    支持版本:Redis 4.0 及以上
    1. 分批删除 key(适用于集合类型)
      如果你用的是老版本 Redis(不支持 UNLINK),或者你想更细致控制,可以手动分批删除 key 内的元素
      举例:删除一个大 hash
        HSCAN myhash 0 COUNT 1000
        HDEL myhash field1 field2 ... field1000
    你可以通过脚本或后台服务批量扫描并删除字段,确保每次操作耗时短。
    优点:控制删除粒度
    缺点:逻辑复杂、过程长、删除期间 key 仍存在
    1. 设置过期时间(让 Redis 自己异步删除)
        EXPIRE mybigkey 60
    - 给大 key 设置过期时间,交由 Redis 定时删除
    - Redis 的过期删除是**惰性 + 定期 + 分批策略**,不会一下子删完,也不会阻塞主线程
    - 适用于可接受数据短时间“滞留”的场景
    1. 后台迁移到空数据库,再删除(高级操作)
      • 将大 key 转移到 Redis 空闲的 DB(如 SELECT 15)然后对整个 DB 执行 FLUSHDB ASYNC(Redis 4.0+)
      bash
      MOVE mybigkey 15
      SELECT 15
      FLUSHDB ASYNC
     - FLUSHDB ASYNC 会在后台清除整个数据库,不阻塞主线程
    
     - 非常适合清理批量垃圾数据或灰度下线数据
    
    ---
    
    ## 九、分布式锁
    
    Redis 分布式锁是基于 Redis 的原子操作实现的一种跨进程、跨服务的互斥机制,常用于分布式系统中协调资源访问、保证操作串行化。
    
    1. **基本原理**
    
       利用 Redis 提供的 `SET` 命令的原子性,在设置 key 的同时带上互斥标志和过期时间,达到加锁效果。
    
       核心命令:

    bash
    SET lock_key lock_value NX PX 3000

       含义:
    
       - `NX`:只有当 key 不存在时才设置,确保互斥
       - `PX 3000`:设置过期时间(毫秒),防止死锁
       - `lock_value`:唯一标识当前持锁方(如 UUID)
    
       释放锁:

    bash
    if redis.call(“GET”, KEYS[1]) == ARGV[1] then
    return redis.call(“DEL”, KEYS[1])
    else
    return 0
    end

       避免误删:只有当前持锁者才能释放锁
    
    2. **使用流程**
    
       1. 客户端尝试加锁,使用 `SET NX PX`
       2. 加锁成功:继续执行业务
       3. 加锁失败:可以重试(带退避)或放弃
       4. 业务执行完毕后,校验后释放锁
    
    3. **Redlock 分布式锁协议(官方推荐)**
    
       适用于多节点 Redis 的高可用场景,基本思想是:
    
       - 同时向多个 Redis 实例写入锁(一般是 5 个)
       - 成功获取过半实例的锁(如 3 个),视为加锁成功
       - 设置统一过期时间,所有节点自动释放
       - 释放锁时向所有 Redis 释放本客户端持有的锁
    
       Redlock 优点:
    
       - 容错性强,任意少数实例故障不影响锁可用性
       - 避免主从不一致或节点抖动造成的锁误用
    
       但也存在争议,要求系统时钟稳定、Redis 之间时延低
    
    4. **常见应用场景**
    
       - 秒杀场景:防止超卖,控制库存扣减操作的并发性
       - 防止重复提交表单
       - 分布式定时任务:控制定时任务只在一个节点执行
       - 创建唯一资源(如订单号、快照 ID)
    
    5. **实现时的注意事项**
    
       1. 锁必须设置过期时间,防止程序异常死锁
       2. 锁标识必须唯一,释放锁前必须比对持有者身份
       3. 锁不可重入(默认),如需可重入需自行扩展
       4. 尽量用 Lua 脚本执行解锁逻辑,避免非原子性
       5. 不推荐使用 `SETNX` 和 `EXPIRE` 分开写,可能造成非原子加锁
    
    6. **示例伪代码**(Go)

    go
    // 加锁
    success := redisClient.Set(“lock:order”, uuid, “NX”, “PX”, 3000).Result()

    // 解锁(Lua 脚本)
    script := if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end
    redisClient.Eval(script, []string{“lock:order”}, uuid)

    ---
    
    ## 十、实战设计
    
    1. 生成以小时为单位的前 100 首歌曲榜单,怎么设计?
    
       - 每小时一个 zset,key 格式为 `rank:songs:2025080710`
       - 成员为歌曲 ID,score 为播放次数
       - 业务写入时使用 `ZINCRBY rank:songs:2025080710 1 song123`
       - 使用 `ZREVRANGE key 0 99 WITHSCORES` 获取排行榜
    
    2. 听歌业务和榜单业务如何通信?
    
       - 听歌服务负责更新计数
       - 榜单服务定时统计,可以使用:
         - Redis Stream(推送播放事件)
         - 发布订阅 PubSub(轻量级广播)
         - Kafka 等异步日志(可跨服务)
    
    3. 听歌用户量非常大,都写入一个 zset 会不会有问题?
    
       是,会导致高并发写入冲突和大 key 问题。
    
       解决方案:
    
       - 按时间窗口分 key(小时或分钟)
       - 按用户 ID 取模 hash 到多个 zset,汇总时用 `ZUNIONSTORE`
       - 使用队列异步写入,分批更新
       - 关键写操作用 Lua 脚本合并多操作
    
    ---
    
    # MongoDB
    
    端口号:27017
    
    JSON-like 存储 → 和 Go 的 struct/json 转换天然兼容;无需严格表结构,开发快。
    
    查询语言:BSON 文档查询语言--MongoDB Query Language
    
    查找语句

    mariadb
    db.users.find({id:”123″})

    由于 Json 不支持二进制,所以 Json + Binary = Bson。MySQL 的一行对应的就是 MongoDB 的一个 Bson 文档。一个表就是一个 `xxx.wt` 文件。
    
    ## 数据页
    
    直接读取大的 wt 文件很慢,所以一个 wt 内部拆成多个页。读取时按页读取即可。一般每个页是 32 KB。
    
    ### 快速找到某数据的数据页
    
    每个页有页号,每条记录有 ID。把每个 ID 的页号放在一个页里快查,这就是 B+ 树索引。
    
    ## 写时复制机制
    
    MySQL 在更新 B+ 树索引时加锁,而 MongoDB 更新索引,把要写的复制出来写,而原数据则继续对外供读。后面再找机会合并。
    
    ## Cache 和 Journal
    
    MongoDB 带有内存 Cache 机制,为防止 内存的 Cache 丢失,用 Journal Buffer 机制,把所有 写 操作同步到 内存的 Journal Buffer 里,再定时把这个 Journal Buffer 写入磁盘的 Journal 文件里。为什么不直接写入 DB?因为 Journal 文件顺序写入,MongoDB 分散,顺序写入性能高很多。所以这也是日志备份重放的一个重要原因。定时从内存 Cache 写入磁盘 MongoDB,就是 CheckPoint 机制,写入后可以安全删除写入前的 Journal 记录。
    
    ## WiredTiger
    
    以上这些机制,加上对数据库增删查改的接口,就是一个存储引擎,称为 WiredTiger。
    
    ## Server 层
    
    分为
    
    1. 连接管理,管理外部对数据库的连接
    2. 解析器,解析语句,检查语法
    3. 优化器,选择索引生成执行计划
    4. 执行器,执行计划
    
    ## 解耦合
    
    存储引擎层和 Server 层通过接口函数进行解耦,只要实现了需要的接口,就可以替换。
    
    ## 扩展性
    
    分库分表分节点。把数据分成多个节点存。要怎么找呢?加一层中间层“路由服务” `Mongos`。每个节点将自身资源信息上报给 `Config Server`,然后 Mongos 从中读取,选择适当的路由方法。
    
    ## 高可用
    
    类似 Redis 主从和集群。为了保证节点掉了数据还能读,就做主从。当主节点挂了,就把副本节点通过选举机制选成主节点。
    
    # MySQL
    
    端口:3306
    
    MySQL 是一个**多存储引擎框架**,最常用的是 **InnoDB**,其底层数据结构包括:
    
    | 层次                 | 描述                                                         |
    | -------------------- | ------------------------------------------------------------ |
    | 页(Page)           | InnoDB 的最小存储单位,默认 16KB,一个页存多个行记录         |
    | 行(Row)            | 表中一行数据,存储在页中(支持变长字段)                     |
    | 表空间(Tablespace) | 存放多个页的集合,支持表独立文件(.ibd)或共享文件(ibdata) |
    | 数据组织结构         | 使用 **B+ 树** 作为索引结构,包括主索引(聚簇索引)和二级索引(辅助索引) |
    | 缓存结构             | 包括 buffer pool(缓存页)、redo log、undo log、change buffer 等 |
    
    ## InnoDB 简介
    
    InnoDB 是 MySQL 默认且最常用的存储引擎,它具有以下特性:
    
    | 特性                        | 说明                                     |
    | --------------------------- | ---------------------------------------- |
    | 支持事务                    | 使用 redo log、undo log 实现 ACID        |
    | 支持行级锁                  | 并发性能更高                             |
    | 支持 MVCC                   | 多版本并发控制,提升读性能               |
    | 聚簇索引(Clustered Index) | 主键索引即为数据本身,数据按主键排序存储 |
    | 外键支持                    | 可定义引用完整性约束                     |
    | 使用 B+ 树 作为索引结构     | 高效查找、范围查询、排序                 |
    
    ## MyISAM 引擎对比
    
    | 项目       | InnoDB                          | MyISAM                           |
    | ---------- | ------------------------------- | -------------------------------- |
    | 事务支持   | 支持 ACID,支持事务与回滚       | 不支持事务                       |
    | 锁粒度     | 行级锁,支持并发                | 表级锁,写时阻塞其他操作         |
    | 外键支持   | 支持外键约束                    | 不支持                           |
    | 数据缓存   | 缓存数据和索引                  | 只缓存索引,不缓存数据           |
    | 主索引结构 | 聚簇索引                        | 非聚簇索引                       |
    | 崩溃恢复   | 支持崩溃恢复(redo、undo 日志) | 崩溃后需手动修复                 |
    | 适用场景   | 更新频繁、事务要求高的业务      | 查询为主、数据只读或更新少的业务 |
    
    ## 聚簇索引和非聚簇索引
    
    - 聚簇索引:数据行存储在主键索引的叶子节点上,索引本身就是数据。例如 InnoDB 主键索引
    - 非聚簇索引:索引的叶子节点存储的是指向数据行的地址或主键值,需要回表查询
    
    总结:
    
    - 聚簇索引读取主键快,但只能有一个
    - 非聚簇索引可建多个,查询非主键字段时需要回表
    
    ## 缓存的机制
    
    MySQL 缓存包括:
    
    1. 查询缓存(早期版本,8.0 已移除)
       - 相同 SQL 和结果可命中缓存
       - 对动态数据支持差,命中率低
    2. InnoDB Buffer Pool(主流缓存机制)
       - 缓存数据页和索引页,减少磁盘 IO
       - 默认占用内存 75%,支持冷热页淘汰策略
    3. File System Cache(操作系统层)
       - OS 页缓存也会提升数据访问效率
    4. 内存临时表(如 GROUP BY 或 ORDER BY 中间结果)
       - 超过内存限制后会转为磁盘临时表
    
    ## 事务的几种机制以及 MVCC 实现原理
    
    InnoDB 实现事务的机制包括:
    
    1. redo log(重做日志):保证已提交事务的持久性
    2. undo log(回滚日志):支持事务回滚、MVCC 读快照
    3. binlog(二进制日志):主从同步、恢复数据
    4. MVCC(多版本并发控制):实现快照读,减少加锁冲突
    
    MVCC 实现原理:
    
    - 每行记录附带两个隐藏字段:创建版本号和删除版本号
    - 读取时比较当前事务的版本号,只读取可见版本(读未提交、读提交、可重复读、串行化)
    
    ## 幻读是怎么解决的
    
    幻读指同一事务中执行相同语句两次,第二次多了“幻影”记录(例如 insert)
    
    - 可重复读(默认隔离级别)下,通过 MVCC 避免幻读
    - 若需要防止 insert 造成的幻读,可使用 next-key lock(行锁 + 间隙锁)
    
    总结:InnoDB 在可重复读隔离级别下,通过加锁和 MVCC 共同解决幻读
    
    ## 慢 SQL 排查与优化思路
    
    1. 使用工具定位慢 SQL
       - 开启 slow_query_log,查看日志
       - 使用 `SHOW PROCESSLIST` 查看当前执行的 SQL
       - 使用 `EXPLAIN` 分析执行计划
    2. 优化方法
       - 确认是否走了索引(extra 字段中是否有 Using index)
       - 避免全表扫描、filesort、临时表
       - 确保 WHERE 条件能命中合适索引
       - 减少函数调用、类型转换、模糊查询
       - 分库分表、缓存热点数据
    3. 索引设计
       - 用覆盖索引减少回表
       - 联合索引设计考虑最左匹配原则
    4. 结构优化
       - 表太大时拆表或分区
       - 热字段做单独表做冗余
    
    ## 超大表建表分表分页查询优化方法
    
    问题:大分页如 offset 1000000,会造成极大的扫描负担
    
    优化方案:
    
    1. ID 设计
       - 不建议用 MySQL 自增主键,分布式环境难以协调
       - 推荐使用分布式 ID(如雪花算法、UUIDV4/Base62 编码等)
    2. 避免 offset
       - 使用“延续性分页”或“游标分页”
       - 例如使用 `WHERE id > last_id LIMIT N` 替代 offset
    3. 建索引
       - 对分页字段建立合适索引(如时间戳、id)
       - 避免冗余索引,只为高频查询字段建索引
       - 联合索引遵循最左匹配原则
    4. 覆盖索引
       - 优先只查索引中的字段,避免回表
    5. 延迟关联
       - 第一步只查主键列表,第二步再根据主键查完整数据
       - 分两步执行,减轻查询负担
    6. 缓存热点分页
       - 对前几页缓存(如第一页),避免频繁访问数据库
    
    总结:越往后的分页效率越低,应尽量用主键位置替代 offset
    
    ## 主从
    
    ### 主从同步机制如何保证实时性与完整性
    
    1. **MySQL 主从同步流程**
       - 主库将写操作记录到 binlog(二进制日志)
       - 从库 I/O 线程拉取主库 binlog,写入 relay log
       - 从库 SQL 线程读取 relay log 并执行,重放数据变化
    2. **如何确认同步完整且实时**
       - **半同步复制(semi-sync replication)**:
         - 主库写完 binlog 后,必须等待至少一个从库确认已收到,才返回客户端成功
         - 提高了数据可靠性(不是强一致性)
       - **GTID 模式(全局事务 ID)**:
         - 可唯一标识每个事务,防止重复或丢失执行
         - 从库可基于 GTID 精确知道同步到哪个事务点
       - **延迟监控**:
         - 查询 `Seconds_Behind_Master` 判断从库滞后
         - 使用 `pt-heartbeat` 工具精确监测复制延迟
       - **主从一致性校验**:
         - `pt-table-checksum` 检查主从数据是否一致
         - 定期做冷备 + 校验
    3. **性能建议**
       - 开启并行复制(多线程复制)
       - 主库启用 binlog group commit
       - 合理设置 innodb_flush_log_at_trx_commit,减少刷盘瓶颈
    
    ## 什么是最左匹配原则?
    
    联合索引(如 `INDEX(col1, col2, col3)`)在使用时,只有**从最左列开始连续匹配**时,索引才会生效。

    sql
    — 联合索引 (a, b, c)
    WHERE a = 1 — ✅ 使用索引
    WHERE a = 1 AND b=2 — ✅ 使用索引
    WHERE b = 2 — ❌ 不使用索引(跳过了 a)
    WHERE a = 1 AND c=3 — ✅ 部分使用 (a),c 不参与索引
    “`

    为什么会这样?

    • B+ 树索引是按从左到右依次排序的
    • 索引条目按 (a, b, c) 排序,一旦跳过 a,就没法快速定位分支
    • 所以必须“从左往右连续使用”

    联合索引用于 ORDER BY 时,也要遵守最左匹配原则吗?

    是的:排序使用联合索引,也需要最左匹配原则。

    原因:

    排序是否能用索引,依赖于“查询的 WHERE 子句 + ORDER BY 是否与索引方向一致”。

    为什么 MySQL 不用单线程

    • 并发需求
      MySQL 需要同时处理多个连接和 SQL 语句,单线程会导致阻塞等待。
    • 磁盘 I/O 等待
      MySQL 有大量磁盘读写操作,如果是单线程,一个查询阻塞会影响所有请求。
    • 事务隔离与并行执行
      多线程可利用多核 CPU 提高吞吐,事务间可以并发执行,减少响应时间。
    • 网络延迟影响
      单线程容易被慢连接拖慢,影响整体处理速度。

    索引的实现

    MySQL 本身只是一个数据库框架,索引的具体实现由存储引擎决定

    • InnoDB(默认):使用 B+ 树 作为索引结构
    • MyISAM:也用 B+ 树 作为索引结构,但组织方式和 InnoDB 不同
    • Memory 引擎:默认用 哈希索引

    B+ 树为什么适合做索引

    • 有序性:范围查找快(支持最左匹配、区间查询)
    • 磁盘友好:节点存多个 key,减少 IO 次数
    • 链表结构:叶子节点之间有指针,方便范围遍历

    与 B 树对比

    特性B 树B+ 树
    数据存储位置内部节点 + 叶子节点仅叶子节点
    叶子节点连接无连接叶子节点通过链表连接
    查询效率中等更高(尤其是范围查询)
    内存占用较大较小
    插入/删除性能较好稍差(因移动数据到叶子节点)
    支持范围查询不如 B+ 树高效(叶子节点链表)

    为什么 B+ 树比 B 树更适合做数据库索引?

    B+ 树的 所有数据都在叶子节点,并且 叶子节点通过链表连接,这使得 顺序查询和范围查询 比 B 树更高效,尤其是大规模的数据量中,能显著提高数据库性能。

    B 树因为内部节点存储数据,增删时不仅涉及叶子节点,还需要修改内部节点,这使得增删操作较为复杂,性能也稍低。B+ 树因为内部节点只存索引,增删时只影响叶子节点,操作简单高效,性能较好。

    B+ 树的优势是什么?

    主要优势在于它能高效处理范围查询顺序扫描,并且由于只在叶子节点存储数据,内存占用更小

    哈希索引(Memory 引擎默认)

    • 基于哈希表,O(1) 查找
    • 只适合等值查询(=、IN),不支持范围查找、排序
    • InnoDB 的自适应哈希索引(AHI)也是类似的原理,但由引擎自动维护

    字符集

    MySQL 字符集

    MySQL 字符集(Character Set)是一套符号和编码的规则。它告诉 MySQL 如何存储和处理文本数据。MySQL 中有几个关键的字符集设置:

    • 数据库(Database)字符集:这是创建数据库时指定的默认字符集。
    • 表(Table)字符集:这是创建表时指定的默认字符集。如果未指定,它会继承数据库的字符集。
    • 列(Column)字符集:这是为表的每一列(例如 VARCHARTEXT)指定的字符集。如果未指定,它会继承表的字符集。
    • 连接(Connection)字符集:这是客户端与 MySQL 服务器通信时使用的字符集。

    UTF-8 和 UTF8mb4 的区别

    这是 MySQL 中一个非常常见的问题。简而言之,utf8utf8mb4 都是 UTF-8 编码,但它们支持的字符范围不同:

    • utf8 (MySQL 的):这个字符集是 MySQL 在早期实现的 UTF-8 版本。它最多支持 3 个字节的 UTF-8 编码。这意味着它能存储大多数常用语言的字符,比如中文、英文等。然而,它无法存储一些需要 4 个字节的字符,比如:
    • 某些生僻字或特殊符号。
    • Emoji 表情符号。
    • utf8mb4:这个字符集是 MySQL 对完整 UTF-8 编码的实现。mb4 的意思是 “most bytes 4″,它最多支持 4 个字节的 UTF-8 编码。
    • 它能存储所有 3 个字节的字符。
    • 它能存储 4 个字节的字符,因此完全支持 Emoji 表情和所有 Unicode 字符。

    总结:为了避免潜在的字符集问题,强烈建议在所有新项目中默认使用 utf8mb4。它提供了更好的兼容性和未来扩展性,而性能开销几乎可以忽略不计。

    表字符集和字段字符集的关系

    表字符集是该表的默认字符集。当你为表指定了字符集后,任何没有单独指定字符集的新字段都会自动继承这个表的字符集。

    字段字符集是针对具体字段设置的字符集。你可以为表中的某个字段指定一个不同于表字符集的字符集。

    关系:字段字符集是表的字符集的一个子集,但它可以被独立地配置。表字符集提供了一个默认值,而字段字符集提供了更细粒度的控制。

    字段字符集和表的字符集不一样会怎么样?

    这完全是允许的,而且在某些情况下是有用的。

    当你为某个字段指定了不同于表字符集的字符集时,MySQL 会只为这个字段使用你指定的字符集来存储和排序数据。

    例子:假设你有一张名为 products 的表,其默认字符集是 latin1。但你有一个 description 字段需要存储中文,所以你为这个字段单独指定了 utf8mb4 字符集。

    在这个例子中,name 字段只能存储 latin1 编码的字符,而 description 字段则可以存储中文和 Emoji 表情。

    连接字符集和库字符集不一致会发生什么?

    这是一个非常常见的问题,也是导致乱码的罪魁祸首。

    连接字符集指的是客户端和服务器之间进行数据传输时使用的编码。当客户端发送 SQL 语句和数据给服务器时,它会使用连接字符集进行编码。当服务器返回查询结果给客户端时,也会使用连接字符集进行编码。

    数据库字符集是数据在存储时使用的编码。

    如果这两种字符集不一致,就会导致乱码。这个过程可以用一个简单的模型来理解:

    1. 客户端发送一条 SQL 语句,比如 INSERT INTO table (col) VALUES ('你好');
    2. 客户端会用它的连接字符集(比如 latin1)将 '你好' 这两个字进行编码,然后发送给服务器。
    3. 服务器接收到数据后,它会根据客户端声明的连接字符集(latin1)去解码。
    4. 然后,服务器会试图用表的字符集(比如 utf8mb4)对解码后的字符重新编码,并存储到数据库中。

    如果连接字符集和数据库字符集不匹配,服务器可能无法正确地将数据从连接字符集转换到数据库字符集,从而导致存储的数据变成乱码。

    如何避免?

    最简单的办法是在建立连接后,立即通过以下命令设置连接字符集为你的数据库字符集(通常是 utf8mb4):SET NAMES utf8mb4;

    这条命令会同时设置 character_set_clientcharacter_set_connectioncharacter_set_results 三个变量,确保客户端和服务器之间的所有通信都使用 utf8mb4 字符集,从而避免乱码问题。

    MongoDB 对比 MySQL

    对比维度MySQL(关系型数据库)MongoDB(文档型数据库)
    数据模型表(table)+ 行(row)+ 列(column)集合(collection)+ 文档(document)
    数据结构固定字段,强类型灵活字段,BSON 文档,可嵌套
    查询语言SQLJSON 风格的查询语法
    事务支持原生支持 ACID 事务(InnoDB 引擎)4.0+ 支持事务,但性能不如 MySQL
    索引机制支持 B+ 树索引、多列索引支持单字段、多字段、复合索引,查询灵活
    扩展性主从复制、分库分表较复杂原生支持分片(Sharding)和副本集
    一致性模型强一致性默认最终一致性,可配置强一致性
    性能特点结构化、高并发读写、聚合强写入快、扩展性好、适合灵活查询和大数据量写入
    适用场景银行、电商、ERP 等结构固定、强一致业务CMS、日志系统、IoT、社交应用等灵活结构场景
    模型适配性面向关系建模,适合规范化数据接近对象模型,适合非结构化、变化快的业务
    部署难度架构成熟但分布式扩展较重天然支持分布式,易于横向扩展

    Kubernetes(简称 K8s

    是什么

    K8s 是一个容器编排平台,用来管理容器化应用的部署、扩缩容、负载均衡和更新。它的核心是通过 Pod、Service、Deployment 等资源,结合 API Server、Scheduler、kubelet 等组件,实现自动化运维和弹性伸缩。

    Kubernetes(简称 K8s)是一个开源的容器编排平台,用于自动化部署、扩缩容、负载均衡以及管理容器化应用。它最初由 Google 开发,后来捐赠给 CNCF(Cloud Native Computing Foundation)维护。

    • 容器编排:管理成百上千个容器的部署、更新和运行状态。
    • 自动化运维:自动扩容/缩容、故障容器自动替换。
    • 服务发现与负载均衡:容器的 IP 可能变化,K8s 自动提供稳定的访问入口。
    • 资源调度:根据 CPU、内存等资源限制,把容器分配到合适的节点。
    • 滚动更新与回滚:平滑更新应用,出问题可一键回滚。

    工作流程(简化版)

    1. 编写配置文件(YAML/JSON),定义应用部署方式(副本数、镜像、端口等)。
    2. kubectl apply 提交到 API Server
    3. Scheduler 选择合适的节点运行 Pod。
    4. kubelet 在节点上启动容器,并监控健康状态。
    5. Service 暴露服务,外部或集群内部可访问。
    6. 出现故障时,K8s 会自动重建 Pod,保证服务可用。

    核心组件

    组件作用
    PodK8s 中最小的部署单元,封装一个或多个容器
    Node容器运行的主机(物理机或虚拟机)
    Deployment定义应用的期望状态(副本数、镜像版本等),K8s 会自动维持它
    Service稳定的访问入口,屏蔽 Pod IP 变化
    Ingress提供 HTTP/HTTPS 访问规则(如域名路由)
    ConfigMap / Secret配置管理和敏感信息管理
    Namespace资源隔离(如测试环境、生产环境分开)
    kube-apiserver控制平面的核心,提供 API
    etcd分布式键值存储,用于存储集群状态
    kube-scheduler负责调度 Pod 到合适的 Node
    kubelet节点上运行的代理,负责启动容器、汇报状态

    问题

    背景

    • a 服务有 10 个 Pod(IP1、IP2、…)
    • b 服务有 10 个 Pod(IP21、IP22、…)
    • Pod IP 是 临时的(Pod 删除重建后会变)
    • a 服务要访问 b 服务中某一个 Pod

    K8s 的访问原则

    在 K8s 里,Pod 之间的直接 IP 调用是不推荐的,因为:

    • Pod IP 会变,不稳定
    • 无法做负载均衡
    • 没有健康检查

    所以 K8s 引入了 Service(ClusterIP / NodePort / LoadBalancer) 来作为稳定入口

    访问流程(默认情况:访问整个 b 服务,而不是指定某个 Pod)

    1. a 服务访问 b 服务时,用的是 Service 名字(比如 http://b-service:8080)。
    2. Pod 内的 kube-dns(CoreDNS) 会把 b-service 解析为 ClusterIP(一个虚拟 IP)。
    3. ClusterIP 不是真实 Pod 的 IP,而是 kube-proxy 维护的虚拟地址。
    4. kube-proxy 在每个 Node 上用 iptables / IPVS 建立规则,把 ClusterIP 请求转发到 b 服务的 10 个 Pod 之一
    5. 转发过程有负载均衡策略(轮询、随机、IP 哈希)。
    6. 最终,a 服务访问的实际上是 b 服务的某个 Pod 的 IP,但这个 IP 是由 kube-proxy 动态选择的。

    如果要访问 b 服务的某个 特定 Pod

    有几种方式:

    • 直接用 Pod IP(需要 a 服务和 b 服务在同一网络,且 Pod 没被删除)
      不推荐,因为 Pod 重建后 IP 会变。
    • Headless ServiceClusterIP: None
    • DNS 不解析成一个 ClusterIP,而是直接解析成所有 Pod 的 IP 列表。
    • a 服务解析到多个 IP 后,可以自己选择某个 Pod。
    • Pod 直接暴露(HostPort / NodePort)
    • 一般用于调试或特定场景,不常用。
    • StatefulSet + 固定 Pod 名称
    • 每个 Pod 都有稳定的 DNS,比如 b-0.b-serviceb-1.b-service,这样可以精确访问某个 Pod。

    简答版

    在 K8s 中,a 服务访问 b 服务通常是通过 b 的 Service 名称解析成 ClusterIP,然后由 kube-proxy 根据负载均衡策略把流量转发到某个 Pod。如果需要访问 b 的特定 Pod,可以使用 Headless Service 或 StatefulSet 来获取固定的 Pod DNS 名称。

    gRPC

    gRPC 是 Google 开源的一种 高性能、通用的 RPC(Remote Procedure Call)框架,基于 HTTP/2 协议Protocol Buffers(protobuf)序列化
    它的主要特点:

    • 跨语言:支持 Go、Java、Python、C++、Node.js 等十几种语言。
    • 高性能:HTTP/2 支持多路复用、头部压缩、二进制传输,提升效率。
    • IDL(接口描述语言):用 .proto 文件定义服务和消息结构,自动生成多语言客户端和服务端代码。
    • 多种通信模式
    • 一元 RPC(普通请求-响应)
    • 服务端流式 RPC(服务端连续推送数据)
    • 客户端流式 RPC(客户端连续上传数据)
    • 双向流式 RPC(两边同时流式通信)
    1. gRPC 和 REST 的区别是什么?
    • 协议层:gRPC 基于 HTTP/2 + protobuf,REST 一般基于 HTTP/1.1 + JSON。
    • 性能:gRPC 二进制更小、更快;REST 文本可读性更强但开销大。
    • 接口约定:gRPC 通过 .proto 强类型定义;REST 主要靠文档。
    • 通信模式:REST 只能单次请求响应;gRPC 支持双向流。
    1. gRPC 为什么要用 HTTP/2?
    • HTTP/2 支持 多路复用,避免队头阻塞。
    • 支持 双向流,天然适合 gRPC 的流式调用。
    • 支持 头部压缩,减少带宽开销。
    1. gRPC 为什么使用 protobuf 而不是 JSON?
    • protobuf 是二进制序列化,体积小、解析快;
    • JSON 可读性强但开销大,适合调试,不适合高性能场景。
    1. gRPC 的四种通信模式?
    • Unary RPC:普通请求-响应
    • Server streaming RPC:客户端一次请求,服务端多次响应
    • Client streaming RPC:客户端多次请求,服务端一次响应
    • Bidirectional streaming RPC:客户端和服务端都可以持续发送数据
    1. gRPC 的负载均衡是怎么做的?
    • 客户端内置 轮询负载均衡
    • 结合 服务发现(如 etcd、Consul、k8s service)实现动态负载均衡。
    1. gRPC 的拦截器(Interceptor)是什么?
    • 类似中间件,可以在 RPC 调用前后插入逻辑。
    • 常见用途:日志、认证、限流、监控。
    1. gRPC 如何保证安全性?
    • 基于 TLS(SSL)保证加密通信;
    • 支持认证(如 token 校验、OAuth);
    • 也可以结合 API Gateway 做额外安全控制。
    1. gRPC 的应用场景?
    • 微服务之间的高性能通信(内部 RPC)
    • 流式数据传输(日志、监控数据上报)
    • 移动端与服务端之间的高效通信

    Protocol Buffers(Protobuf)

    Protocol Buffers 是 Google 开发的一种 语言无关、平台无关、可扩展的序列化协议,简称 Protobuf
    主要作用:

    • 序列化:把结构化数据(对象)转成二进制,方便网络传输或持久化存储;
    • 反序列化:从二进制还原出原始对象。

    核心点:

    • 通过 .proto 文件定义数据结构和服务接口;
    • 编译 .proto 文件,自动生成各种语言的类/结构体和序列化代码;
    • 支持前向和后向兼容(字段可新增、删除)。
    1. Protobuf 和 JSON、XML 的区别?
    • 体积:Protobuf 是二进制,数据体积比 JSON、XML 小很多;
    • 性能:Protobuf 序列化/反序列化速度比 JSON 快;
    • 可读性:JSON/XML 可读性好,Protobuf 可读性差(需依赖 .proto)。
    • 扩展性:Protobuf 有严格的字段编号和类型定义,支持版本兼容;JSON 没有强约束。
    1. Protobuf 的工作流程?
    2. 定义 .proto 文件,描述数据结构和服务接口;
    3. protoc 编译器生成对应语言的代码;
    4. 在应用程序中使用生成的类进行序列化和反序列化。
    5. Protobuf 为什么要用字段编号(tag)?
    • 每个字段都有一个唯一的 tag number(如 string name = 1;),二进制格式里只存 tag,不存字段名;
    • 这样能减少体积,同时确保版本兼容(新增字段不会影响老版本)。
    1. Protobuf 如何保证前向兼容和后向兼容?
    • 新增字段:老版本会忽略未识别的字段,不会报错;
    • 删除字段:不要复用旧的 tag number,可以标记为 reserved
    • 修改字段类型:尽量保持兼容,比如 int32 -> int64 是可以的,但 int32 -> string 会有问题。
    1. Protobuf 的数据类型有哪些?
    • 标量类型int32, int64, uint32, bool, string, bytes
    • 复合类型message(嵌套结构)、enum(枚举)
    • 集合repeated 表示数组/列表
    • 映射map<key_type, value_type>
    1. Protobuf v2 和 v3 的区别?
    • v2:字段必须加 optionalrequired
    • v3:默认字段是 optional,取消了 required(避免兼容性问题);
    • v3:增加了 map 类型,更方便表示键值对。
    1. Protobuf 的使用场景?
    • gRPC 通信(默认序列化方式就是 Protobuf);
    • 分布式系统内部通信;
    • 存储配置文件、日志数据等高性能场景;
    • 移动端和服务端之间的高效通信。
    1. 为什么 gRPC 选择 Protobuf 而不是 JSON?
    • 二进制传输,性能更高、体积更小
    • .proto 提供强类型约束,避免接口文档和代码不一致;
    • 跨语言自动生成代码,减少手写代码。

    etcd

    etcd 是一个 分布式键值存储系统,由 CoreOS 开发,用 Go 语言实现。
    基于 Raft 共识算法 实现高可用,保证数据在集群中的一致性。
    etcd 最主要的用途是:

    • 服务发现:保存服务注册信息,让服务可以互相发现;
    • 配置中心:存储分布式系统的配置,支持动态更新;
    • 分布式协调:类似 ZooKeeper,提供锁、Leader 选举等能力;
    • Kubernetes 的底层依赖:K8s 所有的资源对象(Pod、Service、ConfigMap 等)都存储在 etcd 中。
    1. etcd 的核心特点是什么?
    • 强一致性:基于 Raft 算法,保证数据在多副本下的一致性;
    • 高可用性:支持集群部署,少数节点宕机仍可用;
    • 高性能:顺序写日志,支持高吞吐;
    • Watch 机制:客户端可以实时订阅 key 变化;
    • TTL / 租约机制:支持临时 key,用于服务注册和心跳检测。
    1. etcd 和 ZooKeeper 的区别?
    • 实现语言:etcd 用 Go 写的,ZooKeeper 用 Java 写的;
    • 协议:etcd 用 Raft,ZooKeeper 用 ZAB;
    • API 友好度:etcd 提供 HTTP/gRPC API,更易用;ZooKeeper 使用 Java 客户端;
    • 性能:etcd 在高并发下通常优于 ZooKeeper;
    • 生态:etcd 是 Kubernetes 的核心组件,云原生领域更常用。
    1. etcd 是如何保证一致性的?
    • 使用 Raft 算法:所有写请求先由 Leader 节点处理,然后复制到 Follower,超过半数确认后提交;
    • 线性一致性读写:保证客户端看到的数据是最新的。
    1. etcd 的 Watch 机制是什么?
    • 客户端可以 Watch 某个 key 或前缀,当 key 变化时,etcd 主动推送通知;
    • 用于服务发现(监听服务上下线)、动态配置更新。
    1. etcd 的 Lease / TTL 有什么用?
    • Lease(租约):key 可以绑定一个租约,租约过期时 key 自动删除;
      用途:
    • 服务注册(自动过期代表服务下线);
    • 分布式锁(锁超时自动释放);
    • 心跳检测。
    1. etcd 在 Kubernetes 中的作用?
    • Kubernetes 所有数据(Pod、Service、ConfigMap、Deployment 等)都存放在 etcd;
    • API Server 负责与 etcd 交互,保证 Kubernetes 状态持久化;
    • etcd 相当于 K8s 的数据库。
    1. etcd 集群节点数为什么建议是奇数?
    • Raft 需要 超过半数节点 同意才能提交日志;
    • 偶数节点和奇数节点在容错能力上是一样的,但奇数更节省资源。
      例如:
    • 3 节点集群,允许 1 个宕机;
    • 4 节点集群,也只允许 1 个宕机。
    1. etcd 的使用场景有哪些?
    • Kubernetes(最典型应用);
    • 微服务注册与发现(替代 Consul、ZooKeeper);
    • 分布式配置中心;
    • 分布式锁、Leader 选举。
    1. etcd 的性能优化方法?
    • 合理设置集群节点数(3/5/7 个);
    • 定期压缩历史版本(避免存储过大);
    • 使用 SSD 磁盘,提升 IO 性能;
    • 调整 gRPC KeepAlive,优化客户端连接。
  • Golang 面经

    最近在找工作,emmm 真是行业深秋了,活人微死了已经。发个网上大佬做的面经,我大概改了一小半,表述甚至答案都改了。

  • 韭花帖

    最近花了大心思去研究韭花帖,还花时间把韭花帖处理了一下,拿来当壁纸用。

  • 解决VSCode remote-ssh连接腾讯云服务器死机的一个方法

    一个2核4GB的腾讯云 Ubuntu 服务器,三番两次死机。最后也没发现到底为啥,反正用 VSCode 连接就老死机,搜了一下可能是爆内存了,因此我们使用增加Swap的方法。(最新消息,别开 VSC 的自动保存,非常占内存!)

    找 ChatGPT 问怎么加虚存。得到答案如下:

    在Linux系统中,你可以通过以下步骤增加虚拟内存:

    1. 创建一个用作交换空间的文件:可以使用dd命令创建一个指定大小的空文件。例如,创建一个8GB大小的交换文件可以使用以下命令:
      bash sudo fallocate -l 8G /swapfile
    2. 设置文件的权限:交换文件应该具有合适的权限以供系统使用,你可以使用以下命令修改文件的权限: sudo chmod 600 /swapfile
    3. 将文件格式化为交换空间: sudo mkswap /swapfile
    4. 启用交换空间:
      bash sudo swapon /swapfile
    5. 设置交换空间的启动顺序:
      为了在系统重启后自动启用交换空间,你需要将交换文件的信息添加到/etc/fstab文件中。打开该文件并在末尾添加以下行: /swapfile none swap sw 0 0 保存并关闭文件。
    6. 使用
      bash free -h
      查看虚存情况。

    实测完美解决死机问题。

  • Linux 学习记录

    快捷键

    窗口相关的

    最大窗口 win + ↑

    最小化窗口 win + ↓

    隐藏窗口 win + H

    窗口放左边 win + ←

    窗口放右边 win + →

    桌面常用快捷键

    Alt + F1:聚焦到桌面左侧任务导航栏,可按上下键进行导航

    Alt + F2:运行命令

    Alt + F4:关闭当前窗口

    Alt + Tab:切换程序窗口

    Alt + 空格:打开窗口菜单

    PrtSc:桌面截图

    Win + A:搜索/浏览程序

    Win + F:搜索/浏览文件

    Win + M:搜索/浏览音乐文件

    Win:搜索/浏览程序、文件、音乐文件等

    常见终端快捷键

    Ctrl + Alt + T:打开终端

    Tab:命令或文件名自动补全

    Ctrl + Shift + C:复制

    Ctrl + Shift + V:粘贴

    Ctrl + Shift + T:在同一个窗口新建终端标签页

    Ctrl + Shift + W:关闭标签页

    Ctrl + Shift + N:新建终端窗口

    Ctrl + Shift + Q:关闭终端窗口

    Ctrl + Shift + PageUp:标签页左移

    Ctrl + Shift + PageDown:标签页右移

    Ctrl + D:关闭标签页

    Ctrl + L:清除屏幕

    Ctrl + C:终止当前任务

    Ctrl + P:显示上一条历史命令

    Ctrl + N:显示下一条历史命令

    Ctrl + R:反向搜索历史命令

    Ctrl + J/M:回车(同enter键功能)

    Ctrl + A:光标移动到行首

    Ctrl + E:光标移动到行尾

    Ctrl + B:关闭想后移动一个位置(backward)

    Ctrl + Z:把当前任务放到后台运行

    Ctrl + PageUp:前一个终端标签页

    Ctrl + PageDown:下一个终端标签页

    F1:打开帮助指南

    F11:全屏切换

    Alt + F:打开“文件”菜单(file)

    Alt + E:打开“编辑”菜单(edit)

    Alt + V:打开“查看“菜单(view)

    Alt + S:打开“搜索”菜单(search)

    Alt + T:打开“终端”菜单(terminal)

    Alt + H:打开“帮助”菜单(help)

    Ctrl + →:光标移动到上一个单词的词首

    Ctrl + ←:光标移动到下一个单词的词尾

    Ctrl + T:将光标位置的字符和前一个字符进行位置交换

    Ctrl + U:剪切从行的开头到光标前一个位置的所有字符

    Ctrl + K:剪切从光标位置到行末的所有字符

    Ctrl + Y:粘贴Ctrl + U/Ctrl + K剪切的内容

    Ctrl + H/*:删除光标位置的前一个字符(backspace键功能)

    Ctrl + D:删除光标位置的一个字符(delete键功能)

    Ctrl + W:删除光标位置的前一个单词(Alt + Backspace组合键功能)

    Ctrl + &:恢复Ctrl + H/D/W删除的内容

    Ctrl + Win + ↑:最大化当前窗口

    Ctrl + Win + ↓:还原/最小化当前窗口

    Ctrl + Win + D:最小化所有窗口

    Win + W:展示所有窗口

    Win + T:打开回收站

    2次连续Tab/4次连续Esc/2次连续Ctrl + I:将显示所有命令和工具名称

    常用Gedit编辑器快捷键

    Ctrl + N:新建文档

    Ctrl + W:关闭文档

    Ctrl + S:保存文档

    Ctrl + Shift + S:另存为

    Ctrl + F:搜索

    Ctrl + H:搜索并替换

    Ctrl + I:跳到某一行

    Ctrl + C:复制

    Ctrl + V:粘贴

    Ctrl + X:剪切

    Ctrl + Q:退出

    常用的一些命令

    查看端口占用

    lsof 命令

    lsof -i:端口号
    #例如
    $ lsof -i:8080
    
    # 其它命令
    lsof -i:8080:查看8080端口占用
    lsof abc.txt:显示开启文件abc.txt的进程
    lsof -c abc:显示abc进程现在打开的文件
    lsof -c -p 1234:列出进程号为1234的进程所打开的文件
    lsof -g gid:显示归属gid的进程情况
    lsof +d /usr/local/:显示目录下被进程开启的文件
    lsof +D /usr/local/:同上,但是会搜索目录下的目录,时间较长
    lsof -d 4:显示使用fd为4的进程
    lsof -i -U:显示所有打开的端口和UNIX domain文件
    

    netstat 命令

    netstat -tunlp | grep 端口号
    netstat -ntlp   //查看当前所有tcp端口
    
    -t (tcp) 仅显示 tcp 相关选项
    -u (udp) 仅显示 udp 相关选项
    -n 拒绝显示别名,能显示数字的全部转化为数字
    -l 仅列出在 Listen (监听) 的服务状态
    -p 显示建立相关链接的程序名
    

    杀进程

    kill 命令

    kill -9 PID
    

    一行解决

    sudo lsof -ti:port | xargs kill -9
    

    升级

    sudo apt-get update
    sudo apt-get upgrade
    

    N

    是很好的Node.js版本管理工具,使用npm安装

    npm -g install n
    

    sudo前缀 管理员运行

    环境变量配置

    管理员打开/etc/profile

    sudo vim /etc/profile
    

    格式如下

    export PATH=写上路径:$PATH
    

    例如

    export PATH=/usr/local/bin/node:$PATH
    

    curl

    cURL是一个利用URL语法在命令行下工作的文件传输工具,它支持文件上传和下载,所以是综合传输工具,但按传统,习惯称cURL为下载工具。

    Node.js

    npm

    npm -g 全局安装,不写入节点, npm install 时不下载该依赖.

    npm -s 安装依赖到 mode_modules 目录下,写入dependencies节点, npm install 时下载该依赖,一般安装生成环境所用依赖, 如 vue, element-ui, echarts

    npm -d 安装依赖到 mode_modules 目录下,写入devDependencies节点, npm install 时下载该依赖,一般安装开发环境所用依赖, 如 wabpack, babel… 原文

    1. npm install moduleName

    安装依赖到 `mode_modules` 目录下,不写入节点, `npm install` 时不下载该依赖.

    2. npm install -g moduleName

    全局安装,不在 `mode_modules` 目录下,不写入节点, `npm install` 时不下载该依赖.

    3. npm install -save moduleName

    安装依赖到 `mode_modules` 目录下,写入`dependencies`节点, `npm install` 时下载该依赖,一般安装生成环境所用依赖, 如 `vue, element-ui, echarts`…

    4. npm install -save -dev moduleName

    安装依赖到 `mode_modules` 目录下,写入`devDependencies`节点, `npm install` 时下载该依赖,一般安装开发环境所用依赖, 如 `wabpack, babel`…

    code-server

    官网

    安装方法

    推荐两种方法

    1. 一个是npm安装 npm i code-server -g #安装 code-server #启动 npm安装我用不了,版本问题,使用N控制版本,但没成功安装。懒得折腾了。
    2. deb安装 deb安装就是安装包安装 curl -fsSL https://code-server.dev/install.sh | sh curl命令把文件下载到了~/.cache/,如果需要打开,执行 cd ~/.cache/ 找到下载的文件,使用dpkg -i命令,在这里是 sudo dpkg -i code-server_3.4.0_amd64.deb #要不要sudo取决于是不是root用户 就安装好了。

    配置文件,修改密码和端口。

    vim   ~/.config/code-server/config.yaml
    

    端口

    bind-addr: 127.0.0.1:8080
    #添加这行 端口任意,我设置的256
    bind-addr: 0.0.0.0:8080
    #外网访问 加这行,上面那行删掉
    

    扩展换源

    打开这个文件 /usr/lib/code-server/lib/vscode/product.json ,添加如下文本(注意json格式,英文逗号的问题,否则可能会导致错误)

      "extensionsGallery": {
        "serviceUrl": "https://marketplace.visualstudio.com/_apis/public/gallery",
        "itemUrl": "https://marketplace.visualstudio.com/items",
        "cacheUrl": "https://vscode.blob.core.windows.net/gallery/index",
        "controlUrl": "",
        "recommendationsUrl": ""
      }
    

    tmux命令介绍:

    # 开启新会话
    tmux new -s code_server
    
    # 如果你还未启动过code-server,则执行以下命令
    systemctl enable --now code-server@root
    
    # 此处的root可改成USER或者你当前linux用户名
    systemctl start [email protected]
    
    # 返回code_server会话
    tmux a -t code_server
    
    # 杀掉code_server会话
    tmux kill -session -t code_server
    
    # 查看code-server状态
    systemctl status [email protected]
    
    # 查看code-server进程
    ps -aux | grep code-server
    
    # 停止code-server服务
    systemctl stop [email protected]
    
    # 杀死code-server进程
    kill <pid>
    


    MySQL

    1. 显示MySQL全部用户 SELECT user, host FROM mysql.user; #显示MySQL全部用户
    2. 登录 mysql #以当前用户登录 mysql -u username #以 username 登录

    help文件

    help

    TeamViewer

    安装后缺少依赖,dpkg –list 软件状态iU

    sudo apt-get -f install
    

    Tmux

    一、简介

    1.1 会话与进程

    命令行的典型使用方式是,打开一个终端窗口(terminal window,以下简称”窗口”),在里面输入命令。用户与计算机的这种临时的交互,称为一次”会话”(session)

    会话的一个重要特点是,窗口与其中启动的进程是连在一起的。打开窗口,会话开始;关闭窗口,会话结束,会话内部的进程也会随之终止,不管有没有运行完。

    一个典型的例子就是,SSH 登录远程计算机,打开一个远程窗口执行命令。这时,网络突然断线,再次登录的时候,是找不回上一次执行的命令的。因为上一次 SSH 会话已经终止了,里面的进程也随之消失了。

    为了解决这个问题,会话与窗口可以”解绑”:窗口关闭时,会话并不终止,而是继续运行,等到以后需要的时候,再让会话”绑定”其他窗口。

    1.2 Tmux 的作用

    Tmux 就是会话与窗口的”解绑”工具,将它们彻底分离。

    (1)它允许在单个窗口中,同时访问多个会话。这对于同时运行多个命令行程序很有用。

    (2)它可以让新窗口”接入”已经存在的会话。

    (3)它允许每个会话有多个连接窗口,因此可以多人实时共享会话。

    (4)它还支持窗口任意的垂直和水平拆分。

    类似的终端复用器还有 GNU Screen。Tmux 与它功能相似,但是更易用,也更强大。

    二、基本用法

    2.1 安装

    Tmux 一般需要自己安装。

    # Ubuntu 或 Debian
    $ sudo apt-get install tmux
    
    # CentOS 或 Fedora
    $ sudo yum install tmux
    
    # Mac
    $ brew install tmux
    

    2.2 启动与退出

    安装完成后,键入tmux命令,就进入了 Tmux 窗口。

    $ tmux
    

    上面命令会启动 Tmux 窗口,底部有一个状态栏。状态栏的左侧是窗口信息(编号和名称),右侧是系统信息。

    按下Ctrl+d或者显式输入exit命令,就可以退出 Tmux 窗口。

    $ exit
    

    2.3 前缀键

    Tmux 窗口有大量的快捷键。所有快捷键都要通过前缀键唤起。默认的前缀键是Ctrl+b,即先按下Ctrl+b,快捷键才会生效。

    举例来说,帮助命令的快捷键是Ctrl+b ?。它的用法是,在 Tmux 窗口中,先按下Ctrl+b,再按下?,就会显示帮助信息。

    然后,按下 ESC 键或q键,就可以退出帮助。

    三、会话管理

    3.1 新建会话

    第一个启动的 Tmux 窗口,编号是0,第二个窗口的编号是1,以此类推。这些窗口对应的会话,就是 0 号会话、1 号会话。

    使用编号区分会话,不太直观,更好的方法是为会话起名。

    $ tmux new -s <session-name>
    

    上面命令新建一个指定名称的会话。

    3.2 分离会话

    在 Tmux 窗口中,按下Ctrl+b (松开Ctrl b) d或者输入tmux detach命令,就会将当前会话与窗口分离。

    $ tmux detach
    

    上面命令执行后,就会退出当前 Tmux 窗口,但是会话和里面的进程仍然在后台运行。

    tmux ls命令可以查看当前所有的 Tmux 会话。

    $ tmux ls
    # or
    $ tmux list-session
    

    3.3 接入会话

    tmux attach命令用于重新接入某个已存在的会话。

    # 使用会话编号
    $ tmux attach -t 0
    
    # 使用会话名称
    $ tmux attach -t <session-name>
    

    3.4 杀死会话

    tmux kill-session命令用于杀死某个会话。

    # 使用会话编号
    $ tmux kill-session -t 0
    
    # 使用会话名称
    $ tmux kill-session -t <session-name>
    

    3.5 切换会话

    tmux switch命令用于切换会话。

    # 使用会话编号
    $ tmux switch -t 0
    
    # 使用会话名称
    $ tmux switch -t <session-name>
    

    3.6 重命名会话

    tmux rename-session命令用于重命名会话。

    $ tmux rename-session -t 0 <new-name>
    

    上面命令将0号会话重命名。

    3.7 会话快捷键

    下面是一些会话相关的快捷键。

    • Ctrl+b d:分离当前会话。
    • Ctrl+b s:列出所有会话。
    • Ctrl+b $:重命名当前会话。

    四、最简操作流程

    综上所述,以下是 Tmux 的最简操作流程。

    1. 新建会话tmux new -s my_session
    2. 在 Tmux 窗口运行所需的程序。
    3. 按下快捷键Ctrl+b d将会话分离。
    4. 下次使用时,重新连接到会话tmux attach-session -t my_session

    五、窗格操作

    Tmux 可以将窗口分成多个窗格(pane),每个窗格运行不同的命令。以下命令都是在 Tmux 窗口中执行。

    5.1 划分窗格

    tmux split-window命令用来划分窗格。

    # 划分上下两个窗格
    $ tmux split-window
    
    # 划分左右两个窗格
    $ tmux split-window -h
    

    5.2 移动光标

    tmux select-pane命令用来移动光标位置。

    # 光标切换到上方窗格
    $ tmux select-pane -U
    
    # 光标切换到下方窗格
    $ tmux select-pane -D
    
    # 光标切换到左边窗格
    $ tmux select-pane -L
    
    # 光标切换到右边窗格
    $ tmux select-pane -R
    

    5.3 交换窗格位置

    tmux swap-pane命令用来交换窗格位置。

    # 当前窗格上移
    $ tmux swap-pane -U
    
    # 当前窗格下移
    $ tmux swap-pane -D
    

    5.4 窗格快捷键

    下面是一些窗格操作的快捷键。

    • Ctrl+b %:划分左右两个窗格。
    • Ctrl+b ":划分上下两个窗格。
    • Ctrl+b <arrow key>:光标切换到其他窗格。<arrow key>是指向要切换到的窗格的方向键,比如切换到下方窗格,就按方向键
    • Ctrl+b ;:光标切换到上一个窗格。
    • Ctrl+b o:光标切换到下一个窗格。
    • Ctrl+b {:当前窗格与上一个窗格交换位置。
    • Ctrl+b }:当前窗格与下一个窗格交换位置。
    • Ctrl+b Ctrl+o:所有窗格向前移动一个位置,第一个窗格变成最后一个窗格。
    • Ctrl+b Alt+o:所有窗格向后移动一个位置,最后一个窗格变成第一个窗格。
    • Ctrl+b x:关闭当前窗格。
    • Ctrl+b !:将当前窗格拆分为一个独立窗口。
    • Ctrl+b z:当前窗格全屏显示,再使用一次会变回原来大小。
    • Ctrl+b Ctrl+<arrow key>:按箭头方向调整窗格大小。
    • Ctrl+b q:显示窗格编号。

    六、窗口管理

    除了将一个窗口划分成多个窗格,Tmux 也允许新建多个窗口。

    6.1 新建窗口

    tmux new-window命令用来创建新窗口。

    $ tmux new-window
    
    # 新建一个指定名称的窗口
    $ tmux new-window -n <window-name>
    

    6.2 切换窗口

    tmux select-window命令用来切换窗口。

    # 切换到指定编号的窗口
    $ tmux select-window -t <window-number>
    
    # 切换到指定名称的窗口
    $ tmux select-window -t <window-name>
    

    6.3 重命名窗口

    tmux rename-window命令用于为当前窗口起名(或重命名)。

    $ tmux rename-window <new-name>
    

    6.4 窗口快捷键

    下面是一些窗口操作的快捷键。

    • Ctrl+b c:创建一个新窗口,状态栏会显示多个窗口的信息。
    • Ctrl+b p:切换到上一个窗口(按照状态栏上的顺序)。
    • Ctrl+b n:切换到下一个窗口。
    • Ctrl+b <number>:切换到指定编号的窗口,其中的<number>是状态栏上的窗口编号。
    • Ctrl+b w:从列表中选择窗口。
    • Ctrl+b ,:窗口重命名。

    七、其他命令

    下面是一些其他命令。

    # 列出所有快捷键,及其对应的 Tmux 命令
    $ tmux list-keys
    
    # 列出所有 Tmux 命令及其参数
    $ tmux list-commands
    
    # 列出当前所有 Tmux 会话的信息
    $ tmux info
    
    # 重新加载当前的 Tmux 配置
    $ tmux source-file ~/.tmux.conf
    

    八、参考链接

    NGINX

    本质上是代理,或者说,转发。

    指令

    $ nginx
    # 启动
    $ nginx -s stop
    # 停止
    $ nginx -s reload
    # 重载
    $ nginx 
    

    相同端口(非默认) http 跳 https

    http访问https端口,默认497错误,是返回400 Bad Request: The plain HTTP request was sent to HTTPS port
    可以修改这个错误处理。 在 server { .. } 中加入一行。

    error_page 497 https://$host$request_uri;
    #默认用302,临时重定向
    error_page 497 =301 https://$host$request_uri;
    #永久重定向
    error_page 497 =307 https://$host$request_uri;
    #临时重定向,不改变请求的方法(如post还是post)
    

    如果重定向后,没有带上正确的端口号,则显式的加上端口号 $server_port

    error_page 497 https://$host:$server_port$request_uri;

    还有一种写法,

    error_page 497 https://$host:$server_port$uri$is_args$args;

    最终写法:

    error_page 497 301 =307 https://$host:$server_port$request_uri;
    #永久重定向,不改变请求的方法(如post还是post),其它端口
    
    error_page 497 301 =307 https://$host$request_uri;
    #443端口
    

    Swap

    free -h #查看内存状态
    sudo fallocate -l 6G /swapfile
    sudo chmod 600 /swapfile
    sudo mkswap /swapfile
    sudo swapon /swapfile
    echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
    
    

    然后修改/etc/sysctl.conf中的vm.swappiness = 70,阿里云可能默认为0,0就是不倾向于使用,100就是更积极地使用。

    最后执行

    sudo sysctl -p
    

  • 物联网工程设计与实施

    范围

    1~5 章,8、9 章

    第一章 物联网工程设计与实施

    1.1 物联网工程主要内容

    1.1.1 概念

    物联网工程 是 研究物联网系统的规划、设计、实施与管理的工程科学。

    1.1.2 内容

    1. 数据感知系统
    2. 数据接入与传输系统
    3. 数据存储系统
    4. 数据处理系统
    5. 应用系统
    6. 控制系统
    7. 安全系统
    8. 机房
    9. 网络管理系统

    1.2 物联网工程设计的目标与约束条件

    1.2.1 目标

    在系统工程科学方法指导下,根据用户需求,设计完善的方案,优选各种技术和产品,科学组织工程实施,保证建设成一个可靠性高、性价比高、易于使用、满足用户需求的系统。

    1.2.2 约束条件

    1. 政策
    2. 预算
    3. 时间
    4. 技术

    1.3 物联网工程设计原则

    1. 围绕设计目标
    2. 考虑应用要求
    3. 在需求、成本、应用、技术等因素之间寻找平衡
    4. 优先选用简单可行方案
    5. 避免简单照抄其它设计方案
    6. 具有可预见性和可扩展性

    第二章 需求分析 可行性研究

    2.1 需求分析

    目标:

    明确用户需求,为设计提供依据。

    内容:

    1. 了解背景
    2. 了解业务和安全性需求
    3. 了解物联网通信需求
    4. 物联网环境条件
    5. 信息处理能力要求
    6. 管理需求
    7. 可扩展性需求

    2.4 可行性研究

    概念:在投资决策之前,对拟建项目进行全面技术、经济分析论证的科学方法。

    目的:综合论证项目建设的必要性、财务的盈利性、经济上的合理性、技术的先进性适应性及建设条件的可能性和可行性,为投资决策提供科学依据。

    内容要求:

    1. 方案具体
    2. 内容真实
    3. 预测准确
    4. 论证严密

    第三章 网络设计

    3.1 逻辑网络设计

    逻辑网络结构描述了设备的互联和分布。

    目标

    1. 合适的运行环境
    2. 成熟稳定的技术选型
    3. 合理的网络结构
    4. 合适的运营成本
    5. 逻辑网络的可扩充性
    6. 逻辑网络的易用性
    7. 逻辑网络的可管理性
    8. 逻辑网络的安全性

    原则

    1. 先进性
    2. 高可靠性
    3. 标准化
    4. 可扩展性
    5. 易管理性
    6. 安全性
    7. 实用性
    8. 开放性

    层次化网络模型

    优点

    1. 降低网络成本
    2. 简化设计元素,易于理解,提高故障隔离程度,保证了网络的稳定性
    3. 改变一个网络节点对整体影响小

    五层模型

    研究角度

    1. 感知层
    2. 传输层
    3. 处理层
    4. 应用层

    物联网工程及实施的角度

    1. 感知层对客观世界物品或环境信息的感知(传感器)。
    2. 接入层为感知层和局域网接入汇聚层/广域网/终端用户访问网络提供支持(网卡/ZigBee模块)。
    3. 汇聚层将网络业务连接到骨干网,负责安全、流量负载、路由相关的策略。
    4. 骨干层(核心层)提供高速的数据传输,连接汇聚层,将数据传输到其他网络。
    5. 数据中心数据汇聚、存储、处理、分发。

    3.2 物理网络设计

    任务是为所涉及的逻辑网络设计特定的物理环境平台。

    第四章 数据中心设计

    任务和目标

    1. 设计高性能计算机系统
    2. 设计服务器系统
    3. 设计数据存储系统
    4. 设计核心网络
    5. 设计机房
    6. 设计机房装修方案

    4.4.2 独立磁盘冗余阵列 RAID

    把多块独立硬盘按不同方式组合形成硬盘组,提供比单个硬盘更高的存储性能 和 数据备份技术。

    第五章 物联网安全设计

    5.1 感知与标识系统安全设计

    5.1.1 RFID 系统安全设计

    RFID 使用的 4 阶段

    1. 感应
    2. 选中
    3. 认证
    4. 应用

    RFID 系统的三个特征

    1. 正确性特征,真实标签被认可
    2. 安全性特征,伪造的标签不被认可
    3. 隐私性特征,标签在未授权时不可被识别跟踪

    第八章 物联网工程实施

    实施过程

    1. 项目招投标阶段
    2. 项目启动阶段
    3. 具体实施阶段
    4. 测试阶段
    5. 项目验收阶段
    6. 售后服务和培训阶段

    第九章 物联网运行维护与管理

    测试内容

    1. 终端测试
    2. 通信线路测试
    3. 网络测试
    4. 数据中心设备测试
    5. 应用系统测试
    6. 安全测试

    测试方法

    1. 主动测试
    2. 被动测试

    ThingsBoard 简介 优缺点 应用

    ThingsBoard 是一种开源的物联网(IoT)平台,旨在帮助用户快速构建、部署和扩展物联网解决方案。它提供了一个灵活的架构和功能丰富的工具,用于连接和管理各种设备、收集和处理设备生成的数据,并通过仪表板和可视化报表展示数据。

    以下是 ThingsBoard 的一些优点:

    • 开源平台:ThingsBoard 是开源的,可以自由访问、使用和修改。这使得用户可以根据自己的需求进行定制和扩展,以满足特定的物联网应用场景。
    • 可扩展性:ThingsBoard 的架构设计具有良好的可扩展性,可以轻松地处理大规模的设备连接和数据处理需求。它支持分布式部署和集群配置,可以处理数千个设备和海量数据。
    • 多协议支持:ThingsBoard 支持多种常见的物联网通信协议,如 MQTT、CoAP、HTTP 等,使得用户可以与各种类型的设备进行连接和通信。
    • 设备管理和控制:ThingsBoard 提供了功能强大的设备管理和控制功能,包括设备注册、配置管理、远程命令和固件升级等。这使得用户可以方便地监控和管理设备状态,并进行远程控制操作。
    • 数据可视化和分析:ThingsBoard 提供了灵活的仪表板和可视化报表功能,可以实时展示设备生成的数据,并支持数据分析和报警。用户可以自定义仪表板来监控设备状态、趋势和关键指标。

    一些 ThingsBoard 的缺点可能包括:

    • 学习曲线:对于没有经验的用户来说,开始使用 ThingsBoard 可能需要一些时间来了解其概念和配置。特别是对于非技术背景的用户,可能需要一些学习和培训。
    • 自定义开发:虽然 ThingsBoard 提供了一些基本的功能和组件,但如果需要更复杂的自定义功能,可能需要进行额外的开发和定制。这可能需要一些编程知识和开发资源。

    ThingsBoard 在许多物联网应用场景中都可以应用,包括但不限于以下几个领域:

    • 工业物联网(IIoT):ThingsBoard 可以用于监控和管理工厂设备、生产线和供应链的数据。它可以提供实时数据分析、故障检测和预测维护等功能,以提高生产效率和质量。
    • 智能城市:ThingsBoard 可以用于监控和控制城市基础设施,如交通信号灯、智能路灯、垃圾桶等。它可以帮助城市管理者优化资源利用、改善交通流动性和提供智能服务。
    • 农业和环境监测:ThingsBoard 可以用于农业领域,监测土壤湿度、气象条件和农作物生长状况,以实现智能灌溉和精细化农业管理。它也可以用于环境监测,监测空气质量、水质和能源消耗等指标。
    • 物流和供应链:ThingsBoard 可以帮助物流和供应链领域监控货物的位置、温度和湿度等信息,以实现实时跟踪和预警。它可以提供实时数据分析和仓储优化,以改善物流效率和降低成本。
  • 物联网工程设计与实施实验

    要求

    一、安装部署分析开源物联网系统ThingsBoard。

    ThingsBoard安装软件及文档百度网盘:

    https://pan.baidu.com/share/init?surl=yOLXUmWXgyO9MYqDzd-aIw

    提取码:1234

    二、分析研究ThingsBoard在不同领域的应用。例如:

    1. 智慧农业监控系统;
    2. 智能家居安防系统;
    3. 物联网粮食仓储系统;
    4. 远程医疗系统:
    5. 智能停车系统;
    6. 智能无人超市系统;
    7. 文物(博物馆)预警监测系统;
    8. 平安校园系统;
    9. 智慧公交系统;
    10. 智能大厦系统等等。

    三、要求:

    1. 在上述应用领域中自选一题,基于ThingsBoard,从 需求分析、方案设计、工程实施 三方面着手,遵循 物联网工程规划与设计原则 ,实现一个较全面的物联网工程项目,撰写一份较完整的设计报告,最后要有自己对该设计的 体会与感悟 ;(也可着重进行某一方面的阐述)。一个项目不超过2人,且分 不同侧重点 独立成文。
    2. 最好10页( 20面左右 ,A4纸双面打印,带封面(包括课程名、班级、学号、姓名、任课老师、成绩等内容)。
    3. 上交时间为本课程结课时间。6.14,纸质档

    要求分析

    物联网工程规划与设计原则:

    1. 清晰的目标和用例定义:在开始物联网项目之前,明确定义项目的目标和用例非常重要。这有助于确定所需的功能和性能,并为后续的工程规划和设计提供指导。
    2. 弹性和可扩展性:考虑到物联网系统的增长和变化,设计具有弹性和可扩展性的架构是关键。系统应能够适应新设备的添加、增加处理能力和容量,并支持未来的功能扩展。
    3. 安全性和隐私保护:物联网系统需要具备安全性和隐私保护的能力。包括数据加密、身份验证和访问控制等安全措施,以确保数据的机密性和完整性,并保护用户的隐私。
    4. 设备和网络互操作性:物联网涉及多个设备和网络之间的通信和互操作性。确保设备和网络能够相互通信、交换数据,并支持不同的通信协议和标准是重要的。
    5. 数据管理和分析:物联网系统生成大量的数据,因此,合理的数据管理和分析策略至关重要。包括数据采集、存储、处理、分析和可视化等方面的设计,以从数据中提取有用的信息和洞察力。
    6. 设备生命周期管理:考虑到物联网设备的寿命周期,包括设备的部署、配置、监测、维护和更新等方面的管理是必要的。确保设备的可管理性和远程维护能力是物联网工程设计中的重要考虑因素。
    7. 故障恢复和容错能力:物联网系统需要具备故障恢复和容错能力,以应对设备故障、通信中断和其他问题。采用冗余设计、备份和恢复策略,以及监测和警报机制,可以帮助及时发现和解决问题。

    软件

    需要安装的一些软件。

    1. JDK(教程中说的是JDK11)
    2. Maven
    3. Node.js
    4. git
    5. idea
    6. PostgreSQL

    软件安装过程

    建议使用Docker

    好处是删的快,用完就删。

    安装文档

    步骤

    1. 安装 Docker Desktop 并运行
    2. 管理员终端运行 docker pull thingsboard/tb-postgres这是安装 ThingsBoard 镜像的命令。
    3. 在Docker Desktop下启动ThingsBoard,不需要多余的操作。
    4. containers里查看容器,大概如下图image-20230610165241946点 9090 端口,它映射到我电脑的 32768 端口上。
    5. 登录,密码如下
  • 传感器网络

    重点

    第一章 绪论

    第三章 WSN 拓扑控制与覆盖技术

    第四章 WSN 通信与组网技术

    第五章 WSN 支撑技术

    为重点

    题型

    选择(20,10*2)

    名词解释(10 分,5*2)

    简答 (25 分,5*5)

    分析计算(35 分)

    综合(10 分)

    WSN 定义

    WSN(Wireless Sensor Network)即无线传感器网络。

    无线传感器网络是大量的静止的或移动的传感器以自组织或多跳的方式构成的无线网络。目的是协作地探测、处理和传输网络覆盖区域内感知对象的监测信息,并报告给用户。

    WSN 特点

    1. 自组织性
    2. 以数据为中心
    3. 应用相关性
    4. 动态性
    5. 网络规模大
    6. 可靠性

    各种拓扑结构的优缺点

    平面网络结构

    特点

    1. 所有节点对等,功能特性一致。
    2. (优点)结构简单,易维护,较好健壮性。
    3. (缺点)没有中心管理节点,采用自组织协同算法,组网算法较为复杂。

    分级网络结构

    特点

    1. 网络分为上层和下层,上层为中心骨干节点,下层一般为传感器节点。
    2. 一个或多个骨干节点,骨干节点之间、一般传感器之间采用平面网络结构。
    3. 骨干节点为对等结构。
    4. 分级网络以簇的形式存在,簇首和成员节点
    5. 扩展性好,便于集中管理。降低成本,提高网络覆盖率、可靠性。
    6. 开销大,硬件成本高,一般传感器之间不能直接通信。

    混合网络结构

    特点

    1. 网络骨干节点之间、一般传感器节点之间都采用平面网络结构。网络骨干节点与一般传感器节点之间采用分级网络结构。
    2. 一般传感器节点之间可以相互通信,不需要通过汇聚骨干节点来转发数据。
    3. 相较于分级网络节点,功能更强,硬件成本更高。

    Mesh 网络结构

    特点

    1. 通常只允许节点与其邻居节点直接通信。
    2. 网络内部节点一般相同。
    3. 节点之间存在多条通信路径,较强容错能力和健壮性。
    4. 虽然不同节点是对等地位,但某个节点可以被指定为簇首,执行额外功能,簇首节点失效,可以由其他节点接替。
    5. 由无线节点构成网络。
    6. 每个节点至少可以和一个其它节点通信。
    7. 自我形成:节点可以自动加入网络,也可以自动离开网络。
    8. 自愈功能:当网络中某个节点失效时,网络可以自动调整,使得网络仍然可以正常工作。
    9. 支持多跳路由:节点可以通过多跳路由的方式与网络中的任何节点通信。
    10. 功耗限制和移动性取决于节点类型。
    11. 多种网络接入方式,星型、Mesh 等。

    定位

    定义

    根据有限的位置已知的节点(锚节点)来确定无线传感器网络中的其他节点(盲节点)的位置。

    四类定位算法

    1. 基于测距的定位算法、距离无关的定位算法。
    2. 基于锚节点定位和无锚节点辅助的定位算法。
    3. 集中式计算定位与分布式计算定位。
    4. 紧密耦合定位与松散耦合定位。

    性能分析

    1. 定位精度
    2. 规模
    3. 锚节点密度
    4. 节点密度
    5. 覆盖率
    6. 容错性和自适应性
    7. 功耗
    8. 成本

    节点位置计算方法

    1. 三边测量法
    2. 三角测量法
    3. 极大似然估计法
    4. 最小最大法

    基于测距的定位算法

    三个阶段:测距、定位、校正

    1. RSSI(Radio Signal Strength Indicator) 定位算法基于接收信号强度,将信号强度转换为距离,然后根据距离计算位置。
    2. TOA(Time of Arrival) 定位算法基于信号传播速度,根据信号传播时间计算距离,然后根据距离计算位置。
    3. TDOA(Time Difference of Arrival) 定位算法基于信号传播速度,根据信号传播时间差计算距离,然后根据距离计算位置。
    4. AOA(Angle of Arrival) 定位算法基于到达角定位机制,通过测量信号的方向,多点定位。(两条线的交点)

    距离无关的定位算法

    1. 质心定位算法
    2. 凸规划定位算法

    典型定位算法

    1. Active Badge 定位算法
    2. Active Office 定位算法
    3. Cricket 定位算法

    WSN 中存在的隐藏终端和暴露终端问题(书上未找到)

    “隐藏终端”(Hidden Stations):在通信领域,基站 A 向基站 B 发送信息,基站 C 未侦测到 A 也向 B 发送,故 A 和 C 同时将信号发送至 B,引起信号冲突,最终导致发送至 B 的信号都丢失了。”隐藏终端”多发生在大型单元中(一般在室外环境),这将带来效率损失,并且需要错误恢复机制。当需要传送大容量文件时,尤其需要杜绝”隐藏终端”现象的发生。

    暴露终端是指在发送节点的覆盖范围内而在接收节点的覆盖范围外的节点,暴露终端因听到发送节点的发送而可能延迟发送。但是,它其实是在接收节点的通信范围之外,它的发送不会造成冲突。这就引入了不必要的延时。

    隐藏终端和暴露终端问题产生的原因:

    由于 ad hoc 网络具有动态变化的网络拓扑结构,且工作在无线环境中,采用异步通信技术,各个移动节点共享同一个通信信道,存在信道分配和竞争问题;为了提高信道利用率,移动节点电台的频率和发射功率都比较低;并且信号受无线信道中的噪声、信道衰落和障碍物的影响,因此移动节点的通信距离受到限制,一个节点发出的信号,网络中的其它节点不一定都能收到,从而会出现“隐藏终端”和“暴露终端”问题。

    隐藏终端和暴露终端问题对 ad hoc 网络的影响:

    隐藏终端”和“暴露终端”的存在,会造成 ad hoc 网络时隙资源的无序争用和浪费,增加数据碰撞的概率,严重影响网络的吞吐量、容量和数据传输时延。在 ad hoc 网络中,当终端在某一时隙内传送信息时,若其隐藏终端在此时隙发生的同时传送信息,就会产生时隙争用冲突。受隐藏终端的影响,接收端将因为数据碰撞而不能正确接收信息,造成发送端的有效信息的丢失和大量时间的浪费(数据帧较长时尤为严重),从而降低了系统的吞吐量和量。当某个终端成为暴露终端后,由于它侦听到另外的终端对某一时隙的占用信息,而放弃了预约该时隙进行信息传送。其实,因为源终端节点和目的终端节点都不一样,暴 露终端是可以占用这个时隙来传送信息的。这样,就造成了时隙资源的浪费。

    隐藏终端和暴露终端问题的解决方法:

    解决隐藏终端问题的思路是使接收节点周围的邻居节点都能了解到它正在进行接收,目前实现的方法有两种:一种是接收节点在接收的同时发送忙音来通知邻居节点,即 BTMA 系列;另一种方法是发送节点在数据发送前与接收节点进行一次短控制消息握手交换,以短消息的方式通知邻居节点它即将进行接收,即 RTS/CTS 方式。这种方式是目前解决这个问题的主要趋势,如已经提出来的 CSMA/CA、MACA、MACAW 等。还有将两种方法结合起来使用的多址协议,如 DBTMA。

    对于隐藏发送终端问题,可以使用控制分组进行握手的方法加以解决。一个终端发送数据之前,首先要发送请求发送分组,只有听到对应该请求分组的应答信号后才能发送数据,而是收到此应答信号的其他终端必须延迟发送。

    在单信道条件下使用控制分组的方法只能解决隐发送终端,无法解决隐藏接收终端和暴露终端问题。为此,必须采用双信道的方法。即利用数据信道收发数据,利用控制信道收发控制信号 .

    数据融合

    概念:充分利用网络中各个节点的信息,通过一定的算法,将各个节点的信息进行融合,得到更加准确的信息。

    好处:

    1. 提高了信息的可信度、可靠性。
    2. 扩展系统的空间、时间覆盖能力。
    3. 减小系统的信息模糊程度。
    4. 改善系统的检测能力。
    5. 提高系统决策的正确性。

    分类:

    根据处理融合信息的方法不同:

    1. 集中式:所有节点的数据都发送到一个中心节点,由中心节点进行数据融合。
    2. 分布式:传感器对自己的数据单独进行处理,再将处理结果发送到中心节点,由中心节点进行数据融合。带宽要求低、计算速度快、可靠性和延续性好、系统生命力强。融合精度没有集中式高。
    3. 混合式:集中式和分布式相结合。系统结构变得复杂。

    根据融合处理的数据种类不同:

    1. 时间融合:同一传感器在不同时间对目标的测量值进行融合。
    2. 空间融合:不同传感器同一时刻对目标的测量值进行融合。
    3. 时空融合:不同传感器在一段时间内对目标的测量值进行融合。

    信息抽象程度不同:

    1. 数据级融合:对原始数据进行融合。
    2. 特征级融合:对数据进行特征提取,再进行融合。
    3. 决策级融合:对数据进行决策,再进行融合。优点:
      1. 灵活
      2. 带宽要求低
      3. 有效反映环境或目标的特征
      4. 具有容错性
      5. 通信量小,抗干扰能力强
      6. 对传感器依赖小
      7. 处理代价低

    MAC层协议中的CSMA/CA协议

    CSMA/CA协议是一种基于信道监听的MAC层协议,其工作原理是:在发送数据前,先监听信道,如果信道空闲,则发送数据;如果信道忙,则等待一段时间再监听,直到信道空闲,然后发送数据。如果信道忙,等待的时间超过了一个阈值,则放弃发送,等待下一次发送机会。

    信道(通信介质资源)分配的方式(动、静态)区别

    静态信道分配:在通信开始前,就已经确定了通信资源的分配情况,通信过程中不再改变。

    动态信道分配:通信过程中,根据通信的需要,动态地分配通信资源。

    RFID 系统的工作流程

    RFID 的定义:

    RFID(Radio Frequency Identification)射频识别技术,是一种无线通信技术,它利用电磁场自动识别标签(Tag)上的信息,存储的信息可以是一个电子产品代码,也可以是一个人的身份证号码,还可以是一个包裹的运输信息等等。

    RFID 系统的组成:

    1. RFID 标签:由芯片和天线组成,用于存储和传输数据。
    2. 阅读器:用于读取标签中的数据。

    RFID 系统的工作流程

    阅读器向标签发送激励信号,激励信号的频率与标签的工作频率相同,标签接收到激励信号后,从中提取能量,激励标签工作。

  • 软件测试复习

    题型

    判断、简答、应用、综合

    重点

    软件测试的定义

    使用人工或自动手段来运行和测定某个系统的过程,其目的在于检验系统是否 满足规定的需求 或是弄清预期结果与实际结果之间的差异。

    缺陷的概念

    质量的含义

    是产品或服务所满足明示或暗示需求能力的固有属性和特征的集合

    质量的特性

    1. 功能适应性
    2. 效率
    3. 兼容性
    4. 易用性
    5. 可靠性
    6. 安全性
    7. 可维护性
    8. 可移植性

    缺陷的定义

    1. 从产品内部看,软件缺陷是指产品开发或维护过程中存在的错误、毛病。
    2. 从外部看,软件缺陷是系统所需要实现的某种功能的失效或违背。

    缺陷的来源、构成

    1. 需求定义
    2. 初步设计
    3. 详细设计
    4. 代码

    集成

    集成是指将各个独立的软件组件(模块、子系统等)合并在一起,以验证它们在整体系统中的交互和协作是否正常工作。集成测试旨在检测不同组件之间的接口问题、数据传递问题、功能一致性以及整体系统的稳定性和可靠性。

    自动化测试的概念

    自动化测试是指使用软件来控制测试设备,执行测试用例,比较实际结果和预期结果,从而自动化地完成测试过程。

    了解什么是压力、动态、静态、回归、阿尔法(伽马、贝塔)测试

    压力测试

    压力测试是指在系统正常工作负载的情况下,通过长时间或超大负荷地运行测试软件,来测试系统的性能指标。可以了解系统可靠性、性能瓶颈。

    动态测试

    动态测试是指在软件运行时,通过运行测试用例来检测软件的功能、性能、安全性等特性。

    静态测试

    静态测试是指在软件运行之前,通过检查源代码、设计文档、用户手册等来检测软件的功能、性能、安全性等特性。

    回归测试

    回归测试是指在软件发生变更之后,通过运行测试用例来检测软件的功能、性能、安全性等特性。

    阿尔法(伽马、贝塔)测试(α测γ测β测)

    阿尔法测试是指在软件开发过程中,由软件开发者在开发环境下,通过运行测试用例来检测软件的功能、性能、安全性等特性。

    黑白盒测试

    黑盒测试

    黑盒测试是指在软件运行时,通过运行测试用例来检测软件的功能、性能、安全性等特性,但是不考虑软件内部的结构和工作原理。

    白盒测试

    白盒测试是指在软件运行时,通过运行测试用例来检测软件的功能、性能、安全性等特性,同时考虑软件内部的结构和工作原理。

    语句覆盖

    语句覆盖是指在软件运行时,要求每个语句至少被执行一次。

    判定覆盖

    判定覆盖是指在软件运行时,要求每个判定的每个可能的结果至少被执行一次。

    条件覆盖

    条件覆盖是指在软件运行时,要求每个判定的每个条件的每个可能的结果至少被执行一次。

    因果图

    因果图是一种图形化的测试用例设计方法,它将程序的逻辑结构表示为一个有向图,图中的节点表示程序中的语句,图中的边表示程序中的控制流。

    测试过程的目的是什么?

    1. 功能测试:验证每个功能是否按照事先定义的要求工作。
    2. 性能测试:评测分析系统在不同负载下的运行情况、性能指标。
    3. 安全性测试:测试系统在应对非授权的访问、故意损坏时的系统防护能力。
    4. 兼容性测试:测试系统在不同的硬件、软件、网络环境下的运行情况。也包括共存、互操作性测试。
    5. 可靠性测试:检验系统是否能保持长期稳定、正常地运行。
    6. 易用性测试:测试系统是否容易学习、使用、理解,界面美观、交互友好。
    7. 回归测试:测试系统在发生变更之后,是否仍然能够正常工作。

    软件缺陷的分级

    分级

    1. 致命的:系统崩溃、数据丢失、系统不可用、系统不稳定、系统不可恢复。
    2. 严重的:系统功能不完整、系统功能不正确、系统性能不足、系统安全性不足。
    3. 一般的:系统界面不美观、系统界面不友好、系统易用性不足、系统易学性不足。
    4. 微小的:错别字、文字排列不整齐等。

    完整软件测试的流程

    软件工程过程来看

    1. 需求评审
    2. 设计评审
    3. 单元测试
    4. 集成测试
    5. 系统测试
    6. 验收测试

    项目管理的角度看

    1. 测试计划
    2. 测试设计
    3. 执行与监控
    4. 结果分析与评估
    5. 项目总结

    接口测试概念

    验证不同软件系统、模块或组件之间的接口是否正常工作。

    接口是不同软件实体之间进行通信和交互的方式,接口测试的目的是确保这些接口能够按照规范进行正确的数据传递、请求处理和响应。

    自动化测试流程

    什么是自动化测试

    把以人为驱动的测试行为转化为机器执行的一种过程。

    流程

    1. 测试环境的搭建和设置
    2. 基于模型实现测试设计的自动化
    3. 脚本自动生成
    4. 测试数据的自动产生
    5. 测试操作步骤的自动执行
    6. 测试结果分析
    7. 测试流程的自动处理
    8. 测试报告自动生成

    上机实验的内容和原理(等价类、决策表、有效等价类和无效等价类)【不会很复杂】

    等价类

    等价类是指在测试过程中,将输入域划分为若干个子集,使得每个子集中的输入数据对于测试来说是等价的。

    1. 弱一般等价类测试
    2. 强一般等价类测试
    3. 弱健壮等价类测试
    4. 强健壮等价类测试

    一般/健壮:是否考虑无效等价类 强/弱:单缺陷假设还是多缺陷假设

    如果实现语言是强类型,则不必使用健壮形式测试。

    决策表

    决策表是一种测试设计技术,它是一种表格,用来描述系统的行为,它的每一行代表一个测试用例,每一列代表一个输入条件或一个动作。

    有效等价类和无效等价类

    有效等价类是指在测试过程中,将输入域划分为若干个子集,使得每个子集中的输入数据对于测试来说是有效的。

    无效等价类是指在测试过程中,将输入域划分为若干个子集,使得每个子集中的输入数据对于测试来说是无效的。

    边界值分析

    边界值测试是软件测试中的一种策略,旨在检测系统在边界条件下的行为和处理能力。边界值测试的基本概念是,错误和异常往往在接近数据范围的边界处发生。

    目前流行的技术怎么进行测试

    1. 单元测试
    2. 集成测试
    3. 系统测试
    4. 专项测试
    5. 软件本地化测试

    笔记

    软件测试-Sofeware Test

    测试用例-Test Case

    测试执行前设计的一套详细测试方案,包括测试环境(硬件、软件、网络)、测试步骤、测试数据、预期结果等。

    SQA – Software Quality Assurance-软件质量保证

    是一项管理性工作,侧重于对流程的评审和监控。

    Test – 测试

    是一项技术性工作,侧重于对产品的评估和验证。

    TDD – Test Driven Development-测试驱动开发

    是一种敏捷开发的技术,它要求在编写功能代码之前,先编写单元测试代码,然后只编写使单元测试通过的功能代码,从而达到测试驱动开发的目的。

    ALAC – Act Like A Customer-像一个客户一样做

    基于客户使用产品的知识开发出的测试方法

    如何降低测试规模,并保证测试的典型性?

    组合分析是一种基于每对参数组合的测试技术。

    V 模型

    一种软件开发过程模型,它将软件开发过程分为软件开发和软件测试两个部分,两个部分是对称的,相互对应。

    W 模型

    V 模型的修改版。

    1. 测试过程和开发过程同步
    2. 测试过程是对开发过程中阶段性成果和最终的产品进行验证的过程。两者相互依赖
    3. 两者各有特点,存在差异。

    判定表

    条件桩:条件(就是表头)

    动作桩:动作(结果的取值)

    条件项:条件桩中的一项的取值

    动作项:在各种条件项组合下采取的动作

    驱动程序和桩模块

    这是单元测试中的概念,在单元测试中为了隔离单元,需要用到驱动程序和桩程序。

    驱动程序:用于驱动被测程序的模块,它调用被测模块,给被测模块传递参数。

    桩程序:模拟被测模块调用的下级模块。当被测模块依赖于其它单元,测试时为了隔离被测模块,需要用桩程序来模拟被测模块调用的下级模块。

    JUnit 5 的常用注解

    1. @Test:标记测试方法
    2. @ParameterizedTest:参数化测试
    3. @RepeatedTest:重复测试
    4. @TestFactory:动态测试
    5. @BeforeEach:在每个测试方法之前执行
    6. @AfterEach:在每个测试方法之后执行
    7. @BeforeAll:在所有测试方法之前执行
    8. @AfterAll:在所有测试方法之后执行
    9. @TestTemplate:测试模板
    10. @TestMethodOrder:测试方法的执行顺序
    11. @DisplayName:测试方法的显示名称
  • Git 学习记录

    Git CMD 和 Git Bash

    • Git CMD 是让 Git 在 Windows CMD 里执行命令,也可以执行 Windows 原生命令。
    • Git Bash 是模拟 Linux 终端环境,支持 Linux 命令。

    Git 基础配置

    配置 Git 用户信息

     git config --global user.name "你的用户名"
     git config --global user.email "你的邮箱"

    说明:

    • --global 表示对当前电脑所有仓库生效。
    • user.nameuser.email 是每个提交的作者信息,记录在 commit 中。

    查看当前配置

     git config --list

    创建和管理仓库

    在 GitHub 上新建仓库

    1. 登录 GitHub,点击右上角“+”,选择“New repository”。
    2. 填写仓库名称,点击“Create repository”。
    3. 复制远程仓库地址,例如:
     https://github.com/你的用户名/你的仓库名.git

    克隆远程仓库

     git clone [email protected]:用户名/仓库名.git

    或 HTTPS:

     git clone https://github.com/用户名/仓库名.git

    本地项目上传流程

    假设本地已有项目目录 my_project/

     cd my_project             # 进入项目目录
     git init                  # 初始化 Git 仓库
     git add .                 # 添加所有文件
     git commit -m "首次提交"   # 本地提交
     git branch -M main        # 将默认分支改名为 main(推荐)
     git remote add origin https://github.com/你的用户名/你的仓库名.git  # 添加远程仓库
     git push -u origin main   # 推送到 GitHub 并设置上游分支

    以后更新项目的流程

     git add .                  # 添加改动文件
     git commit -m "更新说明"    # 提交改动
     git push                   # 推送到远程仓库

    快速创建新项目并上传示例

     mkdir new_project && cd new_project
     echo "# My Project" > README.md
     git init
     git add .
     git commit -m "init"
     git remote add origin https://github.com/你的用户名/你的仓库.git
     git push -u origin main

    Git 常用操作与状态管理

    查看仓库状态

    git status

    查看提交历史

    git log

    简洁版:

    git log --oneline --graph --all

    撤销操作

    • 撤销暂存:
    git reset HEAD <文件名>
    • 撤销工作区修改(恢复到最后一次提交):
    git checkout -- <文件名>
    • 修改最后一次提交信息:
    git commit --amend

    删除分支

    • 删除本地分支:
    git branch -d <分支名>
    • 强制删除未合并本地分支:
    git branch -D <分支名>
    • 删除远程分支:
    git push origin --delete <分支名>

    分支管理

    创建分支

    git branch <分支名>

    切换分支

    git checkout <分支名>
    git switch <分支名>

    创建并切换分支

    git checkout -b <分支名>

    合并分支

    git merge <另一个分支>

    在当前分支执行,合并另一个分支内容到当前分支。

    变基操作

    git rebase <另一个分支>

    使当前分支基于另一个分支的最新提交重新应用,保持提交历史线性。


    处理远程冲突

    当 push 被拒绝,提示:

    error: failed to push some refs

    通常是远程有新的提交,本地未同步。

    解决方法:

    git pull origin main --rebase
    git push origin main

    强制推送的风险

    ⚠️ 强制推送命令:

    git push -f

    会覆盖远程历史,使用前需确认风险,避免覆盖他人工作。


    .gitignore 文件

    用于忽略不想提交的文件,如:

    *.log
    node_modules/
    .env

    Git SSH 连接配置流程

    生成 SSH Key

    ssh-keygen -t ed25519 -C "备注信息"

    参数说明:

    • ssh-keygen:生成 SSH 密钥对。
    • -t ed25519:密钥算法类型,推荐使用 ed25519。
    • -C:注释,便于识别。

    默认保存在 ~/.ssh/id_ed25519,可用 -f 自定义文件名:

    ssh-keygen -t ed25519 -C "备注" -f ~/.ssh/自定义文件名

    添加公钥到 GitHub

    1. 查看公钥内容:
    cat ~/.ssh/id_ed25519.pub

    或自定义路径:

    cat ~/.ssh/自定义文件名.pub
    1. 登录 GitHub:
    • 头像 → Settings → SSH and GPG keys → New SSH key
    • 填写 Title,粘贴公钥内容,保存。

    修改远程仓库地址为 SSH 格式

    查看远程地址:

    git remote -v

    修改为 SSH 地址:

    git remote set-url origin [email protected]:你的用户名/你的仓库名.git

    配置 SSH 使用自定义密钥(可选)

    编辑或创建 ~/.ssh/config 文件:

    Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/自定义密钥文件名

    测试 SSH 连接

    ssh -T [email protected]

    成功显示:

    Hi 你的用户名! You've successfully authenticated, but GitHub does not provide shell access.

    失败则显示 Permission denied (publickey),需检查配置。

    推送代码

    git add .
    git commit -m "首次提交"
    git push origin main

    若远程已有内容,先拉取再推送:

    git pull origin main --rebase
    git push origin main

    SSH 多账户配置(进阶)

    配置多个 SSH Key:

    Host github-personal
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_personal

    Host github-work
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_work

    克隆或推送时用对应别名:

    git clone git@github-personal:用户名/仓库.git

    SSH Key 权限设置

    私钥权限要正确:

     chmod 600 ~/.ssh/id_ed25519

    权限过宽可能导致 SSH 拒绝使用密钥。


    设置默认推送分支

    首次推送时用:

     git push -u origin main

    以后只需:

     git push

    GPG 签名(进阶)

    用于保证提交身份真实性:

     git commit -S -m "message"