在我的一台服务器上(i7 Ivy Core,32 GB RAM,Debian 6 @ 64bit,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
包装在insert语句周围(这给我200000 q / s)来加速这一点,但即使没有事务,这应该多更快。在其他(较旧的)机器上(相同的PHP版本),我没有明确的事务就达到300多个查询/秒。
原因可能是什么?我是否需要调整Sqlite或O / S?
答案 0 :(得分:34)
我在使用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
函数中,即:
iotop
,iostat
)通过关闭同步,我的SQLite3性能提高了大约三千倍:
$db = new PDO('sqlite:test.db');
$db->exec('pragma synchronous = off;');
我在两台非常相似的机器上也有两个不同的值(一个有ext4,另一个有XFS,但我不肯定这是主要原因 - 它们的负载配置文件也不同)。
顺便说一句,使用准备好的语句只是将执行速度提高一倍(从45k到110k INSERT,从3000个批量生成,因为在那个速度上30个INSERT必然会产生虚假的时间),并将最低速度从大约6提高到大约150.
所以这(使用准备好的语句)可能是一个很好的解决方案,可以在不触及文件同步的情况下改进重复操作,即,仍然明确确定数据风险级别保持不变。之后,我会尝试事务或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
当然,必须考虑适当的数据组织和索引,对于大型数据集,可能会变得更加重要。
更新:为了获得更多性能,还可以将SQLite日记放入内存pragma journal_mode=MEMORY;