数据库速度比较

注意:这份文件非常非常旧。它描述了 SQLite、MySQL 和 PostgreSQL 的古老版本之间的速度比较。

这里的数字已经变得毫无意义。此页面仅作为历史文物保留。

执行摘要

运行了一系列测试来衡量 SQLite 2.7.6、PostgreSQL 7.1.3 和 MySQL 3.23.41 的相对性能。以下是从这些实验得出的一般结论:

此处显示的结果带有以下警告:

测试环境

用于这些测试的平台是一个 1.6GHz 的 Athlon,具有 1GB 或内存和一个 IDE 磁盘驱动器。操作系统是带有标准内核的 RedHat Linux 7.2。

使用的 PostgreSQL 和 MySQL 服务器是 RedHat 7.2 上默认提供的。(PostgreSQL 版本 7.1.3 和 MySQL 版本 3.23.41。)没有对这些引擎进行调整。请特别注意 RedHat 7.2 上的默认 MySQL 配置不支持事务。不必支持事务为 MySQL 提供了很大的速度优势,但 SQLite 仍然能够在大多数测试中保持优势。

我被告知 RedHat 7.3 中的默认 PostgreSQL 配置过于保守(它被设计为在具有 8MB RAM 的机器上工作)并且通过一些知识渊博的配置调整可以使 PostgreSQL 运行得更快。Matt Sergeant 报告说他已经调整了他的 PostgreSQL 安装并重新运行如下所示的测试。他的结果表明 PostgreSQL 和 MySQL 的运行速度大致相同。对于 Matt 的结果,请访问

http://www.sergeant.org/sqlite_vs_pgsync.html

SQLite 在网站上显示的相同配置中进行了测试。它是使用 -O6 优化和 -DNDEBUG=1 开关编译的,该开关禁用了 SQLite 代码中的许多“assert()”语句。-DNDEBUG=1 编译器选项大致使 SQLite 的速度加倍。

所有测试都在一台静止的机器上进行。一个简单的 Tcl 脚本用于生成和运行所有测试。可以在文件tools/speedtest.tcl的 SQLite 源代码树中找到此 Tcl 脚本的副本。

所有测试报告的时间代表以秒为单位的挂钟时间。为 SQLite 报告了两个单独的时间值。第一个值是针对 SQLite 的默认配置,并启用了全磁盘同步。开启同步后,SQLite 执行一个fsync()在关键点进行系统调用(或等效),以确保关键数据已实际写入磁盘驱动器表面。如果操作系统崩溃或计算机在数据库更新过程中意外断电,则必须进行同步以保证数据库的完整性。第二次报告 SQLite 是在同步关闭时。关闭同步后,SQLite 有时会快得多,但存在操作系统崩溃或意外电源故障可能损坏数据库的风险。一般来说,同步 SQLite 时间用于与 PostgreSQL(也是同步的)进行比较,异步 SQLite 时间用于与异步 MySQL 引擎进行比较。

测试 1:1000 个 INSERT

创建表 t1(a 整数,b 整数,c VARCHAR(100));
INSERT INTO t1 VALUES(1,13153,'一万三千一百五十三');
INSERT INTO t1 VALUES(2,75560,'七万五千五百六十');
... 省略 995 行
INSERT INTO t1 VALUES(998,66289,'66289');
INSERT INTO t1 VALUES(999,24322,'24322');
INSERT INTO t1 VALUES(1000,94142,'94142');
PostgreSQL:   4.373
MySQL:   0.114
SQLite 2.7.6:   13.061
SQLite 2.7.6(不同步):   0.223

因为它没有中央服务器来协调访问,所以 SQLite 必须关闭并重新打开数据库文件,从而使每个事务的缓存失效。在此测试中,每个 SQL 语句都是一个单独的事务,因此必须打开和关闭数据库文件,并且必须刷新缓存 1000 次。尽管如此,SQLite 的异步版本仍然几乎和 MySQL 一样快。但是,请注意同步版本要慢得多。SQLite 在每个同步事务之后调用fsync()以确保所有数据在继续之前安全地位于磁盘表面。对于同步测试中 13 秒的大部分时间,SQLite 处于空闲状态,等待磁盘 I/O 完成。

测试 2:一个事务中有 25000 个 INSERT

开始;
创建表 t2(a 整数,b 整数,c VARCHAR(100));
INSERT INTO t2 VALUES(1,59672,'五万九千六百七十二');
... 省略 24997 行
INSERT INTO t2 VALUES(24999,89569,'八万九千五百六十九');
INSERT INTO t2 VALUES(25000,94666,'九万四千六百六十六');
犯罪;
PostgreSQL:   4.900
MySQL:   2.184
SQLite 2.7.6:   0.914
SQLite 2.7.6(不同步):   0.757

