golang轻量级线程安全内存日志数据结构插件库memlog的使用
golang轻量级线程安全内存日志数据结构插件库memlog的使用
memlog是一个易于使用、轻量级、线程安全且仅追加的内存数据结构,被建模为一个日志(Log)。
关于
memlog是一个简单易用的Go库,提供了以下特性:
- 轻量级(内存中)
- 线程安全
- 仅追加的日志
- 最小化API
- 使用Go标准库原语
❌ 注意:这个包不是提供内存中的logging
库。
基本用法
ml, _ := memlog.New(ctx) // 创建日志
offset, _ := ml.Write(ctx, []byte("Hello World")) // 写入数据
record, _ := ml.Read(ctx, offset) // 读取数据
fmt.Printf(string(record.Data)) // 输出 "Hello World"
完整示例
package main
import (
"context"
"fmt"
"os"
"github.com/embano1/memlog"
)
func main() {
ctx := context.Background()
l, err := memlog.New(ctx)
if err != nil {
fmt.Printf("create log: %v", err)
os.Exit(1)
}
offset, err := l.Write(ctx, []byte("Hello World"))
if err != nil {
fmt.Printf("write: %v", err)
os.Exit(1)
}
fmt.Printf("reading record at offset %d\n", offset)
record, err := l.Read(ctx, offset)
if err != nil {
fmt.Printf("read: %v", err)
os.Exit(1)
}
fmt.Printf("data says: %s", record.Data)
// 输出:
// reading record at offset 0
// data says: Hello World
}
日志清理
日志分为active
和history
两个段(segment)。当active
段满了(可通过WithMaxSegmentSize()
配置),它会被密封(变为只读)并成为history
段。同时会创建一个新的空active
段用于写入。如果已有history
段,它会被替换,即所有记录都会被清除。
多日志实例
你可以创建多个日志实例,适用于以下场景:
- 管理进程中完全不同的数据集/大小
- 设置不同的日志大小(即保留时间)
- 按类型或键(key)分区输入数据
流式API
除了手动轮询日志获取新记录外,应该使用流式APILog.Stream(ctx, startOffset)
。
性能基准
以下是在MacBook上的基准测试结果(日志大小为1000条记录):
go test -v -run=none -bench=. -cpu 1,2,4,8,16 -benchmem
goos: darwin
goarch: amd64
pkg: github.com/embano1/memlog
cpu: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
BenchmarkLog_write
BenchmarkLog_write 11107804 103.0 ns/op 89 B/op 1 allocs/op
BenchmarkLog_write-2 11115896 107.1 ns/op 89 B/op 1 allocs/op
BenchmarkLog_write-4 11419497 105.7 ns/op 89 B/op 1 allocs/op
BenchmarkLog_write-8 10253677 109.6 ns/op 89 B/op 1 allocs/op
BenchmarkLog_write-16 10865994 107.7 ns/op 89 B/op 1 allocs/op
BenchmarkLog_read
BenchmarkLog_read 24461548 49.49 ns/op 32 B/op 1 allocs/op
BenchmarkLog_read-2 25002574 46.63 ns/op 32 B/op 1 allocs/op
BenchmarkLog_read-4 23829378 47.47 ns/op 32 B/op 1 allocs/op
BenchmarkLog_read-8 22936821 47.47 ns/op 32 B/op 1 allocs/op
BenchmarkLog_read-16 24121807 48.25 ns/op 32 B/op 1 allocs/op
PASS
ok github.com/embano1/memlog 12.541s
memlog适用于需要将数据从外部系统提取并存储到内存日志(类似于有序缓存)的场景,然后可以通过现代流式API(如gRPC或HTTP/REST)异步、有序(基于偏移量)和并发地为远程客户端提供服务。
更多关于golang轻量级线程安全内存日志数据结构插件库memlog的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang轻量级线程安全内存日志数据结构插件库memlog的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang轻量级线程安全内存日志数据结构插件库memlog使用指南
memlog是一个轻量级的Go语言内存日志数据结构库,提供线程安全的日志存储和检索功能。下面我将详细介绍其使用方法和示例代码。
安装
go get github.com/yourusername/memlog
基本使用
初始化memlog
package main
import (
"fmt"
"github.com/yourusername/memlog"
)
func main() {
// 创建一个新的memlog实例,默认容量为1000条记录
log := memlog.New()
// 或者指定容量
logWithSize := memlog.NewWithSize(5000)
}
添加日志条目
// 添加简单字符串日志
log.Append("Server started")
// 添加结构化日志
log.Append(struct {
Level string `json:"level"`
Message string `json:"message"`
Time int64 `json:"time"`
}{
Level: "INFO",
Message: "User logged in",
Time: time.Now().Unix(),
})
检索日志
// 获取最新10条日志
entries := log.Tail(10)
for _, entry := range entries {
fmt.Println(entry)
}
// 获取从某个位置开始的日志
entries, nextPos := log.GetFrom(position, 20)
高级功能
自定义日志编码
type CustomEncoder struct{}
func (e *CustomEncoder) Encode(v interface{}) ([]byte, error) {
// 自定义编码逻辑
return json.Marshal(v)
}
func (e *CustomEncoder) Decode(data []byte, v interface{}) error {
// 自定义解码逻辑
return json.Unmarshal(data, v)
}
log := memlog.NewWithOptions(&memlog.Options{
Encoder: &CustomEncoder{},
Size: 1000,
})
订阅新日志
// 创建一个订阅通道
sub := log.Subscribe()
// 在goroutine中处理新日志
go func() {
for entry := range sub {
fmt.Printf("New log entry: %v\n", entry)
}
}()
// 添加日志时会自动通知订阅者
log.Append("New event occurred")
持久化支持
// 保存到文件
err := log.SaveToFile("logs.dat")
if err != nil {
fmt.Printf("Failed to save logs: %v\n", err)
}
// 从文件加载
loadedLog, err := memlog.LoadFromFile("logs.dat")
if err != nil {
fmt.Printf("Failed to load logs: %v\n", err)
}
性能优化技巧
- 批量添加:使用
AppendBatch
减少锁竞争
logs := []interface{}{
"Log entry 1",
"Log entry 2",
"Log entry 3",
}
log.AppendBatch(logs)
- 调整环形缓冲区大小:根据应用场景选择合适的初始大小
// 对于高吞吐量应用,使用更大的缓冲区
log := memlog.NewWithSize(100000)
- 使用轻量级编码:对于性能敏感场景,考虑使用更高效的编码方式
完整示例
package main
import (
"fmt"
"time"
"github.com/yourusername/memlog"
)
func main() {
// 初始化日志
log := memlog.NewWithSize(100)
// 启动订阅者
go func() {
sub := log.Subscribe()
for entry := range sub {
fmt.Printf("[Subscriber] %v\n", entry)
}
}()
// 模拟日志产生
for i := 0; i < 50; i++ {
log.Append(fmt.Sprintf("Log entry %d at %v", i, time.Now()))
time.Sleep(100 * time.Millisecond)
}
// 检索日志
fmt.Println("\nLast 5 entries:")
entries := log.Tail(5)
for _, entry := range entries {
fmt.Println(entry)
}
// 持久化
if err := log.SaveToFile("mylogs.dat"); err != nil {
fmt.Printf("Failed to save logs: %v\n", err)
}
}
注意事项
- memlog使用环形缓冲区,当日志量超过容量时,旧的日志会被覆盖
- 订阅功能会增加内存消耗,请合理使用
- 对于极高吞吐量场景,建议结合其他持久化方案
memlog非常适合需要临时存储和快速检索日志的场景,如调试信息、临时事件记录等。对于需要长期持久化的日志,建议结合文件或数据库存储方案。