Skip to content

Commit 7c740d3

Browse files
author
liuboping
committed
feat: add Snappy compression support and set as default
- Add Snappy compression algorithm (github.com/golang/snappy) - Benchmark results show Snappy has best throughput - Change default compression from LZ4 to Snappy - Add compression benchmarks for comparison - Version bump to v8.1.0 BREAKING CHANGE: Default compression algorithm changed from LZ4 to Snappy
1 parent b73bcfc commit 7c740d3

6 files changed

Lines changed: 196 additions & 7 deletions

File tree

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.25.7
44

55
require (
66
github.com/dgraph-io/badger/v4 v4.8.0
7+
github.com/golang/snappy v1.0.0
78
github.com/google/uuid v1.6.0
89
github.com/klauspost/compress v1.18.1
910
github.com/pierrec/lz4/v4 v4.1.21

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4
2323
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
2424
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
2525
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
26+
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
27+
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
2628
github.com/google/flatbuffers v25.9.23+incompatible h1:rGZKv+wOb6QPzIdkM2KxhBZCDrA0DeN6DNmRDrqIsQU=
2729
github.com/google/flatbuffers v25.9.23+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
2830
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=

internal/server/info.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
// Version 版本号,通过 ldflags 注入
11-
var Version = "8.0.9"
11+
var Version = "8.1.0"
1212

1313
// GitCommitID Git commit ID,通过 ldflags 注入
1414
var GitCommitID = ""

internal/store/compression.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"time"
77

88
"github.com/dgraph-io/badger/v4"
9+
"github.com/golang/snappy"
910
"github.com/klauspost/compress/zstd"
1011
"github.com/lbp0200/BoltDB/internal/logger"
1112
lz4 "github.com/pierrec/lz4/v4"
@@ -15,15 +16,17 @@ import (
1516
type CompressionType string
1617

1718
const (
18-
CompressionNone CompressionType = "none" // 不压缩
19-
CompressionLZ4 CompressionType = "lz4" // LZ4压缩(默认)
20-
CompressionZSTD CompressionType = "zstd" // ZSTD压缩
19+
CompressionNone CompressionType = "none" // 不压缩
20+
CompressionLZ4 CompressionType = "lz4" // LZ4压缩
21+
CompressionZSTD CompressionType = "zstd" // ZSTD压缩
22+
CompressionSnappy CompressionType = "snappy" // Snappy压缩(默认)
2123
)
2224

2325
// compressionMagic 压缩数据的前缀魔数,用于识别压缩算法
2426
var (
25-
compressionMagicLZ4 = []byte{0x4C, 0x5A, 0x34, 0x01} // "LZ4\01"
26-
compressionMagicZSTD = []byte{0x5A, 0x53, 0x54, 0x44} // "ZSTD"
27+
compressionMagicLZ4 = []byte{0x4C, 0x5A, 0x34, 0x01} // "LZ4\01"
28+
compressionMagicZSTD = []byte{0x5A, 0x53, 0x54, 0x44} // "ZSTD"
29+
compressionMagicSnappy = []byte{0x53, 0x4E, 0x41, 0x50} // "SNAP"
2730
)
2831

2932
// compressData 压缩数据
@@ -37,6 +40,8 @@ func compressData(data []byte, compressionType CompressionType) ([]byte, error)
3740
return compressLZ4(data)
3841
case CompressionZSTD:
3942
return compressZSTD(data)
43+
case CompressionSnappy:
44+
return compressSnappy(data)
4045
default:
4146
return nil, fmt.Errorf("unsupported compression type: %s", compressionType)
4247
}
@@ -57,6 +62,9 @@ func decompressData(data []byte) ([]byte, error) {
5762
if bytes.HasPrefix(data, compressionMagicZSTD) {
5863
return decompressZSTD(data[len(compressionMagicZSTD):])
5964
}
65+
if bytes.HasPrefix(data, compressionMagicSnappy) {
66+
return decompressSnappy(data[len(compressionMagicSnappy):])
67+
}
6068
}
6169

6270
// 没有压缩标记,返回原始数据
@@ -136,6 +144,28 @@ func decompressZSTD(data []byte) ([]byte, error) {
136144
return decompressed, nil
137145
}
138146

147+
// compressSnappy 使用Snappy压缩
148+
func compressSnappy(data []byte) ([]byte, error) {
149+
compressed := snappy.Encode(nil, data)
150+
151+
// 添加压缩标记
152+
result := make([]byte, len(compressionMagicSnappy)+len(compressed))
153+
copy(result, compressionMagicSnappy)
154+
copy(result[len(compressionMagicSnappy):], compressed)
155+
156+
return result, nil
157+
}
158+
159+
// decompressSnappy 使用Snappy解压缩
160+
func decompressSnappy(data []byte) ([]byte, error) {
161+
decompressed, err := snappy.Decode(nil, data)
162+
if err != nil {
163+
return nil, fmt.Errorf("snappy decompress error: %w", err)
164+
}
165+
166+
return decompressed, nil
167+
}
168+
139169
// shouldCompress 判断是否应该压缩数据
140170
// 对于小数据,压缩可能反而增加大小,所以设置一个阈值
141171
func shouldCompress(data []byte, compressionType CompressionType) bool {

internal/store/compression_test.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,137 @@
11
package store
22

33
import (
4+
"fmt"
45
"strings"
56
"testing"
67

78
"github.com/zeebo/assert"
89
)
910

11+
// BenchmarkCompression 对比各压缩算法的性能
12+
func BenchmarkCompression(b *testing.B) {
13+
// 使用较大的数据块进行压缩测试
14+
largeValue := strings.Repeat("This is a test string that will be compressed. ", 100)
15+
data := []byte(largeValue)
16+
17+
testCases := []struct {
18+
name string
19+
algo CompressionType
20+
}{
21+
{"LZ4", CompressionLZ4},
22+
{"Snappy", CompressionSnappy},
23+
{"ZSTD", CompressionZSTD},
24+
}
25+
26+
for _, tc := range testCases {
27+
b.Run(tc.name, func(b *testing.B) {
28+
b.Run("Compress", func(b *testing.B) {
29+
for i := 0; i < b.N; i++ {
30+
compressed, err := compressData(data, tc.algo)
31+
if err != nil {
32+
b.Fatal(err)
33+
}
34+
// 防止编译器优化
35+
_ = len(compressed)
36+
}
37+
})
38+
39+
compressed, err := compressData(data, tc.algo)
40+
if err != nil {
41+
b.Fatal(err)
42+
}
43+
44+
b.Run("Decompress", func(b *testing.B) {
45+
for i := 0; i < b.N; i++ {
46+
decompressed, err := decompressData(compressed)
47+
if err != nil {
48+
b.Fatal(err)
49+
}
50+
// 防止编译器优化
51+
_ = len(decompressed)
52+
}
53+
})
54+
})
55+
}
56+
}
57+
58+
// BenchmarkCompressionRatio 对比各算法的压缩率
59+
func BenchmarkCompressionRatio(b *testing.B) {
60+
testCases := []struct {
61+
name string
62+
algo CompressionType
63+
}{
64+
{"LZ4", CompressionLZ4},
65+
{"Snappy", CompressionSnappy},
66+
{"ZSTD", CompressionZSTD},
67+
}
68+
69+
dataSizes := []int{1024, 10240, 102400} // 1KB, 10KB, 100KB
70+
71+
for _, size := range dataSizes {
72+
// 生成不同大小的测试数据
73+
var data []byte
74+
for i := 0; i < size; i++ {
75+
data = append(data, byte('A'+i%26))
76+
}
77+
78+
for _, tc := range testCases {
79+
tc := tc
80+
size := size
81+
b.Run(fmt.Sprintf("%s-%dB", tc.name, size), func(b *testing.B) {
82+
for i := 0; i < b.N; i++ {
83+
compressed, err := compressData(data, tc.algo)
84+
if err != nil {
85+
b.Fatal(err)
86+
}
87+
ratio := float64(len(compressed)) / float64(len(data))
88+
// 防止编译器优化
89+
_ = ratio
90+
}
91+
})
92+
}
93+
}
94+
}
95+
96+
// BenchmarkStoreCompression 带存储的压缩性能测试
97+
func BenchmarkStoreCompression(b *testing.B) {
98+
testCases := []struct {
99+
name string
100+
algo CompressionType
101+
}{
102+
{"LZ4", CompressionLZ4},
103+
{"Snappy", CompressionSnappy},
104+
{"ZSTD", CompressionZSTD},
105+
}
106+
107+
for _, tc := range testCases {
108+
tc := tc
109+
b.Run(tc.name, func(b *testing.B) {
110+
dbPath := b.TempDir()
111+
store, err := NewBotreonStoreWithCompression(dbPath, tc.algo)
112+
if err != nil {
113+
b.Fatal(err)
114+
}
115+
defer store.Close()
116+
117+
largeValue := strings.Repeat("This is a test string that will be compressed. ", 100)
118+
key := "bench_key"
119+
120+
b.ResetTimer()
121+
for i := 0; i < b.N; i++ {
122+
err := store.Set(key, largeValue)
123+
if err != nil {
124+
b.Fatal(err)
125+
}
126+
_, err = store.Get(key)
127+
if err != nil {
128+
b.Fatal(err)
129+
}
130+
}
131+
})
132+
}
133+
}
134+
10135
func TestCompressionLZ4(t *testing.T) {
11136
dbPath := t.TempDir()
12137
store, err := NewBotreonStoreWithCompression(dbPath, CompressionLZ4)
@@ -67,6 +192,17 @@ func TestCompressionNone(t *testing.T) {
67192
assert.Equal(t, largeValue, value)
68193
}
69194

195+
func TestCompressionDefaultIsSnappy(t *testing.T) {
196+
dbPath := t.TempDir()
197+
// 不指定压缩算法,使用默认
198+
store, err := NewBotreonStore(dbPath)
199+
assert.NoError(t, err)
200+
defer store.Close()
201+
202+
// 验证默认压缩算法是 Snappy
203+
assert.Equal(t, CompressionSnappy, store.GetCompression())
204+
}
205+
70206
func TestCompressionSmallData(t *testing.T) {
71207
dbPath := t.TempDir()
72208
store, err := NewBotreonStoreWithCompression(dbPath, CompressionLZ4)
@@ -134,6 +270,26 @@ func TestCompressionBackwardCompatibility(t *testing.T) {
134270
assert.True(t, len(value2) > 0)
135271
}
136272

273+
func TestCompressionSnappy(t *testing.T) {
274+
dbPath := t.TempDir()
275+
store, err := NewBotreonStoreWithCompression(dbPath, CompressionSnappy)
276+
assert.NoError(t, err)
277+
defer store.Close()
278+
279+
// 测试大字符串压缩
280+
largeValue := strings.Repeat("This is a test string that will be compressed. ", 100)
281+
key := "large_key"
282+
283+
// 写入
284+
err = store.Set(key, largeValue)
285+
assert.NoError(t, err)
286+
287+
// 读取
288+
value, err := store.Get(key)
289+
assert.NoError(t, err)
290+
assert.Equal(t, largeValue, value)
291+
}
292+
137293
func TestCompressionSwitch(t *testing.T) {
138294
dbPath := t.TempDir()
139295
store, err := NewBotreonStoreWithCompression(dbPath, CompressionLZ4)

internal/store/define.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ type BotreonStore struct {
6262

6363
// NewBotreonStore 创建新的BotreonStore实例
6464
func NewBotreonStore(path string) (*BotreonStore, error) {
65-
return NewBotreonStoreWithCompression(path, CompressionLZ4)
65+
return NewBotreonStoreWithCompression(path, CompressionSnappy)
6666
}
6767

6868
// NewBotreonStoreWithCompression 创建新的BotreonStore实例,指定压缩算法

0 commit comments

Comments
 (0)