SQLite作为嵌入式数据库的代表,几乎是移动端和小型工具的标配。但当业务规模扩大,多台服务器需要同时读写同一份数据时,SQLite单文件的架构就成了瓶颈。腾讯云TDSQL-C兼容MySQL协议,采用计算存储分离架构,既保留了关系型数据库的易用性,又能弹性扩容。这篇文章完整记录一次从SQLite迁移到TDSQL-C的实战过程,涵盖数据导出、类型映射、批量导入和性能调优四个环节。

一、SQLite与TDSQL-C的核心差异分析
动手迁移之前,必须先弄清两者的底层差异。SQLite是进程内数据库,数据存在一个跨平台磁盘文件里,没有独立的服务进程,读写依赖文件锁。TDSQL-C则是基于云的分布式数据库,通过MySQL协议对外服务,支持主备高可用、自动备份和按需扩缩容。
最直接的差异体现在并发能力上。SQLite在写入时会锁整个数据库文件,即便开启WAL模式,也只允许多读单写。而TDSQL-C基于MVCC多版本并发控制,读写互不阻塞,几十个连接并发写入完全没问题。下面的表格列出了迁移中需要重点关注的差异点:
| 对比项 | SQLite | TDSQL-C |
|---|---|---|
| 部署形态 | 嵌入式单文件 | 云端独立服务 |
| 并发写入 | 数据库级锁 | 行级锁,MVCC |
| 数据类型 | 动态类型,弱约束 | 严格类型,强约束 |
| 自增主键 | INTEGER PRIMARY KEY | AUTO_INCREMENT |
| 最大数据量 | 建议2TB以内 | 最大可选TB级到PB级 |
还有一个容易被忽视的坑:SQLite允许同一列存不同类型的数据,比如TEXT列里塞整数也不会报错。迁移到强类型数据库后,这类脏数据会直接导致导入失败,所以迁移前必须做一轮数据清洗。
二、数据导出与类型映射处理
迁移的第一步是把SQLite数据导出。常用的做法是用Python脚本读取SQLite,再转换写入TDSQL-C。这里不建议直接用.dump命令导出SQL文件,因为SQLite的建表语句和MySQL方言不完全兼容,比如AUTOINCREMENT关键字、无符号类型等都需要手工修正。
类型映射是关键环节。SQLite只有五种存储类型(NULL、INTEGER、REAL、TEXT、BLOB),而TDSQL-C支持完整的MySQL类型体系。经验映射规则如下:INTEGER对应INT或BIGINT,REAL对应DOUBLE,TEXT按实际长度选VARCHAR或TEXT,BLOB对应BLOB或VARBINARY。如果SQLite里用了datetime函数存储时间字符串,建议转成DATETIME类型并统一格式。
import sqlite3
# 连接本地SQLite数据库
src = sqlite3.connect('app_local.db')
cursor = src.cursor()
# 获取所有表名和建表信息
cursor.execute("SELECT name, sql FROM sqlite_master WHERE type='table'")
tables = cursor.fetchall()
for name, ddl in tables:
print(f"表名: {name}")
print(f"原始DDL: {ddl}")
# 这里可以对DDL做类型替换后输出到文件
new_ddl = ddl.replace('AUTOINCREMENT', 'AUTO_INCREMENT')
new_ddl = new_ddl.replace('INTEGER PRIMARY KEY AUTO_INCREMENT',
'BIGINT PRIMARY KEY AUTO_INCREMENT')
print(f"转换后DDL: {new_ddl}")
src.close()
上面这段脚本只处理了表结构,实际项目中还要处理索引、触发器和视图。SQLite的触发器语法和MySQL差异较大,如果业务强依赖触发器,建议改写成应用层逻辑或存储过程,这样维护成本更低。
三、批量导入TDSQL-C的实战代码
表结构在TDSQL-C创建好之后,就轮到数据导入了。小数据量直接逐条INSERT也能接受,但几十万行以上的数据必须用批量插入,否则导入速度会慢到无法忍受。实践证明,使用executemany配合每批1000到5000条的分批提交,导入性能比逐条插入快几十倍。
import sqlite3
import pymysql
# 源库:本地SQLite
src = sqlite3.connect('app_local.db')
src_cursor = src.cursor()
# 目标库:腾讯云TDSQL-C(MySQL协议)
dst = pymysql.connect(
host='gz-cynosdbmysql.xxxxx.sql.tencentcdb.com',
port=3306,
user='app_user',
password='your_password',
database='app_db',
charset='utf8mb4'
)
dst_cursor = dst.cursor()
# 分批读取并写入
BATCH_SIZE = 2000
src_cursor.execute("SELECT id, username, email, created_at FROM users")
batch = []
while True:
rows = src_cursor.fetchmany(BATCH_SIZE)
if not rows:
break
batch.extend(rows)
dst_cursor.executemany(
"INSERT INTO users (id, username, email, created_at) VALUES (%s, %s, %s, %s)",
batch
)
dst.commit()
batch = []
src.close()
dst.close()
print("迁移完成")
有两个细节值得注意。第一,导入前建议临时关闭唯一索引和外键约束检查,导入完成后再重建,否则每写一批都要做约束校验,速度会明显下降。第二,主键ID要原样保留导入,避免业务侧关联关系错乱,全部导完后再用ALTER TABLE把AUTO_INCREMENT的起始值调整到最大ID之后。
如果数据量达到千万级,Python脚本就不是最优解了,可以把数据导出成CSV格式,再用TDSQL-C控制台提供的数据导入功能或LOAD DATA语句加载,速度能再提升一个量级。
四、迁移后的性能优化与踩坑记录
数据迁完只是开始,性能调优才是决定体验的部分。首先是连接池的配置,SQLite时代习惯每次操作都开新连接,迁到云端后每次建连都有网络开销,必须引入连接池。Python生态里DBUtils的PooledDB就很好用,配置最小连接5个、最大连接20个,基本能覆盖中等规模业务的并发需求。
from dbutils.pooled_db import PooledDB
import pymysql
pool = PooledDB(
creator=pymysql,
minconnections=5, # 初始连接数
maxconnections=20, # 最大连接数
host='gz-cynosdbmysql.xxxxx.sql.tencentcdb.com',
port=3306,
user='app_user',
password='your_password',
database='app_db',
charset='utf8mb4',
autocommit=True
)
# 业务代码中直接从池里取连接
conn = pool.connection()
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM users WHERE created_at > %s", ('2024-01-01',))
print(cursor.fetchone())
conn.close() # 实际是归还连接池,并非真正关闭
索引优化方面,SQLite里跑得快的SQL到TDSQL-C不一定快,因为两者的查询优化器行为不同。迁移后应该对核心慢查询重新执行EXPLAIN检查执行计划,重点确认是否走索引、是否存在隐式类型转换。例如字段是VARCHAR类型,查询条件却传了数字,MySQL会做隐式转换导致索引失效,这是迁移后最常见的性能劣化原因。
另一个实战踩坑点是事务语义差异。SQLite默认每条语句自动提交,而pymysql默认开启事务,写操作必须显式commit。如果代码沿用了SQLite的写法忘记提交,数据看似插入成功但重启后全部丢失。建议在连接池配置里明确设置autocommit策略,并对批量写操作统一封装事务管理。
总结一下这次迁移的关键收获:先做数据清洗再迁、结构转换用脚本自动化、数据导入必须分批、迁移后重查执行计划。SQLite适合单机轻量场景,TDSQL-C适合高并发、需要弹性扩展的业务,两者并不冲突,很多项目其实可以本地用SQLite做缓存和离线存储,云端用TDSQL-C做主库,各取所长。
SQLite腾讯云TDSQL-C数据迁移修改时间:2026-09-12 10:44:38