|
|
@@ -52,8 +52,11 @@ type WALWriter struct {
|
|
|
}
|
|
|
|
|
|
type walEntry struct {
|
|
|
- pageID uint32
|
|
|
- data []byte // 完整页镜像
|
|
|
+ pageID uint32
|
|
|
+ data []byte // 完整页镜像(原有逻辑,用于 Commit)
|
|
|
+ originalData []byte // 原始行数据(事务回滚用)← 新增
|
|
|
+ newData []byte // 新行数据(事务提交用)← 新增
|
|
|
+ isRowLevel bool // 是否为行级记录(true=存数据,false=存整页)← 新增
|
|
|
}
|
|
|
|
|
|
// BeginTxn 开始新事务
|
|
|
@@ -72,12 +75,6 @@ func (db *DB) BeginTxn() *WALWriter {
|
|
|
|
|
|
// RecordPage 记录一个被修改的页 (同一 pageID 只保留最新镜像)
|
|
|
func (w *WALWriter) RecordPage(pageID uint32, data []byte) {
|
|
|
- // 确保状态为 Mem (如果已写盘,不应再修改)
|
|
|
- if w.state != WALStateMem && w.state != WALStateWritten {
|
|
|
- // 理论上不应发生,因为 Commit 后会清空 entries
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
d := make([]byte, len(data))
|
|
|
copy(d, data)
|
|
|
// 同一 pageID 只保留最新镜像
|
|
|
@@ -87,7 +84,37 @@ func (w *WALWriter) RecordPage(pageID uint32, data []byte) {
|
|
|
return
|
|
|
}
|
|
|
}
|
|
|
- w.entries = append(w.entries, walEntry{pageID, d})
|
|
|
+ w.entries = append(w.entries, walEntry{
|
|
|
+ pageID: pageID,
|
|
|
+ data: d,
|
|
|
+ isRowLevel: false, // 原有逻辑:存整页
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// RecordRowLevel 记录行级数据(用于事务支持)
|
|
|
+// 存储原始数据和新数据,而非整页,节省空间
|
|
|
+func (w *WALWriter) RecordRowLevel(pageID uint32, originalData []byte, newData []byte) {
|
|
|
+ orig := make([]byte, len(originalData))
|
|
|
+ copy(orig, originalData)
|
|
|
+
|
|
|
+ newD := make([]byte, len(newData))
|
|
|
+ copy(newD, newData)
|
|
|
+
|
|
|
+ // 同一 pageID 只保留最新
|
|
|
+ for i := range w.entries {
|
|
|
+ if w.entries[i].pageID == pageID && w.entries[i].isRowLevel {
|
|
|
+ w.entries[i].originalData = orig
|
|
|
+ w.entries[i].newData = newD
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ w.entries = append(w.entries, walEntry{
|
|
|
+ pageID: pageID,
|
|
|
+ originalData: orig,
|
|
|
+ newData: newD,
|
|
|
+ isRowLevel: true, // 行级记录
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
// Rollback 回滚: 丢弃所有条目
|
|
|
@@ -183,7 +210,7 @@ func (w *WALWriter) writeWALPages() ([]uint32, error) {
|
|
|
// === 首页: 写入 TxnID + TotalEntries + State + NextWalPage + EntryCount ===
|
|
|
binary.LittleEndian.PutUint64(buf[walPHDR:], w.txnID)
|
|
|
binary.LittleEndian.PutUint32(buf[walPHDR+8:], uint32(len(w.entries)))
|
|
|
- buf[walPHDR+12] = WALStateWritten // State = Written (已写盘,未 fsync)
|
|
|
+ buf[walPHDR+12] = WALStateWritten // State = Written (已写盘,未 fsync)
|
|
|
binary.LittleEndian.PutUint32(buf[walPHDR+13:], 0) // NextWalPage (稍后填)
|
|
|
binary.LittleEndian.PutUint16(buf[walPHDR+17:], 0) // EntryCount (稍后填)
|
|
|
|
|
|
@@ -195,26 +222,54 @@ func (w *WALWriter) writeWALPages() ([]uint32, error) {
|
|
|
off = walContHdr
|
|
|
}
|
|
|
|
|
|
- // 写入当前 entry (只存 UseSize 字节, 减少 WAL 空间)
|
|
|
+ // 写入当前 entry
|
|
|
e := w.entries[entryIdx]
|
|
|
- storeLen := uint32(len(e.data)) // 默认存全部
|
|
|
- if len(e.data) >= 20 {
|
|
|
- useSize := binary.LittleEndian.Uint32(e.data[16:20])
|
|
|
- if useSize > 0 && useSize < uint32(len(e.data)) {
|
|
|
- storeLen = useSize
|
|
|
- }
|
|
|
- // HeaderPage (pageID=0): 至少存 PageSize4K
|
|
|
- if e.pageID == 0 && storeLen < PageSize4K {
|
|
|
- storeLen = PageSize4K
|
|
|
+
|
|
|
+ if e.isRowLevel {
|
|
|
+ // === 行级记录: [PageID][OrigLen][NewLen][Checksum][OriginalData][NewData] ===
|
|
|
+ origLen := uint32(len(e.originalData))
|
|
|
+ newLen := uint32(len(e.newData))
|
|
|
+ totalLen := origLen + newLen
|
|
|
+
|
|
|
+ // 计算 checksum (覆盖 originalData + newData)
|
|
|
+ combined := make([]byte, 0, totalLen)
|
|
|
+ combined = append(combined, e.originalData...)
|
|
|
+ combined = append(combined, e.newData...)
|
|
|
+ checksum := crc32.ChecksumIEEE(combined)
|
|
|
+
|
|
|
+ // 写入元数据
|
|
|
+ binary.LittleEndian.PutUint32(buf[off:], e.pageID)
|
|
|
+ binary.LittleEndian.PutUint32(buf[off+4:], origLen) // 用 origLen 替代 storeLen
|
|
|
+ binary.LittleEndian.PutUint32(buf[off+8:], newLen) // 新增: newLen
|
|
|
+ binary.LittleEndian.PutUint32(buf[off+12:], checksum)
|
|
|
+ off += 16 // PageID + OrigLen + NewLen + Checksum
|
|
|
+
|
|
|
+ // 写入数据
|
|
|
+ copy(buf[off:], e.originalData)
|
|
|
+ off += origLen
|
|
|
+ copy(buf[off:], e.newData)
|
|
|
+ off += newLen
|
|
|
+ } else {
|
|
|
+ // === 整页记录: [PageID][DataLen][Checksum][Data] (原有逻辑) ===
|
|
|
+ storeLen := uint32(len(e.data)) // 默认存全部
|
|
|
+ if len(e.data) >= 20 {
|
|
|
+ useSize := binary.LittleEndian.Uint32(e.data[16:20])
|
|
|
+ if useSize > 0 && useSize < uint32(len(e.data)) {
|
|
|
+ storeLen = useSize
|
|
|
+ }
|
|
|
+ // HeaderPage (pageID=0): 至少存 PageSize4K
|
|
|
+ if e.pageID == 0 && storeLen < PageSize4K {
|
|
|
+ storeLen = PageSize4K
|
|
|
+ }
|
|
|
}
|
|
|
+ checksum := crc32.ChecksumIEEE(e.data[:storeLen])
|
|
|
+ binary.LittleEndian.PutUint32(buf[off:], e.pageID)
|
|
|
+ binary.LittleEndian.PutUint32(buf[off+4:], storeLen)
|
|
|
+ binary.LittleEndian.PutUint32(buf[off+8:], checksum)
|
|
|
+ copy(buf[off+12:], e.data[:storeLen])
|
|
|
+ // storeLen 可能 < len(e.data), 剩余字节保持零填充
|
|
|
+ off += walEntryMeta + storeLen
|
|
|
}
|
|
|
- checksum := crc32.ChecksumIEEE(e.data[:storeLen])
|
|
|
- binary.LittleEndian.PutUint32(buf[off:], e.pageID)
|
|
|
- binary.LittleEndian.PutUint32(buf[off+4:], storeLen)
|
|
|
- binary.LittleEndian.PutUint32(buf[off+8:], checksum)
|
|
|
- copy(buf[off+12:], e.data[:storeLen])
|
|
|
- // storeLen 可能 < len(e.data), 剩余字节保持零填充
|
|
|
- off += walEntryMeta + storeLen
|
|
|
|
|
|
hdr.UseSize = off
|
|
|
entryIdx++
|