Menu

SQLite 和 MySQL 的区别:该选哪个数据库?

SQLite 和 MySQL 到底有什么不同:架构、并发、数据类型,以及各自适合什么样的项目。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

两种不同形态的数据库

SQLite 和 MySQL 都使用 SQL,都把行存放在表里,但它们在系统中的位置完全不同。SQLite 是一个库:你的应用链接它,从磁盘上的一个文件中读取数据。MySQL 是一个服务器:一个独立的进程,你通过套接字或网络连接它。

这一个区别决定了其他所有方面:怎么安装、能有多少个写入者同时工作、怎么备份、怎么部署。人们问的大多数“SQLite 还是 MySQL”的问题,其实都是“嵌入式还是客户端/服务器”的问题。

-- SQLite: open a file, you have a database.
sqlite3 app.db

-- MySQL: connect to a running server.
mysql -h localhost -u root -p

SQLite 的命令打开(或创建)一个文件。MySQL 的命令则是连接一个进程,而这个进程必须已经在运行、配置好,并且接受登录。

架构:嵌入式与客户端/服务器

在 SQLite 应用中,数据库引擎运行在你的程序内部。没有端口,没有守护进程,也不用 systemctl start。调用一次 sqlite3 库函数,就直接从磁盘上的文件读写数据页。

MySQL 正好相反。mysqld 服务器保存数据、管理连接、执行权限控制、运行查询优化器、处理锁。你的应用是客户端,通过网络发送 SQL 字符串,再接收返回的结果行。

实际影响是:

  • 部署。 SQLite 随你的应用一起发布:一个可执行文件,一个数据库文件。MySQL 需要单独安装、加固、监控和备份一台服务器。
  • 网络访问。 MySQL 开放一个端口,所以多台应用服务器可以连接同一个数据库。SQLite 假设只有一个进程(或几个相互配合的进程)在同一台机器上使用它。
  • 权限。 MySQL 有用户、角色和 GRANT 语句。SQLite 唯一的权限系统,就是操作系统对数据库文件的文件权限。

两种形态没有哪个“更好”。它们解决的是不同的问题。

并发与写入

这是两者真正分道扬镳的地方。MySQL 的 InnoDB 引擎使用行级锁:许多连接可以同时写入不同的行,互不阻塞。

SQLite 在数据库级别串行化写入。同一时间只能有一个写入者,没有例外。读取可以和写入同时进行(尤其是在 WAL 模式下),但第二个写入者必须排队等待。

-- SQLite: this works fine for many readers, one writer at a time.
PRAGMA journal_mode = WAL;

-- MySQL: many writers, fine-grained locking.
-- (No special setup - InnoDB does it by default.)

对于只有一两个进程、写入量不大的应用(桌面工具、移动应用、小型 CMS),SQLite 的串行写入通常足够快,你根本察觉不到。对于一个繁忙的 Web 服务,几百个连接都在插入订单或更新会话,MySQL 的行级锁就是“运行顺畅”和“所有请求都排在一把锁后面”的区别。

数据类型

MySQL 有一长串严格的类型:TINYINT、INT、BIGINT、VARCHAR(n)、DATETIME、DECIMAL(p,s)、BLOB、JSON 等等。把一列声明为 INT,MySQL 就会拒绝字符串。

SQLite 使用的则是类型亲和性。列类型只是提示,而不是强制规则。你可以把字符串放进 INTEGER 列,SQLite 会照样存储(除非你选用 3.37 版本加入的 STRICT 表)。

两行都插入成功了。这种灵活性在做原型时很方便,但如果你期望数据库层面的类型安全,就会感到意外。想在 SQLite 中获得 MySQL 那样的强制检查,就用 STRICT 表。

你真正会遇到的语法差异

大多数基础 SQL(SELECT、JOIN、WHERE、GROUP BY)完全一样。差异集中在几个方面:

  • 自增主键。 SQLite 用 INTEGER PRIMARY KEY(默认就会自增)。MySQL 用 INT AUTO_INCREMENT PRIMARY KEY。
  • 引用标识符。 MySQL 允许用反引号包裹标识符(`table`)。SQLite 按照 SQL 标准使用双引号("table")。
  • 日期函数。 MySQL 有 NOW()、CURDATE()、DATE_ADD()。SQLite 有 datetime('now')、date('now')、datetime('now', '+1 day')。
  • LIMIT 语法。 两者都支持 LIMIT n OFFSET m,所以这一点是兼容的。
  • 布尔值。 MySQL 有 BOOLEAN(TINYINT(1) 的别名)。SQLite 在 INTEGER 列中用 0 和 1 存储布尔值。
