<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>后端开发 on Dev TLDRLSS</title>
        <link>https://dev.tldrlss.com/zh-cn/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/</link>
        <description>Recent content in 后端开发 on Dev TLDRLSS</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <lastBuildDate>Tue, 19 May 2026 18:10:00 +0800</lastBuildDate><atom:link href="https://dev.tldrlss.com/zh-cn/categories/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>别被 SQLite 的随性骗了！动态类型陷阱是什么？为什么 ALTER TABLE 是半残的？如何在 Node.js 中做好防御性编程实现无痛数据表升级？</title>
        <link>https://dev.tldrlss.com/zh-cn/article/2026/05/sqlite-pitfall-intro/</link>
        <pubDate>Tue, 19 May 2026 18:10:00 +0800</pubDate>
        
        <guid>https://dev.tldrlss.com/zh-cn/article/2026/05/sqlite-pitfall-intro/</guid>
        <description>&lt;img src="https://dev.tldrlss.com/global-assets/images/database/sqlite-type-pitfall-1.jpg" alt="Featured image of post 别被 SQLite 的随性骗了！动态类型陷阱是什么？为什么 ALTER TABLE 是半残的？如何在 Node.js 中做好防御性编程实现无痛数据表升级？" /&gt;&lt;p&gt;想象一个资源回收桶，你明明在上面贴了“宝特瓶专用”的标签，结果别人丢了一张纸片进去，它居然默默收下，连一声抗议都没有？&lt;/p&gt;
&lt;p&gt;这就是开发者第一次遇见 &lt;code&gt;SQLite&lt;/code&gt; 类型系统时的惊悚体验。&lt;/p&gt;
&lt;p&gt;如果你习惯了 &lt;code&gt;PostgreSQL&lt;/code&gt; 那种严格的海关作风（类型不对直接拒绝入境），&lt;code&gt;SQLite&lt;/code&gt; 的随性程度可能会让你怀疑人生。&lt;/p&gt;
&lt;p&gt;更恐怖的是，当你想 &lt;strong&gt;修改数据表结构&lt;/strong&gt; 时，它会告诉你：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“数据表改不了，请盖一栋新房子，把家具搬过去，然后把旧房子炸掉。”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;sqlite-底层只有-5-种存储类别&#34;&gt;SQLite 底层只有 5 种存储类别
&lt;/h2&gt;&lt;p&gt;不管你在 &lt;code&gt;CREATE TABLE&lt;/code&gt; 时宣告了什么华丽的类型名称（&lt;code&gt;VARCHAR(255)&lt;/code&gt;、&lt;code&gt;BIGINT&lt;/code&gt;、&lt;code&gt;DECIMAL&lt;/code&gt;），&lt;code&gt;SQLite&lt;/code&gt; 底层只认得这 &lt;strong&gt;5 种存储类别&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;存储类别&lt;/th&gt;
          &lt;th&gt;说明&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;NULL&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;空值&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;INTEGER&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;整数（依数字大小自动占用 1 到 8 bytes）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;REAL&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;浮点数（固定 8 bytes）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;TEXT&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;字符串（默认 UTF-8 编码）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;BLOB&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;二进制数据（原封不动存储）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;你在字段上设置的类型，对 &lt;code&gt;SQLite&lt;/code&gt; 来说 &lt;strong&gt;只是一个“建议”，而不是“强制规定”&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这叫做 &lt;strong&gt;“类型亲和性（Type Affinity）”&lt;/strong&gt;，&lt;code&gt;SQLite&lt;/code&gt; 会尝试把你的数据转成建议的类型，但如果转不了，它照样把原始数据塞进去，完全不报错。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;你可以在 &lt;code&gt;SQLite&lt;/code&gt; 宣告一个 &lt;code&gt;age INTEGER&lt;/code&gt; 字段，然后塞入字符串 &lt;code&gt;&#39;永远的十八岁&#39;&lt;/code&gt;，它会欣然接受。&lt;/p&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;三个最容易踩坑的类型陷阱&#34;&gt;三个最容易踩坑的类型陷阱
