ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

生产环境存储实战:块、文件、对象、RAID、NAS与云存储

生产环境存储实战:块、文件、对象、RAID、NAS与云存储 存储这东西刚入行的时候我以为它是最没技术含量的部分——买盘、插上、格式化能用就行。真正被生产环境教训过几轮之后才明白存储是整个系统里最容易被低估、也最容易在深夜把人叫起来的那一环。盘满、IO 卡死、RAID 降级、对象存储 403、数据库字段选错类型导致表膨胀到几十 G、MCU 写日志把 TF 卡写坏、会话数据没地方放导致登录态全丢——这些问题表面看各不相干往下挖一层全是同一套东西块、文件、对象三种形态容量、性能、可靠性三个约束以及缓存、持久化、备份三条路径。这篇笔记就是把这几个月折腾过的东西重新捋一遍从底层的块设备一直讲到应用层的 Celery 结果后端、对象存储服务怎么选、单片机怎么往 TF 卡里按表格形式写数据。写给谁看写过 CRUD 但对存储只有模糊印象的后端同学管着几台 NAS 的小团队运维还有做嵌入式和 IoT 想搞清楚 Flash 寿命的硬件方向朋友。不需要你提前懂 RAID 或者 S3 协议我会把每个参数为什么这么定讲清楚。1. 存储的三种基本形态块、文件、对象1.1 块存储为什么快它的代价藏在哪里块存储是最接近硬件的一层。它把盘抽象成一串固定大小的块——传统机械盘一个扇区 512 字节现在主流是 4K 物理扇区逻辑上对外还常常装作 512 字节这叫 512e。上层操作系统拿到的是一个裸设备比如/dev/sdb你可以往上叠文件系统也可以直接让数据库自己管比如 MySQL 的 InnoDB 用O_DIRECT绕过操作系统的页缓存自己控制什么时候刷盘。为什么快因为它不做任何语义解析没有目录树查找没有权限校验就是给我第 12345 号块路径最短。代价也很实在。第一块设备默认不能多机共享。你把它同时挂给两台机器两边各自维护自己的文件系统缓存几分钟内就能把文件系统写烂。真要多机共享块设备得靠集群文件系统或者卷管理器做分布式锁复杂度直接上一个台阶。第二扩容和快照依赖卷管理层——LVM、存储阵列的 LUN、云上的云盘都是这个角色。第三块存储对这个块里装的是什么一无所知误删了没法像文件那样找回收站。实操心得数据库、消息队列、虚拟机磁盘镜像、Kubernetes 的 PVC优先选块存储。给块设备做mkfs之前先执行lsblk -f确认它没被别的地方用着我见过一次把数据盘当新盘格式化的事故原因是/etc/fstab里那条记录被注释掉了运维以为盘是空的。1.2 文件存储目录树带来的便利与锁的麻烦文件存储就是在块设备上叠一层文件系统把线性地址空间变成树状命名空间加上权限位、时间戳、硬链接软链接、inode。本地用的 ext4、XFS、Btrfs 是文件存储走网络暴露出去的 NFS、SMB/CIFS 也是文件存储。NAS 这个品类本质就是一台专门做文件共享的机器它卖的不是盘是把文件存储这件事做省心快照、配额、回收站、多协议访问一次配齐。文件存储最大的好处是人也能看懂。共享文档、设计稿、代码索引、构建产物缓存丢上去就能用不需要改代码。但有两个坑必须提前知道。一是锁NFSv3 的锁是旁路协议历史上出过大量问题NFSv4 把锁做进了主协议稳定很多能用 v4 就别用 v3。二是小文件性能一次open要走 LOOKUP、GETATTR、OPEN 至少三轮 RPC几百万个小文件放在 NFS 上做遍历延迟能叠到让人怀疑网络断了。我的做法是先用tar打包再传或者上对象存储。1.3 对象存储扁平命名空间与元数据的力量对象存储把目录这个概念彻底删掉了只剩两层桶bucket和对象键key。key写成2024/06/report.pdf看着像路径其实那只是名字里带了斜杠服务端存的是一个扁平映射前缀查询靠的是索引而不是目录遍历。所有读写都是 HTTP 请求PUT 上传GET 下载DELETE 删除头部可以挂任意自定义元数据x-amz-meta-*。这条路子换来三个能力无限扩容加节点横向扩展不用像传统文件系统那样担心单命名空间性能衰减、天然多副本和纠删码数据分散在多台机器上坏一两台不影响读、HTTP 直连前端可以拿预签名 URL 直接上传不经过业务服务器。对象存储也不是万能的。它不支持原地修改只能整体覆盖或者用分段上传multipart拼不支持文件锁早期很多实现只保证最终一致现在主流已经做到读后写强一致但跨区域复制还是异步的。所以它最适合的场景是图片视频、日志归档、备份、数据湖、AI 训练集。不适合的场景是需要频繁随机改字节的数据库文件。2. 从一块盘到一片池RAID、存储池与容量算法2.1 单盘、JBOD、RAID0/1/5/6/10 到底怎么选单盘最简单坏盘就是数据全丢。JBOD 把多块盘首尾相接成一个大设备容量叠加但一块坏全盘废只适合能重新生成的临时数据。RAID 是拿冗余换可靠性的经典手段但每种级别的性格差别很大。级别最少盘数空间利用率容错适用场景明显短板RAID02100%无临时缓存、压测盘单盘故障即全丢RAID1250%1 盘系统盘、小容量高可靠成本高容量浪费RAID53(n-1)/n1 盘读多写少的归档重建慢写惩罚大RAID64(n-2)/n2 盘大容量盘阵列写性能更差成本更高RAID10450%每组 1 盘数据库、高 IOPS利用率低选型的核心是问自己两个问题第一这块盘上的数据丢了能不能重新生成能就 RAID0 或 JBOD别浪费钱。第二单块盘多大如果单盘已经 16TB、18TBRAID5 在重建期间再坏一块的概率不可忽略老老实实上 RAID6 或者 RAID10。我见过 12 块 18TB 组 RAID5 的方案重建一次跑了将近 40 小时那两天机房里谁都不敢动。2.2 有效容量怎么算以 8 块 16TB 为例厂商标称 16TB实际可用要打两次折。第一次是进制折16TB10 的 12 次方等于约 14.55TiB2 的 40 次方差 9%。第二次是 RAID 开销和预留文件系统元数据、预留块、热备盘都要占。以 8 块 16TB 为例算一笔裸容量8 × 16TB 128TB十进制约 116.4TiBRAID6允许任意坏两块可用 (8-2) × 16TB 96TB再减一块热备盘参与冗余实际可保护数据约 (7-2) × 16TB 80TB扣掉文件系统 5% 预留最终可用约 76TB 左右如果换成 RAID10可用只有 64TB还要再打热备的折。这就是为什么128TB 的盘柜到手只剩一半多——不是厂商骗人是进制、冗余、预留三笔账一起算的结果。规划时我习惯直接在裸容量上乘 0.5 到 0.6 作为最终可用容量预估宁可后面发现富余也别上线三个月就报警。还有一个容易被忽略的量重建时间。假设单盘顺序读 200MB/s16TB 全量重建 16777216MB ÷ 200MB/s ≈ 83886 秒 ≈ 23 小时。现实中重建时还要接业务 IO实际速率往往掉到 80-120MB/s30 到 50 小时是常态。这段时间阵列是降级状态性能也差所以重建限速这个参数不同厂商叫rebuild rate、degraded优先级千万别拉满否则业务先崩。2.3 企业阵列运维换盘、卷管理和管理平台的坑Dell EMC 的存储换盘看起来是拔出来插进去这么简单实际有几个点。第一确认槽位号别凭手感。第二新盘必须是同型号或者官方兼容列表里的型号容量可以大不能小小了有的阵列直接拒绝加进来。第三换完盘要盯着重建进度很多阵列默认重建限速很低可以手动调高但要避开业务高峰。第四如果阵列配了热备盘拔盘那一刻热备就会顶上去开始重建此时换上来的新盘会变成新的热备别忘了确认它的状态是Ready而不是Unassigned。IBM V7000 这套系统的概念层级是物理盘 → mdisk阵列→ 存储池pool→ 卷volume→ 主机映射。新手最容易搞混的是 mdisk 和 pool记住一句mdisk 是硬件冗余层pool 是给业务分配的逻辑层。做过migrateextent、expandmdisk这类操作的人都知道命令敲下去之前一定要先lsdependentvdisks看看谁依赖它动错了整池的卷都会受影响。HDS VSP G 系列的管理平台 MPC 安装坑基本都在客户端环境上它依赖特定版本的 Java 运行时浏览器要开兼容模式或者用指定的内核版本管理口的 IP 段一开始是固定的得先改本机网卡地址才能访问。我的做法是专门留一台老系统的虚拟机装管理客户端不跟日常办公环境混在一起省得哪天升级个 JDK 就把工具搞崩。3. NAS、分布式存储与云对象存储三条落地路线3.1 NAS 选型别只看盘位数量小团队选 NAS第一眼看盘位数第二眼看 CPU 型号有没有硬件转码第三眼看内存能不能加。这三样之外还有几个隐形指标网口是千兆还是 2.5G/万兆决定了实际吞吐上限千兆口跑满约 110MB/s一块机械盘顺序读就有 150-200MB/s网口是瓶颈文件系统是 ext4、Btrfs 还是 ZFS后两者支持快照和校验但吃内存ZFS 一般建议每 1TB 容量配 1GB 内存起步有没有原生的 NFS、SMB、iSCSI 三件套。家用和小团队我通常这样配系统盘用一块 SSD 单独装数据盘组 RAID1 或 RAID5重要数据再加一份到外部硬盘或者云上。之所以系统盘分开是因为很多成品 NAS 会把系统和数据放同一组盘上一旦重建系统也跟着抖。至于要不要开压缩看数据类型——文本和日志开 lz4 收益很大已经是压缩过的视频图片再压一次纯属浪费 CPU。3.2 MinIO 分布式存储部署 Java 上传 与云对象存储的取舍MinIO 是我在自建对象存储时最常用的选择接口完全按 S3 协议实现工具链现成部署也简单。单机跑起来就一条命令docker run -d --name minio \ -p 9000:9000 -p 9001:9001 \ -e MINIO_ROOT_USERadmin \ -e MINIO_ROOT_PASSWORDChangeMe_123 \ -v /data/minio:/data \ quay.io/minio/minio server /data --console-address :9001多节点分布式模式原理是纠删码每份对象切成 N 个数据块加 M 个校验块分散在不同节点不同磁盘上。比如 4 节点每节点 4 盘一共 16 个驱动默认纠删码配置下可以容忍最多 8 个驱动同时失效而不丢数据。注意分布式模式最少 4 个节点且节点间时钟要同步MINIO_ROOT_PASSWORD长度不能少于 8 位这些是新手最常踩的。Java 侧上传一段标准代码重点是流式上传不要一次性读进内存分片大小按文件体积来定MinioClient client MinioClient.builder() .endpoint(http://10.0.0.12:9000) .credentials(admin, ChangeMe_123) .build(); client.putObject( PutObjectArgs.builder() .bucket(app-files) .object(2024/06/report.pdf) .stream(inputStream, -1, 10 * 1024 * 1024) // 未知长度 10MB 分片 .contentType(application/pdf) .build());stream的第二个参数传 -1 表示长度未知客户端会自动切成 10MB 的分片走 multipart内存占用峰值就是分片大小不会因为上传一个 5G 的文件把堆撑爆。这是我早期踩过的坑当时用Files.readAllBytes读文件流测试环境 200MB 文件没事生产环境一个大备份包上来直接把服务打挂。什么时候用 MinIO什么时候用云对象存储我的判断标准是三条数据量小、访问频率低、团队没有运维人力直接上公有云对象存储省心且按量付费数据量大、内网访问为主、有合规或者延迟要求自建 MinIO需要跨地域分发、需要 CDN 回源公有云的原生集成更顺。至于 GFS 这类分布式文件系统它的定位是支撑大规模顺序写和大文件吞吐典型场景是计算集群的共享存储跟对象存储不是替代关系而是各管一摊。3.3 云对象存储的成本模型与生命周期策略公有云对象存储的账单由四块组成存储容量费、请求费、流量费、附加功能费。容量费按 GB/月标准型最贵低频型便宜一半左右归档型能便宜到十分之一但取回要等几分钟到几小时还有取回费。流量费通常是外网下行才收内网上行基本免费。请求费按万次计看着便宜但如果你的应用把对象存储当数据库用一秒几十万次 GET 去探测文件是否存在账单会教你做人。所以生命周期规则一定要配。举一条我常用的策略新上传的对象放标准型30 天没被访问自动转到低频型180 天转归档365 天直接删除。要注意的是没被访问的判断依赖访问日志开启访问日志本身也要占空间和花请求费别把日志存到同一个桶里形成自引用。另外跨区域复制、版本控制、对象锁定这些功能都是要额外付费的开之前先算清楚。4. 数据落到哪MySQL、字节序与嵌入式存储4.1 MySQL 整数类型选型与磁盘占用数据库里的存储最容易被忽视因为选错类型当下不会报错只会在数据涨上来之后变成慢查询。MySQL 的整数类型就那几个但含义差别不小类型占用字节有符号范围无符号范围典型用途TINYINT1-128 ~ 1270 ~ 255状态位、年龄、布尔SMALLINT2-32768 ~ 327670 ~ 65535端口号、年级MEDIUMINT3-8388608 ~ 83886070 ~ 16777215中等计数INT4±21 亿0 ~ 42 亿用户 ID、自增主键BIGINT8±9.2e180 ~ 1.8e19雪花 ID、金额分单位关键结论只有一个BIGINT 比 INT 每个值多占 4 字节一张 1 亿行的表就是 400MB 的差距加上主键索引的副本实际占用翻倍。所以自增主键用 INT UNSIGNED 能扛到 42 亿行绝大多数业务够用。但如果主键是分布式生成的雪花 ID那必须 BIGINT没有商量余地。金额我强烈建议用 INT 或 BIGINT 存分别用 FLOAT/DOUBLE浮点累加的误差在对账时能让人崩溃。另外INT(11)这种写法里的 11 只是显示宽度跟存储字节数完全无关MySQL 8 已经废弃了这个用法看到旧代码里这么写不要以为它占了 11 字节。4.2 大端与小端为什么抓包看到的是反的字节序这件事写应用层代码基本碰不到一旦碰到就是三小时起步的排查。定义很简单一个多字节数值在内存里存的时候高位字节放前面叫大端Big-Endian低位字节放前面叫小端Little-Endian。比如0x12345678大端在内存里是12 34 56 78小端是78 56 34 12。x86、ARM 默认都是小端。网络协议为了统一规定用大端这就是所谓的网络字节序写 socket 代码要用htonl、ntohl转换。嵌入式和单片机最常见的问题就在这传感器通过 I2C 吐出来的两个字节你按小端拼读出来是乱码协议手册上写的是 Big-Endian代码里忘了转值永远是理论值的 256 倍或者倒过来。判断口诀看到数值离谱地大或者离谱地小、或者呈现明显的字节调换特征先怀疑字节序。写解析代码时我习惯加一行断言把原始字节打印出来人工核对一次比事后猜快得多。4.3 单片机按表格形式写 TF 卡以及 EEPROM 与 Flash 寿命单片机把数据按表格形式存到 TF 卡本质上要做三件事定义行结构、做缓冲、处理掉电。行结构用 C 结构体定义但不能直接fwrite结构体因为编译器会做内存对齐结构体里会插入填充字节换一个编译器或者换一个编译选项填充位置就变了读出来的数据全错。正确做法是逐字段序列化成定长字节数组或者用 CSV 文本格式。CSV 的好处是通用电脑上双击就能看代价是占空间和解析慢二进制定长记录的好处是快坏处是需要自己维护版本号。typedef struct { uint32_t ts; // 时间戳秒 int16_t temp_x10; // 温度×10避免浮点 uint16_t volt_mv; // 电压毫伏 uint8_t flag; } __attribute__((packed)) rec_t; // 取消对齐填充__attribute__((packed))能取消填充但访问未对齐成员在部分架构上会触发异常或者变慢所以更稳的方式还是手动拼字节。缓冲策略上TF 卡本质是 NAND Flash最怕的是小字节频繁写一次写 512 字节和一次写 4 字节对卡的寿命消耗差几十倍——因为 Flash 的擦除单位是块常见 4KB 到 128KB改一个字节得把整块读出来、改、擦、写回。我的做法是攒够 4KB 或者每 10 秒强制刷一次用f_sync落盘。更深一层的坑是掉电正在擦写的时候断电那块数据可能整块损坏还会破坏文件系统的 FAT 表导致整张卡要重新格式化。所以要么加超级电容做掉电保护要么用双备份加写标志位的方案——先写 B 区、置标志、再写 A 区上电时判断标志决定用哪一份。EEPROM 则适合存那种改一次记一辈子的配置比如设备序列号、校准系数、频道参数它按字节擦写、寿命通常在百万次量级比 Flash 的擦写寿命常见 1 万到 10 万次高很多但容量只有 KB 级。手台写频把频率、亚音、功率档位写进机内小容量非易失存储器本质和单片机存配置是一回事容量小、写入次数有限、掉了就恢复出厂。5. 应用层存储实务缓存后端、会话与日志5.1 Celery 结果后端怎么选Celery 的结果后端决定AsyncResult从哪读任务返回值。常见选项是 Redis、RabbitMQ、数据库、文件系统。默认推荐 Redis因为读写快、支持 TTL任务结果天然是短生命周期数据设个 1 小时过期就够了。坑在于Redis 默认是内存数据库结果堆积会吃内存尤其是任务返回大对象比如整个 DataFrame 序列化后几十 MB几万个任务就能把内存打满。所以我的规矩是结果超过 1MB 就不要塞后端改成把大结果上传对象存储后端只存一个 URL这个模式在生产里非常稳。数据库后端的优点是持久、可查询、能直接写 SQL 排查缺点是每来一个任务就多一次 insert 和 update任务量大时数据库压力明显。文件系统后端适合单机小规模但多 worker 必须共享同一个挂载目录否则结果互相看不见。另外要留意result_expires这个配置不设的话结果会一直留着数据库后端尤其要注意定期清理。5.2 会话存储、压缩与记忆层会话数据的存储看着简单接入时才发现问题集中。以企业微信服务商模式接入自研 Java 系统为例难点是多个租户共用一套服务会话和临时凭证必须按企业 ID 隔离存储Key 的拼写规则一定要统一并写成常量别在三个地方各自拼字符串后面加一个字段就得改三处。存储介质上Redis 是首选但要配好持久化AOF 每秒刷盘是性能和可靠性的平衡点并且给会话 Key 设置合理的 TTL通常对齐凭证有效期避免过期凭证还在缓存里被复用。智能体方向的会话存储又是另一套逻辑。多轮对话的上下文长度涨得很快直接把全部历史塞进去token 成本和延迟都受不了所以出现了记忆压缩这类做法比如 mem0 这类框架的思路把历史对话抽取成结构化的事实条目原始对话只保留最近若干轮更早的压缩成摘要存起来。存储层一般是向量库加关系库的组合向量库负责语义检索关系库存原始条目和元信息。要注意的是压缩是有损的同一句话在不同上下文里含义不同抽取规则写得太激进会丢关键约束我的经验是先把用户明确提出的限制条件和已确认的结论这两类优先保留其余再压。5.3 MCU 日志存储与循环缓冲单片机日志和大系统日志的思路完全不同。资源受限的设备没有文件系统可依赖时通常直接在 Flash 上划一块区域做环形缓冲写指针到末尾就绕回开头用序号判断哪条最新覆盖最旧的。这样做的好处是写入量恒定、不需要擦除管理坏处是历史日志会被覆盖如果需要保留更多就得做双区轮换加落盘转存。环形缓冲的两个关键参数是记录大小和区总大小。假设每条日志 64 字节想要保留 2000 条那就是 128KB 的区域得确认芯片的 Flash 分区够放。写入频率也要控制一条日志一行没问题但如果传感器每秒上报一次还每次都写盘一年下来就是 3000 万次写入Flash 扛不住这时候要么降低频率要么先在 RAM 里聚合再批量落盘。6. 备份、压力测试与故障速查6.1 备份的 3-2-1 与恢复演练3-2-1 原则说的是至少 3 份数据存在 2 种不同介质上其中 1 份在异地。这个原则老但有效因为它同时防住了三种情况——单盘故障、整机故障、单点灾难。落地的时候本机或本阵列保留一份快照应对误删NAS 或另一台机器保留一份完整副本应对阵列故障对象存储或异地机房再放一份应对机房级问题。但真正决定备份有没有用的不是备份动作而是恢复演练。我见过备份任务每天绿着跑出事后才发现备份文件是空的原因是源目录改过名脚本还在备份旧路径rsync报错被|| true吞掉了。所以现在我的规矩是备份脚本必须检查退出码备份完成后自动抽一个文件做校验对比大小和哈希每季度做一次真实恢复演练并且记录耗时。还有一个细节是备份窗口如果备份任务和数据重建任务撞在一起IO 会双双卡死一般把备份安排在业务低峰并且开启限速。6.2 存储压力测试fio 参数与写测试怎么换算压测存储不用 fio 基本等于瞎猜。它的核心参数就那么几个但每个都影响结果解释参数含义常用取值说明--rw读写模式randwrite/randread/readwrite数据库看随机备份看顺序--bs块大小4k/64k/1M4k 测 IOPS1M 测带宽--iodepth队列深度32/64/128越大越能压出设备上限--numjobs并发任务数4/8模拟多线程--direct绕过缓存1必须开否则测的是内存--runtime运行时长300太短数据没意义--filename测试文件独立路径别指向真实数据目录一条典型命令fio --namerandwrite --ioenginelibaio --direct1 --rwrandwrite \ --bs4k --iodepth32 --numjobs4 --size8G --runtime300 \ --group_reporting --filename/data/fio_test结果里最重要的两个数字是 IOPS 和延迟的 99 分位。很多人只看平均值平均值好看但 99 分位飙到几百毫秒业务照样超时。至于app 内部存储执行写测试这种场景思路是一样的只是把测试文件放到应用实际使用的目录这样才能暴露真实路径上的问题——比如某些设备把外部存储挂载成 FAT32单文件 4GB 上限写测试跑到 4GB 直接失败这跟盘本身的性能无关。换算关系记一下就够了顺序带宽 块大小 × IOPS。4K 随机读写 10000 IOPS换算带宽只有 40MB/s但这是机械盘完全正常的水平如果换成 1M 顺序写同样一块盘能跑到 150-200MB/s。所以看到这块盘只有 40MB/s先别急着换看清楚测试的块大小。6.3 常见故障速查表现象常见原因排查动作处理建议写入突然变慢盘接近写满、SMR 盘缓存用尽iostat -x 1看 %util 和 await预留 10% 空间换 CMR 盘RAID 降级告警单盘故障或掉线阵列日志、SMART 状态先备份再换盘控制重建速度文件系统只读写错误、链路抖动dmesgtail 看 IO error日志出现故障存储段、LIVEKERNEL 类事件驱动或硬件异常系统事件日志、厂商诊断工具更新固件驱动硬件送修系统盘莫名少几十 G组件存储、休眠文件、还原点DISM /Online /Cleanup-Image /AnalyzeComponentStore清理组件存储关闭休眠数组扩容后容量没变文件系统未扩展df -h与lsblk对比在线resize2fs或xfs_growfs网络存储断连链路、MTU、防火墙ping -M do -s 1472测 MTU统一 MTU检查交换机端口关于设备固件我补一句海康这类存储设备的固件升级是有风险的升级过程中断电可能导致设备起不来而且升级后配置可能被重置。所以升级前一定导出配置、记录 IP 和通道信息最好挑业务空闲期做并且确认升级包的型号匹配——同系列不同硬件版本的固件不通用。7. 系统侧的存储坑建池、驱动与空间回收7.1 Linux 侧用命令创建存储池ssh登录到目标机器之后建池这一步我一般分两条路走。传统路线是mdadm组软 RAID 再加 LVM 或直接上文件系统# 组 RAID54 块数据盘 1 块热备 mdadm --create /dev/md0 --level5 --raid-devices4 \ /dev/sd[b-e]1 --spare-devices1 /dev/sdf1 # 看重建进度 cat /proc/mdstat # 建 XFS 并挂载 mkfs.xfs /dev/md0 mkdir -p /data mount /dev/md0 /data另一条路是 ZFS优点是自带校验、快照、压缩组池命令极简zpool create tank raidz2 /dev/sd{b,c,d,e,f,g} zpool status tank zfs set compressionlz4 tank zfs set atimeoff tank这里有两个参数值得解释。compressionlz4对文本、日志、代码收益很大通常能省 30% 到 50%代价是极小的 CPU 开销atimeoff关掉访问时间更新能显著减少无谓的写放大绝大多数业务根本不用 atime。建完池记得写进/etc/fstab用 UUID 而不是/dev/sdX否则插拔顺序一变就挂错盘。7.2 Windows 侧组件存储损坏、C 盘瘦身与阵列驱动Windows 上存储相关的坑集中在两处。第一处是组件存储WinSxS损坏症状通常是更新失败、功能启用失败报错里出现组件存储异常的提示。处理顺序是先体检再修复DISM /Online /Cleanup-Image /CheckHealth DISM /Online /Cleanup-Image /ScanHealth DISM /Online /Cleanup-Image /RestoreHealth sfc /scannowRestoreHealth会从更新源拉取健康文件替换损坏项如果本机网络受限需要挂载对应的系统镜像作为源。修复完再用DISM /Online /Cleanup-Image /StartComponentCleanup回收空间这一步会把旧版本组件删掉代价是不能再卸载已安装的更新。第二处是 C 盘莫名其妙变红。除了常规的临时文件和下载目录几个大头是休眠文件hiberfil.sys等于内存大小不用休眠就powercfg -h off关掉、系统还原点、Windows 更新缓存、以及各种开发工具的家目录Docker 镜像、WSL 虚拟磁盘、Node 的~/.npm、Maven 仓库。存储感知这个功能建议开着它能自动清临时文件和回收站但它不管开发工具的大文件那些得自己定期清。至于 Windows Server 2019 装 PERC 阵列卡驱动装系统时如果安装程序看不到盘就是缺vioscsi之类的驱动需要提前把厂商提供的驱动解压到 U 盘在加载驱动程序那一步手动指定装完系统再核对一遍驱动版本别用系统自带的通用驱动凑合。7.3 应用侧存储路径迁移与嵌入式 overlay 扩容应用软件把数据写到 C 盘是 Windows 上最常见的空间问题来源。以 ArcGIS Pro 为例它的缓存、索引、临时工程文件默认都在用户目录下工程一多就占几十 G正确做法是在设置里把缓存目录和默认工程目录改到数据盘改完重启一次让它重建索引。Docker Desktop 同理镜像和容器默认在 C 盘需要改daemon.json或迁移 WSL 的ext4.vhdx位置迁移前必须停止所有容器并退出 Docker否则文件被占用会失败。嵌入式设备那边的情况是反过来的——空间太小。常见的一类是路由器固件系统 overlay 分区只有几百兆装几个插件就满了想做大的需求比如要 8G 空间得靠外置存储扩展把 U 盘或 SSD 分区挂载上来把 overlay 目录迁过去。大致动作是先在已挂载的外部存储里建目录把现有 overlay 内容整体拷过去然后在启动配置里指定新的 overlay 挂载点重启验证。做之前一定要把配置导出备份因为改错挂载点会导致系统起不来得进恢复模式救。另一类是存储设备本身的固件更新除了前面说的断电和型号匹配还要留意有些设备的固件写入会清空用户分区升级前把数据导出是底线操作。8. 安全与合规别让存储变成攻击入口8.1 存储型 XSS 的成因与修复存储型 XSS 之所以危险是因为它不需要你点恶意链接只要打开那个页面就会执行。成因是用户输入被原样存进了数据库取出来的时候又被当成 HTML 渲染。典型场景是评论、昵称、工单标题、富文本编辑器内容。修复的思路很多团队只做了输入过滤这是不够的——过滤规则总有绕过的办法正确的是按上下文转义输出HTML 文本节点用 HTML 实体转义属性值加引号再转义JS 上下文用 JSON 序列化URL 用白名单协议校验。还有一类更隐蔽的低代码平台的表单设计器允许配置动态执行的脚本。设计者本意是让用户做表单联动但如果脚本内容能落库、又能被其他用户查看页面时执行它就是一个打着业务功能的存储型 XSS 入口。这类平台的正确做法是把脚本限制在声明式配置里禁用任意代码执行或者把自定义脚本放进沙箱 iframe同时配合内容安全策略限制外连。排查这类问题手工点太慢可以用自动化方式辅助用 Selenium 遍历页面上所有表单元素逐个填入带标记的测试载荷提交后再重新访问页面检查标记是否被原样输出。这个思路的关键不在工具而在于提交后要重新读取只在当前页面看 DOM 是找不到存储型问题的因为它走的是存进库再取出来这条链路。8.2 数据落盘的边界与最小化存储相关的合规问题绕不开什么数据该存、存多久、存在哪。原则简单能不留就不留。日志里打用户手机号、身份证号、完整银行卡号是极常见的习惯性错误一旦日志文件被打走就等于数据泄露。我的做法是在日志框架层做统一脱敏敏感字段只打前三位后四位或者直接打哈希。会话和令牌不要落到明文文件里凭证走专门的密钥管理服务本地只留引用。另一个边界是数据出境的物理落点。做多地域部署时要知道每一份副本实际落在哪个机房对象存储的跨区域复制会把数据复制到另一个地理区域开之前先确认这件事是允许的。至于加密传输层用 TLS静态数据用磁盘加密或者对象级加密密钥自己管的场景比如 KMS 自持密钥安全等级更高但也意味着密钥丢了数据就真找不回来了备份密钥这件事和备份数据一样重要。说到最后我自己这些年在存储上得到的最实在的一条经验是永远不要在设计阶段假设容量够用。上线前算一遍三个月的增长曲线把监控阈值设在 75% 而不是 90%因为从 75% 到 90% 可能只要两周而申请预算、采购、到货、上架、扩容、数据再平衡这一整套流程两周根本走不完。再加一条任何一次我要不要动 RAID 配置的念头冒出来先把全量数据在另一处落一份再去动机器——我踩过的那次坑就是自信地直接改了阵列参数重建到 60% 的时候才发现逻辑卷表被覆盖了最后靠三天前的异地备份救回来那三天的新数据全没了。备份不贵重来很贵。
返回列表