SQLite学习之路⑭Wal文件相关(2021SC@SDUSC)

2021SC@SDUSC

Wal文件格式

WAL日志包括一个头和0到多个框(frames),每个框记录一个页(page)修改的内容。对数据库所有的修改都通过修改框的方式写入WAL日志。单个的WAL日志可记录多个事务。WAL日志中的内容会定期修改到数据库文件中,这个操作成为检查点。3
单个WAL日志可多次使用。也就是说,WAL可以被框占满,当检查点动作过后,其他的框又能写到原来的位置上。WAL日志总是从开始到结尾顺序增长。附加到框后面的检查总数(checksums)和计数(counters)用来确定WAL中的那些框有效,哪些框是以前检查点动作操作完成的。
WAL头有32字节长,包括以下8个32位的大端无符号整数

0: Magic number. 0x377f0682 or 0x377f0683

4: File format version. Currently 3007000

8: Database page size. Example: 1024

12: Checkpoint sequence number

16: Salt-1, random integer incremented with each checkpoint

20: Salt-2, a different random integer changing with each ckpt

24: Checksum-1 (first part of checksum for first 24 bytes of header).

28: Checksum-2 (second part of checksum for first 24 bytes of header).

在Wal.c中实现:

 struct WalIndexHdr {
   u32 iVersion;
   wal索引版本
   u32 unused; 未使用区域
    u32 iChange; 每次事务的计数器
   u8 isInit;
   u8 bigEndCksum; 判断wal中的checksum的类型是否为big-endian
   u16 szPage; 数据库page数量
   u32 mxFrame; WAL中最后一个有效帧的索引
   u32 nPage; 数据库中的页大小
   u32 aFrameCksum[2]; 日志最后一帧的checksum
   u32 aSalt[2]; 从WAL头复制两个salt值
   u32 aCksum[2];
       预写日志文件是由下列对象的一个实例来表示。    struct Wal {
    sqlite3_vfs pVfs; //用来创建pDbFd的VFS
    sqlite3_file pDbFd; //数据库文件
    sqlite3_file pWalFd; //WAL文件
    u32 iCallback; //传递给回调日志的值
    i64 mxWalSize; //复位后调整WAL到这个大小
    int nWiData; //apWiData数组的大小
    int szFirstBlock; //写入WAL文件的第一个块的大小
    uolatile u32 ** apWiData ; //指向内存中WAL索引内容
    u32 szPage ; //数据库页面大小
    i16 readLock ; //读哪个锁正在举行。 -1表示无
    u8 syncFlags ; //标志使用同步头写
    u8 exclusiveMode ; //非零,如果连接是独占模式
    u8 writeLock ; //如果真在写事务
    u8 ckptLock ; //如果真拿着一个检查站锁定
    u8 readonly; //WAL_RDWR , WAL_RDONLY ,或WAL_SHM_RDONLY
    u8 truncateOnCommit ; //真正截断WAL上提交的文件
    u8 syncHeader ; //FSYNC沃尔玛头如果为true
    u8 padToSectorBoundary //垫记录到下一个扇区
    walIndexHdr HDR ; //为当前事务wal索引头
    const char zWalName ; //WAL文件的文件名
    u32 nCkpt ; //wal检查点序列计数器\
# IFDEF SQLITE_DEBUG
    u8 LOCKERROR ; //发生锁定错误时
   # ENDIF
   } ;

校验

跟在WAL头上的是0到多个框。每个框由一个24字节的框头(frame-header)和一个页大小的页数据组成。框头是6个大端的32位无符号整数,如下:

0: Page number.

4: For commit records, the size of the database image in pages after the commit. For all other records, zero.

8: Salt-1 (copied from the header)

12: Salt-2 (copied from the header)

16: Checksum-1.

20: Checksum-2.

当一个框满足下面条件时,才被认为是有效的:
1.在框头中Salt-1和salt-2的值跟在wal头里面的一致
2.框头的最后的8个字节的校验和完全匹配一下内容的校验结果包括在WAL头上最开始的8个字节和当前框的所有内容。

如果WAL文件的开头4个字节是0x377f0683,那么校验和就使用32位的大端计算,如果是0x377f0682,则用小端计算。校验和始终存储在框头使用大端模式存储而无论使用什么模式计算的。校验和的计算方法如下:

    for i from 0 to n-1 step 2:
    s0 += x[i] + s1;
    s1 += x[i+1] + s0;
    endfor

请添加图片描述

读事务

开始一个读事务时,读者记录WAL中最后一个有效的框的索引。读者用myFrame值记录所有的子顺序读操作。新的事务能被添加到WAl中,只要读者使用原始的myFrame值并且忽略新的增加的内容它将看到一个快照。这个技术允许多事务并行读不同的数据库内容。

读算法
请添加图片描述

从数据库中读一页(假设为页P),读者首先检查WAL来确定是否包含页P,如果包含,那么页P的最后一个有效实例是在该 WAL框中。如果WAl没有包含页P,那么页P就从数据库文件中读入。
实现算法:

请添加图片描述
结束读事务:当读事务结束时,调用此方法,释放锁.
请添加图片描述
请添加图片描述

写事务:

    写事务开始前应该有个一个sqlite3WalBeginWriteTransaction(Wal *pWal)已经被调用开始了一个读事务。         

//写事务开启前必须读事务在运行 //同一时间段只有一个写事务允许进行,如果有多个写事务请求,则返回sql_busy.
//如果另一个连接开始了读事务在这个文件上,这个时候是不允许写入的。

请添加图片描述

写事务的关闭和释放写锁:
请添加图片描述

保存点

建立保存点,在事务回滚时,如果建立保存点,就会大大降低回滚代价。保存WAL_SAVEPOINT_NDATA的值到aWALData中。
请添加图片描述
如果有任何数据被写入(但不提交)到日志文件中,该函数移动写指针返回到事务的开始。此外,该函数被用来调用事务使数据到WAL每一帧。如果回调函数不返回SQLITE_OK,它不会被再次调用,并且错误代码返回给调用者。否则,如果回调函数不返回一个错误,这个函数返回SQLITE_OK。
请添加图片描述
请添加图片描述
恢复WAL索引头的状态,iframe是在客户端开始写入到数据库之前的客户端缓存。
请添加图片描述

性能问题

在一般情况下,WAL会提高SQLite的事务性能但是在某些极端况下,却会导致SQLite事务性能的下降。

1.在事务执行时间较长或者要修改的数据量达到GB级的时候,WAL文件会被占用,它会暂时阻止checkpoint的执行(checkpoint会清空WAL文件),这将导致WAL文件变得很大,增加寻址时间,最终导致读写性能的下降。

 2.当checkpoint执行的时候,会降低当时的读写性能,因此,WAL可能会导致周期性的性能下降。

检查点
我们最终会将追加在Wal文件中的说有事务写回到初始的数据库中,将Wal文件中事务写回到数据库中被叫做“检查点”。
回滚和预写另一个不同是回滚日志有两个原语操作:读和写,而Wal有三个原语操作:读,写,检查点。默认的,Sqlite 当达到1000页这个阀值是就会自动的设置一个检查点。
Sqllite Wal自动设置检查点编译时间 可以指定不同的默认值。使用Wal的程序不必为了使这些检查点产生而做任何事。但他们如果想设置检查点,可以调整自动设置检查点的阀值,他们可以关闭自动设置检查点在空闲时或在只有一个线程或进程时。
sqlite3WalCheckpoint()用来实现sqlite3_wal_checkpoint()和相关接口。获取一个checkpoint锁,然后尽可能多的回填信息到数据中。
请添加图片描述
当用SQLITE_BUSY意味着另一个线程或进程已经在运行checkpoint或则进行恢复。如果无法获得写锁,那么一个被动的检查点将代替运行。由于checkpoint无法加载锁,是毫无意义的阻塞等待任何读者,假设没有其他错误发生时,该函数将返回SQLITE_BUSY给调用者。
请添加图片描述

读取 Wal-index 头和将日志文件中的数据拷贝到数据文件中。
请添加图片描述

释放锁和调用结束事务。请添加图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值