开发笔记

技术探索与开发经验分享

← 返回首页

从 SQLite 到 PostgreSQL:小型项目数据库迁移实战

去年我用 SQLite 做了一个小型的博客系统,部署简单,开发效率高。但随着数据量的增长和并发访问的增加,SQLite 的写锁问题开始显现。这篇文章记录了将该项目迁移到 PostgreSQL 的全过程。

为什么迁移

SQLite 作为嵌入式数据库,在小规模场景下表现出色。但当项目同时在线用户超过 50 人时,写操作串行化导致明显延迟。具体来说:

迁移准备

在开始迁移之前,我梳理了整体的迁移步骤:

  1. 导出 SQLite 数据为中间格式
  2. 在 PostgreSQL 中创建对应的 schema
  3. 数据类型映射与转换
  4. 导入数据并验证完整性
  5. 修改 ORM 配置与连接
  6. 灰度切换与回滚方案

数据类型映射

SQLite 的数据类型比较宽松,而 PostgreSQL 类型严格,需要仔细映射。我整理了一个映射表:

SQLite          → PostgreSQL
-------------------------------
INTEGER         → INTEGER / BIGINT
REAL            → DOUBLE PRECISION
TEXT            → TEXT / VARCHAR(n)
BLOB            → BYTEA
DATETIME        → TIMESTAMP
BOOLEAN         → BOOLEAN (SQLite 存储为 0/1)
AUTOINCREMENT   → SERIAL / IDENTITY

数据迁移脚本

我写了一个 Python 脚本来完成数据导出和导入。核心思路是逐表读取 SQLite 数据,转换成 PostgreSQL 兼容的格式后批量插入。

import sqlite3
import psycopg2
from psycopg2.extras import execute_values

sqlite_conn = sqlite3.connect('blog.db')
pg_conn = psycopg2.connect('postgresql://user:pass@localhost/blog')

tables = ['users', 'posts', 'comments', 'tags']

for table in tables:
    rows = sqlite_conn.execute(f'SELECT * FROM {table}').fetchall()
    cols = [d[0] for d in sqlite_conn.execute(f'PRAGMA table_info({table})')]
    placeholders = ','.join(['%s'] * len(cols))
    col_names = ','.join(cols)
    execute_values(pg_conn.cursor(),
        f'INSERT INTO {table} ({col_names}) VALUES %s',
        rows, page_size=1000)
    pg_conn.commit()
    print(f'Migrated {len(rows)} rows to {table}')

ORM 适配

项目使用 Eloquent ORM(Laravel),迁移主要修改 .env 中的数据库配置。需要注意 PostgreSQL 的 schema 搜索路径和连接池设置:

DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=blog
DB_USERNAME=blog_user
DB_PASSWORD=secure_password
DB_SCHEMA=public

Eloquent 对 PostgreSQL 的支持很好,大部分查询语法无需修改。少数使用了 SQLite 特定函数的查询需要手动适配。

回滚策略

为了安全迁移,我采用了双写 + 灰度切换的策略:

迁移效果

迁移完成后,页面响应时间从平均 180ms 降低到 45ms,并发能力从约 50 并发提升到 500+。全文搜索功能也让博客的站内搜索体验大幅提升。

总结:对于预期会增长的小型项目,建议从一开始就使用 PostgreSQL。如果已经用了 SQLite 也不用担心,迁移过程比想象中要平滑得多。