# CommonLoggerSolution **Repository Path**: eric_ds/CommonLoggerSolution ## Basic Information - **Project Name**: CommonLoggerSolution - **Description**: 通用日志解决方案 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2019-05-22 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 文件日志解决方案 [TOC] ## 背景 部分的框架或者业务系统需要使用“日志”的形式在非数据库场景下保存一些额外的数据。这些数据随着程序的运行持续写入,也会被持续的读取,而已经被读取过的日志就不再需要,也就是文件上的空间可以复用。因此引出如下的一种应用场景: 1. 随着程序运行过程,不断向日志文件写入日志内容。 2. 日志记录对应的业务数据如果失效,则对应的日志记录所在空间可以被回收复用。 3. 业务数据的失效取决于实际运行,因此日志文件会出现空洞,也就是部分可以用,部分不能用。为了避免在连续写入时处理空洞空间,程序会定期整理日志文件,并且将读取到的内容形成更"致密"的归档快照数据,写入快照文件。 4. 已经被读取的日志空间,可以复用。因此日志文件可以循环写入,只要前面的部分已经被读取即可。 ## 功能设计 ### 核心思路 日志一共有三个文件:元数据文件,记录文件和快照文件。 元数据文件记录的信息有2个: 2. 记录文件读取偏移量。8个字节,用于指示记录文件中读取的偏移量。 3. 记录文件写入偏移量。8个字节,用于指示在记录文件中写入的偏移量。 记录文件则用于存储日志记录。日志由许多的记录构成,每一个记录的长度不一。每一个日志记录都包含一个单调递增的LSN。 快照文件则用于存储记录文件某一段上的内容形成的快照。 #### LSN 记录文件中的每一个记录都需要一个LSN进行全局区分。LSN需要单调递增以满足某些特性。由于写入偏移量具备这样的特性,因此使用写入偏移量作为每一个记录的LSN。具体操作方式如下: 当需要向记录文件写入数据时,首先应该申请对应的空间。此时记录返回当前的写入偏移量,而日志记录则使用这个便宜作为自身的LSN,并且在这个偏移量上写入数据。 #### 记录文件循环写入 日志记录用于记录事务的信息,而当事务结束后,之前记录的信息就不再需要,写入的区域就可以被覆盖再次利用。因此记录文件本身可以循环写入。而写入偏移量可以不断递增,只需要在真实写入时用写入偏移量对文件大小执行取模运算即可获得在文件上真实的写入位置。 同理,读取数据的时候也需要执行类似的换算。 #### 快照 记录文件可以循环写入,但是如果因为之前的事务迟迟不结束,即使之后的事务结束了,空间可以重复利用,也会因为之前的事务没有结束,导致文件连续的一块区域不可写入。因此可以将读取偏移量到写入偏移量之间的区域进行整理,并且形成快照,将快照内容写入快照文件后,则可以将读取偏移量移动至快照范围的末尾。 需要注意,快照本身也有LSN,其LSN的等同于快照右边界的偏移量。 快照的操作顺序如下: 1. 以读取偏移量和当前写入偏移量为范围,进行日志记录归档,形成归档内容。归档内容的LSN为快照时的写入偏移量 2. 将归档内容写入归档文件中。 3. 将元数据文件中的读取偏移量更新为归档内容的LSN。 如果执行步骤二之后,步骤三之前宕机。在恢复的时候,首先读取快照文件,再读取记录文件,如果记录的LSN比快照的LSN小,则直接忽略。 **步骤二的写入并不是原子过程,需要考虑宕机的情况。**因此归档内容的写入还应该包括内容的校验码,因此快照的格式如下 | 序号 | 长度 | 内容 | | ---- | --------- | ----------- | | 1 | 8 | LSN | | 2 | 2 | CRC16校验码 | | 3 | 4 | 序号4的内容 | | 4 | 序号3决定 | 快照内容 | #### 日志格式 日志的格式较为简单,如下 | 序号 | 长度 | 内容 | | ---- | ---------- | ---------- | | 1 | 8 | LSN | | 2 | 业务自定义 | 业务自定义 |