&lt;/h2&gt;&lt;h3 id=&#34;陷阱-1没有原生-boolean&#34;&gt;陷阱 1：没有原生 Boolean
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt; &lt;strong&gt;没有布尔类型&lt;/strong&gt;。&lt;code&gt;True&lt;/code&gt; 和 &lt;code&gt;False&lt;/code&gt; 只能用整数 &lt;code&gt;1&lt;/code&gt; 和 &lt;code&gt;0&lt;/code&gt; 代替。&lt;/p&gt;
&lt;p&gt;当你用 &lt;code&gt;Node.js&lt;/code&gt; 从 &lt;code&gt;SQLite&lt;/code&gt; 捞出数据时，拿到的会是数字 &lt;code&gt;1&lt;/code&gt; 或 &lt;code&gt;0&lt;/code&gt;，不是 &lt;code&gt;true&lt;/code&gt; 或 &lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果你直接拿去做 &lt;code&gt;if (user.is_admin === true)&lt;/code&gt; 的判断，永远不会成立。&lt;/p&gt;
&lt;h3 id=&#34;陷阱-2没有-datetime-类型&#34;&gt;陷阱 2：没有 Date/Time 类型
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt; &lt;strong&gt;没有日期时间类型&lt;/strong&gt;。你只能把时间存成：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;存储方式&lt;/th&gt;
          &lt;th&gt;范例&lt;/th&gt;
          &lt;th&gt;优缺点&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;TEXT（ISO-8601 字符串）&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;&#39;2026-05-19T18:00:00Z&#39;&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;最推荐&lt;/strong&gt;，可读性高，未来搬到 PostgreSQL 可无缝转换&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;INTEGER（Unix Timestamp）&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;1747656000&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;占用空间小，但人类不可读&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;千万不要存像 &lt;code&gt;2026/5/19 下午六点&lt;/code&gt; 这种自创格式，否则未来做数据迁移时会是一场灾难。&lt;/p&gt;
&lt;h3 id=&#34;陷阱-3字符串塞进整数字段不会报错&#34;&gt;陷阱 3：字符串塞进整数字段不会报错
&lt;/h3&gt;&lt;p&gt;在 &lt;code&gt;PostgreSQL&lt;/code&gt;，你把字符串塞进 &lt;code&gt;INTEGER&lt;/code&gt; 字段会直接报错。但 &lt;code&gt;SQLite&lt;/code&gt; 只会默默尝试转换，转不了就原样收下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这意味着你的数据库里 &lt;strong&gt;可能悄悄混进了脏数据&lt;/strong&gt;，直到某天你的程序因为拿到非预期的类型而崩溃，才会发现问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;防御性编程用严格的态度对待随性的数据库&#34;&gt;防御性编程：用严格的态度对待随性的数据库
&lt;/h2&gt;&lt;p&gt;面对 &lt;code&gt;SQLite&lt;/code&gt; 的随性，在 &lt;code&gt;Node.js&lt;/code&gt; 开发中必须建立起 &lt;strong&gt;严格的防御机制&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;防御层级&lt;/th&gt;
          &lt;th&gt;工具&lt;/th&gt;
          &lt;th&gt;作用&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;编译期挡错&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;在代码阶段就挡下错误的类型&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;API 入口验证&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;Zod&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;接收数据时严格验证（确认 age 一定是数字）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;底层类型转换&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;Prisma / Drizzle ORM&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;自动处理 SQLite 和 PostgreSQL 之间的类型差异&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;把 &lt;strong&gt;“数据验证的守门员”&lt;/strong&gt; 从数据库层移到 &lt;code&gt;Node.js&lt;/code&gt; 应用层，这是同时驾驭 SQLite 开发速度与未来扩展性的关键策略。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;使用 ORM 时，你只要在程序里宣告 &lt;code&gt;type: &#39;boolean&#39;&lt;/code&gt;，ORM 存入 &lt;code&gt;SQLite&lt;/code&gt; 时自动转成 &lt;code&gt;1/0&lt;/code&gt;，拿出来时自动转回 &lt;code&gt;true/false&lt;/code&gt;，完美遮蔽底层的类型差异。&lt;/p&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;alter-table-是半残的什么能改什么不能改&#34;&gt;ALTER TABLE 是半残的：什么能改、什么不能改
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt; 对数据表结构的修改支持非常有限：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;操作&lt;/th&gt;
          &lt;th&gt;是否支持&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;新增字段（&lt;code&gt;ADD COLUMN&lt;/code&gt;）&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;支持&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;重新命名字段（&lt;code&gt;RENAME COLUMN&lt;/code&gt;）&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;支持&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;删除字段（&lt;code&gt;DROP COLUMN&lt;/code&gt;）&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;支持&lt;/strong&gt; （较新版本）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;重新命名数据表（&lt;code&gt;RENAME TO&lt;/code&gt;）&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;支持&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;更改字段类型&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;不支持&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;增加/移除 &lt;code&gt;UNIQUE&lt;/code&gt;、&lt;code&gt;NOT NULL&lt;/code&gt; 限制&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;不支持&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;修改 Primary Key&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;不支持&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;修改 Foreign Key&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;不支持&lt;/strong&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;一旦你需要做“不支持”的那些修改，&lt;code&gt;SQLite&lt;/code&gt; 会要求你执行 &lt;strong&gt;“重建并搬移”&lt;/strong&gt; 的策略。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;重建搬移四步曲sqlite-的数据表升级之道&#34;&gt;重建搬移四步曲：SQLite 的数据表升级之道
