在我的一台服务器(i7 Ivy Core、32 GB RAM、Debian 6 @ 64 位、PHP 5.4.10)上,我使用 SQLite 进行插入时遇到了极其缓慢的问题。以下测试程序仅报告 2.2 次插入/秒(插入 30 行需要 14 秒)。
unlink("test.db");
$db = new PDO('sqlite:test.db');
$db->exec("CREATE TABLE test (dummy INT)");
$count = 30;
$t = microtime(true);
for ($i=0; $i<$count; $i++) {
$db->exec("INSERT INTO test VALUES ($i)")
or die("SQLite error: ".$db->errorInfo()[2]."\n");
}
$elapsed = microtime(true)-$t;
echo sprintf("%d inserts in %.3f secs (%.1f q/s)\n",
$count, $elapsed, $count/$elapsed);
输出:
$ time php test.php
30 inserts in 13.911 secs (2.2 q/s)
real 0m14.634s
user 0m0.004s
sys 0m0.016s
我知道我可以通过在插入语句周围包装 BEGIN
/END
来加速这个过程(这给了我 200000 q/s),但即使没有交易,这也应该是快得多。在其他(较旧的)机器(相同的 PHP 版本)上,我在没有显式事务的情况下达到 300+ 查询/秒。
这可能是什么原因造成的?我是否必须调整 Sqlite 或 O/S?
最佳答案
我使用 strace -C -tt
在 Linux 64 位机器上进行了类似的测试,以了解 SQLite3 在哪里花费时间。
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
99.03 0.004000 32 124 fsync
0.64 0.000026 0 222 mprotect
0.32 0.000013 0 216 munmap
明显的延迟是在fsync
函数中,也就是:
- 可配置
- 取决于一般磁盘 I/O(查看
iotop
、iostat
) - 严重依赖 IOSS(因此,文件系统和磁盘分配 - 您可能会在 ext3 上获得一个值,在 xfs 上获得另一个值,在 btrfs 上获得第三个值)
- 当然,间接地取决于底层硬件及其怪癖或调整。
通过关闭同步,我的 SQLite3 性能提高了大约三千倍:
$db = new PDO('sqlite:test.db');
$db->exec('pragma synchronous = off;');
我在两台非常相似的机器上也有两个不同的值(一台有 ext4,另一台有 XFS,但我不确定这是主要原因 - 它们的负载配置文件也不同)。
顺便说一句,使用准备好的语句在最快的水平上几乎加倍了执行速度(从 45k 到 110k INSERT,每批 3000,因为在这个速度下 30 INSERT 必然会产生虚假的计时), 并将最低速度从大约 6 提高到大约 150。
因此,这(使用准备好的语句)可能是一个很好的解决方案,可以在不影响文件同步的情况下改进重复操作,即,同时仍然可证明数据风险级别保持不变。之后,我会根据数据中断的风险和值(value)尝试事务或 fsync(甚至可能是内存日志)。
从头开始设计系统时,肯定建议对不同的 FS 进行一些测试。
不同文件系统测试(同一台机器)
ext4 (acl,user_xattr,data=order) 5.5 queries/s
using transactions 170 queries/s
disabling fsync 16000 queries/s
using transactions and disabling fsync 47200 queries/s
在临时文件系统 上,fsync
很便宜,因此关闭它不会产生什么好处。大部分时间都花在守卫上,所以交易是关键。
tmpfs 13700 queries/s
disabling fsync 15350 queries/s
enabling transactions 47900 queries/s
using transactions and disabling fsync 48200 queries/s
当然,必须考虑适当的数据组织和索引,对于大型数据集,这可能更为重要。
更新:为了获得更多性能,还可以使用 pragma journal_mode=MEMORY;
此外,您可以告诉 ext3/4 不要费心在 SQLite 数据库上更新 atime(不过这在很大程度上取决于实现)。你可以尝试在数据库所在的文件系统中加入noatime
,如果可以的话,你可以把它放到/etc/fstab
中(你也可以使用 relatime
而不是更极端的noatime
:
sudo mount /var -o remount,noatime
关于php - 为什么 SQLite 在特定机器上这么慢(~2 q/s)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15413575/