[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"app-site-settings":3,"article-detail-46":41,"plugin-slots":73},{"site_name":4,"site_description":5,"site_keywords":6,"footer_copyright_text":7,"footer_icp_text":8,"footer_icp_url":9,"footer_show_icp":10,"footer_server_info":11,"footer_show_visit":10,"footer_show_sitemap":10,"footer_show_subscribe":10,"contact_email":12,"sec_enable_host_check":10,"sec_allowed_hosts":13,"sec_allowed_origins":14,"sec_enable_hotlink":15,"sec_hotlink_allow":16,"sec_enable_honeypot":10,"sec_rate_limit_window":17,"sec_rate_limit_max":18,"nav_menus":19,"site_logo":11,"site_favicon":20,"comment_moderation":10,"footer_gongan_text":21,"footer_gongan_url":22,"footer_report_text":23,"footer_report_email":12,"comment_ai_review":10,"fl_site_info":24,"auto_defense_enabled":25,"auto_defense_threshold":26,"auto_defense_ban_hours":27,"auto_defense_bad_ua_enabled":10,"auto_defense_scan_enabled":10,"sec_comment_rate_enabled":10,"sec_comment_rate_limit":28,"sec_comment_rate_mode":29,"bg_image_enabled":15,"about_config":30,"about_content":31,"copyright_notice":32,"ack_intro":33,"island_festival_enabled":10,"island_festival_start":34,"island_festival_end":35,"island_festival_text":36,"site_url":37,"theme_active":38,"theme_config":39,"sec_base_domain":11,"brand_name":40,"comment_require_login":10,"ai_nav_seeded":10},"lcrworld's Blog","技术、项目与生活记录。分享技术教程、文学创作、摄影作品与生活感悟。","lcrworld,博客,技术,Vue,Node.js,摄影,文学,井天乐,blog","© {year} {site}","冀ICP备2026028241号","https:\u002F\u002Fbeian.miit.gov.cn\u002F","1","","lcrblog@163.com","[\"lcrworld.xyz\",\"www.lcrworld.xyz\",\"en.lcrworld.xyz\",\"localhost\",\"127.0.0.1\"]","[\"https:\u002F\u002Flcrworld.xyz\",\"https:\u002F\u002Fwww.lcrworld.xyz\",\"https:\u002F\u002Fen.lcrworld.xyz\",\"http:\u002F\u002Flocalhost\",\"http:\u002F\u002F127.0.0.1\",\"http:\u002F\u002Flocalhost:3001\",\"http:\u002F\u002F127.0.0.1:3001\",\"http:\u002F\u002Flocalhost:5173\",\"http:\u002F\u002Flocalhost:5174\"]","0","[]","60","100","[{\"label\":\"首页\",\"path\":\"\u002F\"},{\"label\":\"时光\",\"path\":\"\u002Farchives\"},{\"label\":\"动态\",\"path\":\"\u002Fmoments\"},{\"label\":\"实验\",\"path\":\"\u002Fexperiments\"},{\"label\":\"关于\",\"path\":\"\u002Fabout\"},{\"label\":\"媒体库\",\"path\":\"\u002Fmedia\"},{\"label\":\"小玩具\",\"path\":\"\",\"href\":\"https:\u002F\u002Flab.lcrworld.xyz\",\"exact\":false,\"newtab\":true},{\"label\":\"音乐盒\",\"path\":\"\u002Fmusic\"},{\"label\":\"友人帐\",\"path\":\"\u002Ffriend-links\"},{\"label\":\"LCR AI\",\"path\":\"\u002Fai\"}]","\u002Ffavicon-32x32.png","冀公网安备13010402003584号","https:\u002F\u002Fbeian.mps.gov.cn\u002F#\u002Fquery\u002FwebSearch?code=13010402003584","违法和不良信息举报","{\"name\":\"lcrworld's blog\",\"url\":\"https:\u002F\u002Flcrworld.xyz\",\"avatar\":\"https:\u002F\u002Flcrworld.xyz\u002Fdefault-avatar.jpeg\",\"desc\":\"lcrworld的个人博客，分享文章，记录日常\",\"rss_articles\":\"https:\u002F\u002Flcrworld.xyz\u002Fapi\u002Frss\u002Ffeed\u002Fnewrss\",\"rss_moments\":\"https:\u002F\u002Flcrworld.xyz\u002Fapi\u002Frss\u002Ffeed\u002Fnewrss\"}","true","10","24","3","review","{\"heroSubtitle\":\"lcrworld 的个人数字花园\",\"rssArticleLabel\":\"文章 RSS\",\"rssMomentLabel\":\"动态 RSS\",\"statsLabels\":[\"文章\",\"动态\",\"媒体\",\"评论\",\"总访客\",\"今日访客\"],\"lifeFacts\":[{\"icon\":\"♒\",\"value\":\"水瓶座\",\"label\":\"星座\",\"accent\":\"\"},{\"icon\":\"📆\",\"value\":\"14\",\"label\":\"年龄\",\"accent\":\"\"},{\"icon\":\"❤️\",\"value\":\"女\",\"label\":\"性别\",\"accent\":\"\"}],\"personality\":{\"tips\":\"性格\",\"title\":\"INFJ 提倡者\",\"badge\":\"INFJ\",\"badgeColor\":\"#52b788\",\"desc\":\"理想主义与共情力的结合。喜欢深度思考，安静而坚定，追求有意义的事，相信文字与创造可以改变世界。\",\"linkText\":\"了解 MBTI 人格 →\",\"linkUrl\":\"https:\u002F\u002Fwww.16personalities.com\u002Fch\u002F\",\"imageUrl\":\"https:\u002F\u002Flcrworld.xyz\u002Fuploads\u002Fmedia\u002Fcompressed\u002F6952-1789746513543.webp\"},\"donation\":{\"tips\":\"感谢\",\"title\":\"打赏支持\",\"intro\":\"如果这里的内容对你有帮助，欢迎请我喝一杯咖啡喵～\",\"buttonText\":\"投喂\",\"wechatQr\":\"https:\u002F\u002Flcrworld.xyz\u002Fuploads\u002Fmedia\u002Fcompressed\u002F7363-1790990938950.webp\",\"alipayQr\":\"\"}}","你好，我是 **lcrworld**，一个喜欢折腾的技术爱好者。这里是我的个人数字花园，用来安放技术笔记、文学创作、摄影作品与生活碎片。\n\n## 关于这个博客\n\n博客自 2026 年 5 月上线运行，同年 8 月完成 **Nuxt 4 + Node.js + SQLite** 全栈重构。从数据库到部署、从设计到文案，全部由我一个人完成。\n\n- 文章：Markdown 写作、AI 智能摘要、AI 目录生成、阅读设置（字号 \u002F 行高 \u002F 字体）\n- 动态：碎语板块，记录日常灵感与即兴想法\n- 评论：人机验证 + AI 审核，先审后发，保护讨论环境\n- 其他：背景实验室（BG Lab）、友链、版权中心、更新日志\n\n> 这个站点本身就是我最大的作品——它每天都在被我打磨。\n\n## 我在做什么\n\n- **开发**：Vue \u002F Nuxt \u002F Node.js，喜欢把想法变成能用的东西\n- **写作**：技术教程、文学创作与摄影记录\n- **开源**：在 GitHub \u002F Gitee 维护个人项目，贡献数据见下方\n\n## 联系我\n\n- 邮箱：[lcrblog@163.com](mailto:lcrblog@163.com)\n- B 站：投稿见上方「B站投稿」\n- 友链 \u002F 合作：欢迎前往「友链」页面申请\n\n## 说明\n\n本站内容除特殊声明外均为原创，转载或引用请注明出处并附原文链接，详见[版权中心](\u002Fcopyright)。","{\"source_code\": \"本站源代码为闭源项目，未对外开源，不提供源码下载、分发或二次开发授权。\", \"content\": \"本站文章均为原创并保留所有权利（All Rights Reserved）。任何转载、摘编、翻译、演绎或商业使用，均须事先获得作者书面授权，并注明出处及原文链接。变更生效前（2026 年 9 月 5 日之前）依据 CC BY-NC-SA 协议已合法取得的授权不溯及既往，仍然有效。\", \"updated_at\": \"2026-09-13\"}","{\"title\":\"鸣谢\",\"subtitle\":\"致每一位曾为 v3blog 伸出援手的朋友\",\"text\":\"一个站点的成长，离不开朋友们的真心帮助。以下是 v3blog 开发过程中给予过支持与启发的伙伴，谨以此页，聊表谢意。\",\"updated_at\":\"2026-09-05\"}","2026-09-23","2026-09-30","祖国母亲77周年🎉","https:\u002F\u002Flcrworld.xyz","default","{}","lcrworld",{"id":42,"title":43,"slug":44,"content":45,"excerpt":46,"cover_url":47,"category_id":48,"author_id":49,"status":50,"view_count":51,"like_count":49,"word_count":52,"is_pinned":53,"total_reading_time":53,"reading_count":53,"avg_reading_time":53,"created_at":54,"updated_at":55,"background_url":56,"seo_keywords":57,"ai_summary":58,"lang":59,"translation_of":56,"is_hidden":53,"scheduled_at":56,"license":60,"ai_toc":56,"category_name":61,"category_slug":61,"author_name":40,"tags":62,"prev_article":63,"next_article":68},46,"一条 git reset --hard，我的数据库空了!","一条-git-reset-hard我的数据库空了","\u003Cp style=\"text-align:right;font-size:14px;color:#888;\">2026 年 7 月 9 日 · 一次惊心动魄的线上事故\u003C\u002Fp>\n\n\u003Ch2>事故发生\u003C\u002Fh2>\n\n\u003Cp>今天凌晨在检查博客的 RSS 订阅功能时，顺手测了一下 API，结果看到了令人血压飙升的一幕：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-json\">\u002F\u002F GET \u002Fapi\u002Farticles?page=1&amp;limit=2\n{\"rows\":[],\"total\":0,\"page\":1,\"limit\":2}\n\n\u002F\u002F GET \u002Fapi\u002Fmoments?limit=2\n{\"rows\":[],\"total\":0,\"page\":\"2\",\"totalPages\":0}\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>\u003Cstrong>所有数据都没了。\u003C\u002Fstrong>\u003C\u002Fp>\n\n\u003Cp>文章、动态、评论——全部返回空数组。赶紧登上服务器查数据库：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">$ node -e \"import Database from 'better-sqlite3';\nconst db = new Database('data\u002Fblog.db', { readonly: true });\nconsole.log('articles:', db.prepare('SELECT COUNT(*) as c FROM articles').get().c);\nconsole.log('moments:', db.prepare('SELECT COUNT(*) as c FROM moments').get().c);\"\n\narticles: 0\nmoments: 0\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>数据库文件还在（7.7MB），表结构也都在，但所有业务表的数据——\u003Cu>全部清零\u003C\u002Fu>。只有 \u003Ccode>experiments\u003C\u002Fcode>（实验功能）和 \u003Ccode>visitor_logs\u003C\u002Fcode>（访客日志）还有数据。\u003C\u002Fp>\n\n\u003Cblockquote style=\"border-left:4px solid #e74c3c;padding:12px 20px;margin:20px 0;background:#fdf0ef;\">\n\u003Cp style=\"font-size:18px;\">\u003Cstrong>那一刻的内心：\u003C\u002Fstrong>45 篇文章，160+动态。写了两个月，全没了？\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Ch2>排查过程\u003C\u002Fh2>\n\n\u003Ch3>第一步：检查 PM2 进程状态\u003C\u002Fh3>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">$ pm2 list\n┌────┬──────────────┬──────────┬──────┬──────────┬──────┐\n│ id │ name         │ status   │ ↺    │ uptime   │ cpu  │\n├────┼──────────────┼──────────┼──────┼──────────┼──────┤\n│ 1  │ blog-server  │ online   │ 47   │ 4m       │ 0%   │\n└────┴──────────────┴──────────┴──────┴──────────┴──────┘\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>进程是 \u003Ccode>online\u003C\u002Fcode>，但重启了 \u003Cstrong>47 次\u003C\u002Fstrong>。错误日志里全是：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">❌ Error: no such column: m.lang\n❌ Error: no such column: m.lang\n❌ Error: no such column: m.lang\n...（重复几十次）\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch3>第二步：检查数据库文件\u003C\u002Fh3>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">$ ls -la data\u002F\n-rw-r--r-- 1 root root 7737344 Jul  9 08:49 blog.db\n-rw-r--r-- 1 root root   32768 Jul  9 09:31 blog.db-shm\n-rw-r--r-- 1 root root 1178352 Jul  9 09:31 blog.db-wal\n-rw-r--r-- 1 root root 7528448 Jul  2 12:25 blog.db.bak.20260702\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>WAL 文件有 1.1MB，说明可能有未合并的数据。尝试 checkpoint：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">$ node -e \"db.pragma('wal_checkpoint(TRUNCATE)')\"\narticles: 0\nmoments: 0\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>WAL 合并后数据还是空的。\u003Cmark style=\"background:#fff3cd;padding:2px 4px;\">数据确实丢了，不是 WAL 没合并的问题。\u003C\u002Fmark>\u003C\u002Fp>\n\n\u003Ch3>第三步：检查备份\u003C\u002Fh3>\n\n\u003Cp>幸好服务器上有自动备份脚本，每天凌晨 3 点跑一次：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">$ ls \u002Fopt\u002Fblog\u002Fbackups\u002Fblog_*.db\nblog_20260626_150403.db\nblog_20260627_030001.db\n...\nblog_20260709_030001.db  ← 今天凌晨的备份\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>验证备份数据：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">$ node -e \"const db = new Database('..\u002Fbackups\u002Fblog_20260709_030001.db', {readonly:true});\nconsole.log('articles:', db.prepare('SELECT COUNT(*) as c FROM articles').get().c);\nconsole.log('moments:', db.prepare('SELECT COUNT(*) as c FROM moments').get().c);\"\n\narticles: 45\nmoments: 167\ncomments: 143\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp style=\"font-size:16px;\">\u003Cstrong>备份是完整的！\u003C\u002Fstrong>悬着的心放下一半。\u003C\u002Fp>\n\n\u003Ch2>数据恢复\u003C\u002Fh2>\n\n\u003Cp>立即停服、替换数据库、重启：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">$ pm2 stop blog-server\n$ cp data\u002Fblog.db data\u002Fblog.db.bak.20260709_empty  # 保留现场\n$ cp ..\u002Fbackups\u002Fblog_20260709_030001.db data\u002Fblog.db\n$ rm -f data\u002Fblog.db-wal data\u002Fblog.db-shm  # 删除旧 WAL\n$ pm2 start blog-server\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>验证恢复结果：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-json\">\u002F\u002F GET \u002Fapi\u002Farticles?page=1&amp;limit=1\n{\"rows\":[{\"id\":45,\"title\":\"博客 V2.0 正式发布：我给这个小破站做了一次大手术\"...}]}\n\n\u002F\u002F GET \u002Fapi\u002Fmoments?limit=1\n{\"rows\":[{\"id\":209,\"content\":\"都来测一个😊...\"}]}\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp style=\"text-align:center;font-size:20px;\">\u003Cstrong>✅ 数据恢复成功\u003C\u002Fstrong>\u003C\u002Fp>\n\n\u003Ch2>定位根因\u003C\u002Fh2>\n\n\u003Cp>数据虽然恢复了，但如果不找到根因，下次部署还会丢。于是开始逆向排查。\u003C\u002Fp>\n\n\u003Ch3>关键线索：git 跟踪了不该跟踪的文件\u003C\u002Fh3>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">$ git ls-files | grep 'data\u002F.*\\.db'\nserver\u002Fdata\u002Fblog.db-shm\nserver\u002Fdata\u002Fblog.db-wal\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>\u003Cmark style=\"background:#ffd6d6;padding:2px 6px;\">\u003Cstrong>找到了！\u003C\u002Fstrong>\u003C\u002Fmark> \u003Ccode>blog.db-wal\u003C\u002Fcode> 和 \u003Ccode>blog.db-shm\u003C\u002Fcode> 竟然被 git 跟踪了！\u003C\u002Fp>\n\n\u003Cp>再看 \u003Ccode>.gitignore\u003C\u002Fcode>：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\"># .gitignore\n*.db\n*.sqlite\n*.sqlite3\nThumbs.db\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>只忽略了 \u003Ccode>*.db\u003C\u002Fcode>，\u003Cstrong>没有忽略 \u003Ccode>*.db-wal\u003C\u002Fcode> 和 \u003Ccode>*.db-shm\u003C\u002Fcode>\u003C\u002Fstrong>！\u003C\u002Fp>\n\n\u003Ch3>事故还原\u003C\u002Fh3>\n\n\u003Cp>整个事故的链条非常清晰：\u003C\u002Fp>\n\n\u003Cblockquote style=\"border-left:4px solid #3498db;padding:12px 20px;margin:20px 0;background:#eaf2f8;\">\n\u003Cp>\u003Cstrong>1.\u003C\u002Fstrong> 某次提交时，\u003Ccode>blog.db-wal\u003C\u002Fcode> 和 \u003Ccode>blog.db-shm\u003C\u002Fcode> 被 \u003Ccode>git add\u003C\u002Fcode> 加入了版本控制\u003C\u002Fp>\n\u003Cp>\u003Cstrong>2.\u003C\u002Fstrong> 之后每次部署都执行 \u003Ccode>git reset --hard master\u003C\u002Fcode>\u003C\u002Fp>\n\u003Cp>\u003Cstrong>3.\u003C\u002Fstrong> \u003Ccode>git reset --hard\u003C\u002Fcode> 会把工作区所有被跟踪的文件恢复到仓库中的版本\u003C\u002Fp>\n\u003Cp>\u003Cstrong>4.\u003C\u002Fstrong> 仓库里的 \u003Ccode>blog.db-wal\u003C\u002Fcode> 是某个旧版本，覆盖了服务器上当前的 WAL 文件\u003C\u002Fp>\n\u003Cp>\u003Cstrong>5.\u003C\u002Fstrong> SQLite 的 WAL 模式下，数据变更先写 WAL，再 checkpoint 到主库\u003C\u002Fp>\n\u003Cp>\u003Cstrong>6.\u003C\u002Fstrong> WAL 被旧文件覆盖后，未 checkpoint 的数据丢失，主库可能也被搞乱了\u003C\u002Fp>\n\u003Cp>\u003Cstrong>7.\u003C\u002Fstrong> 服务重启时数据库重新初始化，发现 WAL\u002FSHM 不匹配，数据状态错乱\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Cp>简单说就是：\u003Cu>每次 \u003Ccode>git reset --hard\u003C\u002Fcode> 部署，都用 git 仓库里的旧 WAL 文件覆盖了服务器上正在使用的 WAL 文件\u003C\u002Fu>，导致数据丢失。\u003C\u002Fp>\n\n\u003Ch2>修复\u003C\u002Fh2>\n\n\u003Ch3>1. 从 git 移除 WAL\u002FSHM 文件\u003C\u002Fh3>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">git rm --cached server\u002Fdata\u002Fblog.db-shm server\u002Fdata\u002Fblog.db-wal\necho \"*.db-wal\" &gt;&gt; .gitignore\necho \"*.db-shm\" &gt;&gt; .gitignore\ngit add .gitignore\ngit commit -m \"fix: 从git移除WAL\u002FSHM文件防止git reset覆盖导致数据丢失\"\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch3>2. 验证修复效果\u003C\u002Fh3>\n\n\u003Cp>推送后在服务器执行 \u003Ccode>git reset --hard master\u003C\u002Fcode>，然后检查数据：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-json\">\u002F\u002F GET \u002Fapi\u002Farticles?page=1&amp;limit=1\n{\"rows\":[{\"id\":45,\"title\":\"博客 V2.0 正式发布...\"}]}\n\n\u002F\u002F 数据没有丢失！git reset 不再覆盖数据库文件了。\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>经验教训\u003C\u002Fh2>\n\n\u003Ch3>1. .gitignore 要覆盖所有数据库相关文件\u003C\u002Fh3>\n\n\u003Cp>SQLite 的 WAL 模式会产生三个文件：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>blog.db      ← 主数据库文件\nblog.db-wal  ← Write-Ahead Log（预写日志）\nblog.db-shm  ← Shared Memory（共享内存索引）\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>\u003Ccode>.gitignore\u003C\u002Fcode> 必须\u003Cstrong>三个都忽略\u003C\u002Fstrong>：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode>*.db\n*.db-wal\n*.db-shm\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch3>2. 永远不要把数据库文件放进 git\u003C\u002Fh3>\n\n\u003Cp style=\"font-size:16px;\">Git 是版本控制工具，不是备份工具。数据库文件是二进制的，每次变更都会产生一个全新的 blob，仓库会迅速膨胀。更重要的是——\u003Cmark style=\"background:#fff3cd;padding:2px 4px;\">git reset --hard 会覆盖工作区文件\u003C\u002Fmark>，这对正在运行的数据库是致命的。\u003C\u002Fp>\n\n\u003Ch3>3. 自动备份是救命稻草\u003C\u002Fh3>\n\n\u003Cp>这次能恢复数据，全靠每天凌晨 3 点的自动备份脚本。一个简单的 crontab + \u003Ccode>cp\u003C\u002Fcode> 就能救命：\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\"># \u002Fetc\u002Fcron.d\u002Fblog-backup\n0 3 * * * root cp \u002Fopt\u002Fblog\u002Fserver\u002Fdata\u002Fblog.db \u002Fopt\u002Fblog\u002Fbackups\u002Fblog_$(date +\\%Y\\%m\\%d)_$(date +\\%H\\%M\\%S).db\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch3>4. 部署时检查 git status\u003C\u002Fh3>\n\n\u003Cp>如果部署前看一眼 \u003Ccode>git status\u003C\u002Fcode>，会发现 \u003Ccode>blog.db-wal\u003C\u002Fcode> 出现在跟踪文件列表里。养成部署前检查的习惯，能避免很多事故。\u003C\u002Fp>\n\n\u003Ch2>总结\u003C\u002Fh2>\n\n\u003Cp>这次事故的根因很简单：\u003Cspan style=\"text-decoration:underline;\">一个不完整的 \u003Ccode>.gitignore\u003C\u002Fcode> 规则\u003C\u002Fspan>。但影响是灾难性的——\u003Cstrong>两个月的心血差点付之一炬\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Cp>好在有备份，数据完整恢复了。修复也很简单：从 git 移除 WAL\u002FSHM 文件，补全 \u003Ccode>.gitignore\u003C\u002Fcode>。\u003C\u002Fp>\n\n\u003Cp style=\"text-align:center;font-size:18px;margin-top:30px;\">备份数据，检查 gitignore，\u003Cstrong>别让 git reset 变成删库跑路\u003C\u002Fstrong>。\u003C\u002Fp>","45 篇文章、167 条动态、143 条评论——一瞬间全部消失。罪魁祸首竟是一个被 git 跟踪的 WAL 文件。本文从发现、排查、定位到修复，完整复盘这次生产事故。","https:\u002F\u002Fmy.lcrworld.xyz\u002Fu\u002FFp0cTN2zR4o",12,1,"published",154,3803,0,"2026-07-09 09:46:01","2026-09-05 16:10:20",null,"git reset,数据库丢失,WAL文件,数据恢复,运维事故","作者复盘了一次因执行 `git reset --hard` 导致生产数据库清空的线上事故。事故根源在于 `.gitignore` 配置疏忽，导致 SQLite 的 WAL 文件被错误提交，部署时旧版本覆盖了服务器上的最新文件。幸好有每日自动备份，最终通过替换备份文件成功恢复了所有数据。","zh","all-rights-reserved","技术",[],{"id":64,"title":65,"slug":66,"cover_url":67},45,"博客 V2.0 正式发布：我给这个小破站做了一次大手术","博客-v20-正式发布我给这个小破站做了一次大手术","https:\u002F\u002Flcrworld.xyz\u002Fuploads\u002Fmedia\u002Fcompressed\u002F6425-1783428477926.jpg",{"id":69,"title":70,"slug":71,"cover_url":72},47,"从邮件里捞回丢失的评论：SQLite WAL 备份踩坑全记录","从邮件里捞回丢失的评论sqlite-wal-备份踩坑全记录","https:\u002F\u002Fmy.lcrworld.xyz\u002Fu\u002FsLhAojMb4Fc",{"slots":74,"active":75},{},[]]