&lt;/h2&gt;&lt;p&gt;既然无法直接改，官方建议的标准做法就是 &lt;strong&gt;盖新房子、搬家具、炸旧房子、贴新门牌&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;步骤&lt;/th&gt;
          &lt;th&gt;动作&lt;/th&gt;
          &lt;th&gt;说明&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td&gt;建立新表&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;CREATE TABLE users_new (...)&lt;/code&gt; 使用正确的结构&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;2&lt;/td&gt;
          &lt;td&gt;复制数据&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;INSERT INTO users_new SELECT ... FROM users&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;3&lt;/td&gt;
          &lt;td&gt;删除旧表&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;DROP TABLE users&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;4&lt;/td&gt;
          &lt;td&gt;改名&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;ALTER TABLE users_new RENAME TO users&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这四个步骤必须 &lt;strong&gt;一气呵成&lt;/strong&gt;，中途断电或程序出错就会造成数据丢失。&lt;/p&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;确保升级不丢失数据两道安全防线&#34;&gt;确保升级不丢失数据：两道安全防线
&lt;/h2&gt;&lt;h3 id=&#34;防线-1物理防御直接复制文件&#34;&gt;防线 1：物理防御，直接复制文件
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt; 的本质就是一个文件。在执行任何结构变更前，先把 &lt;code&gt;.db&lt;/code&gt; 文件复制一份当备份。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;fs&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;fs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;copyFileSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;my_project.db&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;my_project_backup.db&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;万一搞砸了，文件盖回去就瞬间复原。这是其他大型数据库做不到的优势。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;防线-2transaction-包装数据库的时光机&#34;&gt;防线 2：Transaction 包装，数据库的时光机
&lt;/h3&gt;&lt;p&gt;把搬家的步骤包在一个 &lt;code&gt;Transaction&lt;/code&gt; 里面，只要其中一步失败，整个过程就会 &lt;strong&gt;自动还原（Rollback）&lt;/strong&gt;，当作没发生过。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Database&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;better-sqlite3&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Database&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;my_project.db&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;migrateData&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;transaction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;    CREATE TABLE users_new (
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;      id INTEGER PRIMARY KEY AUTOINCREMENT,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;      name TEXT NOT NULL,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;      age INTEGER NOT NULL DEFAULT 18
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;    )
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;  `&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;    INSERT INTO users_new (id, name, age)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;    SELECT id, name, COALESCE(age, 18) FROM users
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;sb&#34;&gt;  `&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;DROP TABLE users&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;ALTER TABLE users_new RENAME TO users&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;migrateData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;数据表升级成功&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;升级失败，数据已安全还原：&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“备份文件 + Transaction 绑定”&lt;/strong&gt; 就是你的数据库迁移的安全气囊。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;用严格的态度驾驭随性的-sqlite&#34;&gt;用严格的态度驾驭随性的 SQLite
&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;用 &lt;strong&gt;严格的应用层架构&lt;/strong&gt; 来驾驭随性的 &lt;code&gt;SQLite&lt;/code&gt;，享受它极速开发的优势，同时避开未来的技术债。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt; 的类型系统很随性，&lt;code&gt;ALTER TABLE&lt;/code&gt; 也有诸多限制。&lt;/p&gt;
&lt;p&gt;但只要在 &lt;code&gt;Node.js&lt;/code&gt; 端做好 &lt;strong&gt;TypeScript 类型检查 + Zod 验证 + ORM 抽象层&lt;/strong&gt;，再搭配 &lt;strong&gt;物理备份 + Transaction&lt;/strong&gt; 的安全策略，你就能安心享受 &lt;code&gt;SQLite&lt;/code&gt; 带来的开发效率，同时为未来可能搬移至 &lt;code&gt;PostgreSQL&lt;/code&gt; 铺好后路。&lt;/p&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;reference&#34;&gt;Reference
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://sqlite.org/datatype3.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Datatypes In SQLite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.runoob.com/sqlite/sqlite-data-types.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SQLite 数据类型 | 菜鸟教程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.sqlite.org/lang_transaction.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Transaction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://sqlite.org/lang.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Query Language Understood by SQLite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/SQLite&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SQLite - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        <item>
        <title>不要什么都用 PostgreSQL！SQLite 的嵌入式架构与零配置优势是什么？SQLite 的极限在哪？什么时候该选 SQLite、什么时候该选 PostgreSQL？</title>
        <link>https://dev.tldrlss.com/zh-cn/article/2026/05/sqlite-intro/</link>
        <pubDate>Tue, 19 May 2026 18:00:00 +0800</pubDate>
        
        <guid>https://dev.tldrlss.com/zh-cn/article/2026/05/sqlite-intro/</guid>
        <description>&lt;img src="https://dev.tldrlss.com/global-assets/images/database/sqlite-vs-postgresql-choice-2.jpg" alt="Featured image of post 不要什么都用 PostgreSQL！SQLite 的嵌入式架构与零配置优势是什么？SQLite 的极限在哪？什么时候该选 SQLite、什么时候该选 PostgreSQL？" /&gt;&lt;p&gt;你有没有想过，你每天打开的浏览器、手机里的通讯软件、甚至桌面上的笔记工具，里面都藏着同一个轻巧的数据库？&lt;/p&gt;