当所有 INSERT 都放入事务中时,SQLite 不再需要关闭并重新打开数据库或在每个语句之间使其缓存无效。直到最后它也不必执行任何 fsync()。当以这种方式摆脱束缚时,SQLite 比 PostgreSQL 和 MySQL 快得多。

测试 3:将 25000 条 INSERT 插入索引表

开始;
创建表 t3(a 整数,b 整数,c VARCHAR(100));
在 t3(c) 上创建索引 i3;
... 省略 24998 行
INSERT INTO t3 VALUES(24999,88509,'八万八千五百九');
INSERT INTO t3 VALUES(25000,84791,'八万四千七百九十一');
犯罪;
PostgreSQL:   8.175
MySQL:   3.197
SQLite 2.7.6:   1.555
SQLite 2.7.6(不同步):   1.402

有报告称 SQLite 在索引表上的表现不佳。最近添加了此测试以反驳这些谣言。诚然,SQLite 在创建新索引条目时不如其他引擎快(请参阅下面的测试 6),但它的整体速度仍然更好。

测试 4:100 个没有索引的 SELECT

开始;
从 t2 中选择计数 (*)、平均值 (b),其中 b>=0 且 b<1000;
从 t2 中选择计数 (*)、平均值 (b),其中 b>=100 且 b<1100;
... 省略 96 行
SELECT count(*), avg(b) FROM t2 WHERE b>=9800 AND b<10800;
从 t2 中选择计数 (*)、平均值 (b),其中 b>=9900 且 b<10900;
犯罪;
PostgreSQL:   3.629
MySQL:   2.760
SQLite 2.7.6:   2.494
SQLite 2.7.6(不同步):   2.526

此测试在没有索引的 25000 条目表上执行 100 次查询,因此需要全表扫描。在此测试中,以前版本的 SQLite 比 PostgreSQL 和 MySQL 慢,但最近的性能增强提高了它的速度,因此它现在是组中最快的。

测试 5:字符串比较中的 100 个 SELECT

开始;
SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%one%';
SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%two%';
... 省略 96 行
SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%ninety nine%';
SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%one hundred%';
犯罪;
PostgreSQL:   13.409
MySQL:   4.640
SQLite 2.7.6:   3.362
SQLite 2.7.6(不同步):   3.372

该测试仍然进行 100 次全表扫描,但它使用字符串比较而不是数字比较。SQLite 在这里比 PostgreSQL 快三倍以上,比 MySQL 快大约 30%。

测试 6:创建索引

在 t2(a) 上创建索引 i2a;
在 t2(b) 上创建索引 i2b;
PostgreSQL:   0.381
MySQL:   0.318
SQLite 2.7.6:   0.777
SQLite 2.7.6(不同步):   0.659

SQLite 在创建新索引时速度较慢。这不是一个大问题(因为不经常创建新索引),但这是正在努力的事情。希望未来版本的 SQLite 能在这方面做得更好。

测试 7:5000 个带索引的 SELECT

从 t2 中选择计数 (*)、平均值 (b),其中 b>=0 且 b<100;
从 t2 中选择计数 (*)、平均值 (b),其中 b>=100 且 b<200;
从 t2 中选择计数 (*)、平均值 (b),其中 b>=200 且 b<300;
...省略了 4994 行
SELECT count(*), avg(b) FROM t2 WHERE b>=499700 AND b<499800;
从 t2 中选择 count(*)、avg(b),其中 b>=499800 且 b<499900;
从 t2 中选择计数 (*)、平均值 (b),其中 b>=499900 且 b<500000;
PostgreSQL:   4.614
MySQL:   1.270
SQLite 2.7.6:   1.121
SQLite 2.7.6(不同步):   1.162

当有索引可供使用时,所有三个数据库引擎都运行得更快。但 SQLite 仍然是最快的。

测试 8:1000 个没有索引的更新

开始;
UPDATE t1 SET b=b*2 WHERE a>=0 AND a<10;
UPDATE t1 SET b=b*2 WHERE a>=10 AND a<20;
... 省略了 996 行
UPDATE t1 SET b=b*2 WHERE a>=9980 AND a<9990;
UPDATE t1 SET b=b*2 WHERE a>=9990 AND a<10000;
犯罪;
PostgreSQL:   1.739
MySQL:   8.410
SQLite 2.7.6:   0.637
SQLite 2.7.6(不同步):   0.638

对于这个特定的 UPDATE 测试,MySQL 始终比 PostgreSQL 和 SQLite 慢五到十倍。我不知道为什么。MySQL 通常是一个非常快的引擎。也许这个问题在以后的 MySQL 版本中已经得到解决。

