日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

boltdb 学习和实践

發(fā)布時間:2023/12/19 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 boltdb 学习和实践 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

golang boltdb的學(xué)習(xí)和實踐

1. 安裝

go get github.com/boltdb/bolt

2.創(chuàng)建和啟動數(shù)據(jù)庫

db, err := bolt.Open("my.db", 0600, nil)

其中open的第一個參數(shù)為路徑,如果數(shù)據(jù)庫不存在則會創(chuàng)建名為my.db的數(shù)據(jù)庫, 第二個為文件操作,第三個參數(shù)是可選參數(shù), 內(nèi)部可以配置只讀和超時時間等,
特別需要注意的地方就是因為boltdb是文件操作類型的數(shù)據(jù)庫,所以只能單點寫入和讀取,如果多個同時操作的話后者會被掛起直到前者關(guān)閉操作為止, boltdb一次只允許一個讀寫事務(wù),但一次允許多個只讀事務(wù)。所以數(shù)據(jù)具有較強的一致性。

因此單個事務(wù)和從它們創(chuàng)建的所有對象(例如桶、鍵)都不是線程安全的。與數(shù)據(jù)在多個概念你必須為每一個或使用鎖機制來保證只有一個goroutine里操作改變數(shù)據(jù)。
只讀事務(wù)和讀寫事物通常不應(yīng)該在同一個goroutine里同時打開。由于讀寫事務(wù)需要周期性地重新映射數(shù)據(jù)文件,這可能導(dǎo)致死鎖。

3.讀寫事務(wù)

boltdb的讀寫事務(wù)操作我們可以使用DB.Update()來完成形如:

err := db.Update(func(tx *bolt.Tx) error {...return nil })

在閉包fun中,在結(jié)束時返回nil來提交事務(wù)。您還可以通過返回一個錯誤在任何點回滾事務(wù)。所有數(shù)據(jù)庫操作都允許在讀寫事務(wù)中進行。
始終要關(guān)注err返回,因為它將報告導(dǎo)致您的事務(wù)不能完成的所有磁盤故障。

4.批量讀寫事物

每一次新的事物都需要等待上一次事物的結(jié)束,這種開銷我們可以通過DB.Batch()批處理來完成

err := db.Batch(func(tx *bolt.Tx) error {...return nil })

在批處理過程中如果某個事務(wù)失敗了,批處理會多次調(diào)用這個函數(shù)函數(shù)返回成功則成功。如果中途失敗了,則整個事務(wù)會回滾。

5.只讀事務(wù)

只讀事務(wù)可以使用DB.View()來完成

err := db.View(func(tx *bolt.Tx) error {...return nil })

不改變數(shù)據(jù)的操作都可以通過只讀事務(wù)來完成, 您只能檢索桶、檢索值,或在只讀事務(wù)中復(fù)制數(shù)據(jù)庫。

6.啟動事務(wù)

DB.Begin()啟動函數(shù)包含在db.update和db.batch中,該函數(shù)啟動事務(wù)開始執(zhí)行事務(wù)并返回結(jié)果關(guān)閉事務(wù),這是boltdb推薦的方式,有時候你可能需要手動啟動事物你可以使用Tx.Begin()來開始,切記不要忘記關(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ù)庫中鍵/值對的集合。桶中的所有鍵必須是唯一的。您可以使用DB.CreateBucket()創(chuàng)建一個桶:

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 })

你也可以是實用Tx.CreateBucketIfNotExists()來創(chuàng)建桶,該函數(shù)會先判斷是否已經(jīng)存在該桶不存在即創(chuàng)建, 刪除桶可以使用Tx.DeleteBucket()來完成

8.使用k-v對

存儲鍵值對到桶里可以使用Bucket.Put()來完成:

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ù)不返回一個錯誤,因為它的運行是保證工作(除非有某種系統(tǒng)故障)。如果鍵存在,那么它將返回它的值。如果它不存在,那么它將返回nil。
還需要注意的是當事務(wù)打開都get返回的值時唯一有效的,如果你需要將該值用于其他事務(wù),你可以通過copy拷貝到其他的byte slice中

9.桶的自增

利用nextsequence()功能,你可以讓boltdb生成序列作為你鍵值對的唯一標識。見下面的示例。

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é)排序順序存儲鍵。這使得在這些鍵上的順序迭代非常快。要遍歷鍵,我們將使用游標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 })

游標Cursor()允許您移動到鍵列表中的特定點,并一次一個地通過操作鍵前進或后退。
光標上有以下函數(shù):

First() 移動到第一個健. Last() 移動到最后一個健. Seek() 移動到特定的一個健. Next() 移動到下一個健. Prev() 移動到上一個健.

這些函數(shù)中的每一個都返回一個包含(key []byte, value []byte)的簽名。當你有光標迭代結(jié)束,next()將返回一個nil。在調(diào)用next()或prev()之前,你必須尋求一個位置使用first(),last(),或seek()。如果您不尋求位置,則這些函數(shù)將返回一個nil鍵。
在迭代過程中,如果鍵為非零,但值為0,則意味著鍵指向一個桶而不是一個值。用桶.bucket()訪問子桶。