&lt;p&gt;它不需要你安装任何服务器软件，不需要设置账号密码，甚至不需要网络连接。它就是一个 &lt;strong&gt;文件&lt;/strong&gt;，静静地躺在你的硬盘里，随时准备好为你服务。&lt;/p&gt;
&lt;p&gt;这个低调的存在，就是 &lt;code&gt;SQLite&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;什么是-sqlite世界上部署最广泛的数据库&#34;&gt;什么是 SQLite？世界上部署最广泛的数据库
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt; 是一个用 C 语言编写的 &lt;strong&gt;嵌入式关系型数据库引擎&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;它&lt;strong&gt;没有独立的服务器程序&lt;/strong&gt;，而是直接嵌入你的应用程序中运作。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这跟你熟悉的 &lt;code&gt;PostgreSQL&lt;/code&gt; 或 &lt;code&gt;MySQL&lt;/code&gt; 完全不同。传统数据库是一台 &lt;strong&gt;独立运作的服务器&lt;/strong&gt;，你的程序必须透过 &lt;strong&gt;网络协议（TCP/IP）&lt;/strong&gt; 去跟它「通讯」。&lt;/p&gt;
&lt;p&gt;但 &lt;code&gt;SQLite&lt;/code&gt; 不一样，它就是一段代码，直接跑在你的应用程序里面，读写硬盘上的那个 &lt;code&gt;.db&lt;/code&gt; 文件。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;比较维度&lt;/th&gt;
          &lt;th&gt;SQLite（嵌入式）&lt;/th&gt;
          &lt;th&gt;PostgreSQL（Client-Server）&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;运作方式&lt;/td&gt;
          &lt;td&gt;直接嵌入应用程序，无独立服务器&lt;/td&gt;
          &lt;td&gt;独立服务器程序，透过网络连接&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;配置需求&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;零配置&lt;/strong&gt;，免安装、免帐密&lt;/td&gt;
          &lt;td&gt;需要安装、配置账号密码与防火墙&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;数据存储&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;单一跨平台文件&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;服务器目录下的多个文件&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;备份方式&lt;/td&gt;
          &lt;td&gt;直接复制那个文件&lt;/td&gt;
          &lt;td&gt;需要使用 pg_dump 等专用工具&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;正因为这种 &lt;strong&gt;「即插即用」&lt;/strong&gt; 的特性，&lt;code&gt;SQLite&lt;/code&gt; 成为了世界上部署最广泛的数据库引擎。&lt;/p&gt;