测试 9:25000 个带有索引的更新

开始;
更新 t2 设置 b=468026,其中 a=1;
更新 t2 设置 b=121928,其中 a=2;
...省略了 24996 行
UPDATE t2 SET b=35065 WHERE a=24999;
更新 t2 SET b=347393 其中 a=25000;
犯罪;
PostgreSQL:   18.797
MySQL:   8.134
SQLite 2.7.6:   3.520
SQLite 2.7.6(不同步):   3.104

在最近的 2.7.0 版本中,SQLite 在此测试中的运行速度与 MySQL 大致相同。但最近对 SQLite 的优化使更新速度提高了一倍以上。

测试 10:25000 个带有索引的文本更新

开始;
UPDATE t2 SET c='148382' WHERE a=1;
UPDATE t2 SET c='三十六万六千五百二' WHERE a=2;
... 省略了 24996 行
UPDATE t2 SET c='three83999' WHERE a=24999;
UPDATE t2 SET c='256830' WHERE a=25000;
犯罪;
PostgreSQL:   48.133
MySQL:   6.982
SQLite 2.7.6:   2.408
SQLite 2.7.6(不同步):   1.725

同样,SQLite 2.7.0 版的运行速度与 MySQL 大致相同。但现在 2.7.6 版本比 MySQL 快两倍以上,比 PostgreSQL 快二十多倍。

公平地说,PostgreSQL 在此测试中开始表现不佳。知识渊博的管理员可能能够通过稍微调整和调整服务器来让 PostgreSQL 在这里运行得更快。

测试 11:来自 SELECT 的 INSERT

开始;
插入 t1 从 t2 中选择 b、a、c;
插入 t2 从 t1 中选择 b、a、c;
犯罪;
PostgreSQL:   61.364
MySQL:   1.537
SQLite 2.7.6:   2.787
SQLite 2.7.6(不同步):   1.599

在此测试中,异步 SQLite 仅比 MySQL 慢一点。(MySQL 似乎特别擅长 INSERT...SELECT 语句。)PostgreSQL 引擎仍在颠簸 - 它使用的 61 秒中的大部分时间都花在等待磁盘 I/O 上。

测试 12:没有索引的 DELETE

从 t2 WHERE c LIKE '%50%' 中删除;
PostgreSQL:   1.509
MySQL:   0.975
SQLite 2.7.6:   4.004
SQLite 2.7.6(不同步):   0.560

SQLite 的同步版本是本次测试组中最慢的,但异步版本是最快的。不同之处在于执行 fsync() 所需的额外时间。

测试 13:使用索引删除

从 t2 中删除 a>10 且 a<20000;
PostgreSQL:   1.316
MySQL:   2.262
SQLite 2.7.6:   2.068
SQLite 2.7.6(不同步):   0.752

这个测试很重要,因为它是少数几个 PostgreSQL 比 MySQL 更快的测试之一。然而,异步 SQLite 比其他两个都快。

测试 14:大 DELETE 之后的大 INSERT

插入 t2 从 t1 选择 *;
PostgreSQL:   13.168
MySQL:   1.815
SQLite 2.7.6:   3.210
SQLite 2.7.6(不同步):   1.485

某些旧版本的 SQLite(2.4.0 之前的版本)在执行一系列 DELETE 后跟新的 INSERT 后性能会下降。如该测试所示,问题现已解决。

测试 15:一个大的 DELETE 后面跟着许多小的 INSERT

开始;
从 t1 中删除;
INSERT INTO t1 VALUES(1,10719,'一万七百十九');
... 省略 11997 行
INSERT INTO t1 VALUES(11999,72836,'72836');
INSERT INTO t1 VALUES(12000,64231,'64231');
犯罪;
PostgreSQL:   4.556
MySQL:   1.704
SQLite 2.7.6:   0.618
SQLite 2.7.6(不同步):   0.406

SQLite 非常擅长在事务中执行 INSERT,这可能解释了为什么它在本次测试中比其他数据库快得多。

测试 16:删除表

删除表 t1;
删除表 t2;
删除表 t3;
PostgreSQL:   0.135
MySQL:   0.015
SQLite 2.7.6:   0.939
SQLite 2.7.6(不同步):   0.254

在删除表方面,SQLite 比其他数据库慢。这可能是因为当 SQLite 删除一个表时,它必须遍历并删除处理该表的数据库文件中的记录。另一方面,MySQL 和 PostgreSQL 使用单独的文件来表示每个表,因此它们可以通过删除文件来删除表,这样速度要快得多。

另一方面,删除表并不是一个很常见的操作,所以如果 SQLite 需要更长的时间,那也不是什么大问题。