golangでLevelDBを使用する
DBの作成からデータの登録、取得、範囲取得、削除など基本的な使い方を紹介します。
プログラム全体は最後に載せてあります。
コピーしてGo Playground
に貼り付ければ実行できますので、実際の動作を確認してみてください。
解説
LevelDBを作成
1
2
3
4
5
6
|
//DBを開く(なければ作成される)
db, err := leveldb.OpenFile(dbName, nil)
if err != nil {
return err
}
defer db.Close()
|
DBの作成はOpenFile
関数を使用します。
コメントに記載のある通り、ディレクトリがなければ新しく作成してくれます。
今回の場合はtestdb
というディレクトリが作成されます。
defer db.Close()のdefer
は関数を抜ける前にコードを実行してくれるステートメントです。
この例ではleveldbTest関数を抜ける前にdb.Close()が実行されることになります。
※このコードはleveldbTest関数から呼び出されています
毎回return文の前に記述しなくてよくなるのでとても便利ですね。
第1引数はDB名を指定します。
第2引数はオプションを指定します。
今回は指定しないのでnil
を渡しています。
Close
関数は、データのファイルへの保存やメモリの解放など重要な後始末を実行していますので、忘れずに呼び出すようにしましょう。
キーの登録
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//key1~key5を登録
dataNum := 5
for i := 1; i <= dataNum; i++ {
key := fmt.Sprintf("key%d", i)
value := fmt.Sprintf("value%d", i)
err = db.Put([]byte(key), []byte(value), nil)
if err != nil {
return err
}
}
//mofumofu1~mofumofu5を登録
dataNum = 5
for i := 1; i <= dataNum; i++ {
key := fmt.Sprintf("mofumofu%d", i)
value := fmt.Sprintf("mofumofu-value%d", i)
err = db.Put([]byte(key), []byte(value), nil)
if err != nil {
return err
}
}
|
キーの登録はPut
関数を使用します。
第1引数に設定したいキーをbyte配列で指定します。
第2引数にキーの値をbyte配列で指定します。
keyとvalue共にbyte配列で渡す仕様になっています。
渡す前のデータ変換がめんどくさいですが、その分内部はシンプルな作りになっています。
第3引数はオプションを指定します。
今回は指定しないのでnil
を渡しています。
指定キーのデータを取得
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//指定したキー(mofumofu2)を取得
fmt.Println("指定したキー(mofumofu2)を取得")
valueBytes, err := db.Get([]byte("mofumofu2"), nil)
if err != nil {
return err
}
fmt.Println(string(valueBytes))
//指定したキー(hogehoge2)を取得
fmt.Println("指定したキー(hogehoge2)を取得")
valueBytes, err := db.Get([]byte("hogehoge2"), nil)
if err == leveldb.ErrNotFound {
fmt.Println("hogehoge2は見つかりません")
} else if err != nil {
return err
}
|
指定キーのデータの取得はGet
関数を使用します。
第1引数に取得したいデータのキーをbyte配列で渡します。
第2引数はオプションを指定します。
今回は指定しないのでnil
を渡しています。
もし指定したキーが見つからなかった場合は、leveldb.ErrNotFound
が返却されます。
キーを全て取得
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//全て取得
fmt.Println("\n全て取得")
iter := db.NewIterator(nil, nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Println(string(key), string(value))
}
iter.Release()
err = iter.Error()
if err != nil {
return err
}
|
全てのキーを一つずつ取得したい場合はNewIterator
関数を使用します。
1番目の引数はキーを取得する範囲を指定します。
今回は全体を取得するのでnil
を渡しています。
2番目の引数はオプションを指定します。
こちらはGet
関数で指定するオプションと同じものです。
今回は指定しないのでnil
を渡しています。
イテレーターのNext
関数は呼び出すたびに値がセットされ、keyとvalueが取得できるようになります。
Next
関数は最終キーの位置で使用するとfalseを返却します。
使い終わったらRelease
関数を使用して後始末を行います。
Error
関数も使用して問題が発生していないかも確認しておきましょう。
キーを一部取得
最初から指定キーまで
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//最初~mofumofu2までを取得
fmt.Println("\n最初~mofumofu2までを取得")
iter = db.NewIterator(&util.Range{
Start: nil, //[]byte{}でも処理されるが、未指定はnil想定なので[]byte{}は指定しない方がいい
Limit: []byte("mofumofu3"),
}, nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Println(string(key), string(value))
}
iter.Release()
err = iter.Error()
if err != nil {
return err
}
|
ある範囲のキーを取得する場合もNewIterator
関数を使用します。
第1引数にutil.Range
を指定して範囲を設定します。
util.Range
はStart
とLimit
のメンバ変数があります。
最初のデータから取得したい場合はStart
にnil
を設定します。
Limit
にmofumofu3
を指定して終端を設定します。
この場合は最初からmofumofu2まで
のキーを取得できます。
コメントに書いてますがStart
の未指定はnil
です。
[]byte{}
でも動きますが指定しない方がいいです。
指定キーから最後のキーまで
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//key3以降を取得
fmt.Println("\nkey3以降を取得")
iter = db.NewIterator(&util.Range{
Start: []byte("key3"),
Limit: nil, //[]byte{}を指定するとパニックになるので注意、未指定はnil
}, nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Println(string(key), string(value))
}
iter.Release()
err = iter.Error()
if err != nil {
return err
}
|
指定キーから最後のキーまで取得する例です。
Start
にkey3
を設定して、Limit
にnil
を設定します。
この場合はkey3から最後のキーまで
取得できます。
こちらもコメントに書いてますが、Limit
の未指定はnil
です。
Start
と違ってLimit
に[]byte{}
を指定するとパニックになりますのでご注意ください。
指定キーから指定キーまで
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//key2~key3を取得
fmt.Println("\nkey2~key3を取得")
iter = db.NewIterator(&util.Range{
Start: []byte("key2"),
Limit: []byte("key4"),
}, nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Println(string(key), string(value))
}
iter.Release()
err = iter.Error()
if err != nil {
return err
}
|
指定キーから指定キーまで取得する例です。
Start
にkey2
を設定して、Limit
にKey4
を設定します。
この場合はkey2からKey3まで
取得できます。
前方一致のキー
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//mofumofu*を取得
fmt.Println("\nmofumofu*を取得")
iter = db.NewIterator(util.BytesPrefix([]byte("mofumofu")), nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Println(string(key), string(value))
}
iter.Release()
err = iter.Error()
if err != nil {
return err
}
|
前方一致のキーを取得する例です。
前方一致はutil.BytesPrefix
関数を使用してutil.Range
を作成してもらいます。
第1引数にmofumofu
を設定します。
この場合はmofumofu1~mofumofu5
のキーが取得できます。
キーの削除
1
2
3
4
5
6
7
8
9
10
11
12
|
//指定したキー(key2)を削除
fmt.Println("\n指定したキー(key2)を削除")
err = db.Delete([]byte("key2"), nil)
if err != nil {
return err
}
fmt.Println("\n存在しないキー(hogehoge2)を削除")
err = db.Delete([]byte("hogehoge2"), nil)
if err != nil {
//ここにはこない(エラーにならない)
return err
}
|
キーの削除はDelete
関数を使用します。
第1引数に削除したいキーを設定します。
第2引数はオプションを指定します。
今回は指定しないのでnil
を渡しています。
存在しないキーを削除してもエラーにはなりません。
まとめ
leveldbは他のgolang製のkvsに比べて高速でメモリ使用量も少なくとても使いやすいです。
MySQLの用に複数のプロセスからアクセスできないですが、バッチ処理で作成したデータの保存先として使用するなど特定の用途で効果を発揮します。
ぜひ一度使ってみて下さい。
今回オプションは全てnil
設定でしたが、オプションをうまく使うことでメモリ消費を抑えられるメリットがありますので、別記事でまた書きたいと思います。
プログラム全体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
package main
import (
"fmt"
"runtime"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/util"
)
func main() {
err := leveldbTest("testdb")
if err != nil {
panic(err)
}
fmt.Printf("\nGo version: %s\n", runtime.Version())
}
func leveldbTest(dbName string) error {
//DBを開く(なければ作成される)
db, err := leveldb.OpenFile(dbName, nil)
if err != nil {
return err
}
defer db.Close()
//key1~key5を登録
dataNum := 5
for i := 1; i <= dataNum; i++ {
key := fmt.Sprintf("key%d", i)
value := fmt.Sprintf("value%d", i)
err = db.Put([]byte(key), []byte(value), nil)
if err != nil {
return err
}
}
//mofumofu1~mofumofu5を登録
dataNum = 5
for i := 1; i <= dataNum; i++ {
key := fmt.Sprintf("mofumofu%d", i)
value := fmt.Sprintf("mofumofu-value%d", i)
err = db.Put([]byte(key), []byte(value), nil)
if err != nil {
return err
}
}
//指定したキー(mofumofu2)を取得
fmt.Println("指定したキー(mofumofu2)を取得")
valueBytes, err := db.Get([]byte("mofumofu2"), nil)
if err != nil {
return err
}
fmt.Println(string(valueBytes))
//指定したキー(hogehoge2)を取得
fmt.Println("指定したキー(hogehoge2)を取得")
valueBytes, err = db.Get([]byte("hogehoge2"), nil)
if err == leveldb.ErrNotFound {
fmt.Println("hogehoge2は見つかりません")
} else if err != nil {
return err
}
//全て取得
fmt.Println("\n全て取得")
iter := db.NewIterator(nil, nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Println(string(key), string(value))
}
iter.Release()
err = iter.Error()
if err != nil {
return err
}
//最初~mofumofu2までを取得
fmt.Println("\n最初~mofumofu2までを取得")
iter = db.NewIterator(&util.Range{
Start: nil, //[]byte{}でも処理されるが、未指定はnil想定なので[]byte{}は指定しない方がいい
Limit: []byte("mofumofu3"),
}, nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Println(string(key), string(value))
}
iter.Release()
err = iter.Error()
if err != nil {
return err
}
//key3以降を取得
fmt.Println("\nkey3以降を取得")
iter = db.NewIterator(&util.Range{
Start: []byte("key3"),
Limit: nil, //[]byte{}を指定するとパニックになるので注意、未指定はnil
}, nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Println(string(key), string(value))
}
iter.Release()
err = iter.Error()
if err != nil {
return err
}
//key2~key3を取得
fmt.Println("\nkey2~key3を取得")
iter = db.NewIterator(&util.Range{
Start: []byte("key2"),
Limit: []byte("key4"),
}, nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Println(string(key), string(value))
}
iter.Release()
err = iter.Error()
if err != nil {
return err
}
//mofumofu*を取得
fmt.Println("\nmofumofu*を取得")
iter = db.NewIterator(util.BytesPrefix([]byte("mofumofu")), nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Println(string(key), string(value))
}
iter.Release()
err = iter.Error()
if err != nil {
return err
}
//指定したキー(key2)を削除
fmt.Println("\n指定したキー(key2)を削除")
err = db.Delete([]byte("key2"), nil)
if err != nil {
return err
}
fmt.Println("\n存在しないキー(hogehoge2)を削除")
err = db.Delete([]byte("hogehoge2"), nil)
if err != nil {
//ここにはこない(エラーにならない)
return err
}
//全て取得
fmt.Println("\n全て取得")
iter = db.NewIterator(nil, nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
fmt.Println(string(key), string(value))
}
iter.Release()
err = iter.Error()
if err != nil {
return err
}
return nil
}
|
実行結果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
指定したキー(mofumofu2)を取得
mofumofu-value2
指定したキー(hogehoge2)を取得
hogehoge2は見つかりません
全て取得
key1 value1
key2 value2
key3 value3
key4 value4
key5 value5
mofumofu1 mofumofu-value1
mofumofu2 mofumofu-value2
mofumofu3 mofumofu-value3
mofumofu4 mofumofu-value4
mofumofu5 mofumofu-value5
最初~mofumofu2までを取得
key1 value1
key2 value2
key3 value3
key4 value4
key5 value5
mofumofu1 mofumofu-value1
mofumofu2 mofumofu-value2
key3以降を取得
key3 value3
key4 value4
key5 value5
mofumofu1 mofumofu-value1
mofumofu2 mofumofu-value2
mofumofu3 mofumofu-value3
mofumofu4 mofumofu-value4
mofumofu5 mofumofu-value5
key2~key3を取得
key2 value2
key3 value3
mofumofu*を取得
mofumofu1 mofumofu-value1
mofumofu2 mofumofu-value2
mofumofu3 mofumofu-value3
mofumofu4 mofumofu-value4
mofumofu5 mofumofu-value5
指定したキー(key2)を削除
存在しないキー(hogehoge2)を削除
全て取得
key1 value1
key3 value3
key4 value4
key5 value5
mofumofu1 mofumofu-value1
mofumofu2 mofumofu-value2
mofumofu3 mofumofu-value3
mofumofu4 mofumofu-value4
mofumofu5 mofumofu-value5
Go version: go1.17.7
Program exited.
|
実行環境
1
2
|
Go Playgroundで実行
Go version go1.17.7
|