&lt;p&gt;从 Android 和 iOS 操作系统、Chrome 和 Firefox 浏览器，到 Adobe Lightroom、WhatsApp，甚至是 Airbus A350 的飞行系统，处处都有它的身影。&lt;/p&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;在-nodejs-中使用-sqlite&#34;&gt;在 Node.js 中使用 SQLite
&lt;/h2&gt;&lt;p&gt;如果你是 Node.js 开发者，使用 &lt;code&gt;SQLite&lt;/code&gt; 非常简单。你不需要在电脑上安装任何数据库服务器软件，只需要安装一个 npm 套件就能开始。&lt;/p&gt;
&lt;p&gt;目前行业最常用的选择：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;套件名称&lt;/th&gt;
          &lt;th&gt;特点&lt;/th&gt;
          &lt;th&gt;建议情境&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;sqlite3&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;最老牌，支持异步 API&lt;/td&gt;
          &lt;td&gt;需要大量同时处理多个异步任务&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;better-sqlite3&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;性能极佳&lt;/strong&gt;，API 设计直观，速度极快&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;首选推荐&lt;/strong&gt;，追求开发效率与执行速度&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;用 &lt;code&gt;better-sqlite3&lt;/code&gt; 建立数据库并查询，不用五分钟就能完成：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Database&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;require&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;better-sqlite3&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Database&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;my_project.db&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;CREATE TABLE IF NOT EXISTS users (name TEXT, age INTEGER)&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;insert&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;INSERT INTO users (name, age) VALUES (?, ?)&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;insert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;John&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;25&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prepare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;SELECT * FROM users WHERE name = ?&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;John&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// { name: &amp;#39;John&amp;#39;, age: 25 }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;如果文件不存在，&lt;code&gt;better-sqlite3&lt;/code&gt; 会 &lt;strong&gt;自动帮你建立&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;sqlite-支持的-sql-语法比你想的强大&#34;&gt;SQLite 支持的 SQL 语法比你想的强大
&lt;/h2&gt;&lt;p&gt;很多人以为 &lt;code&gt;SQLite&lt;/code&gt; 很阳春，但它支持绝大多数的标准 SQL 语法，包含许多进阶功能：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;语法类别&lt;/th&gt;
          &lt;th&gt;支持项目&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;基础操作&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;SELECT&lt;/code&gt;、&lt;code&gt;INSERT&lt;/code&gt;、&lt;code&gt;UPDATE&lt;/code&gt;、&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;数据定义&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;CREATE TABLE&lt;/code&gt;、&lt;code&gt;CREATE INDEX&lt;/code&gt;、&lt;code&gt;CREATE VIEW&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;进阶查询&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;WITH&lt;/code&gt; （CTE 递归查询）、窗口函数（Window Functions）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;冲突处理&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;UPSERT&lt;/code&gt; （&lt;code&gt;INSERT ... ON CONFLICT DO UPDATE&lt;/code&gt;）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;JSON 处理&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;json_extract&lt;/code&gt;、&lt;code&gt;json_array&lt;/code&gt;、&lt;code&gt;json_object&lt;/code&gt; 等内建函数&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;事务控制&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;BEGIN&lt;/code&gt;、&lt;code&gt;COMMIT&lt;/code&gt;、&lt;code&gt;ROLLBACK&lt;/code&gt;、&lt;code&gt;SAVEPOINT&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;连接查询&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;INNER JOIN&lt;/code&gt;、&lt;code&gt;LEFT JOIN&lt;/code&gt;、&lt;code&gt;RIGHT JOIN&lt;/code&gt; 和 &lt;code&gt;FULL OUTER JOIN&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SQLite 的核心哲学是「小而美」&lt;/strong&gt;，它支持多数你日常需要的 SQL 能力，同时保持极致的轻量。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;sqlite-的极限在哪&#34;&gt;SQLite 的极限在哪？
