Docker 新手入门:从零开始容器化你的应用

Docker 新手入门:从零开始容器化你的应用 如果你的程序在你电脑上能跑,那就把你的电脑也一起发给客户吧。——Docker 之前的世界 写在前面 这篇文章适合谁? 听说过 Docker 但从未用过的开发者 被「在我电脑上明明能跑」折磨过的人 想了解容器化部署但不知道从哪开始的人 读完你将获得什么? 理解 Docker 核心概念(镜像、容器、仓库) 能独立编写 Dockerfile 并构建镜像 能用 Docker Compose 编排多容器应用 能将一个 Web 应用容器化部署 一、Docker 是什么? 1.1 一句话解释 Docker 是一个应用打包、分发、运行的平台。它把你的应用和所有依赖(库、配置、系统工具)打包成一个镜像,然后在任何安装了 Docker 的机器上以容器的形式运行。 1.2 虚拟机 vs 容器 对比项 虚拟机 (VM) Docker 容器 隔离级别 硬件级(Hypervisor) 进程级(内核共享) 启动速度 分钟级 秒级 体积 GB 级 MB 级 性能损耗 10-20% 接近原生 资源占用 高(每个 VM 一个完整 OS) 低(共享宿主内核) 1 2 3 4 5 6 7 8 9 10 11 12 13 ┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │ 虚拟机架构 │ │ Docker 架构 │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ App A │ App B │ App C │ │ App A │ App B │ App C │ │ Libs │ Libs │ Libs │ │ Libs │ Libs │ Libs │ │ OS │ OS │ OS │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ Docker Engine │ │ Hypervisor │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ Host OS │ │ Host OS │ ├─────────────────────────────────┤ ├─────────────────────────────────┤ │ Hardware │ │ Hardware │ └─────────────────────────────────┘ └─────────────────────────────────┘ 1.3 核心三概念 镜像(Image):只读模板,包含运行应用所需的一切。类比:安装光盘 容器(Container):镜像的运行实例。类比:用光盘装好的一台电脑 仓库(Registry):存放镜像的地方。类比:应用商店(Docker Hub) 三者关系: ...

October 1, 2025 · 10 min · 2113 words

深入学习 std::flat_map

深入学习 std::flat_map 头文件:<flat_map> 命名空间:std 编译器要求:C++23 起(GCC 15+ / Clang 18+ / MSVC 19.38+) 一、设计动机:std::map 的性能痛点 1.1 红黑树的缓存问题 std::map 底层是红黑树——每个节点独立分配在堆上: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 std::map 内存布局(红黑树): ┌──────┐ │ Node │ ← 堆上随机位置 │ k=5 │ └──┬───┘ ┌───┴───┐ ┌────▼──┐ ┌─▼─────┐ │ Node │ │ Node │ ← 另一个堆上随机位置 │ k=3 │ │ k=8 │ └───────┘ └────────┘ 每次查找跳转 O(log n) 个节点,每个节点可能在不同的缓存行 → 大量 cache miss → 对于只读查找密集的场景,性能远不如连续内存 1.2 flat_map 的解法:排序 vector 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 std::flat_map 内存布局(两个排序 vector): Keys vector(连续内存): ┌───┬───┬───┬───┬───┬───┬───┐ │ 1 │ 3 │ 5 │ 7 │ 9 │ 12│ 15│ ← 有序排列 └───┴───┴───┴───┴───┴───┴───┘ Values vector(连续内存): ┌───┬───┬───┬───┬───┬───┬───┐ │ A │ B │ C │ D │ E │ F │ G │ ← 与 keys 一一对应 └───┴───┴───┴───┴───┴───┴───┘ 查找 key=7: 二分查找 keys vector → 命中索引 3 → 返回 values[3] = D 二分查找在连续内存上进行 → CPU 预取高效 → 极少 cache miss 1.3 性能对比 操作 std::map std::flat_map 原因 查找 O(log n),多次 cache miss O(log n),极少 cache miss 连续内存二分 vs 树节点跳转 有序遍历 O(n),频繁指针追逐 O(n),顺序内存访问 vector 遍历 vs 树 in-order 遍历 插入/删除 O(log n) O(n)(需移动元素) vector 中间插入需后移所有元素 内存占用 每节点 ≥ 32 bytes 开销 几乎零开销 无节点指针/颜色位 迭代器稳定性 插入/删除不影响其他 全部失效 vector reallocation 一句话总结:flat_map 用插入性能换取查找和遍历性能——适合"少写多读"的场景。 ...

June 10, 2025 · 10 min · 1996 words

深入学习 std::unordered_map

深入学习 std::unordered_map 头文件:<unordered_map> 命名空间:std 编译器要求:C++11 起(C++17 新增 try_emplace / insert_or_assign / extract / merge) 一、为什么需要 unordered_map 1.1 有序 vs 无序:性能差异 操作 std::map(红黑树) std::unordered_map(哈希表) 查找 O(log n) 平均 O(1),最坏 O(n) 插入 O(log n) 平均 O(1) 删除 O(log n) 平均 O(1) 有序遍历 天然有序 无序 内存布局 散列树节点 bucket 数组 + 链表 核心取舍: 如果不需要按 key 有序遍历,unordered_map 几乎总是更快。 1.2 典型应用场景 玩家 ID → 会话数据(海量玩家快速查找) 配置表 key → value(启动时加载,运行时只读查询) 字符串 → 枚举映射(协议解析) 缓存 / 去重(快速判断"是否存在") 二、内部结构:Bucket + 链表 2.1 经典分离链接法(Separate Chaining) 标准要求 unordered_map 使用分离链接法(不是开放寻址): ...

May 21, 2022 · 8 min · 1687 words

深入学习 std::vector 与 std::array

深入学习 std::vector 与 std::array 头文件:<vector> / <array> 命名空间:std 编译器要求:std::vector — C++98 起;std::array — C++11 起 一、为什么 vector 是默认首选容器 1.1 Bjarne Stroustrup 的建议 “Use std::vector by default.” 这不是随便说说。现代 CPU 的性能瓶颈往往不在计算而在内存访问。vector 的数据在堆上连续存储,对 CPU 缓存极其友好: 特性 vector list deque 内存布局 连续 散列节点 分段连续 缓存命中率 极高 极低 中等 遍历性能 最优 最差 中等 随机访问 O(1) O(n) O(1) 尾部插入 摊还 O(1) O(1) O(1) 1.2 实测:连续内存的威力 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <vector> #include <list> #include <chrono> #include <cstdio> int main() { constexpr int N = 1'000'000; // vector:连续内存,CPU 预取器能猜到下一个地址 std::vector<int> vec(N); // list:每个节点独立 new,内存地址随机分布 std::list<int> lst(N); auto start = std::chrono::high_resolution_clock::now(); long long sum = 0; for (auto& v : vec) sum += v; // 顺序访问,缓存行一次载入 16 个 int auto vecTime = std::chrono::high_resolution_clock::now() - start; start = std::chrono::high_resolution_clock::now(); sum = 0; for (auto& v : lst) sum += v; // 每次跳转到随机地址,缓存行浪费 auto lstTime = std::chrono::high_resolution_clock::now() - start; printf("vector: %lld us\n", std::chrono::duration_cast<std::chrono::microseconds>(vecTime).count()); printf("list: %lld us\n", std::chrono::duration_cast<std::chrono::microseconds>(lstTime).count()); // 典型结果:vector 比 list 快 10~50 倍 } 二、vector 内存模型 2.1 三指针架构 vector 内部通常只有三个指针(24 字节 on x64): ...

May 20, 2022 · 9 min · 1718 words