-- MySQL
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    created_at DATETIME DEFAULT NOW()
);

-- SQLite
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    created_at TEXT DEFAULT (datetime('now'))
);

意图相同,关键字不同。思路可以照搬,语法需要做些小调整。

性能:取决于你问的是什么

“SQLite 比 MySQL 快吗?”这个问题没有唯一的答案。

对于单个进程在本地读写,SQLite 往往更快:没有网络跳转,没有进程间通信,也没有在另一个地址空间里运行的查询解析器。在 SQLite 中执行一次 SELECT,本质上就是一次函数调用。

对于许多并发连接写入同一个数据库,MySQL 凭借行级锁领先。在这种负载下,SQLite 的单写入者模型很快就会出现争用。

对于开启了 WAL 模式、以读取为主的负载,SQLite 的扩展能力好得出奇:读取者之间互不阻塞,也不会阻塞那唯一的写入者。不少生产网站都在用 SQLite 承载真实的流量。

不要根据网上看到的基准测试来选。根据你实际的访问模式来选。

什么时候该选哪个

以下情况选 SQLite:

  • 数据库只服务于一个应用(移动应用、桌面工具、命令行工具、小型网站)。
  • 你想要零配置部署:直接把文件带上就行。
  • 读取远多于写入,或者写入并不频繁。
  • 你需要一个和生产环境 SQL 一致的嵌入式测试数据库。
  • 你在做原型,暂时还不想考虑服务器。

以下情况选 MySQL:

  • 多台应用服务器需要共享一个数据库。
  • 有大量并发写入者。
  • 你需要细粒度的用户权限和角色管理。
  • 你使用的技术栈(LAMP、常见的托管云方案)默认搭配 MySQL。
  • 复制、按时间点恢复、监控这些运维工具是硬性要求。

一个粗略的判断方法:如果你会把存储需求描述为“一个应用,一块磁盘”,SQLite 大概就够了。如果你会说“一个服务,有运维人员”,那就选 MySQL(或 PostgreSQL)。

在两者之间迁移

先用 SQLite,以后再迁移到 MySQL,是一条很多人走过的路,也是一个不错的计划。表结构稍作调整就能转换,数据也可以通过 SQLite 命令行工具的 .dump 干净地导出。你主要需要修改自增语法、日期函数,以及 MySQL 中没有直接对应的 SQLite 特有功能(形式特殊的部分索引、WITHOUT ROWID、STRICT 表)。

反方向,从 MySQL 迁移到 SQLite,比较少见但也可行,通常用于离线分析、嵌入部分数据的副本,或者测试数据。

重点是:今天选择 SQLite 并不会把你锁死。你写的 SQL 可以迁移,你的理解也一样。

下一步:SQLite 与 PostgreSQL

MySQL 是最常见的比较对象,但 PostgreSQL 是另一个常被拿来和 SQLite 比较的数据库,而那里的差异又不一样了。这就是下一页的内容。

常见问题

SQLite 和 MySQL 的主要区别是什么?

SQLite 是嵌入式数据库:就是一个文件,你的应用直接读写它,没有服务器进程。MySQL 是客户端/服务器数据库:一个独立的 mysqld 进程监听某个端口,你的应用通过网络和它通信。这一个架构上的区别,决定了两者之间几乎所有其他的取舍。

SQLite 比 MySQL 快吗?

对于单个进程执行读取和少量写入,是的:SQLite 省去了网络往返和进程间通信的开销,所以往往更快。对于大量并发写入,MySQL 轻松胜出,因为 SQLite 在数据库级别串行化写入。正确答案取决于你的工作负载,而不是抽象地比较两个引擎。

什么时候应该用 SQLite 而不是 MySQL?

嵌入式应用、移动端、桌面工具、命令行工具、本地缓存、测试,以及只有一台应用服务器的中小型网站,都适合用 SQLite。当你需要多台应用服务器访问同一个数据库、需要细粒度的用户权限,或者写入负载重到行级锁变得重要时,就该用 MySQL。

以后可以从 SQLite 迁移到 MySQL 吗?

可以,而且这是很常见的路线。两者的 SQL 方言在 CREATE TABLE、INSERT 和 SELECT 上大量重合,但你需要调整类型(INTEGER PRIMARY KEY 要改成 INT AUTO_INCREMENT)、日期函数,以及 WITHOUT ROWID、部分唯一索引这类 SQLite 特有的功能。pgloader 之类的工具和自定义的导出脚本能完成大部分工作。

Coddy 编程语言插图

和 Coddy 一起学编程

开始