<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>存储与数据库 on 扎塔-Zata</title><link>https://www.zata.cc/tags/%E5%AD%98%E5%82%A8%E4%B8%8E%E6%95%B0%E6%8D%AE%E5%BA%93/</link><description>Recent content in 存储与数据库 on 扎塔-Zata</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>Example Person</copyright><lastBuildDate>Thu, 24 Sep 2026 17:09:06 +0800</lastBuildDate><atom:link href="https://www.zata.cc/tags/%E5%AD%98%E5%82%A8%E4%B8%8E%E6%95%B0%E6%8D%AE%E5%BA%93/index.xml" rel="self" type="application/rss+xml"/><item><title>S3 兼容存储踩坑记：boto3 新默认校验和撞上 NotImplemented</title><link>https://www.zata.cc/p/s3-boto3-checksum-notimplemented/</link><pubDate>Thu, 10 Sep 2026 23:58:52 +0800</pubDate><guid>https://www.zata.cc/p/s3-boto3-checksum-notimplemented/</guid><description>&lt;img src="https://www.zata.cc/p/s3-boto3-checksum-notimplemented/images/index/index.svg" alt="Featured image of post S3 兼容存储踩坑记：boto3 新默认校验和撞上 NotImplemented" />&lt;h2 id="一场全线-500的发布事故">一场&amp;quot;全线 500&amp;quot;的发布事故
&lt;/h2>&lt;p>用自家 CLI 发布一篇博客，服务端返回 500。第一反应：肯定是包格式不对——毕竟几分钟前刚被 422 拒过一次（一个 2.9MB 的自包含 HTML 超了单条目 2MiB 上限），说明接口是通的，可能是我 ZIP 打包的方式不合规范。&lt;/p>
&lt;p>于是按标准流程排查：换 Markdown 源文件 + 图片资源重新打包、用最小草稿（三行文字的 md）测基本链路、HTML 单文件再试一遍……结果所有创建请求一律 500，draft 和 publish 两个端点无一幸免。&lt;/p>
&lt;p>但诡异的是：登录正常，草稿列表正常，健康检查正常，公开博客列表也能刷出来。&lt;strong>所有读路径全绿，所有写路径全红。&lt;/strong>&lt;/p>
&lt;p>如果真是包格式问题，最小草稿不可能挂；如果是认证问题，读接口不可能全通。这不像&amp;quot;我的请求有问题&amp;quot;，更像&amp;quot;服务端某个环节坏了&amp;quot;。&lt;/p>
&lt;h2 id="422-和-500-之间隔着一道分界线">422 和 500 之间隔着一道分界线
&lt;/h2>&lt;p>回头看现象，有一条隐藏的分界线：&lt;/p>
&lt;ul>
&lt;li>超限的 index.html 报 &lt;strong>422&lt;/strong>（校验错误，&lt;code>Zip entry exceeds the 2 MiB limit&lt;/code>）&lt;/li>
&lt;li>所有通过校验的请求报 &lt;strong>500&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>后端是分层架构：API 层收包 → core 层的 &lt;code>BlogService.extract_zip_package&lt;/code> 做校验（格式、大小、路径白名单、symlink）→ 校验通过后 &lt;code>upload_blog&lt;/code> 把正文和附件逐个写入对象存储。&lt;/p>
&lt;p>422 在校验层抛出，说明请求已经正常穿过 API 层；500 卡在&lt;strong>校验通过后的第一步&lt;/strong>——&lt;code>put_object&lt;/code>。把服务端日志拉出来，最底下赫然一行：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">botocore.exceptions.ClientError: An error occurred (NotImplemented) when
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">calling the PutObject operation: Aws MultiChunkedEncoding
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">STREAMING-UNSIGNED-PAYLOAD-TRAILER is not supported.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>问题根本不在博客业务，也不在包格式——是&lt;strong>对象存储的写入&lt;/strong>挂了。博客发布只是撞在枪口上的第一个受害者：任何要往 S3 写东西的功能（博客、语音、知识库）此时全部阵亡。&lt;/p>
&lt;h2 id="先补一点背景s3-兼容生态与请求编码">先补一点背景：S3 兼容生态与请求编码
&lt;/h2>&lt;h3 id="s3-api-是事实标准但兼容是程度问题">S3 API 是事实标准，但&amp;quot;兼容&amp;quot;是程度问题
&lt;/h3>&lt;p>AWS S3 的 REST API 已经是对象存储的事实标准，MinIO、Cloudflare R2、Backblaze B2、各家云厂商的兼容端点都实现了这套协议。自建 MinIO 时，客户端通过几个参数接入：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">boto3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">client&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;s3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">endpoint_url&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;http://your-minio:9000&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 指向自建端点，而非 aws&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">region_name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;us-east-1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># MinIO 通常随便填一个&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">aws_access_key_id&lt;/span>&lt;span class="o">=...&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">aws_secret_access_key&lt;/span>&lt;span class="o">=...&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">boto3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">session&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Config&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;addressing_style&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;path&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="c1"># 关键：path-style&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中 &lt;code>addressing_style&lt;/code> 决定 bucket 怎么进 URL：&lt;code>path&lt;/code> 风格是 &lt;code>http://endpoint/bucket/key&lt;/code>，&lt;code>virtual_hosted&lt;/code> 风格是 &lt;code>http://bucket.endpoint/key&lt;/code>。自建 MinIO 没法给每个 bucket 配域名泛解析，所以必须用 path-style——这是自建存储的第一个常识坑。&lt;/p>
&lt;p>但&amp;quot;实现了 S3 API&amp;quot;不等于&amp;quot;实现了 AWS 最新加的每一个字段&amp;quot;。S3 协议在演进，兼容实现永远在追赶，追赶不上的时候，就会返回一个很直白的错误码：&lt;strong>NotImplemented&lt;/strong>。&lt;/p>
&lt;h3 id="签名-v4-之下请求体有三种泳姿">签名 V4 之下，请求体有三种&amp;quot;泳姿&amp;quot;
&lt;/h3>&lt;p>AWS 签名协议 V4 在传输 payload 时有这么几种形态：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>形态&lt;/th>
&lt;th>特点&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>signed payload&lt;/td>
&lt;td>请求体整体参与签名计算，头里放完整哈希&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>UNSIGNED-PAYLOAD&lt;/code>&lt;/td>
&lt;td>请求体不参与签名，靠 TLS 保证完整性&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>STREAMING-*&lt;/code>&lt;/td>
&lt;td>请求体按 &lt;code>aws-chunked&lt;/code> 分块流式传输，边传边签&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>而这次出事的 &lt;code>STREAMING-UNSIGNED-PAYLOAD-TRAILER&lt;/code> 是最新的变体：分块流式 + 不签名 + &lt;strong>trailer&lt;/strong>——即在所有数据块传完之后，再追加一个尾部块，携带整个请求体的 CRC32 校验和。这套机制是 AWS 2024 年底力推的&amp;quot;默认数据完整性&amp;quot;改造的一部分。&lt;/p>
&lt;h2 id="根因boto3-136-改了一个默认值">根因：boto3 1.36 改了一个默认值
&lt;/h2>&lt;p>2025 年 1 月，boto3 1.36.0 发布，官方 changelog 里有一条不起眼的变更：&lt;/p>
&lt;blockquote>
&lt;p>Default value of &lt;code>request_checksum_calculation&lt;/code> is changed from &lt;code>when_required&lt;/code> to &lt;code>when_supported&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>翻译成人话：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>以前（&lt;code>when_required&lt;/code>）&lt;/strong>：只有 API 明确要求校验和时才算。&lt;code>PutObject&lt;/code> 不要求，所以请求体就是普通字节流，任何 S3 兼容实现都吃得下。&lt;/li>
&lt;li>&lt;strong>现在（&lt;code>when_supported&lt;/code>）&lt;/strong>：只要 API &lt;em>支持&lt;/em> 校验和（&lt;code>PutObject&lt;/code> 正好支持），就默认给请求体算 CRC32，并用上面那套 &lt;code>STREAMING-UNSIGNED-PAYLOAD-TRAILER&lt;/code> 编码传输。&lt;/li>
&lt;/ul>
&lt;p>于是新版 boto3 对旧 MinIO（以及一批没跟进的兼容端点）发出的每个 PutObject 都长成了它们看不懂的样子，对端的回应整齐划一：&lt;code>NotImplemented&lt;/code>。&lt;/p>
&lt;p>为什么是&amp;quot;部署之后&amp;quot;才炸？仓库里 &lt;code>pyproject.toml&lt;/code> 写的是 &lt;code>boto3&amp;gt;=1.43.28&lt;/code>，依赖锁在 1.43.28。上一版镜像构建时拉的依赖、和这次重新构建拉的依赖，中间隔着这次默认值变更——&lt;strong>代码一行没改，重新构建镜像本身就成了变更&lt;/strong>。这也是&amp;quot;锁文件 + 常态化重新部署&amp;quot;模式的一个隐性代价：依赖漂移会以最意想不到的方式找上门。&lt;/p>
&lt;h2 id="修复一行-config三个层次">修复：一行 Config，三个层次
&lt;/h2>&lt;p>修复本身轻得可笑，在创建 boto3 客户端的 Config 里显式把行为钉回旧默认：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 修复前：跟随 SDK 默认，1.36+ 对 PutObject 附带流式校验尾&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">boto3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">session&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Config&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">s3&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;addressing_style&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">addressing_style&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 修复后：仅在 API 要求时才计算校验和&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">boto3&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">session&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Config&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">s3&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;addressing_style&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">addressing_style&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">request_checksum_calculation&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;when_required&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>除了改代码，还有两条等价路径，按生效成本排序：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>环境变量&lt;/strong>（不重新构建镜像，改完配置重启即生效）：给后端进程加 &lt;code>AWS_REQUEST_CHECKSUM_CALCULATION=when_required&lt;/code>，botocore 会读这个变量；&lt;/li>
&lt;li>&lt;strong>代码 Config&lt;/strong>（本文做法）：写死在适配器里，不依赖部署环境是否记得配这个变量；&lt;/li>
&lt;li>&lt;strong>升级存储端&lt;/strong>：新版本 MinIO 已支持流式校验尾，升级后可以吃回新默认。但注意必须升到 2026-04 之后的补丁版本——这条 trailer 路径先后曝出 CVE-2025-31489、CVE-2026-40344 等认证绕过漏洞（签名校验不完整，持有任意 secret 即可上传），修复分别落在 &lt;code>RELEASE.2025-04-03T14-56-28Z&lt;/code> 和 &lt;code>RELEASE.2026-04-11T03-20-12Z&lt;/code>。对安全敏感的自建场景，&lt;code>when_required&lt;/code> 依旧是更稳的答案。但&amp;quot;为了让客户端新默认能跑而去升级存储&amp;quot;这个依赖方向，在自建场景里通常不如反过来稳。&lt;/li>
&lt;/ol>
&lt;p>选代码层还有一个架构上的理由：全仓库只有一个创建 boto3 客户端的地方——infrastructure 层的对象存储适配器，博客、语音、知识库全部经由它读写。&lt;strong>修一处，全站愈合&lt;/strong>；这也意味着这类坑永远值得修在适配器里，而不是散落在各调用方。&lt;/p>
&lt;h2 id="同类清扫适配器里还埋着一个-ascii-坑">同类清扫：适配器里还埋着一个 ASCII 坑
&lt;/h2>&lt;p>顺着这次排查把对象存储适配器翻了一遍，发现它早已修过另一个&amp;quot;协议约束&amp;quot;类的问题，记录于此，因为它们本质上是同一类坑：&lt;strong>S3 的某些约束不在文档第一页，而在 HTTP 协议的物理规则里。&lt;/strong>&lt;/p>
&lt;p>S3 的用户元数据（&lt;code>x-amz-meta-*&lt;/code>）走的是 HTTP header，而 RFC 7230 规定 header 值必须是 ASCII。存中文描述时，botocore 的 &lt;code>validate_ascii_metadata&lt;/code> 会在&lt;strong>发请求之前&lt;/strong>就拒绝你：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">put_object&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="o">=...&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="o">=...&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">metadata&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;融入&amp;#34;&lt;/span>&lt;span class="p">})&lt;/span> &lt;span class="c1"># ParamValidationError&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>适配器里的解法是&amp;quot;写入时编码 + 侧车标记还原&amp;quot;：非 ASCII 值先 percent-encode，同时多写一个 &lt;code>&amp;lt;key&amp;gt;-encoding: utf-8-pct&lt;/code> 的侧车键；读取时发现有侧车标记就 &lt;code>unquote&lt;/code> 还原。不依赖任何带外信息，元数据自描述。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">_sanitize_s3_metadata&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">key&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">value&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">items&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">isascii&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sanitized&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">value&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sanitized&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">quote&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">value&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">safe&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1"># 值编码&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sanitized&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">key&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">-encoding&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;utf-8-pct&amp;#34;&lt;/span> &lt;span class="c1"># 侧车标记&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>把它和这次的校验和坑放在一起看，S3 兼容存储的踩坑地图就清晰了：&lt;strong>一类的坑源于&amp;quot;协议在演进，兼容端在追赶&amp;quot;（NotImplemented）；另一类源于&amp;quot;协议在收紧，客户端在把关&amp;quot;（ASCII 校验）&lt;/strong>。前者修客户端编码方式，后者修数据编码方式，但都该修在同一个适配器里。&lt;/p>
&lt;h2 id="验证">验证
&lt;/h2>&lt;p>修复的验证分三层，也正好对应问题的影响面：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>单元测试&lt;/strong>：对象存储适配器的元数据编解码测试全过，确认 Config 变更不影响既有行为；&lt;/li>
&lt;li>&lt;strong>影响面&lt;/strong>：全仓库搜索 &lt;code>boto3.client&lt;/code> 只有适配器一处调用点——博客、语音、知识库三条业务线同时被这一行修复覆盖；&lt;/li>
&lt;li>&lt;strong>真实入口&lt;/strong>：重新部署后，用 CLI 以原来的命令重新发布那篇文章，一次通过，公开页面正常渲染。&lt;/li>
&lt;/ol>
&lt;p>顺带一提，之前 422 拒掉的那个 2.9MB 自包含 HTML，最后也没有硬塞进去，而是改用设计内的路径：Markdown 正文 + 图片平铺进 &lt;code>assets/&lt;/code> 打包上传。事后看这次事故像是两件事，其实共享同一个教训——&lt;strong>别和平台默认值较劲，走它给你留的路&lt;/strong>。&lt;/p>
&lt;h2 id="几点收获">几点收获
&lt;/h2>&lt;ul>
&lt;li>&lt;strong>&amp;ldquo;兼容 S3&amp;quot;是一个光谱，不是一个开关&lt;/strong>。协议基础操作（对象增删改查）早就稳定，差异都藏在演进中的新特性里。判断一个兼容端点的成色，看它对最新协议扩展的支持比看 README 有用。&lt;/li>
&lt;li>&lt;strong>SDK 的默认值也是 API&lt;/strong>。boto3 这次变更完全符合语义化版本（major 没动，默认值动了），但对你来说行为就是变了。升级依赖 = 引入一次隐形变更，重新部署和改代码应该获得同级别的敬畏。&lt;/li>
&lt;li>&lt;strong>读写分离是天然的故障定位器&lt;/strong>。&amp;ldquo;读全绿、写全红&amp;quot;直接把嫌疑范围从整个 API 收窄到存储写入路径。分层架构平时的意义在排障时兑现：日志栈从 API 层一路穿到 botocore，每层只做一件事，所以每一栈帧都在缩小包围圈。&lt;/li>
&lt;li>&lt;strong>报错信息里最值钱的词是 NotImplemented&lt;/strong>。它不是&amp;quot;你错了&amp;rdquo;，而是&amp;quot;我们（实现方）还没做这个&amp;rdquo;——看到它就该去查协议演进和版本差异，而不是盯着自己的请求参数找茬。&lt;/li>
&lt;/ul></description></item><item><title>数据库备份实战</title><link>https://www.zata.cc/p/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%A4%87%E4%BB%BD%E5%AE%9E%E6%88%98/</link><pubDate>Thu, 28 May 2026 14:55:07 +0800</pubDate><guid>https://www.zata.cc/p/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%A4%87%E4%BB%BD%E5%AE%9E%E6%88%98/</guid><description>&lt;img src="https://www.zata.cc/p/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%A4%87%E4%BB%BD%E5%AE%9E%E6%88%98/images/index/index.png" alt="Featured image of post 数据库备份实战" />&lt;h2 id="前言">前言
&lt;/h2>&lt;p>数据是企业的核心资产，数据库备份是数据安全的最后一道防线。本文将系统介绍 PostgreSQL 和 MySQL 的备份方法、自动化策略以及恢复演练流程。&lt;/p>
&lt;h2 id="一postgresql-备份">一、PostgreSQL 备份
&lt;/h2>&lt;h3 id="11-pg_dump-基础用法">1.1 pg_dump 基础用法
&lt;/h3>&lt;p>&lt;code>pg_dump&lt;/code> 是 PostgreSQL 最常用的逻辑备份工具，支持单数据库导出。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 基本备份命令&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_dump -h localhost -U postgres -d mydb &amp;gt; backup.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 压缩备份（推荐）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_dump -h localhost -U postgres -d mydb -Fc &amp;gt; backup.dump
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 仅备份结构（不含数据）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_dump -h localhost -U postgres -d mydb --schema-only &amp;gt; schema.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 仅备份数据（不含结构）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_dump -h localhost -U postgres -d mydb --data-only &amp;gt; data.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 备份指定表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_dump -h localhost -U postgres -d mydb -t users -t orders &amp;gt; tables.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>参数说明：&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>参数&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>-h&lt;/code>&lt;/td>
&lt;td>数据库主机地址&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>-U&lt;/code>&lt;/td>
&lt;td>连接用户名&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>-d&lt;/code>&lt;/td>
&lt;td>数据库名称&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>-Fc&lt;/code>&lt;/td>
&lt;td>自定义格式压缩（推荐，支持并行恢复）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>-t&lt;/code>&lt;/td>
&lt;td>指定备份的表&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="12-pg_dumpall-全库备份">1.2 pg_dumpall 全库备份
&lt;/h3>&lt;p>备份整个 PostgreSQL 实例（所有数据库 + 全局对象）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 备份所有数据库和全局对象（角色、表空间等）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_dumpall -h localhost -U postgres &amp;gt; all_databases.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 仅备份全局对象（角色定义）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_dumpall -h localhost -U postgres --globals-only &amp;gt; globals.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="13-恢复方法">1.3 恢复方法
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复 SQL 格式备份&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">psql -h localhost -U postgres -d mydb &amp;lt; backup.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复自定义格式备份（支持并行）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h localhost -U postgres -d mydb -j &lt;span class="m">4&lt;/span> backup.dump
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复前创建新数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">createdb -h localhost -U postgres mydb_restore
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h localhost -U postgres -d mydb_restore backup.dump
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="二mysql-备份">二、MySQL 备份
&lt;/h2>&lt;h3 id="21-mysqldump-基础用法">2.1 mysqldump 基础用法
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 基本备份命令&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysqldump -h localhost -u root -p mydb &amp;gt; backup.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 备份所有数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysqldump -h localhost -u root -p --all-databases &amp;gt; all_databases.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 备份指定表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysqldump -h localhost -u root -p mydb users orders &amp;gt; tables.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 仅备份结构&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysqldump -h localhost -u root -p mydb --no-data &amp;gt; schema.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 仅备份数据&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysqldump -h localhost -u root -p mydb --no-create-info &amp;gt; data.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>推荐参数组合：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 生产环境推荐参数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysqldump -h localhost -u root -p &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --single-transaction &lt;span class="se">\ &lt;/span> &lt;span class="c1"># InnoDB 一致性快照&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> --routines &lt;span class="se">\ &lt;/span> &lt;span class="c1"># 包含存储过程和函数&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> --triggers &lt;span class="se">\ &lt;/span> &lt;span class="c1"># 包含触发器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> --events &lt;span class="se">\ &lt;/span> &lt;span class="c1"># 包含事件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> --master-data&lt;span class="o">=&lt;/span>&lt;span class="m">2&lt;/span> &lt;span class="se">\ &lt;/span> &lt;span class="c1"># 记录 binlog 位置（用于主从同步）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mydb &amp;gt; backup.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="22-恢复方法">2.2 恢复方法
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysql -h localhost -u root -p mydb &amp;lt; backup.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复所有数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysql -h localhost -u root -p &amp;lt; all_databases.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="三其他-postgresql-图形化工具">三、其他 PostgreSQL 图形化工具
&lt;/h2>&lt;p>除了 Navicat，还有多种优秀的 PostgreSQL 客户端工具支持备份和恢复功能。&lt;/p>
&lt;h3 id="31-pgadmin">3.1 pgAdmin
&lt;/h3>&lt;p>pgAdmin 是 PostgreSQL 官方推荐的免费图形化管理工具：&lt;/p>
&lt;p>&lt;strong>安装方法：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 使用 Homebrew 安装（macOS）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install --cask pgadmin4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 或从官网下载：https://www.pgadmin.org/download/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>备份步骤：&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>连接数据库服务器，选择目标数据库&lt;/li>
&lt;li>右键点击数据库 →「备份」&lt;/li>
&lt;li>配置备份选项：
&lt;ul>
&lt;li>&lt;strong>格式&lt;/strong>：Custom（推荐）、Directory、Plain SQL、Tar&lt;/li>
&lt;li>&lt;strong>编码&lt;/strong>：UTF8（推荐）&lt;/li>
&lt;li>&lt;strong>中定义&lt;/strong>：选择「仅结构」或「结构和数据」&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>选择备份文件保存位置，点击「备份」&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>恢复步骤：&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>右键点击目标数据库 →「恢复」&lt;/li>
&lt;li>选择备份文件（.backup 或 .dump）&lt;/li>
&lt;li>配置恢复选项：
&lt;ul>
&lt;li>&lt;strong>架构&lt;/strong>：选择要恢复的 schema&lt;/li>
&lt;li>&lt;strong>数据&lt;/strong>：选择要恢复的表&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>点击「恢复」执行&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>优点：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>免费开源，官方推荐&lt;/li>
&lt;li>功能全面，支持服务器管理&lt;/li>
&lt;li>跨平台（Windows/macOS/Linux）&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>缺点：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>界面较复杂，新手上手较难&lt;/li>
&lt;li>大数据库备份可能较慢&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="32-dbeaver">3.2 DBeaver
&lt;/h3>&lt;p>DBeaver 是一款功能强大的免费数据库管理工具，支持 PostgreSQL 及多种数据库：&lt;/p>
&lt;p>&lt;strong>安装方法：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 使用 Homebrew 安装（macOS）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install --cask dbeaver-community
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 或从官网下载：https://dbeaver.io/download/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>备份步骤：&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>右键点击数据库 →「工具」→「备份数据库」&lt;/li>
&lt;li>选择备份对象（整个库或指定表）&lt;/li>
&lt;li>配置备份选项：
&lt;ul>
&lt;li>&lt;strong>格式&lt;/strong>：SQL、Dump、CSV 等&lt;/li>
&lt;li>&lt;strong>选项&lt;/strong>：包含索引、触发器、外键等&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>保存备份文件&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>恢复步骤：&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>右键点击目标数据库 →「工具」→「恢复数据库」&lt;/li>
&lt;li>选择备份文件&lt;/li>
&lt;li>配置恢复选项&lt;/li>
&lt;li>执行恢复&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>优点：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>完全免费开源&lt;/li>
&lt;li>支持 80+ 种数据库&lt;/li>
&lt;li>界面友好，功能丰富&lt;/li>
&lt;li>支持数据导出/导入&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>缺点：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>功能较多，界面稍显复杂&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="33-datagrip">3.3 DataGrip
&lt;/h3>&lt;p>JetBrains DataGrip 是一款专业的数据库 IDE（付费）：&lt;/p>
&lt;p>&lt;strong>安装方法：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 使用 Homebrew 安装（macOS）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install --cask datagrip
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 或从官网下载：https://www.jetbrains.com/datagrip/download/&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>备份方式：&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>右键数据库 →「Dump to File」导出&lt;/li>
&lt;li>使用 Database Diff 进行数据库同步备份&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>特点：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>强大的 SQL 编辑和调试功能&lt;/li>
&lt;li>支持多数据库管理&lt;/li>
&lt;li>与 JetBrains 全家桶集成良好&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="34-heidisql">3.4 HeidiSQL
&lt;/h3>&lt;p>HeidiSQL 主要用于 MySQL/MariaDB，但也支持 PostgreSQL：&lt;/p>
&lt;p>&lt;strong>备份：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>右键数据库 →「导出数据库为 SQL」&lt;/li>
&lt;li>支持自定义导出选项&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>特点：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>免费、轻量级&lt;/li>
&lt;li>Windows 专用&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="35-phppgadmin">3.5 phpPgAdmin
&lt;/h3>&lt;p>基于 Web 的 PostgreSQL 管理工具：&lt;/p>
&lt;p>&lt;strong>特点：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>浏览器访问，无需安装客户端&lt;/li>
&lt;li>适合服务器管理&lt;/li>
&lt;li>功能相对基础&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="36-adminer">3.6 Adminer
&lt;/h3>&lt;p>轻量级单文件数据库管理工具：&lt;/p>
&lt;p>&lt;strong>特点：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>仅一个 PHP 文件，非常轻量&lt;/li>
&lt;li>支持 PostgreSQL、MySQL、MongoDB 等&lt;/li>
&lt;li>可直接通过 Web 访问&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="37-工具对比">3.7 工具对比
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>工具&lt;/th>
&lt;th>类型&lt;/th>
&lt;th>价格&lt;/th>
&lt;th>跨平台&lt;/th>
&lt;th>特点&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Navicat&lt;/strong>&lt;/td>
&lt;td>桌面应用&lt;/td>
&lt;td>付费&lt;/td>
&lt;td>Windows/macOS/Linux&lt;/td>
&lt;td>功能全面，界面美观&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>pgAdmin&lt;/strong>&lt;/td>
&lt;td>桌面应用&lt;/td>
&lt;td>免费&lt;/td>
&lt;td>全平台&lt;/td>
&lt;td>官方推荐，功能强大&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>DBeaver&lt;/strong>&lt;/td>
&lt;td>桌面应用&lt;/td>
&lt;td>免费&lt;/td>
&lt;td>全平台&lt;/td>
&lt;td>开源社区活跃&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>DataGrip&lt;/strong>&lt;/td>
&lt;td>IDE&lt;/td>
&lt;td>付费&lt;/td>
&lt;td>全平台&lt;/td>
&lt;td>开发者友好&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>HeidiSQL&lt;/strong>&lt;/td>
&lt;td>桌面应用&lt;/td>
&lt;td>免费&lt;/td>
&lt;td>Windows&lt;/td>
&lt;td>轻量级&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>phpPgAdmin&lt;/strong>&lt;/td>
&lt;td>Web 应用&lt;/td>
&lt;td>免费&lt;/td>
&lt;td>全平台&lt;/td>
&lt;td>无需安装客户端&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Adminer&lt;/strong>&lt;/td>
&lt;td>Web 应用&lt;/td>
&lt;td>免费&lt;/td>
&lt;td>全平台&lt;/td>
&lt;td>极轻量级&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h3 id="38-命令行工具补充">3.8 命令行工具补充
&lt;/h3>&lt;p>除了图形化工具，以下命令行工具也非常有用：&lt;/p>
&lt;p>&lt;strong>pg_dumpall&lt;/strong>（已介绍）：备份所有数据库&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pg_dumpall -h localhost -U postgres &amp;gt; all.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>psql&lt;/strong>（已介绍）：恢复 SQL 文件&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">psql -h localhost -U postgres -d mydb &amp;lt; backup.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>pg_basebackup&lt;/strong>：物理备份（完整数据库复制）&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建物理备份&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_basebackup -h localhost -U postgres -D /backup/base -Ft -z -P
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复物理备份&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -d postgres /backup/base
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Barman&lt;/strong>：企业级备份管理工具&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 配置备份服务器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">barman check pg_server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">barman backup pg_server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">barman recover pg_server latest /var/lib/postgresql/data
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="四navicat-图形化备份">四、Navicat 图形化备份
&lt;/h2>&lt;p>Navicat 是一款流行的数据库管理工具，支持 MySQL、PostgreSQL、SQL Server、Oracle 等多种数据库，提供直观的图形界面进行备份操作。&lt;/p>
&lt;p>&lt;strong>安装方法：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 使用 Homebrew 安装 Navicat Premium（macOS）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install --cask navicat-premium
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 或安装 Navicat for PostgreSQL 专用版&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew install --cask navicat-for-postgresql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 或从官网下载：https://www.navicat.com.cn/download/navicat-premium&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="41-postgresql-备份">4.1 PostgreSQL 备份
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>连接数据库&lt;/strong>：在左侧连接列表中选择 PostgreSQL 连接&lt;/li>
&lt;li>&lt;strong>开始备份&lt;/strong>：
&lt;ul>
&lt;li>右键点击目标数据库 → 选择「备份数据库」&lt;/li>
&lt;li>或菜单栏选择「工具」→「备份数据库」&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>配置备份选项&lt;/strong>：
&lt;ul>
&lt;li>选择备份格式（建议选择「自定义」格式）&lt;/li>
&lt;li>可选择要包含的对象（表、视图、函数等）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>保存备份&lt;/strong>：点击「开始」后选择保存位置&lt;/li>
&lt;/ol>
&lt;h3 id="42-mysql-备份">4.2 MySQL 备份
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>连接数据库&lt;/strong>：在左侧连接列表中选择 MySQL 连接&lt;/li>
&lt;li>&lt;strong>数据同步（迁移备份）&lt;/strong>：
&lt;ul>
&lt;li>菜单栏选择「工具」→「数据同步」&lt;/li>
&lt;li>源选择当前数据库&lt;/li>
&lt;li>目标选择文件或另一个数据库&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>导出数据库&lt;/strong>：
&lt;ul>
&lt;li>右键数据库 →「转储 SQL 文件」→「结构和数据」或「仅结构」&lt;/li>
&lt;li>自动生成 .sql 文件保存到本地&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="43-定时自动备份navicat-premium">4.3 定时自动备份（Navicat Premium）
&lt;/h3>&lt;p>Navicat Premium 支持定时任务自动备份：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>新建批处理作业&lt;/strong>：
&lt;ul>
&lt;li>菜单栏选择「工具」→「自动运行」或「计划」&lt;/li>
&lt;li>点击「新建批处理作业」&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>添加备份任务&lt;/strong>：
&lt;ul>
&lt;li>从左侧拖拽「备份」任务到右侧流程区&lt;/li>
&lt;li>选择连接和数据库&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>配置定时计划&lt;/strong>：
&lt;ul>
&lt;li>勾选「计划」选项卡&lt;/li>
&lt;li>设置执行频率（每天、每周等）&lt;/li>
&lt;li>设置执行时间&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>保存并启用&lt;/strong>：
&lt;ul>
&lt;li>保存批处理作业&lt;/li>
&lt;li>勾选「启用」启动定时任务&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="44-navicat-cloud-备份">4.4 Navicat Cloud 备份
&lt;/h3>&lt;p>Navicat Cloud 提供云端备份同步功能：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>登录 Navicat Cloud&lt;/strong>：
&lt;ul>
&lt;li>菜单栏「文件」→「登录 Navicat Cloud」&lt;/li>
&lt;li>输入账号密码&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>同步备份&lt;/strong>：
&lt;ul>
&lt;li>右键数据库 →「备份到 Navicat Cloud」&lt;/li>
&lt;li>或通过「工具」→「同步」上传备份&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>恢复云端备份&lt;/strong>：
&lt;ul>
&lt;li>从 Navicat Cloud 下载备份文件&lt;/li>
&lt;li>恢复到本地数据库&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h3 id="45-navicat-恢复数据库">4.5 Navicat 恢复数据库
&lt;/h3>&lt;p>备份的最终目的是恢复，Navicat 提供了便捷的恢复功能：&lt;/p>
&lt;p>&lt;strong>PostgreSQL 恢复步骤：&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>右键点击目标数据库 → 选择「还原数据库」&lt;/li>
&lt;li>选择备份文件（.backup 或 .dump 格式）&lt;/li>
&lt;li>配置恢复选项：
&lt;ul>
&lt;li>&lt;strong>仅还原结构&lt;/strong>：只恢复表结构，不恢复数据&lt;/li>
&lt;li>&lt;strong>仅还原数据&lt;/strong>：只恢复数据，表结构需已存在&lt;/li>
&lt;li>&lt;strong>结构和数据&lt;/strong>：完整恢复（推荐）&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>点击「开始」执行恢复&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>MySQL 恢复步骤：&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>运行 SQL 文件&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>右键目标数据库 →「运行 SQL 文件」&lt;/li>
&lt;li>选择之前导出的 .sql 备份文件&lt;/li>
&lt;li>执行导入&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>数据同步恢复&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>菜单栏「工具」→「数据同步」&lt;/li>
&lt;li>源选择备份文件或备份库&lt;/li>
&lt;li>目标选择要恢复的数据库&lt;/li>
&lt;li>执行同步&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>恢复注意事项：&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>场景&lt;/th>
&lt;th>注意事项&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>覆盖恢复&lt;/strong>&lt;/td>
&lt;td>确保目标库已清空或删除，避免数据冲突&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>部分恢复&lt;/strong>&lt;/td>
&lt;td>可选择仅恢复特定表，减少恢复时间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>跨版本恢复&lt;/strong>&lt;/td>
&lt;td>高版本备份可能无法恢复到低版本数据库&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>字符集问题&lt;/strong>&lt;/td>
&lt;td>确保备份和恢复使用相同字符集，避免乱码&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="46-navicat-备份注意事项">4.6 Navicat 备份注意事项
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>注意事项&lt;/th>
&lt;th>说明&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>备份位置&lt;/strong>&lt;/td>
&lt;td>建议保存到非系统盘，避免系统崩溃丢失&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>文件命名&lt;/strong>&lt;/td>
&lt;td>包含日期时间，便于追溯（如 &lt;code>mydb_20260528.sql&lt;/code>）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>定期检查&lt;/strong>&lt;/td>
&lt;td>定期验证备份文件能否正常恢复&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>大数据库&lt;/strong>&lt;/td>
&lt;td>超大数据库建议使用命令行工具，性能更优&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>密码保护&lt;/strong>&lt;/td>
&lt;td>敏感数据库建议设置备份文件密码&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="五自动化备份脚本">五、自动化备份脚本
&lt;/h2>&lt;h3 id="51-postgresql-自动备份脚本">5.1 PostgreSQL 自动备份脚本
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># pg_backup.sh - PostgreSQL 自动备份脚本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 配置变量&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PG_HOST&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;localhost&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PG_USER&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;postgres&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PG_PORT&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;5432&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">BACKUP_DIR&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/backup/postgresql&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">RETENTION_DAYS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">DATE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>date +%Y%m%d_%H%M%S&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建备份目录&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkdir -p &lt;span class="nv">$BACKUP_DIR&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 获取所有数据库列表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">DATABASES&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>psql -h &lt;span class="nv">$PG_HOST&lt;/span> -U &lt;span class="nv">$PG_USER&lt;/span> -p &lt;span class="nv">$PG_PORT&lt;/span> -t -c &lt;span class="s2">&amp;#34;SELECT datname FROM pg_database WHERE datistemplate = false;&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 遍历备份每个数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> DB in &lt;span class="nv">$DATABASES&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">do&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">DB&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>&lt;span class="nb">echo&lt;/span> &lt;span class="nv">$DB&lt;/span> &lt;span class="p">|&lt;/span> tr -d &lt;span class="s1">&amp;#39; &amp;#39;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="o">[&lt;/span> -n &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$DB&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">]&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">then&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;正在备份数据库: &lt;/span>&lt;span class="nv">$DB&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> pg_dump -h &lt;span class="nv">$PG_HOST&lt;/span> -U &lt;span class="nv">$PG_USER&lt;/span> -p &lt;span class="nv">$PG_PORT&lt;/span> -d &lt;span class="nv">$DB&lt;/span> -Fc &amp;gt; &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$BACKUP_DIR&lt;/span>&lt;span class="s2">/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">DB&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">_&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">DATE&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">.dump&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">done&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 清理过期备份&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">find &lt;span class="nv">$BACKUP_DIR&lt;/span> -name &lt;span class="s2">&amp;#34;*.dump&amp;#34;&lt;/span> -mtime +&lt;span class="nv">$RETENTION_DAYS&lt;/span> -delete
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;备份完成！&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="52-mysql-自动备份脚本">5.2 MySQL 自动备份脚本
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># mysql_backup.sh - MySQL 自动备份脚本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 配置变量&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">MYSQL_HOST&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;localhost&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">MYSQL_USER&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;root&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">MYSQL_PASS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;your_password&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">BACKUP_DIR&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/backup/mysql&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">RETENTION_DAYS&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">7&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">DATE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>date +%Y%m%d_%H%M%S&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 创建备份目录&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mkdir -p &lt;span class="nv">$BACKUP_DIR&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 获取所有数据库列表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">DATABASES&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>mysql -h &lt;span class="nv">$MYSQL_HOST&lt;/span> -u &lt;span class="nv">$MYSQL_USER&lt;/span> -p&lt;span class="nv">$MYSQL_PASS&lt;/span> -e &lt;span class="s2">&amp;#34;SHOW DATABASES;&amp;#34;&lt;/span> &lt;span class="p">|&lt;/span> grep -v Database &lt;span class="p">|&lt;/span> grep -v information_schema &lt;span class="p">|&lt;/span> grep -v performance_schema&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 遍历备份每个数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> DB in &lt;span class="nv">$DATABASES&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="k">do&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;正在备份数据库: &lt;/span>&lt;span class="nv">$DB&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> mysqldump -h &lt;span class="nv">$MYSQL_HOST&lt;/span> -u &lt;span class="nv">$MYSQL_USER&lt;/span> -p&lt;span class="nv">$MYSQL_PASS&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --single-transaction --routines --triggers --events &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &lt;span class="nv">$DB&lt;/span> &lt;span class="p">|&lt;/span> gzip &amp;gt; &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$BACKUP_DIR&lt;/span>&lt;span class="s2">/&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">DB&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">_&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nv">DATE&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">.sql.gz&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">done&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 清理过期备份&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">find &lt;span class="nv">$BACKUP_DIR&lt;/span> -name &lt;span class="s2">&amp;#34;*.sql.gz&amp;#34;&lt;/span> -mtime +&lt;span class="nv">$RETENTION_DAYS&lt;/span> -delete
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;备份完成！&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="53-配置定时任务">5.3 配置定时任务
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 编辑 crontab&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">crontab -e
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 每天凌晨 2 点执行备份&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="m">0&lt;/span> &lt;span class="m">2&lt;/span> * * * /scripts/pg_backup.sh &amp;gt;&amp;gt; /var/log/pg_backup.log 2&amp;gt;&lt;span class="p">&amp;amp;&lt;/span>&lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="m">0&lt;/span> &lt;span class="m">2&lt;/span> * * * /scripts/mysql_backup.sh &amp;gt;&amp;gt; /var/log/mysql_backup.log 2&amp;gt;&lt;span class="p">&amp;amp;&lt;/span>&lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="六云存储备份">六、云存储备份
&lt;/h2>&lt;h3 id="51-上传到-aws-s3">5.1 上传到 AWS S3
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># upload_to_s3.sh&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">BACKUP_DIR&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/backup/postgresql&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">S3_BUCKET&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;s3://your-bucket/database-backups&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">DATE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>date +%Y%m%d&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 同步备份文件到 S3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">aws s3 sync &lt;span class="nv">$BACKUP_DIR&lt;/span> &lt;span class="nv">$S3_BUCKET&lt;/span>/&lt;span class="nv">$DATE&lt;/span>/ &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --storage-class STANDARD_IA &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --delete
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 设置生命周期规则自动清理旧备份（在 S3 控制台配置）&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="52-上传到-cloudflare-r2">5.2 上传到 Cloudflare R2
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 使用 rclone 同步到 R2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">rclone sync /backup/postgresql r2:your-bucket/database-backups/&lt;span class="k">$(&lt;/span>date +%Y%m%d&lt;span class="k">)&lt;/span>/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="七备份策略最佳实践">七、备份策略最佳实践
&lt;/h2>&lt;h3 id="61-备份类型对比">6.1 备份类型对比
&lt;/h3>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>类型&lt;/th>
&lt;th>说明&lt;/th>
&lt;th>优点&lt;/th>
&lt;th>缺点&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>全量备份&lt;/strong>&lt;/td>
&lt;td>备份所有数据&lt;/td>
&lt;td>恢复简单快速&lt;/td>
&lt;td>占用空间大，耗时长&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>增量备份&lt;/strong>&lt;/td>
&lt;td>仅备份变化数据&lt;/td>
&lt;td>占用空间小&lt;/td>
&lt;td>恢复需要全量+所有增量&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>差异备份&lt;/strong>&lt;/td>
&lt;td>备份自上次全量后的变化&lt;/td>
&lt;td>恢复比增量快&lt;/td>
&lt;td>比增量占用更多空间&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="62-推荐策略">6.2 推荐策略
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">生产环境推荐备份策略：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">┌─────────────────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 周日：全量备份 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 周一~周六：增量备份 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 保留周期：至少 4 周 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 异地备份：同步到云存储（S3/R2/OSS） │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ 恢复演练：每月至少 1 次 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─────────────────────────────────────────────────────┘
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="63-备份验证清单">6.3 备份验证清单
&lt;/h3>&lt;ul>
&lt;li>&lt;input disabled="" type="checkbox"> 备份文件是否完整生成&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 备份文件大小是否合理&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 备份是否按时执行（检查 cron 日志）&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 异地备份是否同步成功&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> 定期进行恢复演练&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="八数据恢复命令行">八、数据恢复（命令行）
&lt;/h2>&lt;p>命令行恢复是生产环境中最重要的技能，本节详细介绍 PostgreSQL 和 MySQL 的恢复操作。&lt;/p>
&lt;h3 id="71-postgresql-恢复详解">7.1 PostgreSQL 恢复详解
&lt;/h3>&lt;p>&lt;strong>基础恢复命令：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复 SQL 格式备份（纯文本）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">psql -h localhost -U postgres -d mydb &amp;lt; backup.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复自定义格式备份（.dump 文件）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h localhost -U postgres -d mydb backup.dump
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 并行恢复（加快大数据库恢复速度）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h localhost -U postgres -d mydb -j &lt;span class="m">4&lt;/span> backup.dump
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>创建新数据库恢复：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 创建新数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">createdb -h localhost -U postgres mydb_new
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 恢复数据到新数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h localhost -U postgres -d mydb_new backup.dump
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>恢复前清理目标库：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 如果目标库存在且要完全覆盖，先删除&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">dropdb -h localhost -U postgres mydb
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 重新创建数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">createdb -h localhost -U postgres mydb
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复数据&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h localhost -U postgres -d mydb backup.dump
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>选择性恢复（仅恢复指定表）：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 列出备份中的所有对象&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -l backup.dump
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 仅恢复特定表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h localhost -U postgres -d mydb -t users backup.dump
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h localhost -U postgres -d mydb -t orders backup.dump
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 仅恢复存储过程&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h localhost -U postgres -d mydb -P -t function_name backup.dump
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>恢复前检查选项：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 先检查备份文件内容（不执行恢复）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore --help &lt;span class="c1"># 查看所有选项&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 不带数据恢复（仅检查结构）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h localhost -U postgres -d mydb --schema-only backup.dump
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复到另一个表空间&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h localhost -U postgres -d mydb --tablespace&lt;span class="o">=&lt;/span>new_space backup.dump
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="72-mysql-恢复详解">7.2 MySQL 恢复详解
&lt;/h3>&lt;p>&lt;strong>基础恢复命令：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复单个数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysql -h localhost -u root -p mydb &amp;lt; backup.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复所有数据库（备份时包含 CREATE DATABASE 语句）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysql -h localhost -u root -p &amp;lt; all_databases.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 恢复压缩备份（gzip 格式）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">gunzip &amp;lt; backup.sql.gz &lt;span class="p">|&lt;/span> mysql -h localhost -u root -p mydb
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 或使用 zcat&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">zcat backup.sql.gz &lt;span class="p">|&lt;/span> mysql -h localhost -u root -p mydb
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>指定字符集恢复：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 如果有字符集问题，指定字符集&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysql -h localhost -u root -p --default-character-set&lt;span class="o">=&lt;/span>utf8mb4 mydb &amp;lt; backup.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 或在 SQL 文件开头设置&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysql -h localhost -u root -p --init-command&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;SET NAMES utf8mb4&amp;#34;&lt;/span> mydb &amp;lt; backup.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>覆盖恢复（先清空后导入）：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 删除并重建数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysql -h localhost -u root -p -e &lt;span class="s2">&amp;#34;DROP DATABASE IF EXISTS mydb; CREATE DATABASE mydb;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 恢复数据&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysql -h localhost -u root -p mydb &amp;lt; backup.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>选择性恢复（仅恢复指定表）：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 方法1：使用 sed 提取指定表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sed -n &lt;span class="s1">&amp;#39;/DROP TABLE.*`表名`;/,/UNLOCK TABLES;/p&amp;#39;&lt;/span> backup.sql &amp;gt; table.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysql -h localhost -u root -p mydb &amp;lt; table.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 方法2：使用 mysqlimport（需要先创建空表）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysqlimport -h localhost -u root -p mydb table_data.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 方法3：使用 mydumper 并行导出/导入指定表&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mydumper -h localhost -u root -p -o /backup/tables -t users -t orders
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">myloader -h localhost -u root -p -d /backup/tables -o mydb
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>表结构与数据分离恢复：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 仅恢复表结构&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysql -h localhost -u root -p mydb &amp;lt; schema.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 仅恢复数据（表结构需已存在）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysql -h localhost -u root -p mydb --init-command&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;SET foreign_key_checks=0; SET unique_checks=0; SET SESSION tx_isolation=&amp;#39;READ-UNCOMMITTED&amp;#39;;&amp;#34;&lt;/span> &amp;lt; data.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="73-恢复高级技巧">7.3 恢复高级技巧
&lt;/h3>&lt;p>&lt;strong>时间点恢复（PITR）：&lt;/strong>&lt;/p>
&lt;p>PostgreSQL 支持基于时间点的恢复：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 停止 PostgreSQL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo systemctl stop postgresql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 备份当前数据目录&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo cp -r /var/lib/postgresql/data /var/lib/postgresql/data.bak
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 恢复到指定时间点&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PGDATA&lt;/span>&lt;span class="o">=&lt;/span>/var/lib/postgresql/data pg_restore -P --clean --create --dbname&lt;span class="o">=&lt;/span>postgres backup.dump
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 或编辑 postgresql.conf 设置恢复目标时间&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># recovery_target_time = &amp;#39;2026-05-28 10:00:00&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>MySQL binlog 恢复：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 确定恢复时间点&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysqlbinlog --start-datetime&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;2026-05-28 10:00:00&amp;#34;&lt;/span> --stop-datetime&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;2026-05-28 12:00:00&amp;#34;&lt;/span> mysql-bin.000001 &amp;gt; recovery.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 应用恢复&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysql -h localhost -u root -p mydb &amp;lt; recovery.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>增量备份恢复：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># PostgreSQL WAL 归档恢复&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 恢复基础全量备份&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h localhost -U postgres -d postgres backup.dump
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 应用 WAL 文件&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">psql -h localhost -U postgres -d mydb &amp;lt; &lt;span class="m">000000010000000000000001&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">psql -h localhost -U postgres -d mydb &amp;lt; &lt;span class="m">000000010000000000000002&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>跨服务器恢复：&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># PostgreSQL：恢复远程数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_dump -h remote_host -U postgres -d mydb &lt;span class="p">|&lt;/span> pg_restore -h localhost -U postgres -d mydb
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># MySQL：远程到本地&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mysqldump -h remote_host -u root -p mydb &lt;span class="p">|&lt;/span> mysql -h localhost -u root -p mydb
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="74-恢复检查清单">7.4 恢复检查清单
&lt;/h3>&lt;p>执行恢复前务必检查以下项目：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>检查项&lt;/th>
&lt;th>操作&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>备份文件完整性&lt;/strong>&lt;/td>
&lt;td>检查文件大小和校验和&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>磁盘空间&lt;/strong>&lt;/td>
&lt;td>确认目标磁盘有足够空间&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>数据库版本&lt;/strong>&lt;/td>
&lt;td>确保备份和目标版本兼容&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>字符集&lt;/strong>&lt;/td>
&lt;td>确认字符集设置一致&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>连接权限&lt;/strong>&lt;/td>
&lt;td>确认恢复账号有足够权限&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>依赖关系&lt;/strong>&lt;/td>
&lt;td>检查存储过程、触发器依赖&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>外键约束&lt;/strong>&lt;/td>
&lt;td>恢复时关闭外键检查可加速&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="九恢复演练">九、恢复演练
&lt;/h2>&lt;p>恢复演练是备份策略中&lt;strong>最容易被忽视但最重要&lt;/strong>的环节！&lt;/p>
&lt;h3 id="91-演练脚本">9.1 演练脚本
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="c1"># restore_drill.sh - 恢复演练脚本&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">BACKUP_FILE&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;/backup/postgresql/mydb_20260528_020000.dump&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">TEST_DB&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;mydb_drill_test&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PG_HOST&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;localhost&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">PG_USER&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;postgres&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;=== 数据库恢复演练开始 ===&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;时间: &lt;/span>&lt;span class="k">$(&lt;/span>date&lt;span class="k">)&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 创建测试数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;1. 创建测试数据库: &lt;/span>&lt;span class="nv">$TEST_DB&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">dropdb -h &lt;span class="nv">$PG_HOST&lt;/span> -U &lt;span class="nv">$POSTGRES_USER&lt;/span> &lt;span class="nv">$TEST_DB&lt;/span> 2&amp;gt;/dev/null &lt;span class="o">||&lt;/span> &lt;span class="nb">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">createdb -h &lt;span class="nv">$PG_HOST&lt;/span> -U &lt;span class="nv">$PG_USER&lt;/span> &lt;span class="nv">$TEST_DB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 执行恢复&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;2. 执行恢复...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pg_restore -h &lt;span class="nv">$PG_HOST&lt;/span> -U &lt;span class="nv">$PG_USER&lt;/span> -d &lt;span class="nv">$TEST_DB&lt;/span> &lt;span class="nv">$BACKUP_FILE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 验证数据&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;3. 验证数据完整性...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">TABLE_COUNT&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="k">$(&lt;/span>psql -h &lt;span class="nv">$PG_HOST&lt;/span> -U &lt;span class="nv">$PG_USER&lt;/span> -d &lt;span class="nv">$TEST_DB&lt;/span> -t -c &lt;span class="s2">&amp;#34;SELECT count(*) FROM information_schema.tables WHERE table_schema = &amp;#39;public&amp;#39;;&amp;#34;&lt;/span>&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34; 恢复表数量: &lt;/span>&lt;span class="nv">$TABLE_COUNT&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. 清理测试数据库&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;4. 清理测试数据库...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">dropdb -h &lt;span class="nv">$PG_HOST&lt;/span> -U &lt;span class="nv">$PG_USER&lt;/span> &lt;span class="nv">$TEST_DB&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;=== 恢复演练完成 ===&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="92-演练报告模板">9.2 演练报告模板
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">数据库恢复演练报告
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">==================
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">日期：2026-05-28
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">演练人员：张三
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">备份文件：mydb_20260528_020000.dump
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">备份大小：1.2 GB
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">演练结果：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- 恢复耗时：3分28秒
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- 表数量：45
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- 数据完整性：✓ 通过
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">- 应用连接测试：✓ 通过
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">结论：备份有效，恢复流程正常。
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="十常见问题">十、常见问题
&lt;/h2>&lt;h3 id="q1-备份时数据库是否需要停服">Q1: 备份时数据库是否需要停服？
&lt;/h3>&lt;p>&lt;strong>不需要。&lt;/strong> PostgreSQL 的 &lt;code>pg_dump&lt;/code> 和 MySQL 的 &lt;code>mysqldump --single-transaction&lt;/code> 都支持在线热备份，不会阻塞业务。&lt;/p>
&lt;h3 id="q2-大数据库备份太慢怎么办">Q2: 大数据库备份太慢怎么办？
&lt;/h3>&lt;ul>
&lt;li>PostgreSQL：使用 &lt;code>-j&lt;/code> 参数并行备份/恢复&lt;/li>
&lt;li>MySQL：考虑使用 &lt;code>mydumper&lt;/code> 工具并行导出&lt;/li>
&lt;li>超大数据库建议使用物理备份工具（pg_basebackup、XtraBackup）&lt;/li>
&lt;/ul>
&lt;h3 id="q3-如何备份-docker-中的数据库">Q3: 如何备份 Docker 中的数据库？
&lt;/h3>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># PostgreSQL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker &lt;span class="nb">exec&lt;/span> postgres pg_dump -U postgres mydb &amp;gt; backup.sql
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># MySQL&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker &lt;span class="nb">exec&lt;/span> mysql mysqldump -u root -pPassword mydb &amp;gt; backup.sql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="总结">总结
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>备份是刚需&lt;/strong>：没有备份的数据库就是在裸奔&lt;/li>
&lt;li>&lt;strong>自动化是关键&lt;/strong>：手动备份一定会被遗忘&lt;/li>
&lt;li>&lt;strong>异地备份是保障&lt;/strong>：服务器挂了，备份还在&lt;/li>
&lt;li>&lt;strong>演练是验证&lt;/strong>：没测试过的备份等于没有备份&lt;/li>
&lt;li>&lt;strong>监控是补充&lt;/strong>：备份失败要及时告警&lt;/li>
&lt;/ol>
&lt;p>记住：&lt;strong>数据无价，备份先行！&lt;/strong>&lt;/p></description></item><item><title>Object Storage (对象存储服务)</title><link>https://www.zata.cc/p/object-storage-%E5%AF%B9%E8%B1%A1%E5%AD%98%E5%82%A8%E6%9C%8D%E5%8A%A1/</link><pubDate>Tue, 03 Mar 2026 15:36:55 +0800</pubDate><guid>https://www.zata.cc/p/object-storage-%E5%AF%B9%E8%B1%A1%E5%AD%98%E5%82%A8%E6%9C%8D%E5%8A%A1/</guid><description>&lt;img src="https://www.zata.cc/p/object-storage-%E5%AF%B9%E8%B1%A1%E5%AD%98%E5%82%A8%E6%9C%8D%E5%8A%A1/images/index/index.png" alt="Featured image of post Object Storage (对象存储服务)" />&lt;h1 id="backblaze-b2-与-cloudflare-r2-全方位对比分析">Backblaze B2 与 Cloudflare R2 全方位对比分析
&lt;/h1>&lt;p>Backblaze B2 和 Cloudflare R2 都是目前市场上非常受欢迎且兼容 S3 API 的对象存储服务。它们都以比 Amazon S3 更具性价比作为主打优势，但在定价逻辑和底层架构上存在显著区别。&lt;/p>
&lt;p>以下是基于最新数据的核心对比：&lt;/p>
&lt;p>&lt;strong>核心定位差异&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Backblaze B2&lt;/strong>：传统的“中心化”对象存储。主打极低的存储成本和简单的计费模式，是数据备份、归档和大容量媒体存储的理想选择。&lt;/li>
&lt;li>&lt;strong>Cloudflare R2&lt;/strong>：基于“边缘网络”的对象存储。主打&lt;strong>零下行流量费&lt;/strong>和与 Cloudflare 边缘计算的深度集成，非常适合需要频繁读取、分发数据的应用。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>价格对比（核心决胜点）&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: left">计费项目&lt;/th>
&lt;th style="text-align: left">Backblaze B2&lt;/th>
&lt;th style="text-align: left">Cloudflare R2&lt;/th>
&lt;th style="text-align: left">优势方&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>基础存储费&lt;/strong>&lt;/td>
&lt;td style="text-align: left">$6/TB/月 ($0.006/GB)&lt;/td>
&lt;td style="text-align: left">$15/TB/月 ($0.015/GB) (标准层)&lt;br>$10/TB/月 ($0.010/GB) (低频层)&lt;/td>
&lt;td style="text-align: left">&lt;strong>B2 胜&lt;/strong> (便宜60%)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>下行流量费&lt;/strong>&lt;/td>
&lt;td style="text-align: left">每月免费额度为总存储量的3倍，超出后 $0.01/GB。&lt;/td>
&lt;td style="text-align: left">&lt;strong>永远 $0&lt;/strong> (完全免费)&lt;/td>
&lt;td style="text-align: left">&lt;strong>R2 胜&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>写入 API 费&lt;/strong>&lt;/td>
&lt;td style="text-align: left">&lt;strong>完全免费&lt;/strong> (上传文件不计费)。&lt;/td>
&lt;td style="text-align: left">免费100万次/月，超出后 $4.50/百万次。&lt;/td>
&lt;td style="text-align: left">&lt;strong>B2 胜&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>读取 API 费&lt;/strong>&lt;/td>
&lt;td style="text-align: left">免费2500次/天，超出后 $0.004/万次。&lt;/td>
&lt;td style="text-align: left">免费1000万次/月，超出后 $0.36/百万次。&lt;/td>
&lt;td style="text-align: left">&lt;strong>平局&lt;/strong> (都很便宜)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: left">&lt;strong>免费额度&lt;/strong>&lt;/td>
&lt;td style="text-align: left">前10GB存储免费&lt;/td>
&lt;td style="text-align: left">前10GB存储免费&lt;/td>
&lt;td style="text-align: left">&lt;strong>平局&lt;/strong>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>价格维度的注意点：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>R2 的 API 写入成本&lt;/strong>：如果你的业务是写入海量小文件（例如日志收集、IoT数据），R2 高达 $4.50/百万次的写入费用可能带来较高成本。而 B2 写入是完全免费的。&lt;/li>
&lt;li>&lt;strong>R2 的低频层&lt;/strong>：虽然存储费降到 $10/TB，但有&lt;strong>30天最短存储期&lt;/strong>要求，且读取时会收取**$0.01/GB 的数据检索费**。B2 则没有任何最短存储时长或最小文件限制。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>性能与网络架构&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Cloudflare R2&lt;/strong>：在性能与延迟方面占优。它依托 Cloudflare 庞大的全球 CDN 边缘节点构建。请求数据时，R2 会动态地在离用户最近的节点处理，实现全球范围内的低延迟访问。&lt;/li>
&lt;li>&lt;strong>Backblaze B2&lt;/strong>：采用传统的区域化存储架构（主要区域在美国西部、美国东部和欧洲）。如果用户在远离这些数据中心的地方（如亚洲），直接访问的物理延迟可能较高。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>生态与高级特性&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>S3 兼容性&lt;/strong>：两者都提供了优秀的 S3 API 兼容性，大部分支持 Amazon S3 的备份软件（如 Restic, Veeam, AWS CLI）都可以直接无缝切换。&lt;/li>
&lt;li>&lt;strong>计算集成&lt;/strong>：R2 与 Cloudflare Workers 无缝集成，极度适合构建 Serverless 全栈应用。&lt;/li>
&lt;li>&lt;strong>带宽联盟&lt;/strong>：B2 与 Cloudflare、Fastly 等 CDN 有合作。如果将 Cloudflare CDN 置于 B2 之前，从 B2 传输到 Cloudflare 网络的流量是完全免费的。&lt;/li>
&lt;/ul>
&lt;h2 id="总结你应该怎么选">总结：你应该怎么选？
&lt;/h2>&lt;p>&lt;strong>什么时候该选 Backblaze B2？&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>数据备份与归档&lt;/strong>：你需要存放几十 TB 甚至 PB 级的备份数据（如 NAS 备份、数据库快照），且读取频率不高。B2 每月 $6/TB 的价格能节省大量成本。&lt;/li>
&lt;li>&lt;strong>写多读少、海量小文件&lt;/strong>：因为 B2 没有写入 API 费用，也没有最小文件体积限制。&lt;/li>
&lt;li>&lt;strong>经典的 “B2 + Cloudflare CDN” 组合玩家&lt;/strong>：你可以将 B2 作为源站，搭配 Cloudflare 的免费 CDN。这样既能享受 B2 的极低存储费，又能利用 Cloudflare 的分发网络和免流量费优势。（注意：Cloudflare 对免费版有大文件/纯媒体分发的服务条款限制，需视具体业务而定。）&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>什么时候该选 Cloudflare R2？&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>高频下行读取（大流量应用）&lt;/strong>：如果你的存储量不大（比如几百GB），但每月会产生数TB甚至数十TB的下载流量（例如播客音频、软件安装包分发、高频访问的图床）。R2 的零流量费优势明显。&lt;/li>
&lt;li>&lt;strong>全球化前端 / Serverless 架构&lt;/strong>：适合搭配 Cloudflare Workers 构建现代 Web 应用，需要极低的读写延迟和极致的边缘计算体验。&lt;/li>
&lt;li>&lt;strong>想要避免流量账单风险&lt;/strong>：R2 的定价简单可预测，无需担心突然爆发的流量或恶意盗刷流量导致的高额费用，零下行流量费提供了很高的安全感。&lt;/li>
&lt;/ul></description></item><item><title>PostgreSQL</title><link>https://www.zata.cc/p/postgresql/</link><pubDate>Tue, 30 Dec 2025 12:27:59 +0800</pubDate><guid>https://www.zata.cc/p/postgresql/</guid><description>&lt;img src="https://www.zata.cc/p/postgresql/images/index/index.png" alt="Featured image of post PostgreSQL" />&lt;h2 id="安装">安装
&lt;/h2>&lt;ol>
&lt;li>PG数据库安装&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>&lt;strong>Docker (最快方式):&lt;/strong>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run --name some-postgres -e &lt;span class="nv">POSTGRES_PASSWORD&lt;/span>&lt;span class="o">=&lt;/span>mysecretpassword -d -p 5432:5432 postgres
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;strong>Linux (Ubuntu/Debian):&lt;/strong>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo apt update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt install postgresql postgresql-contrib
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;strong>Windows/Mac:&lt;/strong> 推荐直接去官网下载 &lt;a class="link" href="https://www.postgresql.org/download/" target="_blank" rel="noopener"
>PostgreSQL Installer&lt;/a>。&lt;/li>
&lt;/ul>
&lt;ol start="2">
&lt;li>相关工具的安装&lt;/li>
&lt;/ol>
&lt;p>&lt;a class="link" href="https://www.navicat.com.cn/download/navicat-premium-lite" target="_blank" rel="noopener"
>navicat for PG lite&lt;/a>&lt;/p>
&lt;hr>
&lt;ol start="2">
&lt;li>连接 (使用命令行 psql)&lt;/li>
&lt;/ol>
&lt;p>PostgreSQL 安装后会创建一个默认的超级用户，叫 &lt;code>postgres&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>在 Linux/Mac 终端中登录：&lt;/strong>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 切换到 postgres 用户并启动 psql 工具&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo -u postgres psql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;strong>登录成功后：&lt;/strong> 你会看到提示符变成 &lt;code>postgres=#&lt;/code>，这表示你已经是超级管理员了。&lt;/li>
&lt;/ul>
&lt;h2 id="基础操作实战-crud">基础操作实战 (CRUD)
&lt;/h2>&lt;p>以下命令请在 &lt;code>psql&lt;/code> 命令行或图形化客户端的 SQL 编辑器中执行。**注意：SQL 语句末尾必须加分号 &lt;code>;**&lt;/code>。&lt;/p>
&lt;ol>
&lt;li>数据库操作&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 创建一个新数据库
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DATABASE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">my_app&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 切换/连接到这个数据库 (psql 专用指令，非 SQL)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="err">\&lt;/span>&lt;span class="k">c&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">my_app&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 提示符会变为 my_app=#
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>创建表 (Create Table)&lt;/li>
&lt;/ol>
&lt;p>PostgreSQL 的数据类型非常丰富。&lt;/p>
&lt;ul>
&lt;li>&lt;code>SERIAL&lt;/code>: 相当于 MySQL 的 &lt;code>AUTO_INCREMENT&lt;/code>（自增主键）。&lt;/li>
&lt;li>&lt;code>TEXT&lt;/code>: PG 中推荐使用 &lt;code>TEXT&lt;/code> 代替 &lt;code>VARCHAR&lt;/code>，性能几乎一样但没有长度限制。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">SERIAL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">PRIMARY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">KEY&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">username&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">TEXT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">UNIQUE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NOT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">NULL&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">age&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">INT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">created_at&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TIMESTAMP&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DEFAULT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">CURRENT_TIMESTAMP&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>插入数据 (Insert)&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 插入单条数据
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">username&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">age&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">VALUES&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;zhang_san&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;zhang@example.com&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">25&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 插入多条数据
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">username&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">age&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">VALUES&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;li_si&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;li@example.com&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">30&lt;/span>&lt;span class="p">),&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;wang_wu&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;wang@example.com&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">22&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="4">
&lt;li>查询数据 (Select)&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 查询所有
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 条件查询
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">username&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">email&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">age&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">24&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 排序与限制
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ORDER&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">BY&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">age&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">DESC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">LIMIT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="5">
&lt;li>更新与删除 (Update &amp;amp; Delete)&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 更新
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">UPDATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SET&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">age&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">26&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">username&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;zhang_san&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 删除
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">DELETE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">age&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">20&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>&lt;code>进阶功能 (PG 的杀手锏)&lt;/code>&lt;/p>
&lt;p>这是大家选择 PG 而不是 MySQL 的主要原因：&lt;strong>对 JSON 的原生支持&lt;/strong>。你可以把它当成 MongoDB 来用。&lt;/p>
&lt;ol>
&lt;li>创建包含 JSON 的表&lt;/li>
&lt;/ol>
&lt;p>假设我们要存用户的“配置信息”，每个人的配置都不一样，且经常变动。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">TABLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">user_configs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nb">INT&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">JSONB&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">-- JSONB 是二进制存储的 JSON，查询速度极快
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>插入 JSON 数据&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">INSERT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">INTO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">user_configs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user_id&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">VALUES&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;{&amp;#34;theme&amp;#34;: &amp;#34;dark&amp;#34;, &amp;#34;notifications&amp;#34;: true, &amp;#34;tags&amp;#34;: [&amp;#34;admin&amp;#34;, &amp;#34;editor&amp;#34;]}&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="3">
&lt;li>查询 JSON 内部的字段&lt;/li>
&lt;/ol>
&lt;p>PG 提供了特殊的运算符 &lt;code>-&amp;gt;&lt;/code> 和 &lt;code>-&amp;gt;&amp;gt;&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">-- 查询 theme 是 dark 的用户
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">user_configs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="o">-&amp;gt;&amp;gt;&lt;/span>&lt;span class="s1">&amp;#39;theme&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;dark&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-- 甚至可以查询 JSON 数组中是否包含某个值
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">user_configs&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">config&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="s1">&amp;#39;tags&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">?&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;admin&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>&lt;code>第五步：常用的 &lt;/code>psql&lt;code> 快捷指令 (作弊条)&lt;/code>&lt;/p>
&lt;p>如果你使用命令行工具 &lt;code>psql&lt;/code>，这些指令必须背下来（注意这些指令前面是反斜杠 &lt;code>\&lt;/code>，且不需要分号）：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>指令&lt;/th>
&lt;th>作用&lt;/th>
&lt;th>对应 MySQL&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>\l&lt;/code>&lt;/td>
&lt;td>列出所有数据库&lt;/td>
&lt;td>&lt;code>SHOW DATABASES;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>\c dbname&lt;/code>&lt;/td>
&lt;td>切换到指定数据库&lt;/td>
&lt;td>&lt;code>USE dbname;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>\dt&lt;/code>&lt;/td>
&lt;td>列出当前库下的所有表&lt;/td>
&lt;td>&lt;code>SHOW TABLES;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>\d tablename&lt;/code>&lt;/td>
&lt;td>查看表结构详细信息&lt;/td>
&lt;td>&lt;code>DESC tablename;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>\q&lt;/code>&lt;/td>
&lt;td>&lt;strong>退出 psql&lt;/strong>&lt;/td>
&lt;td>&lt;code>EXIT;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>\timing&lt;/code>&lt;/td>
&lt;td>开启/关闭查询计时&lt;/td>
&lt;td>(无)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="概念">概念
&lt;/h2>&lt;h4 id="知识点">知识点
&lt;/h4>&lt;p>PostgreSQL（常被简称为 Postgres 或 PG）是一个功能非常强大的开源数据库。对于刚接触它的人来说，最主要的门槛在于它的&lt;strong>架构概念&lt;/strong>和&lt;strong>命令行工具&lt;/strong>与 MySQL 略有不同。&lt;/p>
&lt;p>这是一个从 0 到 1 的详细上手教程。&lt;/p>
&lt;p>在使用之前，需要理解 PG 的层级结构，这比 MySQL 多了一层：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>MySQL:&lt;/strong> 实例 (Instance) -&amp;gt; 数据库 (Database/Schema) -&amp;gt; 表 (Table)&lt;/li>
&lt;li>&lt;strong>PostgreSQL:&lt;/strong> 实例 (Instance) -&amp;gt; 数据库 (Database) -&amp;gt; &lt;strong>模式 (Schema)&lt;/strong> -&amp;gt; 表 (Table)&lt;/li>
&lt;li>&lt;em>注：默认情况下，一切都会放在名为 &lt;code>public&lt;/code> 的模式下，所以初学者可以暂时忽略“模式”这一层，当作它不存在。&lt;/em>&lt;/li>
&lt;/ul>
&lt;h4 id="推荐的图形化客户端-gui">推荐的图形化客户端 (GUI)
&lt;/h4>&lt;p>如果你不想天天敲命令行，推荐下载以下软件来管理 Postgres：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>pgAdmin 4:&lt;/strong> 官方提供的工具，功能最全，web 风格，但有时候稍微有点慢。&lt;/li>
&lt;li>&lt;strong>DBeaver (强烈推荐):&lt;/strong> 免费开源，支持 PG、MySQL 等所有数据库，界面类似 Eclipse，功能非常强大。&lt;/li>
&lt;li>&lt;strong>Navicat for PostgreSQL:&lt;/strong> 收费软件，好用但贵。&lt;/li>
&lt;/ol></description></item><item><title>redis</title><link>https://www.zata.cc/p/redis/</link><pubDate>Fri, 17 Oct 2025 11:26:56 +0800</pubDate><guid>https://www.zata.cc/p/redis/</guid><description>&lt;img src="https://www.zata.cc/p/redis/images/index/index.png" alt="Featured image of post redis" />&lt;h3 id="目录">目录
&lt;/h3>&lt;ul>
&lt;li>一些问题处理&lt;/li>
&lt;/ul>
&lt;p>&lt;a class="link" href="#Redis%e6%9c%aa%e6%8e%88%e6%9d%83%e8%ae%bf%e9%97%ae%e6%bc%8f%e6%b4%9e%e5%a4%84%e7%90%86" >Redis未授权访问漏洞处理&lt;/a>
&lt;a class="link" href="#%e6%9c%ac%e5%9c%b0mac%e4%b8%8a_%e8%bf%90%e8%a1%8c%e4%b8%80%e4%b8%aaredis%e5%ae%b9%e5%99%a8_%e7%84%b6%e5%90%8e%e6%83%b3%e8%a6%81%e5%9c%a8%e5%8f%a6%e4%b8%80%e4%b8%aa%e5%ae%b9%e5%99%a8%e4%b8%ad%e4%bd%bf%e7%94%a8%e8%bf%99%e4%b8%aaredis%e6%80%8e%e4%b9%88%e5%81%9a" >本地mac上_运行一个redis容器_然后想要在另一个容器中使用这个redis怎么做&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Redis 常用命令总结（适用于 apt install redis/redis-server 安装的场景，包括容器内）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">```&lt;/span>bash
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 最简单直接启动（前台运行，用于测试）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 使用配置文件后台启动（推荐方式）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-server /etc/redis/redis.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 带常用参数直接启动（不依赖配置文件）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-server --port &lt;span class="m">6379&lt;/span> --daemonize yes --protected-mode no
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. 检查 Redis 是否正常运行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-cli ping &lt;span class="c1"># 返回 PONG 表示成功&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-cli -p &lt;span class="m">6379&lt;/span> ping
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 5. 优雅停止 Redis（强烈推荐，会自动保存数据）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-cli shutdown &lt;span class="c1"># 默认执行 SAVE&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-cli -p &lt;span class="m">6379&lt;/span> shutdown
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 6. 不保存数据直接停止&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-cli shutdown nosave
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 7. 强制杀死进程（不推荐，可能丢数据）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">kill&lt;/span> -9 &lt;span class="k">$(&lt;/span>pidof redis-server&lt;span class="k">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pkill -9 redis-server
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 8. 重启 Redis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">redis-cli shutdown &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> redis-server /etc/redis/redis.conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 9. 使用 service 管理（你在容器里通过 apt 安装后直接可用！）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service redis-server start &lt;span class="c1"># 启动&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service redis-server stop &lt;span class="c1"># 停止&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service redis-server restart &lt;span class="c1"># 重启&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service redis-server status &lt;span class="c1"># 查看状态&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 10. 等价的更短写法（很多系统都支持）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service redis start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service redis stop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service redis restart
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">service redis status
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 11. 直接调用 init 脚本（永远有效）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/etc/init.d/redis-server start
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/etc/init.d/redis-server stop
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/etc/init.d/redis-server restart
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/etc/init.d/redis-server status
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 12. 查看进程&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ps aux &lt;span class="p">|&lt;/span> grep redis
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pgrep -fl redis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="什么是-redis">什么是 Redis？
&lt;/h3>&lt;p>Redis (Remote Dictionary Server) 是一个开源的、使用 C 语言编写的、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库，并提供多种语言的 API。&lt;/p>
&lt;p>它通常被称为“数据结构服务器”，因为值（value）可以是字符串 (String)、哈希 (Hash)、列表 (List)、集合 (Set) 和有序集合 (Sorted Set) 等类型。&lt;/p>
&lt;p>&lt;strong>Redis 的主要特点：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>速度快：&lt;/strong> 数据存储在内存中，读写速度非常快。&lt;/li>
&lt;li>&lt;strong>数据类型丰富：&lt;/strong> 支持多种复杂的数据结构。&lt;/li>
&lt;li>&lt;strong>持久化：&lt;/strong> 可以将内存中的数据保存到磁盘中，重启时可以再次加载进行使用。&lt;/li>
&lt;li>&lt;strong>原子性：&lt;/strong> Redis 的所有操作都是原子性的，要么执行成功，要么执行失败，不会出现中间状态。&lt;/li>
&lt;li>&lt;strong>丰富的特性：&lt;/strong> 支持发布/订阅、事务、Lua 脚本等功能。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>常见应用场景：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>缓存：&lt;/strong> 这是 Redis 最常见的用途，用于缓存数据库查询结果、网页、或任何可以被缓存的数据，以减轻数据库压力。&lt;/li>
&lt;li>&lt;strong>计数器：&lt;/strong> 利用 Redis 原子性的 &lt;code>INCR&lt;/code> 命令可以轻松实现高并发的计数功能，例如文章阅读量、点赞数。&lt;/li>
&lt;li>&lt;strong>会话存储：&lt;/strong> 存储用户的会话信息（Session），实现分布式系统的会话共享。&lt;/li>
&lt;li>&lt;strong>排行榜：&lt;/strong> 使用有序集合（Sorted Set）可以轻松实现实时更新的排行榜。&lt;/li>
&lt;li>&lt;strong>消息队列：&lt;/strong> 使用列表（List）的 &lt;code>LPUSH&lt;/code> 和 &lt;code>RPOP&lt;/code> 命令可以实现简单的消息队列。&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>第一步：安装 Redis&lt;/p>
&lt;p>你可以通过以下几种方式安装 Redis：&lt;/p>
&lt;p>1. 在 Linux (Ubuntu/Debian) 上安装&lt;/p>
&lt;p>使用包管理器是最简单的方式：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo apt update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt install redis-server
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>安装完成后，Redis 服务会自动启动。你可以使用以下命令检查服务状态：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">sudo systemctl status redis-server
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>使用 Docker 安装 (推荐，跨平台)&lt;/li>
&lt;/ol>
&lt;p>如果你安装了 Docker，这是最简单且最干净的安装方式，适用于任何操作系统。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run --name my-redis -p 6379:6379 -d redis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这个命令会从 Docker Hub 下载最新的 Redis 镜像，并在后台运行一个名为 &lt;code>my-redis&lt;/code> 的容器，将容器的 6379 端口映射到你主机的 6379 端口。&lt;/p>
&lt;p>如果你在一个docker内开发，另一个docker容器安装redis&lt;/p>
&lt;p>&lt;a class="link" href="#%e6%9c%ac%e5%9c%b0mac%e4%b8%8a_%e8%bf%90%e8%a1%8c%e4%b8%80%e4%b8%aaredis%e5%ae%b9%e5%99%a8_%e7%84%b6%e5%90%8e%e6%83%b3%e8%a6%81%e5%9c%a8%e5%8f%a6%e4%b8%80%e4%b8%aa%e5%ae%b9%e5%99%a8%e4%b8%ad%e4%bd%bf%e7%94%a8%e8%bf%99%e4%b8%aaredis%e6%80%8e%e4%b9%88%e5%81%9a" >本地mac上_运行一个redis容器_然后想要在另一个容器中使用这个redis怎么做&lt;/a>&lt;/p>
&lt;hr>
&lt;p>第二步：连接到 Redis&lt;/p>
&lt;p>安装并启动 Redis 后，你可以使用 &lt;code>redis-cli&lt;/code> (Redis Command Line Interface) 连接到它。&lt;/p>
&lt;p>如果你是直接在系统上安装的 Redis，直接在终端输入：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">redis-cli
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果你是通过 Docker 安装的，你需要进入到容器内部：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker &lt;span class="nb">exec&lt;/span> -it my-redis redis-cli
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>连接成功后，你会看到类似下面的提示符：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>现在，你可以开始输入 Redis 命令了。&lt;/p>
&lt;p>&lt;strong>测试连接：&lt;/strong>&lt;/p>
&lt;p>输入 &lt;code>PING&lt;/code> 命令，如果服务器正常运行，它会返回 &lt;code>PONG&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; PING
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PONG
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果你的redis是安装在另外一个容器，使用如下&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. 进入你的应用容器&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">docker &lt;span class="nb">exec&lt;/span> -it &amp;lt;你的应用容器名&amp;gt; bash &lt;span class="c1"># 或者 sh&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. 在容器内部，更新包列表并安装&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">root@&amp;lt;container-id&amp;gt;:/# apt-get update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">root@&amp;lt;container-id&amp;gt;:/# apt-get install redis-tools -y
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. 现在你可以使用了&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">root@&amp;lt;container-id&amp;gt;:/# redis-cli -h my-redis
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">my-redis:6379&amp;gt; PING
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">PONG
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;p>第三步：学习基本命令和数据结构&lt;/p>
&lt;p>Redis 是一个 Key-Value 数据库。每个 Key 都有一个与之关联的 Value。下面是 Redis 最核心的五种数据结构及其常用命令。&lt;/p>
&lt;ol>
&lt;li>字符串 (String)&lt;/li>
&lt;/ol>
&lt;p>这是最基本的数据类型，一个 key 对应一个 value。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>SET key value&lt;/code>&lt;/strong>: 设置一个键值对。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; SET username &amp;#34;gemini&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">OK
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>GET key&lt;/code>&lt;/strong>: 获取一个键的值。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; GET username
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;gemini&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>DEL key&lt;/code>&lt;/strong>: 删除一个键。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; DEL username
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>INCR key&lt;/code>&lt;/strong>: 将 key 中储存的数字值增一（如果 key 不存在，则初始化为 0 再执行 INCR 操作）。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; SET page_views 100
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">OK
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; INCR page_views
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 101
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>2. 哈希 (Hash)&lt;/p>
&lt;p>Hash 是一个 string 类型的 field 和 value 的映射表，它非常适合用于存储对象。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>HSET key field value&lt;/code>&lt;/strong>: 将哈希表 key 中的字段 field 的值设为 value。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; HSET user:1 name &amp;#34;Alice&amp;#34; age 25
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>HGET key field&lt;/code>&lt;/strong>: 获取哈希表 key 中给定字段 field 的值。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; HGET user:1 name
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;Alice&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>HGETALL key&lt;/code>&lt;/strong>: 获取在哈希表中指定 key 的所有字段和值。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; HGETALL user:1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1) &amp;#34;name&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2) &amp;#34;Alice&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3) &amp;#34;age&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4) &amp;#34;25&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>HDEL key field&lt;/code>&lt;/strong>: 删除哈希表 key 中的一个或多个指定字段。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; HDEL user:1 age
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>3. 列表 (List)&lt;/p>
&lt;p>List 是简单的字符串列表，按照插入顺序排序。你可以添加一个元素到列表的头部（左边）或者尾部（右边）。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>LPUSH key element [element ...]&lt;/code>&lt;/strong>: 将一个或多个值插入到列表头部。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; LPUSH tasks &amp;#34;task1&amp;#34; &amp;#34;task2&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>RPUSH key element [element ...]&lt;/code>&lt;/strong>: 将一个或多个值插入到列表尾部。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; RPUSH tasks &amp;#34;task3&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>LRANGE key start stop&lt;/code>&lt;/strong>: 获取列表指定范围内的元素。(&lt;code>0&lt;/code> 是第一个元素, &lt;code>-1&lt;/code> 是最后一个元素)。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; LRANGE tasks 0 -1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1) &amp;#34;task2&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2) &amp;#34;task1&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3) &amp;#34;task3&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>LPOP key&lt;/code>&lt;/strong>: 移出并获取列表的第一个元素。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; LPOP tasks
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;task2&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>RPOP key&lt;/code>&lt;/strong>: 移出并获取列表的最后一个元素。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; RPOP tasks
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&amp;#34;task3&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>4. 集合 (Set)&lt;/p>
&lt;p>Set 是 String 类型的无序集合。集合成员是唯一的，这就意味着集合中不能出现重复的数据。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>SADD key member [member ...]&lt;/code>&lt;/strong>: 向集合添加一个或多个成员。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; SADD tags &amp;#34;database&amp;#34; &amp;#34;nosql&amp;#34; &amp;#34;redis&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>SMEMBERS key&lt;/code>&lt;/strong>: 返回集合中的所有成员。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; SMEMBERS tags
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1) &amp;#34;redis&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2) &amp;#34;nosql&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3) &amp;#34;database&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>SISMEMBER key member&lt;/code>&lt;/strong>: 判断 member 元素是否是集合 key 的成员。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; SISMEMBER tags &amp;#34;redis&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; SISMEMBER tags &amp;#34;mysql&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>SREM key member [member ...]&lt;/code>&lt;/strong>: 移除集合中一个或多个成员。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; SREM tags &amp;#34;nosql&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;p>5. 有序集合 (Sorted Set / ZSET)&lt;/p>
&lt;p>和 Set 一样，Sorted Set 也是 string 类型元素的集合，且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数（score）。Redis 正是通过分数来为集合中的成员进行从小到大的排序。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>ZADD key score member [score member ...]&lt;/code>&lt;/strong>: 向有序集合添加一个或多个成员，或者更新已存在成员的分数。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; ZADD leaderboard 100 &amp;#34;player1&amp;#34; 250 &amp;#34;player2&amp;#34; 50 &amp;#34;player3&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 3
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>ZRANGE key start stop [WITHSCORES]&lt;/code>&lt;/strong>: 通过索引区间返回有序集合成指定区间内的成员（按分数从小到大）。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; ZRANGE leaderboard 0 -1 WITHSCORES
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1) &amp;#34;player3&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2) &amp;#34;50&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3) &amp;#34;player1&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4) &amp;#34;100&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">5) &amp;#34;player2&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">6) &amp;#34;250&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>ZREVRANGE key start stop [WITHSCORES]&lt;/code>&lt;/strong>: 返回有序集中指定区间内的成员，通过索引，分数从高到低。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; ZREVRANGE leaderboard 0 1 WITHSCORES
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1) &amp;#34;player2&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2) &amp;#34;250&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3) &amp;#34;player1&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">4) &amp;#34;100&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>ZREM key member [member ...]&lt;/code>&lt;/strong>: 移除有序集合中的一个或多个成员。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; ZREM leaderboard &amp;#34;player3&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>第四步：一些通用的重要命令&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>KEYS pattern&lt;/code>&lt;/strong>: 查找所有符合给定模式的 key。&lt;strong>注意：&lt;/strong> 在生产环境中要慎用，因为它会遍历所有的键，可能会阻塞服务器。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; KEYS *
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">1) &amp;#34;tasks&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">2) &amp;#34;user:1&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">3) &amp;#34;page_views&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>EXPIRE key seconds&lt;/code>&lt;/strong>: 为给定的 key 设置过期时间，以秒计。当 key 过期后，它会被自动删除。这对于实现缓存非常有用。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; SET cache_data &amp;#34;some important data&amp;#34;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">OK
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; EXPIRE cache_data 60 // 60秒后过期
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>TTL key&lt;/code>&lt;/strong>: 以秒为单位，返回给定 key 的剩余生存时间 (Time To Live)。
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">127.0.0.1:6379&amp;gt; TTL cache_data
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">(integer) 55 // (还剩55秒)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>&lt;strong>&lt;code>FLUSHDB&lt;/code>&lt;/strong>: 删除当前数据库里的所有数据。&lt;strong>极度危险，请谨慎使用！&lt;/strong>&lt;/li>
&lt;li>&lt;strong>&lt;code>FLUSHALL&lt;/code>&lt;/strong>: 删除所有数据库里的所有数据。&lt;strong>极度危险，请谨慎使用！&lt;/strong>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>第五步：在你的代码中使用 Redis&lt;/p>
&lt;p>&lt;code>redis-cli&lt;/code> 是一个很好的学习和调试工具，但在实际应用中，你需要在你的编程语言中通过 Redis 客户端库来连接和操作 Redis。&lt;/p>
&lt;p>几乎所有主流语言都有成熟的 Redis 客户端库。例如：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Python&lt;/strong>: &lt;code>redis-py&lt;/code>&lt;/li>
&lt;li>&lt;strong>Java&lt;/strong>: &lt;code>Jedis&lt;/code>, &lt;code>Lettuce&lt;/code>&lt;/li>
&lt;li>&lt;strong>Node.js&lt;/strong>: &lt;code>node-redis&lt;/code>, &lt;code>ioredis&lt;/code>&lt;/li>
&lt;li>&lt;strong>Go&lt;/strong>: &lt;code>go-redis&lt;/code>&lt;/li>
&lt;li>&lt;strong>PHP&lt;/strong>: &lt;code>phpredis&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Python 示例 (&lt;code>redis-py&lt;/code>)&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>安装库:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install redis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>连接和使用:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">redis&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 连接到本地 Redis 服务&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">r&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">redis&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Redis&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">host&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;localhost&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">port&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">6379&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">db&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">decode_responses&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 使用 String&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">r&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;foo&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;bar&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;foo&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="c1"># 输出: bar&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 使用 Hash&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">r&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;user-session:123&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">mapping&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;John&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;surname&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;Smith&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;company&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;Redis&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;age&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">29&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">r&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hgetall&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;user-session:123&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>总结和后续学习&lt;/p>
&lt;p>恭喜你！你已经完成了 Redis 的入门。你现在应该知道如何安装、连接 Redis，并使用其核心数据结构进行基本操作。&lt;/p>
&lt;p>&lt;strong>下一步可以学习：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Redis 持久化：&lt;/strong> 了解 RDB 和 AOF 两种持久化方式的区别和配置。&lt;/li>
&lt;li>&lt;strong>Redis 事务：&lt;/strong> 学习 &lt;code>MULTI&lt;/code> 和 &lt;code>EXEC&lt;/code> 命令来执行一组原子操作。&lt;/li>
&lt;li>&lt;strong>发布/订阅 (Pub/Sub)：&lt;/strong> 探索 Redis 的消息发布和订阅功能。&lt;/li>
&lt;li>&lt;strong>Redis 安全：&lt;/strong> 学习如何为 Redis 设置密码和配置安全的网络访问。&lt;/li>
&lt;li>&lt;strong>Redis 集群：&lt;/strong> 了解主从复制 (Replication)、哨兵 (Sentinel) 和集群 (Cluster) 模式以实现高可用和扩展性。&lt;/li>
&lt;/ul>
&lt;h3 id="一些操作">一些操作
&lt;/h3>&lt;h4 id="redis未授权访问漏洞处理">Redis未授权访问漏洞处理
&lt;/h4>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-md" data-lang="md">&lt;span class="line">&lt;span class="cl">漏洞描述：Redis 未授权访问漏洞
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Redis 未授权访问漏洞是一个非常严重的安全问题，它通常是因为 Redis 服务器直接暴露在公网或内网中，并且**没有配置密码认证**导致的。攻击者可以利用这个漏洞读取、修改甚至删除数据，更严重的情况下，可以利用 Redis 的一些特性（如写入 &lt;span class="sb">`authorized_keys`&lt;/span> 或计划任务文件）来获取服务器的控制权。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">以下是处理和防御 Redis 未授权访问漏洞的主要步骤和措施：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gu">### 1\. 启用密码认证（最重要）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">在 Redis 配置文件中设置一个**强密码**，启用认证功能。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">*&lt;/span> **修改配置文件：** 找到 &lt;span class="sb">`redis.conf`&lt;/span> 文件（通常在 &lt;span class="sb">`/etc/redis/`&lt;/span> 或 &lt;span class="sb">`/etc/redis/redis.conf`&lt;/span>）。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">*&lt;/span> **设置密码：** 找到或添加 &lt;span class="sb">`requirepass`&lt;/span> 这一行，并设置一个复杂的密码。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ``&lt;span class="sb">`conf
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> requirepass yourStrongPasswordHere
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> *记得替换 `&lt;/span>yourStrongPasswordHere&lt;span class="sb">` 为你设置的实际密码。*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> * **重启 Redis：** 更改配置后，需要重启 Redis 服务使其生效。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> # 例如：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> sudo systemctl restart redis
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> # 或者
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> sudo service redis-server restart
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> * **验证：** 使用 `&lt;/span>redis-cli&lt;span class="sb">` 连接后，尝试执行命令，会提示需要认证。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> redis-cli
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> &amp;gt; PING
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> (error) NOAUTH Authentication required.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> &amp;gt; AUTH yourStrongPasswordHere
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> OK
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> &amp;gt; PING
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> PONG
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">### 2\. 限制网络访问（网络隔离）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">Redis 本身的设计是运行在受信任的环境中，因此不应该直接暴露给公网。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> * **绑定本地或私有 IP：** 仅允许 Redis 监听应用的服务器 IP 或本地回环地址（`&lt;/span>127.0.0.1&lt;span class="sb">`）。在 `&lt;/span>redis.conf&lt;span class="sb">` 中修改 `&lt;/span>bind&lt;span class="sb">` 选项：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> * **仅允许本地访问：**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`conf
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> bind 127.0.0.1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> * **允许特定内网 IP 访问：** 绑定内网 IP 地址。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`conf
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> bind 127.0.0.1 192.168.1.100 # 替换为你的应用服务器IP
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> * **配置防火墙：** 使用防火墙（如 `&lt;/span>iptables&lt;span class="sb">`, `&lt;/span>ufw&lt;span class="sb">`, 安全组等）限制对 Redis 端口（默认是 6379）的访问，**只允许**信任的应用服务器 IP 地址连接。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> * **网络分段：** 将 Redis 服务器放在一个独立的、只有应用服务器能访问的私有网络段中。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">### 3\. 启用保护模式（Protected Mode）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">Redis 3.2.0 及更高版本引入了保护模式（Protected Mode）。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> * **检查配置：** 在 `&lt;/span>redis.conf&lt;span class="sb">` 中确保 `&lt;/span>protected-mode&lt;span class="sb">` 设置为 `&lt;/span>yes&lt;span class="sb">`。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`conf
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> protected-mode yes
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> *在默认配置下，如果 Redis 没有设置密码且绑定了非本地回环地址，保护模式会自动开启，阻止外部连接，仅响应本地回环地址（`&lt;/span>127.0.0.1&lt;span class="sb">`）的请求。*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">### 4\. 禁用或重命名危险命令
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">为了防止攻击者利用 Redis 的配置命令进行恶意操作（如写入 `&lt;/span>authorized_keys&lt;span class="sb">`），可以考虑禁用或重命名一些高风险命令。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> * **禁用或重命名 `&lt;/span>CONFIG&lt;span class="sb">` 命令：** 在 `&lt;/span>redis.conf&lt;span class="sb">` 中添加：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`conf
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> # 禁用 CONFIG 命令
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> rename-command CONFIG &amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> # 或者重命名（推荐，防止应用端需要用到）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> rename-command CONFIG aRandomStringForConfig
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> `&lt;/span>`&lt;span class="sb">`
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb"> * 其他高危命令如 `&lt;/span>FLUSHALL&lt;span class="sb">`、`&lt;/span>FLUSHDB&lt;span class="sb">`、`&lt;/span>SAVE&lt;span class="sb">`、`&lt;/span>BGSAVE&lt;span class="sb">` 也可以根据需要进行限制。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">### 5\. 使用低权限用户运行 Redis
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">不要使用 `&lt;/span>root&lt;span class="sb">` 用户运行 Redis 服务。创建一个专门的低权限用户来运行 Redis 进程，即使被攻击者攻破，也能限制其在系统上的权限。
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">### 总结加固：
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">1. **设置强密码（`&lt;/span>requirepass&lt;span class="sb">`）。**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">2. **限制网络访问（`&lt;/span>bind&lt;span class="sb">` 和防火墙）。**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">3. **确保 `&lt;/span>protected-mode yes&lt;span class="sb">`。**
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sb">4. **禁用或重命名高危命令（`&lt;/span>CONFIG`）。**
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">5.&lt;/span> &lt;span class="gs">**使用低权限用户运行。**&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="本地mac上_运行一个redis容器_然后想要在另一个容器中使用这个redis怎么做">本地mac上_运行一个redis容器_然后想要在另一个容器中使用这个redis怎么做
&lt;/h4>&lt;p>在 Mac 本地上，要让一个容器（应用容器）连接到另一个容器（Redis 容器），最推荐和最现代的方法是使用&lt;strong>自定义的 Docker 网络&lt;/strong>。&lt;/p>
&lt;p>这能确保容器之间可以通过一个稳定且易于记的主机名（即你的 Redis 容器名）进行通信，而不需要关心动态变化的 IP 地址。&lt;/p>
&lt;p>这里是详细的步骤，分为两种主要方法：&lt;/p>
&lt;ul>
&lt;li>方法一：使用 Docker CLI 命令（推荐）&lt;/li>
&lt;/ul>
&lt;p>这是最基础和最清晰的方法，分为三步：&lt;/p>
&lt;p>1. 创建一个自定义的 Docker 网络&lt;/p>
&lt;p>首先，创建一个新的“桥接”网络。所有需要相互通信的容器都应该连接到这个网络。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker network create my-app-network
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>my-app-network&lt;/code> 是你给这个网络起的名字，可以自定义。&lt;/li>
&lt;/ul>
&lt;p>2. 启动 Redis 容器并连接到该网络&lt;/p>
&lt;p>在运行 Redis 容器时，使用 &lt;code>--network&lt;/code> 参数指定它加入你刚创建的网络。同时，使用 &lt;code>--name&lt;/code> 给它一个固定的名字（例如 &lt;code>my-redis&lt;/code>），这个名字将作为它在网络内部的主机名。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -d &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --name my-redis &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --network my-app-network &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> redis
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;code>-d&lt;/code>：在后台运行。&lt;/li>
&lt;li>&lt;code>--name my-redis&lt;/code>：将容器命名为 &lt;code>my-redis&lt;/code>。&lt;/li>
&lt;li>&lt;code>--network my-app-network&lt;/code>：将其连接到 &lt;code>my-app-network&lt;/code>。&lt;/li>
&lt;li>&lt;strong>注意&lt;/strong>：我在这里&lt;strong>没有&lt;/strong>使用 &lt;code>-p 6379:6379&lt;/code>。因为你的应用容器在同一个网络中，它可以直接通过 &lt;code>6379&lt;/code> 端口访问 Redis。只有当你需要从 Mac &lt;em>主机&lt;/em>（例如通过 &lt;code>redis-cli&lt;/code> 或桌面 GUI）访问这个 Redis 时，才需要添加 &lt;code>-p 6379:6379&lt;/code> 来将端口暴露给主机。&lt;/li>
&lt;/ul>
&lt;p>3. 启动你的应用容器并连接到同一网络&lt;/p>
&lt;p>现在，启动你的第二个容器（例如一个 Node.js、Python 或 Java 应用），并同样将它连接到 &lt;code>my-app-network&lt;/code>。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker run -it &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --network my-app-network &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> &amp;lt;你的应用镜像名&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>4. 在应用中连接 Redis&lt;/p>
&lt;p>在你的应用代码中，连接 Redis 的主机地址&lt;strong>不再是 &lt;code>localhost&lt;/code> 或 &lt;code>127.0.0.1&lt;/code>&lt;/strong>，而是你在第二步中为 Redis 容器设置的名字：&lt;code>my-redis&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Redis 连接字符串示例&lt;/strong>：&lt;code>redis://my-redis:6379&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Docker 会自动将 &lt;code>my-redis&lt;/code> 这个主机名解析为 Redis 容器在 &lt;code>my-app-network&lt;/code> 网络中的内部 IP 地址。&lt;/p>
&lt;hr>
&lt;ul>
&lt;li>方法二：使用 Docker Compose（更简单的多容器管理）&lt;/li>
&lt;/ul>
&lt;p>如果你需要同时管理 Redis 和你的应用两个（或更多）容器，使用 &lt;code>docker-compose&lt;/code> 是一个更简单、更可维护的方法。它会自动为你创建和管理网络。&lt;/p>
&lt;ol>
&lt;li>
&lt;p>在你的项目根目录创建一个 &lt;code>docker-compose.yml&lt;/code> 文件。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>在该文件中定义你的两个服务（&lt;code>app&lt;/code> 和 &lt;code>redis&lt;/code>）：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;3.8&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 使用一个较新的版本&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">services&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 你的应用程序服务&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">. &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 假设你的 Dockerfile 在当前目录&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 或者使用 image: &amp;lt;你的应用镜像名&amp;gt;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;8080:80&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 举例：将你应用的80端口映射到主机的8080&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">environment&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 关键：在这里注入 Redis 的主机名&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">REDIS_HOST=my-redis&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">REDIS_PORT=6379&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 确保你的应用在 redis 启动后再启动&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">depends_on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">my-redis&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 你的 Redis 服务&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">my-redis&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 服务的名字&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">redis&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 你可以保留这个名字，也可以不写&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">container_name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">my-redis-container&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># 如果需要从主机访问，取消下面这行的注释&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># ports:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># - &amp;#34;6379:6379&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># Docker Compose 会自动创建一个默认网络&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># 并将 app 和 my-redis 两个服务都加入其中&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>在 &lt;code>docker-compose.yml&lt;/code> 文件所在的目录中，运行以下命令：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">docker-compose up -d
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Docker Compose 会自动完成所有操作：&lt;/p>
&lt;ol>
&lt;li>创建一个名为 &lt;code>项目目录名_default&lt;/code> 的网络。&lt;/li>
&lt;li>启动名为 &lt;code>my-redis&lt;/code> 的 Redis 服务。&lt;/li>
&lt;li>启动你的 &lt;code>app&lt;/code> 服务。&lt;/li>
&lt;li>在 &lt;code>app&lt;/code> 容器内部，可以直接使用主机名 &lt;code>my-redis&lt;/code>（即 &lt;code>services:&lt;/code> 下定义的服务名）来连接到 Redis。&lt;/li>
&lt;/ol>
&lt;hr>
&lt;ul>
&lt;li>补充：连接到 Mac &lt;em>主机&lt;/em> 上的 Redis&lt;/li>
&lt;/ul>
&lt;p>如果你的意思是 Redis &lt;strong>没有&lt;/strong>运行在容器里，而是&lt;strong>直接安装在你的 Mac 电脑上&lt;/strong>，而你想从一个 Docker 容器里去连接它，那么情况有所不同。&lt;/p>
&lt;p>在这种情况下，你不能使用 &lt;code>localhost&lt;/code>（因为 &lt;code>localhost&lt;/code> 在容器内部指的是容器自己）。你需要使用一个特殊的 DNS 名称：&lt;code>host.docker.internal&lt;/code>。&lt;/p>
&lt;ul>
&lt;li>&lt;strong>连接地址&lt;/strong>：&lt;code>host.docker.internal:6379&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>&lt;code>host.docker.internal&lt;/code> 是 Docker for Mac 提供的一个特殊地址，它会解析为你 Mac 主机的 IP 地址。&lt;/p></description></item></channel></rss>