WAL是SQLite3.7.0版本引入的一个重大改进。SQLite官网宣称在很多使用场景下,WAL模型的性能都要好于默认的DELETE模式。下面将针对几个主要场景对WAL性能做测试,测试的硬件与xxx保持一致。
纯写场景
1) 测试说明
测试简单更新事务在WAL模式下与DELETE日志模式性能对比,测试语句形如:update user set c1=c1+1 where id=xxx,id为主键,串行执行更新。通过执行1w个更新事务,统计程序运行时间。
2) 测试结果
测试时间结果以毫秒计算,TPS是每秒执行的事务数,通过事务数与时间相比获取结果。
WAL模式 |
DELETE模式 |
|
第一轮 |
55176 |
159196 |
第二轮 |
54442 |
164341 |
第三轮 |
56171 |
162329 |
第四轮 |
56167 |
166117 |
第五轮 |
55969 |
163139 |
平均值 |
55581 |
163024 |
TPS |
179.9 |
61.3 |
3) 结果分析
从测试结果来看,WAL日志模式下,纯写性能要远远好于DELETE模式,前者大概是后者的3倍。这个性能差异主要源于每次事务提交,WAL模式只需要将更新的日志写入磁盘即可,而DELETE模式修改过程中,首先需要将原始数据页拷贝到日志文件中,并进行fsync;然后再将修改页写入磁盘,同时也需要fsync,确保数据落盘,并且还要将日志文件清除。因此,写事务在WAL模式下,只需要一次fsync,并且是顺序写;而DELETE模式下,需要至少两次fsync(日志,数据),并且更新的数据可能离散分布在多个page中,因此可能需要多个fsync来完成。众所周知,fsync调用是非常耗时的,导致DELTE模式下写性能不如WAL模式。
纯读场景
1) 测试说明
测试WAL模式下,纯读性能数据,测试语句形如:“select * from user where id = xxx”,id为主键,对比WAL模式与DELETE模式下读性能。每个线程执行10w次查询,统计最终执行时间。QPS为每秒执行的查询数
2) 测试结果
WAL模式 |
DELETE模式 |
|||
2线程 |
4线程 |
2线程 |
4线程 |
|
第一轮 |
2847 |
5050 |
4124 |
8168 |
第二轮 |
2730 |
5795 |
3833 |
8074 |
第三轮 |
2688 |
5675 |
3819 |
8077 |
第四轮 |
2959 |
5894 |
3881 |
7787 |
第五轮 |
2836 |
5924 |
3873 |
7958 |
平均值 |
2817 |
5667 |
3906 |
8012 |
QPS |
7w |
7w |
5.1w |
5w |
3) 结果分析
从结果来看,WAL模式下,读性能要好于DELETE模式,大概提高了将近30%。这说明开启WAL模式下,对读性能也有提高。目前我还没有找到性能提升的原因,后续仔细阅读源码后,再做分析。
读写场景
1) 测试说明
在WAL模式下,事务提交时实际是以append方式写wal日志文件,因此读写不冲突;而DELETE模式下,写事务需要写DB(缓存,文件),与读事务读DB(缓存,文件)冲突,因此只能串行读写,通过前面的测试可知,WAL模式下,每秒读写事务数比例大概是500:1;DELETE模式下,每秒读写事务数比例大概是800:1,这里的测试不打算比较读写同时存在时两种模式的对比,而是单独测试WAL,以读写比为500:1场景,看看读写的QPS和TPS是否与单独执行有差异。测试中,读写线程都并发读写同一张表,读采用5个线程,主键查询,每个线程执行100w个读;写采用1个线程,主键更新,执行1w次写。测试时间的单位为毫秒,QPS和TPS分别为每秒的读和写,QPS=500w/时间,TPS=100w/时间。
2) 测试结果
WAL模式 |
||
第一轮 |
67543 |
|
第二轮 |
66812 |
|
第三轮 |
72946 |
|
第四轮 |
78844 |
|
第五轮 |
74501 |
|
平均值 |
72129 |
|
QPS|TPS |
6.94w |
138 |
3) 结果分析
通过读写比500:1的测试模型,基本保证了读写是同时完成的,从另外一方面来说,就是读写是充分竞争的。从测试结果来看,WAL模式下,读的QPS为6.94w,与单独测试读7w的性能几乎无差异;写的TPS为138, 较单独测试写的179略有下降,这可能与设置的读写比有关系。通过测试结果可以充分说明,WAL模式下,读写是充分并发的,并且几乎没有性能损耗。
批量导入场景
1) 测试说明
这个测试主要了了解WAL模式和DELETE模式下,导入数据性能对比。创建2个表,每个表导入10w条记录,总共20w记录,观察导入的时间。
2) 测试结果
WAL模式 |
DELETE模式 |
|
第一轮 |
28019 |
18036 |
第二轮 |
28500 |
17959 |
第三轮 |
28575 |
17578 |
第四轮 |
29078 |
18076 |
第五轮 |
29184 |
17989 |
平均值 |
28671 |
17909 |
TPS |
6975 |
11167 |
3) 结果分析
从结果来看,DELETE模式的批量导入性能要好于WAL模式,这主要源于,DELETE模式记录的是old-page,而insert操作没有old-page,所以无需记录日志,而WAL记录的是修改页,所以代价比DELETE模式高。
wal文件大小测试
1) 测试说明
wal模式下,日志采用单独的wal文件,事务将更新写入wal文件,事务执行过程中,不断写wal文件,直到提交后,才有机会做检查点,控制wal文件不继续膨胀。由于端设备,空间资源也非常稀缺,我们来看看wal 模式和delete模式下日志文件对空间的损耗。WAL模式下, wal_autocheckpoint用来控制做检查点时机,这个参数对WAL文件影响至关重要,测试也围绕、这个参数展开。
2) 测试结果
场景 |
日志文件容量 |
|
WAL模式 |
DELETE模式 |
|
批量导入(大事务) |
200M |
4k |
主键更新(1w行事务) wal_autocheckpoint=1000 |
20M |
19M |
单条更新(单行事务) 更新1w行 wal_autocheckpoint=1000 |
1M |
4k |
单条更新事务 更新1w行 wal_autocheckpoint=4000 |
4M |
4k |
单条更新事务 更新1w行 wal_autocheckpoint=0 |
42M |
无 |
3) 结果分析
从测试结果来看,批量导入大事务时,测试中DB文件200M,产生的WAL日志文件也是200M,由于是INSERT操作,DELETE模式下,日志文件记录原始数据页,所以依然为4k,这4k应该是控制信息。对于大事务更新操作,WAL模式和DELETE模式产生的日志量基本相当,只不过一个记录修改后的数据,一个记录修改前的数据。对于单行事务,DELETE模式的日志文件基本没有增长,而WAL模式下,日志文件与wal_autocheckpoint参数相关,默认情况下该参数值为1000个page,每个page_size为1k,因此大约为1M;调整为4000后,日志文件也随之膨胀到4M;设置为0,亦即关闭检查点后,更新1w条记录,将使wal膨胀到42M。因此,无论是WAL模式还是DELETE模式,事务大小都对日志文件大小有影响,对于WAL模式,为了控制日志文件大小,wal_autocheckpoint参数非常重要。