From 8e464964d938e016702d2c0a3e3aee5596174610 Mon Sep 17 00:00:00 2001 From: kingecg Date: Sun, 23 Feb 2025 19:55:04 +0800 Subject: [PATCH] =?UTF-8?q?add=20flush=20=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Readme.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ kvstore.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 Readme.md diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..8f4582b --- /dev/null +++ b/Readme.md @@ -0,0 +1,64 @@ +### README 文档 + +#### 项目名称:gocache + +#### 简介 +`gocache` 是一个简单的内存键值存储系统,支持持久化、事务处理和内存限制功能。它提供了基本的 `Put`、`Get` 和 `Delete` 操作,并且可以通过日志文件进行恢复。此外,还支持事务处理,确保数据的一致性和可靠性。 + +#### 目录结构 +``` +gocache/ +├── kvstore.go # 主要实现代码 +└── kvstore_test.go # 测试代码 +``` + +#### 安装与使用 +1. **安装依赖** + 本项目仅依赖 Go 标准库,无需额外安装第三方库。 + `go get git.pyer.club/kingecg/gocache` + +#### 功能说明 + +- **KVStore 结构体** + - `NewKVStore(file string, memoryLimit int64, bucketCount int) *KVStore`: 创建一个新的 KVStore 实例。 + - `Put(key, value string)`: 添加或更新键值对。 + - `Get(key string) (string, bool)`: 获取指定键的值。 + - `Delete(key string)`: 删除指定键的键值对。 + - `SaveToFile() error`: 将当前存储的数据保存到文件中。 + - `LoadFromFile(keys ...string) error`: 从文件加载数据,支持部分加载。 + - `BeginTransaction()`: 开始一个新的事务。 + - `Commit() error`: 提交当前事务。 + - `Rollback()`: 回滚当前事务。 + - `PutInTransaction(key, value string)`: 在当前事务中添加或更新键值对。 + - `LogOperation(op string, key, value string) error`: 记录操作日志。 + - `RecoverFromLog() error`: 从日志文件恢复数据。 + - `periodicSave()`: 定时保存脏数据并清空日志文件。 + +#### 测试用例说明 + +- **TestNewKVStore**: 测试创建新的 KVStore 实例是否成功。 +- **TestPutAndGet**: 测试 `Put` 和 `Get` 操作是否正确。 +- **TestDelete**: 测试 `Delete` 操作是否能正确删除键值对。 +- **TestSaveAndLoadFromFile**: 测试将数据保存到文件并从文件加载的功能。 +- **TestLogOperation**: 测试日志记录功能。 +- **TestTransaction**: 测试事务处理功能,包括提交和回滚。 + +#### 注意事项 +- **内存限制**:`memoryLimit` 参数用于限制内存使用量,当超过限制时会触发 panic。可以根据实际需求调整该参数。 +- **持久化**:数据会定期保存到文件中,并且可以通过日志文件进行恢复。请确保有足够的磁盘空间来存储这些文件。 +- **事务处理**:事务可以保证一组操作的原子性,但在高并发场景下需要注意锁机制的影响。 + +#### 贡献指南 +欢迎任何开发者为本项目贡献代码或提出改进建议。请遵循以下步骤: +1. Fork 本仓库。 +2. 创建新分支 (`git checkout -b feature/your-feature`)。 +3. 提交更改 (`git commit -am 'Add some feature'`)。 +4. 推送到远程分支 (`git push origin feature/your-feature`)。 +5. 提交 Pull Request。 + +#### 许可证 +本项目采用 MIT 许可证,详情请参见 [LICENSE](LICENSE) 文件。 + +--- + +希望这份 README 文档能够帮助您更好地理解和使用 `gocache`。如果有任何问题或建议,请随时联系开发者。 \ No newline at end of file diff --git a/kvstore.go b/kvstore.go index ddd34e3..306c7cd 100644 --- a/kvstore.go +++ b/kvstore.go @@ -112,15 +112,54 @@ func hashKey(key string) int { return hash } +// Flush saves dirty keys to file and clears the memory +func (k *KVStore) Flush() error { + k.mu.Lock() + defer k.mu.Unlock() + + if len(k.dirtyKeys) == 0 { + return nil + } + + // 保存变化到文件 + for key := range k.dirtyKeys { + if err := k.saveKeyToBucket(key); err != nil { + return err + } + } + + // 清空dirtyKeys + k.dirtyKeys = make(map[string]bool) + // 清空日志文件 + if err := os.Truncate(k.logFile, 0); err != nil { + return err + } + + // 清空内存 + k.store = make(map[string]string) + k.memoryUsage = 0 + + return nil +} + // Put adds a key/value pair to the store. func (k *KVStore) Put(key, value string) { k.mu.Lock() defer k.mu.Unlock() + // 如果处于事务中,不检查内存限制 + if k.transaction != nil { + k.transaction.store[key] = value + return + } + // 检查内存使用量 newMemoryUsage := k.memoryUsage + int64(len(key)+len(value)) if newMemoryUsage > k.memoryLimit { - panic("Memory limit exceeded") + if err := k.Flush(); err != nil { + panic("Failed to flush store") + } + newMemoryUsage = int64(len(key) + len(value)) } k.store[key] = value @@ -181,10 +220,29 @@ func (k *KVStore) Commit() error { return nil } + // 计算事务中键值对的内存使用量 + var transactionMemoryUsage int64 + for key, value := range k.transaction.store { + transactionMemoryUsage += int64(len(key) + len(value)) + } + + // 检查内存使用量 + newMemoryUsage := k.memoryUsage + transactionMemoryUsage + if newMemoryUsage > k.memoryLimit { + if err := k.Flush(); err != nil { + return err + } + newMemoryUsage = transactionMemoryUsage + } + + // 提交事务 for key, value := range k.transaction.store { k.store[key] = value + k.dirtyKeys[key] = true } + k.memoryUsage = newMemoryUsage k.transaction = nil + return nil }