11.前綴掃描

遍歷一個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.范圍掃描

另一個常見的用例是掃描范圍,例如時間范圍。如果你使用一個合適的時間編碼,如rfc3339然后可以查詢特定日期范圍的數(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)遍歷每一個

如果你知道所在桶中擁有鍵,你也可以使用ForEach()來迭代:

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.嵌套桶

還可以在一個鍵中存儲一個桶,以創(chuàng)建嵌套的桶:

func (*Bucket) CreateBucket(key []byte) (*Bucket, error) func (*Bucket) CreateBucketIfNotExists(key []byte) (*Bucket, error) func (*Bucket) DeleteBucket(key []byte) error

15.數(shù)據(jù)庫備份

boltdb是一個單一的文件,所以很容易備份。你可以使用TX.writeto()函數(shù)寫一致的數(shù)據(jù)庫。如果從只讀事務(wù)調(diào)用這個函數(shù),它將執(zhí)行熱備份,而不會阻塞其他數(shù)據(jù)庫的讀寫操作。
默認情況下,它將使用一個常規(guī)文件句柄,該句柄將利用操作系統(tǒng)的頁面緩存。有關(guān)優(yōu)化大于RAM數(shù)據(jù)集的信息,請參見Tx文檔。
一個常見的用例是在HTTP上進行備份,這樣您就可以使用像cURL這樣的工具來進行數(shù)據(jù)庫備份:

func BackupHandleFunc(w http.ResponseWriter, req *http.Request) {err := db.View(func(tx *bolt.Tx) error {w.Header().Set("Content-Type", "application/octet-stream")w.Header().Set("Content-Disposition", `attachment; filename="my.db"`)w.Header().Set("Content-Length", strconv.Itoa(int(tx.Size())))_, err := tx.WriteTo(w)return err})if err != nil {http.Error(w, err.Error(), http.StatusInternalServerError)} }

然后您可以使用此命令進行備份:
$ curl http://localhost/backup > my.db
或者你可以打開你的瀏覽器以http://localhost/backup,它會自動下載。
如果你想備份到另一個文件,你可以使用TX.copyfile()輔助功能。

16.統(tǒng)計

數(shù)據(jù)庫對運行的許多內(nèi)部操作保持一個運行計數(shù),這樣您就可以更好地了解發(fā)生了什么。通過捕捉這些數(shù)據(jù)的快照,我們可以看到在這個時間范圍內(nèi)執(zhí)行了哪些操作。
例如,我們可以開始一個goroutine里記錄統(tǒng)計每10秒:

go func() {// Grab the initial stats.prev := db.Stats()for {// Wait for 10s.time.Sleep(10 * time.Second)// Grab the current stats and diff them.stats := db.Stats()diff := stats.Sub(&prev)// Encode stats to JSON and print to STDERR.json.NewEncoder(os.Stderr).Encode(diff)// Save stats for the next loop.prev = stats}

17.只讀模式

有時創(chuàng)建一個共享的只讀boltdb數(shù)據(jù)庫是有用的。對此,設(shè)置options.readonly國旗打開數(shù)據(jù)庫時。只讀模式使用共享鎖允許多個進程從數(shù)據(jù)庫中讀取,但它將阻塞任何以讀寫方式打開數(shù)據(jù)庫的進程。

db, err := bolt.Open("my.db", 0666, &bolt.Options{ReadOnly: true}) if err != nil {log.Fatal(err) }

18.移動端支持(ios/android)

boltdb能夠運行在移動設(shè)備上利用的工具結(jié)合特征GoMobile。創(chuàng)建一個結(jié)構(gòu)體,包含您的數(shù)據(jù)庫邏輯和參考一個bolt.db與初始化contstructor需要在文件路徑,數(shù)據(jù)庫文件將存儲。使用這種方法,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ù)庫邏輯應(yīng)定義為此包裝器結(jié)構(gòu)中的方法。
要從本機語言初始化此結(jié)構(gòu)(兩個平臺現(xiàn)在都將本地存儲與云同步)。這些片段禁用數(shù)據(jù)庫文件的功能):
Android

String path; if (android.os.Build.VERSION.SDK_INT >=android.os.Build.VERSION_CODES.LOLLIPOP){path = getNoBackupFilesDir().getAbsolutePath(); } else{path = getFilesDir().getAbsolutePath(); } Boltmobiledemo.BoltDB boltDB = Boltmobiledemo.NewBoltDB(path)

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同級目錄,執(zhí)行如下:

然后打開網(wǎng)站:

2.命令行工具
https://github.com/hasit/bolter
boltdb基礎(chǔ)學(xué)習(xí)暫時就這么多,下一章開始實踐

總結(jié)

以上是生活随笔為你收集整理的boltdb 学习和实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。