&lt;/h2&gt;&lt;p&gt;轻巧是有代价的。如果把 &lt;code&gt;SQLite&lt;/code&gt; 比喻为一间 &lt;strong&gt;只有一个结账柜台的文青杂货店&lt;/strong&gt;，那 &lt;code&gt;PostgreSQL&lt;/code&gt; 就是 &lt;strong&gt;配备 50 个收银台的好市多&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;1-写入大塞车&#34;&gt;1. 写入大塞车
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt; 在写入数据时，会把整个数据库文件 &lt;strong&gt;锁起来&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;想像一间 &lt;strong&gt;只有一间厕所的餐厅&lt;/strong&gt;：100 个人可以同时在外面看菜单（读取），但只要有 1 个人进去把门锁上（写入），其他人就只能排队等。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;虽然开启 &lt;strong&gt;WAL 模式 （Write-Ahead Logging）&lt;/strong&gt; 能改善读写并行的性能，但本质上仍然无法做到多线程同时写入不同的数据行。&lt;/p&gt;
&lt;h3 id=&#34;2-无法跨多台服务器&#34;&gt;2. 无法跨多台服务器
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt; 的本质就是一个实体文件。如果你的系统部署在多台服务器上（水平扩展），这些服务器 &lt;strong&gt;无法安全地共用同一个文件&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;3-缺乏精细的权限管理&#34;&gt;3. 缺乏精细的权限管理
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt; 没有「使用者账号」的概念。谁能在操作系统层面读取那个 &lt;code&gt;.db&lt;/code&gt; 文件，谁就能看光、修改所有数据。&lt;/p&gt;
&lt;p&gt;对于 &lt;strong&gt;需要严格个资审查的商务系统&lt;/strong&gt;，这是无法接受的。&lt;/p&gt;
&lt;h2 id=&#34;sqlite-vs-postgresql技术选型决策&#34;&gt;SQLite vs. PostgreSQL：技术选型决策
&lt;/h2&gt;&lt;p&gt;工具没有好坏，只有适不适合。以下是一份帮助你做决策的终极检核表：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;提問&lt;/th&gt;
          &lt;th&gt;答「是」→ 选 SQLite&lt;/th&gt;
          &lt;th&gt;答「否」→ 选 PostgreSQL&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;后端服务器是不是只有一台，或完全跑在本机端？&lt;/td&gt;
          &lt;td&gt;是&lt;/td&gt;
          &lt;td&gt;否（有水平扩展、多台机器）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;系统行为是不是读远远大于写，且没有高频并行写入？&lt;/td&gt;
          &lt;td&gt;是&lt;/td&gt;
          &lt;td&gt;否（大家会同时抢着写入）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;不需要精细的数据库账号权限或进阶索引？&lt;/td&gt;
          &lt;td&gt;是&lt;/td&gt;
          &lt;td&gt;否（极度依赖进阶功能）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;更具体的情境对照：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;情境&lt;/th&gt;
          &lt;th&gt;推荐选择&lt;/th&gt;
          &lt;th&gt;理由&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;桌面软件、手机 App、IoT 设备&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;SQLite&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;数据跟着设备走，免安装&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;个人部落格、展示型官网&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;SQLite&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;读多写少，省去维护服务器成本&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;快速原型开发、Demo&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;SQLite&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;建个文件就能开工&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;社区论坛、电商平台&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;高并发写入，需要行级锁&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;多台服务器分散部署&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;需要跨机器共享数据源&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;医疗、金融等敏感系统&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;需要严格的角色权限管控&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;!--adsense--&gt;
&lt;h2 id=&#34;你的数据库是随身笔记还是中央总机&#34;&gt;你的数据库是随身笔记还是中央总机
&lt;/h2&gt;&lt;p&gt;如果数据是 &lt;strong&gt;「单机、静态、单一拥有者」&lt;/strong&gt;，选 &lt;code&gt;SQLite&lt;/code&gt; 享受极致的轻量与自由；&lt;/p&gt;
&lt;p&gt;如果数据是 &lt;strong&gt;「云端、動態、高度互动」&lt;/strong&gt;，让 &lt;code&gt;PostgreSQL&lt;/code&gt; 接管。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;SQLite&lt;/code&gt; 的核心哲学是 &lt;strong&gt;「应用程序的内部元件」&lt;/strong&gt;，而 &lt;code&gt;PostgreSQL&lt;/code&gt; 的定位是 &lt;strong&gt;「系统架构的独立中枢」&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;下次在架构决策时，别急着拿出 &lt;code&gt;PostgreSQL&lt;/code&gt;，先问问自己：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;「我的数据库，是拿来当随身笔记，还是中央总机？」&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;答案清楚了，选择自然就明确了。&lt;/p&gt;
&lt;h2 id=&#34;reference&#34;&gt;Reference
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://sqlite.org/lang.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Query Language Understood by SQLite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://sqlite.org/docs.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SQLite Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.sqlitetutorial.net/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SQLite Tutorial - An Easy Way to Master SQLite Fast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/SQLite&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;SQLite - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        
    </channel>
</rss>
