boltdb 学习和实践
golang boltdb的學(xué)習(xí)和實(shí)踐
1. 安裝
go get github.com/boltdb/bolt
2.創(chuàng)建和啟動(dòng)數(shù)據(jù)庫(kù)
db, err := bolt.Open("my.db", 0600, nil)其中open的第一個(gè)參數(shù)為路徑,如果數(shù)據(jù)庫(kù)不存在則會(huì)創(chuàng)建名為my.db的數(shù)據(jù)庫(kù), 第二個(gè)為文件操作,第三個(gè)參數(shù)是可選參數(shù), 內(nèi)部可以配置只讀和超時(shí)時(shí)間等,
特別需要注意的地方就是因?yàn)閎oltdb是文件操作類(lèi)型的數(shù)據(jù)庫(kù),所以只能單點(diǎn)寫(xiě)入和讀取,如果多個(gè)同時(shí)操作的話(huà)后者會(huì)被掛起直到前者關(guān)閉操作為止, boltdb一次只允許一個(gè)讀寫(xiě)事務(wù),但一次允許多個(gè)只讀事務(wù)。所以數(shù)據(jù)具有較強(qiáng)的一致性。
因此單個(gè)事務(wù)和從它們創(chuàng)建的所有對(duì)象(例如桶、鍵)都不是線(xiàn)程安全的。與數(shù)據(jù)在多個(gè)概念你必須為每一個(gè)或使用鎖機(jī)制來(lái)保證只有一個(gè)goroutine里操作改變數(shù)據(jù)。
只讀事務(wù)和讀寫(xiě)事物通常不應(yīng)該在同一個(gè)goroutine里同時(shí)打開(kāi)。由于讀寫(xiě)事務(wù)需要周期性地重新映射數(shù)據(jù)文件,這可能導(dǎo)致死鎖。
3.讀寫(xiě)事務(wù)
boltdb的讀寫(xiě)事務(wù)操作我們可以使用DB.Update()來(lái)完成形如:
err := db.Update(func(tx *bolt.Tx) error {...return nil })在閉包fun中,在結(jié)束時(shí)返回nil來(lái)提交事務(wù)。您還可以通過(guò)返回一個(gè)錯(cuò)誤在任何點(diǎn)回滾事務(wù)。所有數(shù)據(jù)庫(kù)操作都允許在讀寫(xiě)事務(wù)中進(jìn)行。
始終要關(guān)注err返回,因?yàn)樗鼘?bào)告導(dǎo)致您的事務(wù)不能完成的所有磁盤(pán)故障。
4.批量讀寫(xiě)事物
每一次新的事物都需要等待上一次事物的結(jié)束,這種開(kāi)銷(xiāo)我們可以通過(guò)DB.Batch()批處理來(lái)完成
err := db.Batch(func(tx *bolt.Tx) error {...return nil })在批處理過(guò)程中如果某個(gè)事務(wù)失敗了,批處理會(huì)多次調(diào)用這個(gè)函數(shù)函數(shù)返回成功則成功。如果中途失敗了,則整個(gè)事務(wù)會(huì)回滾。
5.只讀事務(wù)
只讀事務(wù)可以使用DB.View()來(lái)完成
err := db.View(func(tx *bolt.Tx) error {...return nil })不改變數(shù)據(jù)的操作都可以通過(guò)只讀事務(wù)來(lái)完成, 您只能檢索桶、檢索值,或在只讀事務(wù)中復(fù)制數(shù)據(jù)庫(kù)。
6.啟動(dòng)事務(wù)
DB.Begin()啟動(dòng)函數(shù)包含在db.update和db.batch中,該函數(shù)啟動(dòng)事務(wù)開(kāi)始執(zhí)行事務(wù)并返回結(jié)果關(guān)閉事務(wù),這是boltdb推薦的方式,有時(shí)候你可能需要手動(dòng)啟動(dòng)事物你可以使用Tx.Begin()來(lái)開(kāi)始,切記不要忘記關(guān)閉事務(wù)。
// Start a writable transaction. tx, err := db.Begin(true) if err != nil {return err } defer tx.Rollback()// Use the transaction... _, err := tx.CreateBucket([]byte("MyBucket")) if err != nil {return err }// Commit the transaction and check for error. if err := tx.Commit(); err != nil {return err }7.使用桶
桶是數(shù)據(jù)庫(kù)中鍵/值對(duì)的集合。桶中的所有鍵必須是唯一的。您可以使用DB.CreateBucket()創(chuàng)建一個(gè)桶:
db.Update(func(tx *bolt.Tx) error {b, err := tx.CreateBucket([]byte("MyBucket"))if err != nil {return fmt.Errorf("create bucket: %s", err)}return nil })你也可以是實(shí)用Tx.CreateBucketIfNotExists()來(lái)創(chuàng)建桶,該函數(shù)會(huì)先判斷是否已經(jīng)存在該桶不存在即創(chuàng)建, 刪除桶可以使用Tx.DeleteBucket()來(lái)完成
8.使用k-v對(duì)
存儲(chǔ)鍵值對(duì)到桶里可以使用Bucket.Put()來(lái)完成:
db.Update(func(tx *bolt.Tx) error {b := tx.Bucket([]byte("MyFriendsBucket"))err := b.Put([]byte("one"), []byte("zhangsan"))return err })獲取鍵值Bucket.Get():
db.View(func(tx *bolt.Tx) error {b := tx.Bucket([]byte("MyFriendsBucket"))v := b.Get([]byte("one"))fmt.Printf("The answer is: %s\n", v)return nil })get()函數(shù)不返回一個(gè)錯(cuò)誤,因?yàn)樗倪\(yùn)行是保證工作(除非有某種系統(tǒng)故障)。如果鍵存在,那么它將返回它的值。如果它不存在,那么它將返回nil。
還需要注意的是當(dāng)事務(wù)打開(kāi)都get返回的值時(shí)唯一有效的,如果你需要將該值用于其他事務(wù),你可以通過(guò)copy拷貝到其他的byte slice中
9.桶的自增
利用nextsequence()功能,你可以讓boltdb生成序列作為你鍵值對(duì)的唯一標(biāo)識(shí)。見(jiàn)下面的示例。
func (s *Store) CreateUser(u *User) error {return s.db.Update(func(tx *bolt.Tx) error {// 創(chuàng)建users桶b := tx.Bucket([]byte("users"))// 生成自增序列id, _ = b.NextSequence()u.ID = int(id)// Marshal user data into bytes.buf, err := json.Marshal(u)if err != nil {return err}// Persist bytes to users bucket.return b.Put(itob(u.ID), buf)}) }// itob returns an 8-byte big endian representation of v. func itob(v int) []byte {b := make([]byte, 8)binary.BigEndian.PutUint64(b, uint64(v))return b }type User struct {ID int... }10. 迭代鍵
boltdb以桶中的字節(jié)排序順序存儲(chǔ)鍵。這使得在這些鍵上的順序迭代非常快。要遍歷鍵,我們將使用游標(biāo)Cursor():
db.View(func(tx *bolt.Tx) error {// Assume bucket exists and has keysb := tx.Bucket([]byte("MyBucket"))c := b.Cursor()for k, v := c.First(); k != nil; k, v = c.Next() {fmt.Printf("key=%s, value=%s\n", k, v)}return nil })游標(biāo)Cursor()允許您移動(dòng)到鍵列表中的特定點(diǎn),并一次一個(gè)地通過(guò)操作鍵前進(jìn)或后退。
光標(biāo)上有以下函數(shù):
這些函數(shù)中的每一個(gè)都返回一個(gè)包含(key []byte, value []byte)的簽名。當(dāng)你有光標(biāo)迭代結(jié)束,next()將返回一個(gè)nil。在調(diào)用next()或prev()之前,你必須尋求一個(gè)位置使用first(),last(),或seek()。如果您不尋求位置,則這些函數(shù)將返回一個(gè)nil鍵。
在迭代過(guò)程中,如果鍵為非零,但值為0,則意味著鍵指向一個(gè)桶而不是一個(gè)值。用桶.bucket()訪(fǎng)問(wèn)子桶。
11.前綴掃描
遍歷一個(gè)key的前綴,你可以結(jié)合seek()和bytes.hasprefix():
db.View(func(tx *bolt.Tx) error {// Assume bucket exists and has keysc := tx.Bucket([]byte("MyBucket")).Cursor()prefix := []byte("1234")for k, v := c.Seek(prefix); bytes.HasPrefix(k, prefix); k, v = c.Next() {fmt.Printf("key=%s, value=%s\n", k, v)}return nil })12.范圍掃描
另一個(gè)常見(jiàn)的用例是掃描范圍,例如時(shí)間范圍。如果你使用一個(gè)合適的時(shí)間編碼,如rfc3339然后可以查詢(xún)特定日期范圍的數(shù)據(jù):
db.View(func(tx *bolt.Tx) error {// Assume our events bucket exists and has RFC3339 encoded time keys.c := tx.Bucket([]byte("Events")).Cursor()// Our time range spans the 90's decade.min := []byte("1990-01-01T00:00:00Z")max := []byte("2000-01-01T00:00:00Z")// Iterate over the 90's.for k, v := c.Seek(min); k != nil && bytes.Compare(k, max) <= 0; k, v = c.Next() {fmt.Printf("%s: %s\n", k, v)}return nil })13.循環(huán)遍歷每一個(gè)
如果你知道所在桶中擁有鍵,你也可以使用ForEach()來(lái)迭代:
db.View(func(tx *bolt.Tx) error {// Assume bucket exists and has keysb := tx.Bucket([]byte("MyBucket"))b.ForEach(func(k, v []byte) error {fmt.Printf("key=%s, value=%s\n", k, v)return nil})return nil })14.嵌套桶
還可以在一個(gè)鍵中存儲(chǔ)一個(gè)桶,以創(chuàng)建嵌套的桶:
func (*Bucket) CreateBucket(key []byte) (*Bucket, error) func (*Bucket) CreateBucketIfNotExists(key []byte) (*Bucket, error) func (*Bucket) DeleteBucket(key []byte) error15.數(shù)據(jù)庫(kù)備份
boltdb是一個(gè)單一的文件,所以很容易備份。你可以使用TX.writeto()函數(shù)寫(xiě)一致的數(shù)據(jù)庫(kù)。如果從只讀事務(wù)調(diào)用這個(gè)函數(shù),它將執(zhí)行熱備份,而不會(huì)阻塞其他數(shù)據(jù)庫(kù)的讀寫(xiě)操作。
默認(rèn)情況下,它將使用一個(gè)常規(guī)文件句柄,該句柄將利用操作系統(tǒng)的頁(yè)面緩存。有關(guān)優(yōu)化大于RAM數(shù)據(jù)集的信息,請(qǐng)參見(jiàn)Tx文檔。
一個(gè)常見(jiàn)的用例是在HTTP上進(jìn)行備份,這樣您就可以使用像cURL這樣的工具來(lái)進(jìn)行數(shù)據(jù)庫(kù)備份:
然后您可以使用此命令進(jìn)行備份:
$ curl http://localhost/backup > my.db
或者你可以打開(kāi)你的瀏覽器以http://localhost/backup,它會(huì)自動(dòng)下載。
如果你想備份到另一個(gè)文件,你可以使用TX.copyfile()輔助功能。
16.統(tǒng)計(jì)
數(shù)據(jù)庫(kù)對(duì)運(yùn)行的許多內(nèi)部操作保持一個(gè)運(yùn)行計(jì)數(shù),這樣您就可以更好地了解發(fā)生了什么。通過(guò)捕捉這些數(shù)據(jù)的快照,我們可以看到在這個(gè)時(shí)間范圍內(nèi)執(zhí)行了哪些操作。
例如,我們可以開(kāi)始一個(gè)goroutine里記錄統(tǒng)計(jì)每10秒:
17.只讀模式
有時(shí)創(chuàng)建一個(gè)共享的只讀boltdb數(shù)據(jù)庫(kù)是有用的。對(duì)此,設(shè)置options.readonly國(guó)旗打開(kāi)數(shù)據(jù)庫(kù)時(shí)。只讀模式使用共享鎖允許多個(gè)進(jìn)程從數(shù)據(jù)庫(kù)中讀取,但它將阻塞任何以讀寫(xiě)方式打開(kāi)數(shù)據(jù)庫(kù)的進(jìn)程。
db, err := bolt.Open("my.db", 0666, &bolt.Options{ReadOnly: true}) if err != nil {log.Fatal(err) }18.移動(dòng)端支持(ios/android)
boltdb能夠運(yùn)行在移動(dòng)設(shè)備上利用的工具結(jié)合特征GoMobile。創(chuàng)建一個(gè)結(jié)構(gòu)體,包含您的數(shù)據(jù)庫(kù)邏輯和參考一個(gè)bolt.db與初始化contstructor需要在文件路徑,數(shù)據(jù)庫(kù)文件將存儲(chǔ)。使用這種方法,Android和iOS都不需要額外的權(quán)限或清理。
func NewBoltDB(filepath string) *BoltDB {db, err := bolt.Open(filepath+"/demo.db", 0600, nil)if err != nil {log.Fatal(err)}return &BoltDB{db} }type BoltDB struct {db *bolt.DB... }func (b *BoltDB) Path() string {return b.db.Path() }func (b *BoltDB) Close() {b.db.Close() }數(shù)據(jù)庫(kù)邏輯應(yīng)定義為此包裝器結(jié)構(gòu)中的方法。
要從本機(jī)語(yǔ)言初始化此結(jié)構(gòu)(兩個(gè)平臺(tái)現(xiàn)在都將本地存儲(chǔ)與云同步)。這些片段禁用數(shù)據(jù)庫(kù)文件的功能):
Android
IOS
- (void)demo {NSString* path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask,YES) objectAtIndex:0];GoBoltmobiledemoBoltDB * demo = GoBoltmobiledemoNewBoltDB(path);[self addSkipBackupAttributeToItemAtPath:demo.path];//Some DB Logic would go here[demo close]; }- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *) filePathString {NSURL* URL= [NSURL fileURLWithPath: filePathString];assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);NSError *error = nil;BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]forKey: NSURLIsExcludedFromBackupKey error: &error];if(!success){NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);}return success; }19.查看工具
1.下載工具
go get github.com/boltdb/boltd
然后編譯cmd下的main文件生成可執(zhí)行文件改名為boltd
拷貝boltd到 *.db同級(jí)目錄,執(zhí)行如下:
然后打開(kāi)網(wǎng)站:
2.命令行工具
https://github.com/hasit/bolter
boltdb基礎(chǔ)學(xué)習(xí)暫時(shí)就這么多,下一章開(kāi)始實(shí)踐
總結(jié)
以上是生活随笔為你收集整理的boltdb 学习和实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 造句简单_造句游戏的心理投射:被现代心理
- 下一篇: 使命召唤16正在获取cdn地址_玩机不求