boltdb 学习和实践
golang boltdb的学习和实践1. 安装go get github.com/boltdb/bolt 2.创建和启动数据库db,err := bolt.Open("my.db",0600,nil) 其中 因此单个事务和从它们创建的所有对象(例如桶、键)都不是线程安全的。与数据在多个概念你必须为每一个或使用锁机制来保证只有一个goroutine里操作改变数据。 3.读写事务boltdb的读写事务操作我们可以使用 err := db.Update(func(tx *bolt.Tx) error { ... return nil }) 在闭包fun中,在结束时返回nil来提交事务。您还可以通过返回一个错误在任何点回滚事务。所有数据库操作都允许在读写事务中进行。 4.批量读写事物每一次新的事物都需要等待上一次事物的结束,这种开销我们可以通过 err := db.Batch(func(tx *bolt.Tx) error { ... return nil }) 在批处理过程中如果某个事务失败了,批处理会多次调用这个函数函数返回成功则成功。如果中途失败了,则整个事务会回滚。 5.只读事务只读事务可以使用 err := db.View(func(tx *bolt.Tx) error { ... return nil }) 不改变数据的操作都可以通过只读事务来完成, 您只能检索桶、检索值,或在只读事务中复制数据库。 6.启动事务
// 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.使用桶桶是数据库中键/值对的集合。桶中的所有键必须是唯一的。您可以使用 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 }) 你也可以是实用 8.使用k-v对存储键值对到桶里可以使用 db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte("MyFriendsBucket")) err := b.Put([]byte("one"),[]byte("zhangsan")) return err }) 获取键值 db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte("MyFriendsBucket")) v := b.Get([]byte("one")) fmt.Printf("The answer is: %sn",v) return nil })
9.桶的自增利用 func (s *Store) CreateUser(u *User) error { return s.db.Update(func(tx *bolt.Tx) error { // 创建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以桶中的字节排序顺序存储键。这使得在这些键上的顺序迭代非常快。要遍历键,我们将使用游标 db.View(func(tx *bolt.Tx) error { // Assume bucket exists and has keys b := tx.Bucket([]byte("MyBucket")) c := b.Cursor() for k,v := c.First(); k != nil; k,v = c.Next() { fmt.Printf("key=%s,value=%sn",k,v) } return nil }) 游标 First() 移动到第一个健. Last() 移动到最后一个健. Seek() 移动到特定的一个健. Next() 移动到下一个健. Prev() 移动到上一个健. 这些函数中的每一个都返回一个包含(key []byte,value []byte)的签名。当你有光标迭代结束,next()将返回一个nil。在调用next()或prev()之前,你必须寻求一个位置使用first(),last(),或seek()。如果您不寻求位置,则这些函数将返回一个nil键。 11.前缀扫描遍历一个key的前缀,你可以结合 db.View(func(tx *bolt.Tx) error { // Assume bucket exists and has keys c := tx.Bucket([]byte("MyBucket")).Cursor() prefix := []byte("1234") for k,v := c.Seek(prefix); bytes.HasPrefix(k,prefix); k,v) } return nil }) 12.范围扫描另一个常见的用例是扫描范围,例如时间范围。如果你使用一个合适的时间编码,如rfc3339然后可以查询特定日期范围的数据: 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: %sn",v) } return nil }) 13.循环遍历每一个如果你知道所在桶中拥有键,你也可以使用 db.View(func(tx *bolt.Tx) error { // Assume bucket exists and has keys b := tx.Bucket([]byte("MyBucket")) b.ForEach(func(k,v []byte) error { fmt.Printf("key=%s,v) return nil }) return nil }) 14.嵌套桶还可以在一个键中存储一个桶,以创建嵌套的桶: func (*Bucket) CreateBucket(key []byte) (*Bucket,error) func (*Bucket) CreateBucketIfNotExists(key []byte) (*Bucket,error) func (*Bucket) DeleteBucket(key []byte) error 15.数据库备份boltdb是一个单一的文件,所以很容易备份。你可以使用 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) } } 然后您可以使用此命令进行备份: 16.统计数据库对运行的许多内部操作保持一个运行计数,这样您就可以更好地了解发生了什么。通过捕捉这些数据的快照,我们可以看到在这个时间范围内执行了哪些操作。 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.只读模式有时创建一个共享的只读boltdb数据库是有用的。对此,设置options.readonly国旗打开数据库时。只读模式使用共享锁允许多个进程从数据库中读取,但它将阻塞任何以读写方式打开数据库的进程。 db,0666,&bolt.Options{ReadOnly: true}) if err != nil { log.Fatal(err) } 18.移动端支持(ios/android)boltdb能够运行在移动设备上利用的工具结合特征GoMobile。创建一个结构体,包含您的数据库逻辑和参考一个bolt.db与初始化contstructor需要在文件路径,数据库文件将存储。使用这种方法,Android和iOS都不需要额外的权限或清理。 func NewBoltDB(filepath string) *BoltDB { db,err := bolt.Open(filepath+"/demo.db",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() } 数据库逻辑应定义为此包装器结构中的方法。 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.下载工具 2.命令行工具 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |