Documentation ¶
Overview ¶
Package wal 实现预写日志(Write-Ahead-Log)
Log 致力于对外提供高性能读、写、截断日志服务,这些操作通过内部加锁保证外部 并发调用不会出现竞态条件,需要注意的是:Open 操作会有竞态问题,请避免并发 打开日志,或者外部主动加锁避免竞态条件。
Log 提供两种数据持久化级别:
- 同步:每次写日志都会将数据同步到磁盘,一致性好,但性能差。
- 异步:日志会先写入内存缓冲中,后台协程周期同步,一致性稍差,但性能优异。
Log 允许通过 Options 自定义配置选项,配置包括权限、容量、持久化级别、同步 周期。下面简要介绍其中涉及到的内部实现概念:
- Block:描述单条日志记录的逻辑概念。用于定位日志记录边界、记录完整性校验、 限制 Log 下最大日志数量(通过maxBlockCapacityInWAL)。
- Segment:日志数据文件,由零或多个 Block 组成。一个 Log 中通常有多个 Segment 这是出于变更 Segment 文件时写时复制(Copy-On-Write)的性能考虑,这意味着 Segment 保证原子写入文件。Segment 允许自定义容量(可通过 Options 配置), 但这不意味只有当一个数据文件写满后才会创建下一个,Segment 会保证 Block完整地 存在一个数据文件中。
- Log:先行日志实体,由多个 Segment 组成。Log 将 Segment 维护在指定目录 下(可通过 Options 配置)。内部通过 LRU 缓存最近读命中的 Segment,同样的, 可以通过 Options 配置 LRU 缓存大小。
TODO(Trino):日志损坏后没有修复机制,如果出现内容被篡改、损坏会导致日志不可用
Index ¶
- type Log
- type Options
- func (opts *Options) SetDataPerm(dataPerm os.FileMode) *Options
- func (opts *Options) SetDirPerm(dirPerm os.FileMode) *Options
- func (opts *Options) SetNoSync() *Options
- func (opts *Options) SetSegmentCacheSize(segmentCacheCapacity int) *Options
- func (opts *Options) SetSegmentCapacity(segmentCapacity int64) *Options
- func (opts *Options) SetSyncInterval(syncInterval time.Duration) *Options
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Log ¶
type Log struct {
// contains filtered or unexported fields
}
Log 预写日志
func Open ¶
Open 打开wal
为了避免内存泄漏,结束使用后需要显示调用 Log.Close 关闭
参数:
- dirPath 存放 Log 日志的目录
- opts 配置选项,不传配置将设置成默认值,见 NewOptions
返回值:
- wal日志(*Log)
- errs 过程中出现的错误,类型是 *errs.KvErr
异常:
- errs.NewFlockFileErr Open 指定的dir已经被其他 Log 实例打开, 此时外部应该检查这个错误并且重试其他目录路径;有小概率是因为初始化 Log 实例过程中出现其他错误(会在日志中输出)之后释放目录锁导致
- errs.NewInvalidParamErr 传入 opts 校验未通过,日志中有描述具体非法参数; 也有可能是传入的目录路径不是一个目录
- errs.NewMkdirErr 配置中目录不存在,且尝试创建目录失败
- errs.NewFileStatErr 获取目录文件信息失败
- errs.NewOpenFileErr 打开目录锁文件、数据失败
- errs.NewWalkDirErr 遍历日志目录失败,具体原因请参考日志
- errs.NewParseIntErr 解析 segment 文件名失败
- errs.NewReadFileErr 读取 segment 文件失败
- errs.NewCorruptErr segment 数据被篡改、损坏
- errs.NewBackgroundErr 这个错误不会抛出,但会出现在日志中,表示后台协程执行出错
func (*Log) Close ¶
Close 关闭 Log,释放资源
返回值:
- errs 过程中出现的错误,类型是 *errs.KvErr
异常:
- errs.NewFileClosedErr 在 Log 或 segment 已经关闭后再次尝试关闭
- errs.NewFlockFileErr 释放目录锁失败
func (*Log) Len ¶
Len 获取 Log 日志中存储的日志数量,即 block 数量
返回值:
- number 日志数量
- errs 过程中出现的错误,类型是 *errs.KvErr
异常:
- errs.NewFileClosedErr 在wal已经关闭的情况下调用
- errs.NewCorruptErr 在wal数据已经被破坏的情况下调用
- errs.NewBackgroundErr 在wal后台协程执行失败的情况下调用
func (*Log) Read ¶
Read 读指定范围内的日志
当出现日志循环时,firstBlockIdx 比合法的 idx 大 此时的读取范围是 [0, idx) & [firstBlockIdx, maxBlockCapacityInWAL] 截断后 firstBlockIdx 会发生改变
参数:
- idx 指定读取 firstBlockIdx ~ idx 范围内的日志记录
返回值:
- blockIdxToData 查询到的日志数据映射,blockIdx -> data
- errs 过程中出现的错误,类型是 *errs.KvErr
异常:
- errs.NewFileClosedErr 在wal已经关闭的情况下读取
- errs.NewCorruptErr 在wal数据已经被破坏的情况下读取
- errs.NewBackgroundErr 在wal后台协程执行失败的情况下读取
- errs.NewNotFoundErr 传入idx不在有效范围内
- errs.NewOpenFileErr 打开目录锁文件、数据失败
- errs.NewReadFileErr 读取 segment 文件失败
- errs.NewCorruptErr 在wal数据已经被破坏的情况下写入
func (*Log) Sync ¶
Sync 同步内存中的日志数据到磁盘中
返回值:
- errs 过程中出现的错误,类型是 *errs.KvErr
异常:
- errs.NewFileClosedErr 在 Log、segment 已经关闭的情况下同步
- errs.NewCorruptErr 在wal数据已经被破坏的情况下同步
- errs.NewBackgroundErr 在wal后台协程执行失败的情况下同步
- errs.NewCreateTempFileErr 创建临时文件失败
- errs.NewSeekFileErr 重定位 segment 文件偏移量失败
- errs.NewCopyFileErr 拷贝 segment 文件内容失败
- errs.NewCloseFileErr 关闭 segment 文件失败
- errs.NewWriteFileErr 写入 segment 文件失败
- errs.NewSyncFileErr 同步 segment 文件到磁盘失败
func (*Log) Truncate ¶
Truncate 截断指定范围内的日志
当出现日志循环时,firstBlockIdx 比指定的 idx 大 此时的截断范围是 [0, idx) & [firstBlockIdx, maxBlockCapacityInWAL] 截断后 firstBlockIdx 会发生改变
参数:
- idx 指定截断 firstBlockIdx ~ idx 范围内的日志记录
返回值:
- errs 过程中出现的错误,类型是 *errs.KvErr
异常:
- errs.NewFileClosedErr 在wal已经关闭的情况下写入
- errs.NewCorruptErr 在wal数据已经被破坏的情况下写入
- errs.NewBackgroundErr 在wal后台协程执行失败的情况下写入
- errs.NewInvalidParamErr 单次写入数据超过 segment 文件容量或写入空数据
- errs.NewRenameFileErr 重命名 segment 文件失败
- errs.NewCreateTempFileErr 创建临时文件失败
- errs.NewSeekFileErr 重定位 segment 文件偏移量失败
- errs.NewCopyFileErr 拷贝 segment 文件内容失败
- errs.NewCloseFileErr 关闭 segment 文件失败
- errs.NewWriteFileErr 写入 segment 文件失败
- errs.NewSyncFileErr 同步 segment 文件到磁盘失败
- errs.NewOpenFileErr 打开 segment 文件失败
- errs.NewRemoveFileErr 移除 segment 文件失败
示例:
// 循环执行完成后,Log 中 firstBlockIdx 为0,blockId 的范围是 [0, 100] for i := 0; i < 100; i++ { _, errs := wal.Write([]byte{1, 2, 3}) } // 截断 [0, 50) 范围内的 block,此时 Log 中维护的 block 范围是 [50, 100],firstBlockIdx 是50 wal.truncate(50) // 继续上面的例子,假设wal能够容纳的最大block数量是100,那么我们继续向wal中写的日志blockId会从0开始循环 // 循环执行完成后,wal中 firstBlockIdx 为50,blockId的范围是[0, 20] & [50, 100] for i := 0; i < 20; i++ { _, errs := wal.Write([]byte{1, 2, 3}) } // 截断 [0, 10) & [50, 100] 范围内的 block,此时 Log 中维护的 block 范围是 [10, 20],firstBlockIdx 是10 wal.truncate(10)
func (*Log) Write ¶
Write 写入日志数据
参数:
- data 日志数据,类型是字节数组
返回值:
- blockIdx block 索引,该值用于 Read 和 Truncate
- errs 过程中出现的错误,类型是 *errs.KvErr
异常:
- errs.NewWalFullErr Log 日志文件夹下存储内容已满,使用者应该主动检查该类型错误 并在 wal.Truncate 后重新尝试写入,此时 Log 不会关闭,因此对 Log 的操作是安全的
- errs.NewFileClosedErr 在wal已经关闭的情况下写入
- errs.NewCorruptErr 在wal数据已经被破坏的情况下写入
- errs.NewBackgroundErr 在wal后台协程执行失败的情况下写入
- errs.NewInvalidParamErr 单次写入数据超过 segment 文件容量或写入空数据
- errs.NewRenameFileErr 重命名 segment 文件失败
- errs.NewParseIntErr 解析 segment 文件名失败
- errs.NewCreateTempFileErr 创建临时文件失败
- errs.NewSeekFileErr 重定位 segment 文件偏移量失败
- errs.NewCopyFileErr 拷贝 segment 文件内容失败
- errs.NewCloseFileErr 关闭 segment 文件失败
- errs.NewWriteFileErr 写入 segment 文件失败
- errs.NewSyncFileErr 同步 segment 文件到磁盘失败
type Options ¶
type Options struct {
// contains filtered or unexported fields
}
Options 日志配置选项
func (*Options) SetDataPerm ¶
SetDataPerm 设置 segment 数据文件权限位
该配置在创建新 segment 时会用到,已经存在的 segment 不会受到影响
参数:
- dataPerm segment 数据文件权限位
返回值:
- 接收器wal配置选项(*options)
func (*Options) SetDirPerm ¶
SetDirPerm 设置 Log 日志目录权限位
如果在启动 Log 时发现指定dirPath下不存在wal目录,该配置会在新建目录时用到
参数:
- dirPerm Log 目录权限位
返回值:
- 接收器 Log 配置选项(*options)
func (*Options) SetNoSync ¶
SetNoSync 设置不要每次写入都持久化到磁盘
当设置为true时,刷盘时机有
- 外部主动调用 wal.Sync
- 当前 segment 文件写满时自动持久化数据到磁盘
- 后台协程每隔 syncInterval 毫秒周期刷盘
通常情况下设置 noSync 选项后性能会有可观提升
返回值:
- 接收器 Log 配置选项(*options)
func (*Options) SetSegmentCacheSize ¶
SetSegmentCacheSize 设置wal中的segment最大缓存数量
segment 缓存只有在读取日志内容时生效
参数:
- segmentCacheCapacity LRU 中的 segment 最大缓存数量
返回值:
- 接收器 Log 配置选项(*options)
func (*Options) SetSegmentCapacity ¶
SetSegmentCapacity 设置 segment 数据文件最大存储容量
segment 文件在写入时通常不会达到文件最大存储容量就开始写入下一个 segment 文件 这是因为写入 segment 的最细粒度是 block,单个 block 无法跨 segment 存储
参数:
- segmentCapacity segment 数据文件最大存储容量
返回值:
- 接收器 Log 配置选项(*options)