• 还在篮子里

    How boltdb Write its Data? · 语雀

    How boltdb Write its Data?

    A Ha!

    Here’re three questions during reading the source code of BoltDB. I’ll explain our testing procedure to dive into the core of BoltDB writing machanism?

    Code link: yhyddr/quicksilver

    First

    • First, mentor asked me :”Did BoltDB have a temporary file when starting a read/write transaction”

    Following the quickstart, all data is stored in a file on disk.

    so have this question , what happened when we store or view data on this file.

    mentor meaned that some methods to change file like tempory file method ,have a copy file to update. When some operations end , it file would replace origin file to realize store/commit.

    Let’s check if boltdb use this method.

    FileSystem Notify

    I heard it from my mentor about how to watch files or diretories by simple go file.

    use this package fsnotify/fsnotify on github.

    I write a small go binary file to log current directory’s file’s change.

    package main
    
    import (
      "log"
    
      "github.com/fsnotify/fsnotify"
    )
    
    func main() {
      hang := make(chan bool)
      watcher, err := fsnotify.NewWatcher()
      if err != nil {
        log.Fatal(err)
      }
    
      watcher.Add("./")
    
      go func() {
        for {
          select {
          case e := <-watcher.Events:
            log.Println(e.Op.String(), e.Name)
          case err := <-watcher.Errors:
            log.Println(err)
          }
        }
      }()
    
      <-hang
    }
    

    use upon code and go build then we have fsnotify (I rename my binary file this name)

    it could tell us what happened on current directory.If you know how to wirte go code , you could change it to watch any file or directory you like by yoursely.

    I’ll show how it works

    Update Boltdb file

    then we need write code to store some data.This way we update boltdb file and know if there has a tempory file to replace origin file. Because it’s obviously that boltdb as simple kv database seems do not have access on system directory but just current directory and the database file.

    We Write code below to insert data :

    package main
    
    import (
      "encoding/binary"
      "log"
      "os"
      "time"
    
      bolt "go.etcd.io/bbolt"
    )
    
    var InsertNum int = 15000
    
    func main() {
      hang := make(chan bool)
      db, err := bolt.Open("./data.db", 0600, nil)
      if err != nil {
        log.Fatal(err)
      }
    
      go func() {
        times := time.Now()
        for {
          db.Update(func(tx *bolt.Tx) error {
            b, err := tx.CreateBucketIfNotExists([]byte("cats"))
            if err != nil {
              return err
            }
    
            num, err := b.NextSequence()
            log.Println(num)
            byteid := make([]byte, 8)
            binary.BigEndian.PutUint64(byteid, num)
    
            b.Put(byteid, byteid)
    
            if num == InsertNum {
              log.Println(time.Now().Sub(times))
              os.Exit(0)
            }
            return nil
          })
        }
    
      }()
    
      <-hang
    }

    attention I use variable InsertNum controll how many data could be inserted.

    then we run it.(sure you need run fsnotify first.)

    image.png

    we inserted 900 data and it cost us 20s.find that it’s just write data.db file. It means that we seems to have anwser to the first question : No!

    In addition , we also could get the answer by source code.

    Second

    We have known the first question’s answer, mentor quickly asked me the second.

    • How much data or Which size could a boltdb file store ?

    we want know how much.

    let’s find it from source code. BoltDB declare these sizes on the head of go file

    image.png

    and we got the miniFillPercent and maxFillPercent are 0.1 and 1.0, in db.go we also know about boltdb page size is determined by OS page size.

    image.png

    It means a page may be used some part.

    important that maxMmanpstep = 1 <<30 is 1 GB for remapping.

    we have know there’s no temporary file that means BoltDB use memory or called Mmap to host temporary data.

    use Mmap means , your BoltDB File should not bigger than your assignable memory space.

    Third

    From upon questions , we know a lot about bolddb. But mentor still have a question:

    • in a node , author set 50% capcity limit otherwise spill it to two nodes.Do our datafile just use 50% space what a boltdb database file hold?

    image.png

    It’s because the stragecy about node spill method to controll size of node do not overflow OS page size.

    that question need we know about the disk layout of BoltDB file . I’ll detailed explanation in my next article. Of course, we know the space ratio should not over 0.5.

    and now I just give some data for you.

    I first insert 100000 data which is auto increment key and the same value.

    1 -> 100000 total 977784 b because of BoltDB store in file as bytes.

    and we found our data file use 8.5m space.

    then I insert 100000 again.

    image.png

    100000 -> 200000 and file expand to 25.2m when I insert nearly 110000th data.

    And at last file is 25.2m when hosted 200000 data.

    image.png

    来源: How boltdb Write its Data? · 语雀

  • 还在篮子里

    BoltDB 使用入门实践 – 知乎

    BoltDB 使用入门实践

    Getting Started

    安装

    go get go.etcd.io/bbolt/...

    会 get 两项

    1. go package -> $GOPATH
    2. bolt command line -> $GOBIN

    Open Database

    使用 kv 数据库都很简单,只需要一个文件路径即可搭建完成环境。

    package main
    
    import (
        "log"
    
        bolt "go.etcd.io/bbolt"
    )
    
    func main() {
        // Open the my.db data file in your current directory.
        // It will be created if it doesn't exist.
        db, err := bolt.Open("my.db", 0600, nil)
        if err != nil {
            log.Fatal(err)
        }
        defer db.Close()
    
        ...
    }

    这里到 db 不支持多链接。这是因为对于 database file 一个链接保持了一个文件锁 file lock

    如果并发,后续链接会阻塞。

    可以为单个链接添加 超时控制

    db, err := bolt.Open("my.db", 0600, &bolt.Options{Timeout: 1 * time.Second})

    Transaction

    本文无关

    与 google 的 levelDB 不同,bbolt 支持事务。 detail bolt 优缺点:detail 同时 bbolt 出自 bolt ,没太多不同,只是 bbolt 目前还在维护。

    事务

    并发读写

    同时只能有

    • 一个读写事务
    • 多个只读事务

    actions⚠️:在事务开始时,会保持一个数据视图 这意味着事务处理过程中不会由于别处更改而改变

    线程安全

    单个事务和它所创建的所有对象(桶,键)都不是线程安全的。

    建议加锁 或者 每一个 goroutine 并发都开启 一个 事务

    当然,从 db 这个 bbolt 的顶级结构创建 事务 是 线程安全 的

    死锁

    前面提到的 读写事务 和 只读事务 拒绝相互依赖。当然也不能在同一个 goroutine 里。

    死锁原因是 读写事务 需要周期性重新映射 data 文件(即database)。这在开启只读事务时是不可行的。

    读写事务

    使用 db.Update开启一个读写事务

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

    上文提过,在一个事务中 ,数据视图是一样的。 (详细解释就是,在这个函数作用域中,数据对你呈现最终一致性)

    返回值

    bboltdb 根据你的返回值判断事务状态,你可以添加任意逻辑并认为出错时返回 return err bboltdb 会回滚,如果 return nil 则提交你的事务。

    建议永远检查 Update 的返回值,因为他会返回如 硬盘压力 等造成事务失败的信息(这是在你的逻辑之外的情况)

    ⚠️:你自定义返回 error 的 error 信息同样会被传递出来。

    只读事务

    使用 db.View 来新建一个 只读事务

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

    同上,你会获得一个一致性的数据视图。

    当然,只读事务 只能检索信息,不会有任何更改。(btw,但是你可以 copy 一个 database 的副本,毕竟这只需要读数据)

    批量读写事务

    读写事务 db.Update 最后需要对 database提交更改,这会等待硬盘就绪。

    每一次文件读写都是和磁盘交互。这不是一个小开销。

    你可以使用 db.Batch 开启一个 批处理事务。他会在最后批量提交(其实是多个 goroutines 开启 db.Batch 事务时有机会合并之后一起提交)从而减小了开销。 ⚠️:db.Batch 只对 goroutine 起效

    使用 批处理事务 需要做取舍,用 幂等函数 换取 速度 ⚠️: db.Batch 在一部分事务失败的时候会尝试多次调用那些事务函数,如果不是幂等会造成不可预知的非最终一致性。

    例:使用事务外的变量来使你的日志不那么奇怪

    var id uint64
    err := db.Batch(func(tx *bolt.Tx) error {
        // Find last key in bucket, decode as bigendian uint64, increment
        // by one, encode back to []byte, and add new key.
        ...
        id = newValue
        return nil
    })
    if err != nil {
        return ...
    }
    fmt.Println("Allocated ID %d", id)

    手动事务

    可以手动进行事务的 开启 ,回滚,新建对象,提交等操作。因为本身 db.Update 和 db.View 就是他们的包装 ⚠️:手动事务记得 关闭 (Close)

    开启事务使用 db.Begin(bool) 同时参数代表着是否可以写操作。如下:

    • true – 读写事务
    • false – 只读事务
    // 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
    }

    Using Store 🔔

    Using Buckets

    桶是键值对的集合。在一个桶中,键值唯一。

    创建

    使用 Tx.CreateBucket() 和 Tx.CreateBucketIfNotExists() 建立一个新桶(推荐使用第二个) 接受参数是 桶的名字

    删除

    使用 Tx.DeleteBucket() 根据桶的名字来删除

    例子

    func main() {
        db, err := bbolt.Open("./data", 0666, nil)
        if err != nil {
            log.Fatal(err)
        }
        defer db.Close()
    
        db.Update(func(tx *bbolt.Tx) error {
            b, err := tx.CreateBucketIfNotExists([]byte("MyBucket"))
            if err != nil {
                return fmt.Errorf("create bucket: %v", err)
            }
    
            if err = tx.DeleteBucket([]byte("MyBucket")); err != nil {
                return err
            }
    
            return nil
        })
    
    }

    Using key/value pairs 🔔

    最重要的部分,就是 kv 存储怎么使用了,首先需要一个 桶 来存储键值对。

    Put

    使用Bucket.Put()来存储键值对,接收两个 []byte 类型的参数

    db.Update(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte("MyBucket"))
        err := b.Put([]byte("answer"), []byte("42"))
        return err
    })

    很明显,上面的例子设置了 Pair: key:answer value:42

    Get

    使用 Bucket.Get() 来查询键值。参数是一个 []byte(别忘了这次我们只是查询,可以使用 只读事务)

    db.View(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte("MyBucket"))
        v := b.Get([]byte("answer"))
        fmt.Printf("The answer is: %s\n", v)
        return nil
    })

    细心会注意到,Get是不会返回 error 的,这是因为 Get() 一定能正常工作(除非系统错误),相应的,当返回 nil 时,查询的键值对不存在。 ⚠️:注意 0 长度的值 和 不存在键值对 的行为是不一样的。(一个返回是 nil, 一个不是)

    func main() {
        db, err := bolt.Open("./data.db", 0666, nil)
        if err != nil {
            log.Fatal(err)
        }
        defer db.Close()
    
        err = db.Update(func(tx *bolt.Tx) error {
            b, err := tx.CreateBucketIfNotExists([]byte("MyBucket"))
            if err != nil {
                return fmt.Errorf("create bucket: %v", err)
            }
    
            if err = b.Put([]byte("answer"), []byte("42")); err != nil {
                return err
            }
    
            if err = b.Put([]byte("zero"), []byte("")); err != nil {
                return err
            }
    
            return nil
        })
    
        db.View(func(tx *bolt.Tx) error {
            b := tx.Bucket([]byte("MyBucket"))
            v := b.Get([]byte("noexists"))
            fmt.Println(reflect.DeepEqual(v, nil)) // false
            fmt.Println(v == nil)                  // true
    
            v = b.Get([]byte("zero"))
            fmt.Println(reflect.DeepEqual(v, nil)) // false
            fmt.Println(v == nil)                  // true
            return nil
        })
    }

    Delete

    使用 Bucket.Delete() 删除键值对

    db.View(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte("MyBucket"))
        fmt.Println(b.Get([]byte("answer")))
    
        err := b.Delete([]byte("answer"))
        if err != nil {
            return err
        }
        return nil
    })

    ⚠️: Get() 获取到的字节切片值只在当前事务(当前函数作用域)有效,如果要在其他事务中使用需要使用 copy() 将其拷贝到其他的字节切片

    Tricks

    桶的自增键

    使用 NextSequence()来创建自增键,见下例

    // CreateUser saves u to the store. The new user ID is set on u once the data is persisted.
    func (s *Store) CreateUser(u *User) error {
        return s.db.Update(func(tx *bolt.Tx) error {
            // Retrieve the users bucket.
            // This should be created when the DB is first opened.
            b := tx.Bucket([]byte("users"))
    
            // Generate ID for the user.
            // This returns an error only if the Tx is closed or not writeable.
            // That can't happen in an Update() call so I ignore the error check.
            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
        ...
    }

    嵌套桶

    很简单的,桶可以实现嵌套存储

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

    例子

    假设您有一个多租户应用程序,其中根级别存储桶是帐户存储桶。该存储桶内部有一系列帐户的序列,这些帐户本身就是存储桶。在序列存储桶(子桶)中,可能有许多相关的存储桶(Users,Note等)。

    // createUser creates a new user in the given account.
    func createUser(accountID int, u *User) error {
        // Start the transaction.
        tx, err := db.Begin(true)
        if err != nil {
            return err
        }
        defer tx.Rollback()
    
        // Retrieve the root bucket for the account.
        // Assume this has already been created when the account was set up.
        root := tx.Bucket([]byte(strconv.FormatUint(accountID, 10)))
    
        // Setup the users bucket.
        bkt, err := root.CreateBucketIfNotExists([]byte("USERS"))
        if err != nil {
            return err
        }
    
        // Generate an ID for the new user.
        userID, err := bkt.NextSequence()
        if err != nil {
            return err
        }
        u.ID = userID
    
        // Marshal and save the encoded user.
        if buf, err := json.Marshal(u); err != nil {
            return err
        } else if err := bkt.Put([]byte(strconv.FormatUint(u.ID, 10)), buf); err != nil {
            return err
        }
    
        // Commit the transaction.
        if err := tx.Commit(); err != nil {
            return err
        }
    
        return nil
    }

    遍历键值

    在桶中,键值对根据 键 的 值是有字节序的。 使用 Bucket.Cursor()对其进行迭代

    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=%s\n", k, v)
        }
    
        return nil
    })

    Cursor 有 5 种方法进行迭代

    1. First() Move to the first key.
    2. Last() Move to the last key.
    3. Seek() Move to a specific key.
    4. Next() Move to the next key.
    5. Prev() Move to the previous key.

    每一个方法都返回 (key []byte, value []byte) 两个值 当方法所指值不存在时返回 两个 nil 值,发生在以下情况:

    1. 迭代到最后一个键值对时,再一次调用 Cursor.Next()
    2. 当前所指为第一个键值对时,调用 Cursor.Prev()
    3. 当使用 4.Next() 和 5. Prev()方法而未使用 1.First() 2.Last() 3. Seek()指定初始位置时

    ⚠️特殊情况:当 key 为 非 nil 但 value 是 nil 是,说明这是嵌套桶,value 值是子桶,使用 Bucket.Bucket() 方法访问 子桶,参数是 key 值

    db.View(func(tx *bolt.Tx) error {
        c := b.Cursor()
        fmt.Println(c.First())
        k, v := c.Prev()
        fmt.Println(k == nil, v == nil) // true,true
    
        if k != nil && v == nil {
            subBucket := b.Bucket()
            // doanything
        }
        return nil
    })

    前缀遍历

    通过使用 Cursor我们能够做到一些特殊的遍历,如:遍历拥有特定前缀的 键值对

    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); k != nil && bytes.HasPrefix(k, prefix); k, v = c.Next() {
            fmt.Printf("key=%s, value=%s\n", k, v)
        }
    
        return nil
    })

    范围遍历

    在一个范围里遍历,如:使用可排序的时间编码(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: %s\n", k, v)
        }
    
        return nil
    })

    ⚠️:Golang 实现的 RFC3339Nano 是不可排序的

    ForEach

    在桶中有值的情况下,可以使用 ForEach()遍历

    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, value=%s\n", k, v)
            return nil
        })
        return nil
    })

    ⚠️:在 ForEach()中遍历的键值对需要copy()到事务外才能在事务外使用

    Advance

    Backup

    boltdb 是一个单一的文件,所以很容易备份。你可以使用Tx.writeto()函数写一致的数据库。如果从只读事务调用这个函数,它将执行热备份,而不会阻塞其他数据库的读写操作。

    默认情况下,它将使用一个常规文件句柄,该句柄将利用操作系统的页面缓存。

    有关优化大于RAM数据集的信息,请参见Tx文档。

    一个常见的用例是在HTTP上进行备份,这样您就可以使用像cURL这样的工具来进行数据库备份:

    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

    或者你可以打开你的浏览器以localhost/backup,它会自动下载。

    如果你想备份到另一个文件,你可以使用TX.copyfile()辅助功能。

    Statistics

    数据库对运行的许多内部操作保持一个运行计数,这样您就可以更好地了解发生了什么。通过捕捉两个时间点数据的快照,我们可以看到在这个时间范围内执行了哪些操作。

    例如,我们可以用一个 goroutine 里记录统计每一个 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
        }
    }()

    将这些信息通过管道输出到监控也很有用。

    Read-Only Mode

    可以开启只读模式防止错误更改

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

    现在使用 db.Update() 等开启读写事务 将会阻塞

    Mobile Use

    移动端支持由 gomobile 工具提供

    Create a struct that will contain your database logic and a reference to a *bolt.DB with a initializing constructor that takes in a filepath where the database file will be stored. Neither Android nor iOS require extra permissions or cleanup from using this method.

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

    Database logic should be defined as methods on this wrapper struct. To initialize this struct from the native language (both platforms now sync their local storage to the cloud. These snippets disable that functionality for the database file):

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

    扩展阅读

    更多指导

    For more information on getting started with Bolt, check out the following articles:

    与其他数据库的比较

    Postgres,MySQL和其他关系数据库

    关系数据库将数据组织成行,并且只能通过使用SQL进行访问。这种方法在存储和查询数据方面提供了灵活性,但是在解析和计划SQL语句时也会产生开销。Bolt通过字节切片键访问所有数据。这使得Bolt可以快速地通过键读取和写入数据,但是不提供将值连接在一起的内置支持。 大多数关系数据库(SQLite除外)都是独立于应用程序运行的独立服务器。这使您的系统具有将多个应用程序服务器连接到单个数据库服务器的灵活性,但同时也增加了在网络上序列化和传输数据的开销。Bolt作为应用程序中包含的库运行,因此所有数据访问都必须经过应用程序的过程。这使数据更接近您的应用程序,但限制了对数据的多进程访问。

    LevelDB,RocksDB

    LevelDB及其派生类(RocksDB,HyperLevelDB)与Bolt类似,因为它们是捆绑到应用程序中的库,但是它们的底层结构是日志结构的合并树(LSM树)。LSM树通过使用预写日志和称为SSTables的多层排序文件来优化随机写入。Bolt在内部使用B +树,并且仅使用一个文件。两种方法都需要权衡。 如果您需要较高的随机写入吞吐量(> 10,000 w / sec),或者需要使用旋转磁盘,那么LevelDB可能是一个不错的选择。如果您的应用程序是大量读取或进行大量范围扫描,那么Bolt可能是一个不错的选择。 另一个重要的考虑因素是LevelDB没有事务。它支持键/值对的批量写入,并且支持读取快照,但不能使您安全地执行比较和交换操作。Bolt支持完全可序列化的ACID事务。

    LMDB

    Bolt最初是LMDB的端口,因此在架构上相似。两者都使用B +树,具有ACID语义和完全可序列化的事务,并支持使用单个写入器和多个读取器的无锁MVCC。 这两个项目有些分歧。LMDB专注于原始性能,而Bolt专注于简单性和易用性。例如,出于性能考虑,LMDB允许执行几种不安全的操作,例如直接写入。Bolt选择禁止可能使数据库处于损坏状态的操作。Bolt唯一的例外是DB.NoSync。 API也有一些区别。打开LMDB时需要最大的mmap大小,mdb_env而Bolt会自动处理增量mmap的大小。LMDB使用多个标志来重载getter和setter函数,而Bolt将这些特殊情况拆分为自己的函数。

    注意事项和局限性

    选择合适的工具来完成这项工作很重要,而Bolt也不例外。在评估和使用Bolt时,需要注意以下几点:

    • Bolt非常适合读取密集型工作负载。顺序写入性能也很快,但是随机写入可能会很慢。您可以使用DB.Batch()或添加预写日志来帮助缓解此问题。
    • Bolt在内部使用B + tree,因此可以有很多随机页面访问。与旋转磁盘相比,SSD可以显着提高性能。
    • 尝试避免长时间运行的读取事务。Bolt使用写时复制功能,因此在旧事务使用旧页时无法回收这些旧页。
    • 从Bolt返回的字节片仅在事务期间有效。一旦事务被提交或回滚,它们所指向的内存就可以被新页面重用,或者可以从虚拟内存中取消映射,unexpected fault address访问时会出现恐慌。
    • Bolt在数据库文件上使用排他写锁定,因此不能被多个进程共享。
    • 使用时要小心Bucket.FillPercent。为具有随机插入的存储桶设置较高的填充百分比将导致数据库的页面利用率非常差。
    • 通常使用较大的水桶。较小的存储桶一旦超过页面大小(通常为4KB),就会导致页面利用率下降。
    • 批量加载大量随机写入新的存储桶可能很慢,因为在提交事务之前页面不会拆分。建议不要在单个事务中将100,000个以上的键/值对随机插入到一个新的存储桶中。
    • Bolt使用内存映射文件,因此底层操作系统可以处理数据的缓存。通常,操作系统将在内存中缓存尽可能多的文件,并根据需要将内存释放给其他进程。这意味着在使用大型数据库时,Bolt可能会显示很高的内存使用率。但是,这是预料之中的,操作系统将根据需要释放内存。只要Bolt的内存映射适合进程虚拟地址空间,它就可以处理比可用物理RAM大得多的数据库。在32位系统上可能会出现问题。
    • Bolt数据库中的数据结构是内存映射的,因此数据文件将是特定于字节序的。这意味着您无法将Bolt文件从小字节序计算机复制到大字节序计算机并使其正常工作。对于大多数用户而言,这不是问题,因为大多数现代CPU的字节序都很少。
    • 由于页面在磁盘上的布局方式,Bolt无法截断数据文件并将可用页面返回到磁盘。取而代之的是,Bolt会在其数据文件中维护未使用页面的空闲列表。这些空闲页面可以被以后的事务重用。由于数据库通常会增长,因此这在许多用例中效果很好。但是,请务必注意,删除大块数据将不允许您回收磁盘上的该空间。 有关页面分配的更多信息,请参见此注释

    阅读资料

    对于嵌入式,可序列化的事务性键/值数据库,Bolt是一个相对较小的代码库(<5KLOC),因此对于那些对数据库的工作方式感兴趣的人来说,Bolt可能是一个很好的起点。

    最佳起点是Bolt的主要切入点:

    • Open()-初始化对数据库的引用。它负责创建数据库(如果不存在),获得文件的排他锁,读取元页面以及对文件进行内存映射。
    • DB.Begin()-根据writable参数的值启动只读或读写事务。这需要短暂获得“元”锁以跟踪未结交易。一次只能存在一个读写事务,因此在读写事务期间将获得“ rwlock”。
    • Bucket.Put()-将键/值对写入存储桶。验证参数之后,使用光标将B +树遍历到将键和值写入的页面和位置。找到位置后,存储桶会将基础页面和页面的父页面具体化为“节点”到内存中。这些节点是在读写事务期间发生突变的地方。提交期间,这些更改将刷新到磁盘。
    • Bucket.Get()-从存储桶中检索键/值对。这使用光标移动到键/值对的页面和位置。在只读事务期间,键和值数据将作为对基础mmap文件的直接引用返回,因此没有分配开销。对于读写事务,此数据可以引用mmap文件或内存节点值之一。
    • Cursor-该对象仅用于遍历磁盘页或内存节点的B +树。它可以查找特定的键,移至第一个或最后一个值,也可以向前或向后移动。光标对最终用户透明地处理B +树的上下移动。
    • Tx.Commit()-将内存中的脏节点和可用页面列表转换为要写入磁盘的页面。然后写入磁盘分为两个阶段。首先,脏页被写入磁盘并fsync()发生。其次,写入具有递增的事务ID的新元页面,然后fsync()发生另一个页面 。这两个阶段的写入操作确保崩溃时会忽略部分写入的数据页,因为指向它们的元页不会被写入。部分写入的元页面是无效的,因为它们是用校验和写入的。

    如果您还有其他可能对他人有用的注释,请通过请求请求将其提交。

    其他使用螺栓的项目

    以下是使用Bolt的公共开源项目的列表:

    • Algernon – A HTTP/2 web server with built-in support for Lua. Uses BoltDB as the default database backend.
    • Bazil – A file system that lets your data reside where it is most convenient for it to reside.
    • bolter – Command-line app for viewing BoltDB file in your terminal.
    • boltcli – the redis-cli for boltdb with Lua script support.
    • BoltHold – An embeddable NoSQL store for Go types built on BoltDB
    • BoltStore – Session store using Bolt.
    • Boltdb Boilerplate – Boilerplate wrapper around bolt aiming to make simple calls one-liners.
    • BoltDbWeb – A web based GUI for BoltDB files.
    • bleve – A pure Go search engine similar to ElasticSearch that uses Bolt as the default storage backend.
    • btcwallet – A bitcoin wallet.
    • buckets – a bolt wrapper streamlining simple tx and key scans.
    • cayley – Cayley is an open-source graph database using Bolt as optional backend.
    • ChainStore – Simple key-value interface to a variety of storage engines organized as a chain of operations.
    • Consul – Consul is service discovery and configuration made easy. Distributed, highly available, and datacenter-aware.
    • DVID – Added Bolt as optional storage engine and testing it against Basho-tuned leveldb.
    • dcrwallet – A wallet for the Decred cryptocurrency.
    • drive – drive is an unofficial Google Drive command line client for *NIX operating systems.
    • event-shuttle – A Unix system service to collect and reliably deliver messages to Kafka.
    • Freehold – An open, secure, and lightweight platform for your files and data.
    • Go Report Card – Go code quality report cards as a (free and open source) service.
    • GoWebApp – A basic MVC web application in Go using BoltDB.
    • GoShort – GoShort is a URL shortener written in Golang and BoltDB for persistent key/value storage and for routing it’s using high performent HTTPRouter.
    • gopherpit – A web service to manage Go remote import paths with custom domains
    • Gitchain – Decentralized, peer-to-peer Git repositories aka “Git meets Bitcoin”.
    • InfluxDB – Scalable datastore for metrics, events, and real-time analytics.
    • ipLocator – A fast ip-geo-location-server using bolt with bloom filters.
    • ipxed – Web interface and api for ipxed.
    • Ironsmith – A simple, script-driven continuous integration (build – > test -> release) tool, with no external dependencies
    • Kala – Kala is a modern job scheduler optimized to run on a single node. It is persistent, JSON over HTTP API, ISO 8601 duration notation, and dependent jobs.
    • Key Value Access Langusge (KVAL) – A proposed grammar for key-value datastores offering a bbolt binding.
    • LedisDB – A high performance NoSQL, using Bolt as optional storage.
    • lru – Easy to use Bolt-backed Least-Recently-Used (LRU) read-through cache with chainable remote stores.
    • mbuckets – A Bolt wrapper that allows easy operations on multi level (nested) buckets.
    • MetricBase – Single-binary version of Graphite.
    • MuLiFS – Music Library Filesystem creates a filesystem to organise your music files.
    • NATS – NATS Streaming uses bbolt for message and metadata storage.
    • Operation Go: A Routine Mission – An online programming game for Golang using Bolt for user accounts and a leaderboard.
    • photosite/session – Sessions for a photo viewing site.
    • Prometheus Annotation Server – Annotation server for PromDash & Prometheus service monitoring system.
    • reef-pi – reef-pi is an award winning, modular, DIY reef tank controller using easy to learn electronics based on a Raspberry Pi.
    • Request Baskets – A web service to collect arbitrary HTTP requests and inspect them via REST API or simple web UI, similar to RequestBin service
    • Seaweed File System – Highly scalable distributed key~file system with O(1) disk read.
    • stow – a persistence manager for objects backed by boltdb.
    • Storm – Simple and powerful ORM for BoltDB.
    • SimpleBolt – A simple way to use BoltDB. Deals mainly with strings.
    • Skybox Analytics – A standalone funnel analysis tool for web analytics.
    • Scuttlebutt – Uses Bolt to store and process all Twitter mentions of GitHub projects.
    • tentacool – REST api server to manage system stuff (IP, DNS, Gateway…) on a linux server.
    • torrent – Full-featured BitTorrent client package and utilities in Go. BoltDB is a storage backend in development.
    • Wiki – A tiny wiki using Goji, BoltDB and Blackfriday.

    If you are using Bolt in a project please send a pull request to add it to the list.

    来源: BoltDB 使用入门实践 – 知乎

  • 还在篮子里

    分布式 KV 数据库使用 · 语雀

    QuickStart

    Install

    • need go env
    brew install go
    • get minikeyvalue
    git clone https://github.com/geohot/minikeyvalue.git && cd minikeyvalue

    Run

    run 3 volume on localhost

    PORT=3001 ./volume /tmp/volume1/
    PORT=3002 ./volume /tmp/volume2/
    PORT=3003 ./volume /tmp/volume3/

    also could use nginx under the hood

    run master on localhost

    ./mkv -volumes localhost:3001,localhost:3002,localhost:3003 -db /tmp/indexdb/ server

    Using

    # put "bigswag" in key "wehave"
    curl -v -L -X PUT -d bigswag localhost:3000/wehave
    
    # get key "wehave" (should be "bigswag")
    curl -v -L localhost:3000/wehave
    
    # delete key "wehave"
    curl -v -L -X DELETE localhost:3000/wehave
    
    # unlink key "wehave", this is a virtual delete
    curl -v -L -X UNLINK localhost:3000/wehave
    
    # list keys starting with "we"
    curl -v -L localhost:3000/we?list
    
    # list unlinked keys ripe for DELETE
    curl -v -L localhost:3000/?unlinked
    
    # put file in key "file.txt"
    curl -v -L -X PUT -T /path/to/local/file.txt localhost:3000/file.txt
    
    # get file in key "file.txt"
    curl -v -L -o /path/to/local/file.txt localhost:3000/file.txt

    Screeshot

    image.png

    BranchUsage

    API

    • GET /key
      • 302 redirect to nginx volume server.
    • PUT /key
      • Blocks. 201 = written, anything else = probably not written.
    • DELETE /key
      • Blocks. 204 = deleted, anything else = probably not deleted.

    ./mkv Usage

    Usage: ./mkv <server, rebuild, rebalance>
    
      -db string
            Path to leveldb
      -fallback string
            Fallback server for missing keys
      -port int
            Port for the server to listen on (default 3000)
      -protect
            Force UNLINK before DELETE
      -replicas int
            Amount of replicas to make of the data (default 3)
      -subvolumes int
            Amount of subvolumes, disks per machine (default 10)
      -volumes string
            Volumes to use for storage, comma separated

    Rebalancing (to change the amount of volume servers)

    # must shut down master first, since LevelDB can only be accessed by one process
    ./mkv -volumes localhost:3001,localhost:3002 -db /tmp/indexdb/ rebalance

    Rebuilding (to regenerate the LevelDB)

    ./mkv -volumes localhost:3001,localhost:3002 -db /tmp/indexdbalt/ rebuild

    Performance

    # Fetching non-existent key: 116338 req/sec
    wrk -t2 -c100 -d10s http://localhost:3000/key
    
    # go run thrasher.go lib.go
    starting thrasher
    10000 write/read/delete in 2.620922675s
    thats 3815.40/sec
    
    

    来源: Editing · QuickStart · 语雀

  • 还在篮子里

    你是谁? · 语雀

    导语

    众里寻她千百度,蓦然回首,灯火阑珊处

    准备

    在分析技术猫之前,我认为最好从你自己开始

    首先问你几个问题,如果有必要的话,最好写下来你的现在的想法。当然,只要着实思考过也可行。

      1. 你是谁
      2. 你的目的是什么
      3. 你能付出多少(即期望的成本是什么)

    我曾经平凡,现在我在这里发光

    正文


    技术猫是什么

    提供创意文化和优秀的人才培养土壤

    文化+人=团队

    你可能是如下目标

      • 对编程感兴趣:我们确实使用编程来达到我们的很多目的
      • 对社团感兴趣:是一个符合设想的社团
      • 自己有创意但没有技术支持:我们能提供
      • 希望能认识更多的朋友
      • 希望能不荒废时间
      • 希望能实现在校期间的财务自由
      • 希望面试能够获得内推
      • 希望自己变得更好(笑)

    当然上述不是我对你们的猜测,是之前加入者的疑问,你的情况不同自然可能会有一些自己的想法,不过我们就以上分析的话,相信有能力的你也能自己验证。

    技术猫的概念


    我们不仅仅是一个组织,同时也是一个社区:我们汇集满怀激情的软件精英,通过技术和学生们共同应对最艰巨的挑战。同时我们寻求IT行业的革新,并致力于对社会产生积极的影响力。

    虽然技术可能不是万灵丹,但它可以用于推动迫切的发展问题。特别是目前未充分利用的移动和云技术, 还有很多可发挥的空间。

    而我们的技术核心也正是这样。

    我们所做的事会让开源普及开来,为中国的软件事业提供源源不断的后备军

    分布式

    不管我们身在何处,我们都能够分享相同的文化特征和价值观,我们各具特色,我们互相尊重,我们坚守诚实透明的品格

    外表和背景不是我们在乎的方面,我们注重思想做正确的事情

    接口式

    • 价值观吻合,松散耦合

    在不同地方的技术猫团队组织,需要的是满足相同的价值观,而作为一个面向青年人的组织,我们不拒绝为一切能够有效扩展同时不违背原则的人提供帮助。

    这是因为当满足我们的价值观和原则的时候,就可以视作实现了 TechCats 的理念

    项目驱动式

    你将会从零开始启动项目,通过工作坊收集信息、设定OKRs、定义需求、面对客户,而客户通常是自己想对社会作出改变的心。

    也有一些时候,你会把时间用来梳理你最新的思考、整理会议的讨论、在一些有意义的内部项目上贡献时间,建立你的社会影响力。

    我们的团队由具体的项目来构成,当你在 TechCatsLab 的时候,你将有机会与风格迥异的 TechCats 在一起工作,在合作中学习。

    你将绘制自己的成长地图:不管你是希望深耕自己感兴趣的方向,还是希望在这里实现职业的转型(比如从工程师转型成业务需求分析师),在 TechCatsLab ,你都能走出真正属于自己的,独特的职业旅程。

    不止是编程

    作为团队的一员,自然也会为团队带来一些除了目的之外的改变。我们会有茶话会,喝茶聊天什么的。也有技术沙龙,大家会交流一些自己的技术。团队的每个人也像一个过滤器,在接受外界的信息之后,一些好的东西也会通过团队成员进入团队成为集体文化。我们也是零食家,觉得好吃的特产也会带过来和大家分享(确实饱了口福了)。我们也会聚在一起读书(当然是有读书兴趣的),技术书籍只是书架的一小部分,因为技术是无限的,同时也有一些时效性很强,但是一些固定的东西是经久不变的,所以会有经济学读物,心理学读物,哲学读物,数学,生物,物理,美术等,哦,还有日语学习资料(笑)。

    也会交流厨艺(大笑),也会弹弹琴,吹吹萨克斯(不存疑),聚在一起玩桌游,大家一起打扫卫生?喝咖啡(因为自己磨豆自己煮所以我觉得还是要提出来),还有交流对未来的幻想,也会打坐修禅,期望找寻本心,冥想自己。

    愿景将我们联系在一起,而遵守原则的我们也逐渐靠的更近,真正成为一股集体。

    by 杨鼎睿

    来源: 你是谁? · 语雀

  • 还在篮子里

    我是如何开始学习编码的:一个月我用过的资源 · 语雀

    想学习如何编程?本文将指导你经过技术和资源的丛林,帮助你从无知识到构建快速、互动、现代的编码知识,沿着我走过的林间小径。

    我花了一个月的时间学习编码入门,虽然有时候很有挑战性,但这是一次非常有益的体验。

    最棘手的一个挑战是选择什么样的学习和在那里学习它。有这么多的语言,框架和库 – 以及许多相互矛盾的建议。

    此外,一些学习资源比其他学习资源要好得多。如果我知道要关注哪些资源,我的进展可能会更快。

    本着这种精神,我想我会分享对我有用的东西。如果感觉很多,我很幸运,我每天可以花几个小时学习这些材料。你做得多快并不代表你作为开发者的潜力,所以不要担心是否需要更长的时间来完成这些材料。但是,如果你能投入时间,可以在半个月内完成。

    这些是我经历过的路程,并不代表你需要全部走过

    第一部分:HTML,CSS和JavaScript

    这些是所有Web应用程序的三种核心语言,并且(几乎)不可能在不使用它们的情况下在Internet上构建任何内容。

    HTML

    如果你以前从未做过任何编码,那么HTML就是一个很好的起点。它是互联网的核心构建块,与大多数其他语言不同,它为学习者提供了他们正在编写的代码的即时可视反馈。

    CSS

    CSS是HTML的自然伴侣。从表面上看,它非常简单,虽然有一些概念 – 特别是与布局和定位有关 – 可能需要一些时间来解决问题。

    CSS最困难的部分是理解布局。用户在StackOverflow上询问了我的大多数布局问题,因此快速的Google搜索解决了我的大多数问题。

    H5 和 CSS 大概花去了我两天时间,通常你想了解一个编码方面的事物时,直接上手总是最快捷的方式。

    JavaScript

    HTML和CSS不是完全成熟的编程语言,因此在开始学习编程语言之前,您将无法完全了解编程。JavaScript是Web的编程语言。

    平心而论,虽然我在初中时期尝试过 js 编程,然而连真正的编码都算不上,直至现在我也是 JavaScript 的菜鸟,所以这上面我并不能给予一些帮助建议

    由于我开始学习后端,js 从熟悉拼写到知道历史花了我一小段时间。

    GO

    Go 是极其酷的语言,尤其是在现代进行高效的 Web 编程方面

    Go 是我学习后端被选择的语言,同时也是我越学越看好的语言。作为语言初学者的时候我还无法分辨各种语言的特性和好坏,这通常需要多年的编码经验或者有过使用多种语言的经历才能清楚。我经历过 C++ STL 的使用和一定的 Java 编码,但是从过来的眼光看待,不过是十数天就能达到的程度。

    在 《The Way To Go》上我花费了大概三天时间,在此之前我阅读了菜鸟教程上关于 Go 语言的入门指南。虽然这本书对于 Go 语言的高级特性讲的比较简略,作为入门无疑是够的。即使我看的是英文,或许你也更喜欢拿着中文书籍看,但是作为编码人员,无疑英语才是必须学习的语言。

    之后我进行了一个小爬虫的练习,涉及到 goquery,gocolly 先爬取的飞卢的小说网站,之后爬取的各个地区的各种商业信息。这大概花费了我一天的时间。(从这里学会的阅读文档的知识)

    随后我开始了 Go 源码包的阅读,将每一个包的每一个函数遍历了一下他们的 godoc ,在我开始每一个函数的内在逻辑阅读时,我被中止了,由于庞大的知识体系。这大概花费了我三到四天。(从这里学会的画图技巧)

    我这个时候开始使用社团的 Mac 电脑,开始了一个关于发送短信的云API接入服务的开发。当时了解并接入阿里云市场给我带来了麻烦,从需求找到解决方法向来非易事。这大概使用了一个星期,了解了关于 go 与 mysql 的接入,和不同终端的 HTTP 通信

    随后是轮播图的后台管理搭建,这里开始学习了表的设计,更多的了解了 mysql 的各种情况。

    完成轮播图(考虑的越多需要做的越多,但是编码不一定会变多,应该是遵循幂律的)开始订单系统的设计,这是关于学习后台功能最难的一个部分了,同时我也花费了不少时间来进行查阅和设计,也接触有 gorm 的使用。做完订单系统的设计让我得到了极大的提升因为考虑了很多实际情况的适应。之后想必更困难的业务是游戏服务端了吧。

    之后我完成了一个 app 后台的搭建,通过各个模块的组合拼接。这里的难点主要是在团队协作上故不在此详述。

    到这里我的 go 学习历程就告一段落了。

    第2部分:任何开发人员工具包的其他必备部分

    Git和GitHub

    Git是所谓的“版本控制系统”,GitHub是最受欢迎的基于云的Git服务。

    简单来说,Git允许您备份文件,跟踪更改,与其他开发人员分享您的进度,以及协作项目。

    当你刚刚学习,并且不需要使用Git时,它可以感觉像是一件苦差事。但这是值得的:绝大多数专业开发人员不仅依赖GitHub(或类似服务),而且拥有活跃的GitHub配置文件可以帮助您确保工作安全。

    越早开始将您的工作交给GitHub,您作为开发人员的经验就会越多。

    命令行

    命令行可以说是最重要的部分,通常了解命令行的操作是非常必要的。同时想要学习也很简单,不仅网上有很多教程,鸟哥的 Linux 私房菜也涉及很多,不过我对于工具的建议是如非必要,仅需了解。

    使用Git的最佳方式是通过“终端”或“命令行”,这对现代Web开发人员也很重要,允许您通过NPM(节点包管理器)或Yarn轻松地将第三方包添加到您的网站。也有很多网站能够帮助你迈出这一步

    第3部分:下一步是什么?

    这就是我在一个月学到的一切。

    要学习的内容包括 Kubernetes,Istio,gRPC等等。这是很多技术,最好也了解它们!

    19/7/22 没想到果然是这个学习路线。现在开始学习 gRPC 了

    来源: 我是如何开始学习编码的:一个月我用过的资源 · 语雀

  • 还在篮子里

    Kick Start快速入门 · 语雀

    Kick Start快速入门

    Background

    Code Jam Kickstart 为同学们提供向 Google 展现自己专业能力以及走近 Google 的机会。在家就可以参加由 Google 的工程师们设计的算法题。通过参加线上测试,同学们可以直观地了解 Google 技术性岗位对编程能力的要求,也是参与 Google 校园招聘的入场券。

    Requirement

    • G家邮箱一枚
    • 科学上网工具
    • 地址传送门(如果从codejam进入的话,选择分类中的kickstart)

    Quickstart-Overview

    G家鼓励灵活的解决方案,所以施行的输入输出方案和其他的ACM在线测评不一样。

    不是让你不断提交代码然后系统给你测试,而是给你数据集让你自己测试后在本地生成输出文件进行上传。

    image.png

    1. 下载题目数据集 .in 文件(小数据集4分钟变一次,大数据集8分钟变一次)
    2. 解题并生成输出 .out 文件
    3. 提交输出文件 和 代码源文件(练习测试不用提交)

    PS. 每一道题都有小数据集和大数据集两个.in文件可供下载,大数据集的数据范围一般都比小数据集的范围更广,有更多的困难情况。

    QuickStart

    必要点

    如果你想参加比赛,请在比赛期间访问你的比赛主页。如果您想练习,请访问“过去的问题”页面并选择一个回合。然后选择一个问题。

    读问题,前面几段话将精确描述您的程序需要解决的问题。仔细注意极限部分。这些限制将帮助您确定处理大小数据集所需的解决方案类型。(通常会说明一些无解的数据不用考虑)

    编写一个程序,在不到3分钟的时间内用所描述的小限制解决问题。确保您的程序接受输入并以给定的格式输出;如果您使用示例输入作为输入运行它,它应该准确地生成示例输出(包括“Case #”文本)。如果您不确定如何处理输入和输出,请参阅下面的标准I/O教程,了解可行的做法。

    在页面顶部,单击以 solve A-small  并下载输入文件。在真正的比赛中,我们一收到下载请求就会启动一个4分钟的计时器。如果你在练习,就不会有计时器。

    在该输入文件上运行程序,并将结果保存到文件中。将该文件作为输出文件提交。(在真正的比赛中,你还需要提交你的源代码。)服务器的响应方式有以下几种:

      • 正确示例:您提交的每个案例都完全正确!
      • 拒绝:您的提交被拒绝的原因与您的答案的正确性或不正确性无关。例如,您可能上传了一个输入文件或源代码,而不是您自己的输出文件。时钟仍然在运行,所以需要考虑您能否在超时之前修复这个问题(在这种情况下,您的提交将被视为不正确)。
      • 不正确:我们不会告诉您哪些情况是错误的,因此您必须自行调试。对于一个小的数据集,您可以修复您的程序,并尽可能多次尝试该数据集。注意,在真正的比赛中,您将为每次尝试下载一个新的输入文件。如果您最终获得正确的数据集,那么您将为每一次错误尝试获得较小的时间惩罚(但是解决问题总比不解决好,即使有惩罚!)

    在您解决小数据集之后,大数据集将变得可用。一旦您的程序准备好处理更高的限制,下载大型数据集。(参赛者通常只编写一个解决方案来解决两个数据集。)我们一收到下载请求就会启动一个8分钟的计时器。

    与小数据集一样运行程序并提交,直到比赛结束你才知道你是否正确地解决了问题。如果您的提交被拒绝或您想提交另一个答案,您可以在8分钟的时间限制内再次尝试,但只有最后一次提交将被判定。

    接着看另一个问题(左边有一个列表)。您正确解出的每个数据集都值下载链接旁边所写的点数。比赛结束时得分最多的选手排名最高。在一个小数据集上,每一次错误的提交最终被正确的回答,将会有4分钟的惩罚时间。为了引人注目,记分牌将显示“乐观”的初步结果,也就是说,它将假设每个大型提交都是正确的。比赛结束后,记分牌将显示真实的结果,包括哪些大的提交是错误的。

    教程

    我们现在只有一个教程,但我们将来可能会添加更多教程。虽然阅读理论很好,但我们强烈建议您练习解决实际问题以使其变得更好。您可以访问我们的 过去问题部分来完成此操作。For Fun!

    “标准”解决方案:使用标准输入和输出

    Kickstart鼓励灵活解决问题:我们会为您提供输入文件,并让您以自己喜欢的方式解决问题。您可以选择语言,开发环境,体系结构,库等。

    我们的许多参赛者使用其他编程竞赛中常见的模型,如ACM-ICPC和Codeforces:一个单文件程序,它读取输入并准确生成所需的输出。本教程演示如何使用标准输入和标准输出将此模型应用于Kickstart!当然,仍然欢迎您以完全不同的方式阅读输入和/或写入输出; 这只是一个选择。

    什么是标准输入和标准输出?

    标准输入(stdin)和标准输出(stdout)是程序用于与外界交互的数据流。当您以最简单的方式从控制台运行程序时,stdin就是从键盘读入程序的内容,而stdout就是打印在屏幕上的内容。但是,这两个流可以被定向读取和写入文件!

    假设您有一个名为“my_program”的程序,通常通过以下命令运行:

    MY_PROGRAM

    请注意,如果使用像C语言++编译为机器码,MY_PROGRAM可能是这样的

    ./my_binary

    对于像Java使用虚拟机,

    java my_java_binary_name

    或者像Python这样的解释型语言

    python my_python_code.py

    在Linux,Mac OS / X和Windows中,您可以使用<和>将stdin和stdout分别重定向到文件。

    命令行如下

    MY_PROGRAM < input_file.txt > output_file.txt

    将使您的程序接收input_file.txt其stdin中的内容,并将输出写入其stdout output_file.txt

    如何在Kickstart中使用stdin和stdout?

    在处理解决方案时,您可能希望从文件中读取(例如,sample.txt您已将问题的样本I / O复制到其中),但想要把输出写入控制台。你可以这样做:

    MY_PROGRAM < sample.txt

    下载输入后,您需要将输出定向到要上载的文件:

    MY_PROGRAM < small_input.txt > small_output.txt

    如果程序的算法适用于Small和Large数据集,则只需重新运行此命令,甚至不需要更改或重新编译程序:

    MY_PROGRAM < large_input.txt > large_output.txt

    如何编写我的代码以从stdin读取并写入stdout?

    考虑以下非常简单的问题。输入一个 T ,T 为接下来的输入行数,每一行包含两个正整数 和 M。所需的输出形式是Case #x: y z,在这里x, yz均为整数; x是的第 x 个情况,y是的MN的总和,并且z是产品NM

    input_file.txt

    3
    1 5
    7 -2
    9001 0

    这里有一些代码来解决这个问题,从stdin读取并以各种语言写入stdout。对于我们的示例,我们使用了Kickstart中一些最常用的语言,但您当然不仅限于使用这些语言!

    C ++(命令行)

    g++ solution.cpp -o solution
    ./solution < input_file.txt > output_file.txt

    C ++(solution.cpp)

    #include <iostream>  // includes cin to read from stdin and cout to write to stdout
    using namespace std;  // since cin and cout are both in namespace std, this saves some text
    int main() {
      int t, n, m;
      cin >> t;  // read t. cin knows that t is an int, so it reads it as such.
      for (int i = 1; i <= t; ++i) {
        cin >> n >> m;  // read n and then m.
        cout << "Case #" << i << ": " << (n + m) << " " << (n * m) << endl;
        // cout knows that n + m and n * m are ints, and prints them accordingly.
        // It also knows "Case #", ": ", and " " are strings and that endl ends the line.
      }
      return 0;
    }

    Java(命令行)

    javac Solution.java
    java Solution < input_file.txt > output_file.txt

    Java(Solution.java)

    import java.util.*;
    import java.io.*;
    public class Solution {
      public static void main(String[] args) {
        Scanner in = new Scanner(new BufferedReader(new InputStreamReader(System.in)));
        int t = in.nextInt();  // Scanner has functions to read ints, longs, strings, chars, etc.
        for (int i = 1; i <= t; ++i) {
          int n = in.nextInt();
          int m = in.nextInt();
          System.out.println("Case #" + i + ": " + (n + m) + " " + (n * m));
        }
      }
    }

    Python 2(命令)

    python2 solution.py < input_file.txt > output_file.txt

    Python 2(solution.py)

    # raw_input() reads a string with a line of input, stripping the '\n' (newline) at the end.
    # This is all you need for most Kickstart problems.
    t = int(raw_input())  # read a line with a single integer
    for i in xrange(1, t + 1):
      n, m = [int(s) for s in raw_input().split(" ")]  # read a list of integers, 2 in this case
      print "Case #{}: {} {}".format(i, n + m, n * m)
      # check out .format's specification for more formatting options

    Python 3(命令)

    python3 solution.py < input_file.txt > output_file.txt

    Python 3(solution.py)

    # input() reads a string with a line of input, stripping the '\n' (newline) at the end.
    # This is all you need for most Kickstart problems.
    t = int(input())  # read a line with a single integer
    for i in range(1, t + 1):
      n, m = [int(s) for s in input().split(" ")]  # read a list of integers, 2 in this case
      print("Case #{}: {} {}".format(i, n + m, n * m))
      # check out .format's specification for more formatting options

    从这些示例中可以看出,大多数语言提供了从stdin读取和写入stdout的简单方法。这通常比直接引用代码中的特定文件更容易 – 从Small数据集切换到Large数据集时,或者从控制台切换输出目标(用于调试)时,无需更改或重新编译代码一个文件(所以你可以提交一样的源文件)。

    您可以随时查看您喜欢的语言的文档,以找到使用stdin和stdout的首选方法。几乎所有语言都有一个。

    来源: Kick Start快速入门 · 语雀

  • 还在篮子里

    《第五项修炼》· 语雀

    《第五项修炼》学习型组织的艺术与实践

    学习型组织的艺术与实践

    以下大量原文引用自第五项修炼

     

    学习型组织的真谛:活出生命的意义

    许多人被问起,作为伟大团体一分子的经验是什么时,最引人深思的回答是:觉得自己属于一个比自我强大的事物的感觉,也就是大伙儿心手相连,共创未来的那种经验。对他们来说,作为真正伟大团体一分子的体验,是他们一生中最突出、生命力完全发挥的一段岁月。有些人竟其余生,希望寻求重温此种经历。

    在过去数百年来的西方文化中,有一个字很少被使用,但却可表达学习型组织的精神,这个字是metanoia,意思是心灵的转变。这十多年来在辅导企业时,我们私底下原先是用metanoic organization来形容学习型组织的。希腊文这个字的意思是心灵意念的根本改变,一种“超觉”的经验。在早期基督徒的传统中,这个字特指醒悟而直接觉知至高无上的、属于上帝的事物。在夭主教的经论中,这个字被翻译成“体悟生命的真义”。

    堂握metanoia的意义,等于掌握“学习”的更深层的意义,因为学习也包括心灵的根本转变或运作。然而学习在目前的用法上已经失去了它的核心意义。在日常用语上,学习已经变成吸收知识,或者是获得信息,然而这和真正的学习还有好大一段距离。

    真正的学习,涉及人之所以为人此一意义的核心。透过学习,我们重新创造自我。透过学习,我们能够做到从未能做到的事情,重新认知这个世界及我们跟它的关系,以及扩展创造未来的能量。事实上你我心底都深深地渴望这种真正的学习。

    这就是学习型组织的真谛。对这样的组织而言,单是适应与生存是不能满足它的。组织为适应与生存而学习,虽然是基本而必要的,但必须与开创性的学习结合起来,才能让大家在组织内由工作中活出生命的意义。

    目前,有些组织已扮演拓荒先锋的角色,朝这条路上走去。但学习型组织的领域,仍然大部分有待开垦。我衷心地期望这本书能加快开垦的速度。

    学习型组织的五项修炼

    今天,在学习型组织的领域里,也有五项新技术正逐渐汇聚起来,使学习型组织演变成一项创新。虽然,它们的发展是分开的,但都紧密相关,对学习型组织之建立,每一项都不可或缺。我们称这五项学习型组织的技能为五项修炼。以下,我们刻意将其中的核心“第五项修炼”先行介绍。

    第五项修炼:系统思考(Systems Thinking)

    当乌云密布、天色昏暗,我们便知道快要下雨了。我们也知道在暴风雨过后,地面的流水将渗入好几英里以外的地下水中,明日天空又要放晴。这一切的事件虽有时空的差距,然而事实上它们都息息相关,且每次运行的模式相同,每个环节都相互影响,这些影响通常是隐匿而不易被察觉的。惟有对整体、而不是对任何单独部分深入地加以思考,你才能够了解暴风雨的系统。

    企业和人类其他活动,也是一种“系统”,也都受到细微且息息相关的行动所牵连,彼此影响着,这种影响往往要经年累月才完全展现出来。身为群体中的一小部分,置身其中而想要看清整体变化,更是加倍的困难。我们因而倾向于将焦点放在系统中某一片段,但总想不通为什么有些最根本的问题似乎从来得不到解决。经过五十年的发展,系统思考已发展出一套思考的架构,它既具备完整的知识体系,也拥有实用的工具,可帮助我们认清整个变化形态,并了解应如何有效地掌握变化,开创新局。

    虽然工具是新的,系统思考的基本观念却是非常浅显的。我们的实验显示,小孩子学习系统思考时非常迅速。

    第一项修炼:自我超越(Personal Mastery)

    “自我超越”的修炼是学习不断厘清并加深个人的真正愿望,集中精力,培养耐心,并客观地观察现实。它是学习型组织的精神基础。精熟“自我超越”的人,能够不断实现他们内心深处最想实现的愿望,他们对生命的态度就如同艺术家对艺术作品一般,全心投入、不断创造和超越,是一种真正的终身“学习”。组织整体对于学习的意愿与能力,植基于个别成员对于学习的意愿与能力。此项修炼兼容并蓄了东方和西方的精神传统。

    遗憾的是,几乎没有任何组织鼓励他们的成员以这种方式成长。这个领域是一片庞大而尚未开发的处女地。汉诺瓦公司的欧白恩说:“企业的员工多半聪明、受过良好的教育、充满活力、全心全力渴望出人头地。但他们到了三十多岁时,通常只有少数平步青云,其余大多数人都失掉了开始时所有的企图心、使命感与兴奋感,对于工作,他们只投入些许精力,心灵几乎完全不在工作上。”这种组织设计所造成的个人生涯是多么可悲!

    而且令人惊讶的是,就个人而言,也只有少数的成年人努力发展本身,超越自我。当你询问成年人的愿望是什么,通常他们首先提到较负面的、想要除掉的人或事。例如他们说:“我想要我的岳母搬走”,或“我想要彻底治好背痛”。然而自我超越的修炼坝,是以厘清对我们真心向往的事情为起点,让我们为自己的最高愿望而活。

    这里,最有趣的部分是:个人学习与组织学习之间的关系、个人与组织之间的相互承诺,以及由一群“学习者”组成的企业所特有的精神。

    第二项修炼:改善心智模式(Improving Mental Models)

    “心智模式”是根深蒂固于心中,影响我们如何了解这个世界,以及如何采取行动的许多假设、成见,或甚至图象、印象。我们通常不易察觉自己的心智模式,以及它对行为的影响。例如,对于常说笑话的人,我们可能认为他乐观豁达;对于不修边幅的人,我们可能觉得他不在乎别人的想法。在管理的许多决策模式中,决定什么可以做或不可以做,也常是一种根深蒂固的心智模式。如果你无法掌握市场的契机和推行组织中的兴革,很可能是因为它们与我们心中隐藏的、强而有力的心智模式相抵触。

    壳牌石油公司是第一家了解加速组织学习好处的大企业,他们发现隐藏的心智模式影响既深且广,尤其是当它成为共有的心智模式时。壳牌石油公司之所以能成功地度过1970和八十年代石油危机的巨大冲击,主要归功于学习如何浮现管理者的心智模式,并加以改善。(在七十年代初期,壳牌石油在世界七大石油公司中敬陪末座;到八十年代末,它已经成了最强的一家)。最近刚由壳牌石油公司退休的企画主任德格说:“要在变动的企业环境中持续调适与成长,有赖组织化的学习,这是管理团体改变对公司、市场与竞争者的共有心智模式的过程。因此我们把企画看成学习,而把公司整体企画看作组织化的学习。”

    把镜子转向自己,是心智模式修炼的起步;借此,我们学习发掘内心世界的图象,使这些图象浮上表面,并严加审视。它还包活进行一种有学习效果的、兼顾质疑与表达的交谈能力——有效地表达自己的想法,并以开放的心灵容纳别人的想法。

    第三项修炼:建立并同愿景(Building Shared Vision)

    如果有任何一项领导的理念,几千年来一直能在组织中鼓舞人心,那就是拥有_种能够凝聚、并坚持实现共同的愿景的能力。一个缺少全体衷心共有的目标、价值观与使命的组织,必定难成大器。IBM公司以“服务”,拍立得公司以“立即摄影”;福特汽车公司以“提供大众公共运输”;苹果电脑公司以“提供大众强大的计算能力”为组织共同努力的最高鹄的。这些组织都在设法以共同的愿景把大家凝聚在一起。

    有了衷心渴望实现的目标,大家会努力学习、追求卓越,不是因为他们被要求这样做,而是因为衷心想要如此。但是许多领导者从未尝试将个人的愿景,转化为能够鼓舞组织的共同愿景。共同的愿景也常以一个伟大的领袖为中心,或激发自一件共同的危机。但是,如果有选择的余地,大多数的人会选择追求更高的目标,而并非只暂时解决危机。组织所缺少的,是将个人的愿景整合为共同愿景的修炼——注意我指的不是一本按部执行的手册,而是一套引导学习的原则。

    共同愿景的整合,涉及发掘共有“未来景象”的技术,它帮助组织培养成员主动而真诚地奉献和投入,而非被动的遵从。领导者在精熟此项修炼的过程中,会得到同样的教训:一味试图主导共同愿景(无论多么的有善意)会产生反效果。

    第四项修炼:团体学习(Teaml,Learing)

    在一个管理团体中,大家都认真参与,每个人的智商都在一百二十以上,何以集体的智商只有六十二?团体学习的修炼即在处理这种困境。然而我们知道团体确实能够共同学习;在运动、表演艺术、科学界,甚至企业中,有不少惊人的实例显示,团体的集体智慧高干个人智慧,团体拥有整体搭配的行动能力。当团体真正在学习的时候,不仅团体整体产生出色的成果,个别成员成长的速度也比其他的学习方式为快。

    团体学习的修炼从“深度汇谈”(dialogue)开始。“深度汇谈”是一个团体的所有成员,摊出心中的假设,而进入真正一起思考的能力。希腊文中“深度汇谈”(dia-logos)指在群体中让想法自由交流,以发现远较个人深入的见解。有趣的是,“深度汇谈”在许多“原始”文化中仍然保存,例如美洲的印第安人,但是在现代社会中则几乎已完全丧失。今天,人们重新发现“深度汇谈”的原理与技巧,并使它更适合现代的需要。(“深度汇谈”与我们熟知的?讨论”或“对话”不同,我们在第十二章会详加介绍。)

    “深度汇谈”的修炼也包括学习找出有碍学习的互动模式。例如“自我防卫”的模式往往根植于团体的互动中,若未察觉,则会妨碍组织学习。如果能以有创造性的方式察觉它,并使其浮现,学习的速度便能大增。

    团体学习之所以非常重要,是因为在现代组织中,学习的基本单位是团体而不是个人。除非团体能够学习,组织便也无法学习。

    日新又新,不断创造未来

    第五项修炼总结

    (abser注:我认为建立共同愿景是最重要的核心,也是内驱)

    但是“系统思考”也需要有“建立共同愿景”、“改善心智模式”、“团体学习”与“自我超越”四项修炼来发挥它的潜力。“建立共同愿景’‘培养成员对团体的长期承诺。“改善心智模式”专注于以开放的方式,体认我们认知方面的缺失,“团体学习”是发展团体力量,使团体力是超乎个人力量加总的技术。“自我超越”则是不断反照个人对周遭影响的一面镜子;缺少自我超越的修炼,人们将陷入“压力一反应”式的结构困境。

    最后,系统思考可以使我们了解学习型组织最重要的部分,也就是以一种新的方式使我们重新认识自己与所处的世界:一种心灵的转变,从将自己看作与世界分开,转变为与世界连结;从将问题看作是由“外面”某些人或事所引起的,转变为看到自己的行动如何造成问题。学习型组织是一个促使人们不断发现自己如何造成目前的处境,以及如何能够加以改变的地方。如同阿基米得所说的:“给我一根够长的杠杆,我单手便可以移动这个世界。”(注:这里是改写了原文的)

    建立共同愿景的修炼

    鼓励个人愿景

    共同愿景是从个人愿景汇聚而成,借着汇集个人愿景,共同愿景获得能量和培养行愿。就如同汉诺瓦保险的欧白恩所观察到的:“我的愿景对你并不重要,惟有你的愿景才能够激励自己。”这并不是说人们只需在乎自己个人的利益;事实上,个人愿景通常包括对家庭、组织、社区、甚至对全世界的关注。欧白恩之所以强调个人对周遭事物的关注,是由于真正的愿景必须根植于个人的价值观、关切与热望中。这就是为什么共同愿景真诚的关注是根植于个人远景。这个简单的道理却被许多领导者给忽略了,他们往往希望自己的组织必须在短期内建立一个共同愿景。

    有意建立共同愿景的组织,必须持续不断地鼓励成员发展自己的个人愿景。如果人们没有自己的愿景,他们所能做的就仅仅是附和别人的愿景,结果只是顺从,决不是发自内心的意愿。另一方面,原本各自拥有强烈目标感的人结合起来,可以创造强大的综效(synergy),朝向个人及团体真正想要的目标迈进。“自我超越”是发展“共同愿景”的基础,这个基础不仅包括个人愿景,还包括忠于真相和创造性张力。而共同愿景能产生远高于个人愿景所能产生的创造性张力。那些能献身去实现崇高愿景的人,都是能够掌握创造性张力的人;也就是对愿景有明确的了解,并持续深入探询真实情况的人。正是因为体会过创造性张力的力量,他们深信有能力创造自己的未来。

    在鼓励个人愿景时,组织必须注意不要侵犯到个人的自由。如第九章“自我超越”所谈的,没有人能将自己的愿景给别人,也不能强迫他人发展愿景。然而,有些正面的行动却能创造鼓励个人愿景的气候。最直接的是由具有愿景意识的领导者,以鼓励其他人分享他愿景的方式沟通。这是愿景的领导艺术:从个人愿景建立共同愿景。

    塑遇整体图象

    如何结合个人愿景以创造共同愿景呢?一个贴切的比喻是全像摄影术(hologram),它是一种以交错的光源,创造出三度空间图象的摄影术。

    如果你分割一张照片成两半,每一半只能显示出整个图象的一部分,但是如果你分割一个全像底片,每一部分仍然不折不扣地显现整个影象。你继续分割全像底片,不论分割得多细,每一部分仍然能显现出整个影象。相同的,当一群人都能分享组织的某个愿景时,每个人都有一个最完整的组织图象,每个人都对整体分担责任,不仅只对自己那一小部分负责。但是全像底片的每一小片并非完全相同,因为每一小片都代表从不同角度所看到的整个影象;就如同你从窗帘戳几个洞看过去,每个洞都提供一个特有的角度来观看整个影象。同样的,每个人所持有的整体愿景也都有其不同之处,因为每个人都有独自观看大愿景的角度。

    如果你把全像底片的各个小片组合起来,整体影象基本上并未改变,毕竟每一个片段都有个整体的图象,但是图象却会愈来愈清晰、愈真实。当有更多人分享共同愿景时,愿景本身虽不会发生根本的改变,但是愿景变得更加生动、更加真实,因而人们能够真正在心中想之、愿景逐渐实现的景象。从此他们拥有伙伴,拥有“共同创造者”;愿景不再单独落在个人的双肩上。在此之前,当他们尚社孕育个人愿景时,人们可能会说那是“我的愿景”,但是当共同愿景形成之时,就变成既是“我的”也是“我们的”愿景。

    学习建立“共同愿景”这项修炼的第一步,是放弃愿景总是由高层宣示,或是来自组织制度化规划过程的传统观念。

    绝非官方说法

    在传统的阶层式组织里,没有人怀疑过愿景应来自高层。在这样的组织中,通常指引公司的大蓝图是没有被大家分享的,每一个人只是听命行事,以便能够完成他们的任务,来支持组织的愿景。赫门米勒家具公司的赛蒙说:“如果我是传统威权组织的总经理,推行新愿景的工作,将会比今天所面对的问题简单得多,因为组织里大多数的人不必了解这个愿景,只须知道我对他们的期望是什么。”

    然而近年流行的、建立愿景的过程,与传统中由上而下形成的愿景没什么不同。最高管理当局通常借由顾问的帮助写下“愿景宣言”,这些愿景通常是为了解决士气低落或缺乏策略方向的问题;它可能结合对竞争者、市场定位,与组织优弱势等项目的广泛分析。然而,结果常令人感到失望,原因如下:

    1.    第一,这样的愿景通常是治标而非治本的;透过一次建立愿景的努力,为公司的策略提供遵循方向,一旦写下来后,管理者就认为他们已卸下建立愿景的职责。最近,我在创新顾问公司的一位同事,向两位企业主管说明我们的顾问群如何协助建立愿景。在他进一步说明之前,其中一位主管打断他的话说:“我们已经做好了,我们已经写下共同愿景宣言了。”这位同事回答说:“那很好,你们得出什么样的愿景?”这位主管转向另一位问道:“乔治,我们的愿景宣言放在哪里?”写下愿景宣言或许是建立共同愿景的第一步;但是,愿景只在纸上陈述而非发自内心,是很难使愿景在组织内扎根。
    1.  由最高管理当局撰写愿景宣言的第二个缺失是,这种愿景并非是从个人愿景中建立起来的。在追寻“策略性的愿景”时,个人愿景常被忽略;而“官方愿景”所反应的仅是一、二个人的个人愿景。这种愿景很少在每一个阶层内进行探询与检验,因此无法使人们了解与感到共同拥有这个愿景,结果新出炉的官方愿景也无从孕育出能量与真诚的投入。事实上,有时它甚至无法在建立它的高阶管理团体中鼓起一丝热情。

    不是单一问题的解答

    此外,愿景不是问题的解答,如果它仅被当成问题的解答,一旦士气低落或策略方向模糊不清的问题解决以后,愿景背后的动力也会跟着消逝。领导者必须把建立共同愿景当成日常工作的中心要素,是持续进行、永无止境的工作。实际上它是经营理念的一部分;经营理念不仅是企业的愿景,还包括企业的目的与核心价值,远比通常占满最高主管脑海的统计图表或部门结构重要。

    有时候,管理者期望从公司的策略规划过程中显现共同愿景,但结果与意图由上而下建立愿景一般的失败;大多数的策略规划无法孕育真正的愿景。根据哈梅尔与普拉哈拉德所说的:“创造性策略很少是从每年例行的规划过程中产生的。每年例行的规划虽然逐年有所改进,但下个年度策略的起点,几乎仍然总是这个年度的策略,因此在大部分的情况下,公司仍会对它熟悉的市场区隔与地区坚守不移。推动佳能公司进入个人式复印机事业的想法,出自海外销售分支机构,而不是在日本本土的企画人员。”

    这并不是说愿景不能从高层发散出来。但是有时愿景是源自不在权力核心者的个人愿景,有时是从许多阶层互动的人们中激荡而出。分享愿景的过程,远比愿景源自何处重要。除非共同愿景与组织内个人的愿景连成一体,否则它就不是真正的共同愿景。对那些身居领导位置的人而言,最要紧是必须记得他们的愿景最终仍然只是个人愿景,位居领导位置并不代表他们的个人愿景自然就是组织的愿景。

    最后,意图建立共同愿景的领导者,必须乐于不断把自己的个人愿景与他人分享。他们也必须试着问别人:“你是否愿意跟我追求此一愿景?”对领导者而言,这并不是一件容易的事。

    投入

    在现今的组织中,真正投入的人只有少数,而真正奉献的人则更少,大多数的人仍在遵从的地步。遵从的跟随者跟着愿景走,他们依照别人的要求做事情。他们对愿景都有某种程度的支持,但是,他们并非真正地投入或奉献。

    遵从常被误认为是投入及奉献,部分原因是大多数的组织一直都是以遵从为基本要求,以致不知道如何识别真正的奉献。另外,遵从有数个层次,有些层次的遵从会使得某些行为看起来非常类似投入和奉献。以下便以几个层级来说明,成员对组织共同愿景的支持程度:

    •    奉献:衷心向往之,并愿意创造或改变任何必要的“法则”(结构性的),以全心全意地实现它。
    •    投入:衷心向往之,愿意在“精神的法则”内做任何事情。
    •    真正遵从:看到愿景的好处。去做所有被期望做的事情,或做得更多。遵从明文规定,像个“好战士”。
    •    适度遵从:大体上,看到了愿景的好处。做所有被期望做的事情,但仅此而已。是个“不错的战士”。
    • 🔲   勉强遵从:未看到愿景的好处,但是也不想打破饭碗。不得不做刚好符合期望的事,但也会让人知道,他不是

    真的愿意做。

    • 🔲   不遵从:看不到愿景的好处,也不愿做被期望做的事情。“任你苦口婆心,我就是不干。”
    • 🔲   冷漠:既不支持也不反对愿景。既不感兴趣,也没有干劲。“下班时间到了吗?”

    是什么 为什么 怎么做

    10CB008EAC9B1211246BD852DFD49EC9.jpg

    @Abser(abser)

  • 还在篮子里

    《论毅力》· 语雀

    《论毅力》

    梁启超的《论毅力》。

    首先推出:

    “有毅力者成,反是者败。“

    说,成事的人,无不是有毅力之辈。这个可以暂时在一个范围内赞同的继续看下去。

    而顺逆两境,又常相间以迭乘。无论事之大小,而必有数次乃至十数次之阻力。

    讲人生中会有顺境和逆境,同时他们是相互间杂的,意在证明一件事中,无论大小,同样有顺有逆。

    心流的概念中 有一个心流通道的概念。

    image.png可以简单的认为这里的顺境和逆境对应左上区域和右下区域。

    不过心流讲的做一件事的动态变化,你长时间做一件事,针对性的技巧提升让你本来感觉的逆境成为一中顺境,但是旧的问题解决之后,你的能力和你的解决问题的工具扩展了你接触的事件边界,故而产生新的或者进一步的问题,从这个概念来讲,这是由于人自身的体验造成的顺逆的起伏,亦或者是各种波形的相互影响造成的变换。这里仿佛有一种隐喻安排的意味,将顺境和逆境分配到你的人生一样。

    其在志力薄弱之士,始固曰吾欲云云,吾欲云云。其意以为天下事固易易也。及骤尝焉,而阻力猝来,颓然丧矣。

    天下意志薄弱的人,总是说我想干什么,以为万事都简单,尝试一下遇到挫折就放弃,没有任何的志气毅力。这样的人我们在生活中能见到很多,无非是尝试的多少或者承受挫折能力不同而已,反观我们自己也是这样,我们要做的不是怨天尤人,抱怨自己的本性懒惰,最终也无法做到大毅力,而是坚持做到下一步,马上下几句就会论述坚持下一步的好处。而我们往往需要做成事的毅力也不是需要传说中的宏毅,因为我们很多时候做的事,还真的就不那么难。既然开始了,或者说叫立了 Flag ,一点点挫折才哪里到哪里,保持继续前进,为自己获得一个善终不好吗??

     夫苟其挫而不退矣,则小逆之后必有小顺,大逆之后必有大顺。盘根错节之既破,而遂有应刃而解之一日。

    这里开始 引用到了 上面关于 生活中事件的连续性是由顺逆境组成的。不过多加了一条,就是大的顺境对应大的逆境,小的对应小的的对应关系。也就是说,解决的困难和盘根错节的地方越多,斩破之后接下来的路就越宽越顺。

    这里我认为有些想当然了,或者说定义有一些耍流氓,一个大的逆境自然不会是一次挫折组成的,常言道,坏事成双,挫折如果一波又一波的涌来,一浪比一浪高,我们是认为这是一个大的逆境呢还是一次又一次越来越大的逆境。那么逆境之后对应的顺境又该如何理解?

    我认为真正的顺畅就是,你什么都没有感觉到,就像我们身体上的病痛一样,如果平时健康无碍,就会觉得身体仿佛不存在一般。

    旁观者徒艳羡其功之成,以为是殆幸运儿,而天有以宠彼也。

    这里说到不懂成功的旁人的徒有艳羡,这里的成功讲做功之成更没有争议。像这样把别人的成功归为幸运的人,一定会把自己的不成功归为不幸。这种人一定是要远离的。

    更譬诸操舟,如以兼旬之期行千里之地者,其间风潮之或顺或逆,常相参伍。彼以坚苦忍耐之力,冒其逆而突过之,而后得从容以容度其顺。我则或一日而返焉,或二三日而返焉,或五六日而返焉;故彼岸终不可得达也。

    这里讲到操舟,实际上讲的是风潮。风潮的顺逆正巧对应 文中的 顺逆境 的概念,同时又和上述的关于大逆之后大顺的相契合。我认为这里最合适的类比是 关于 抵达彼岸的说法。逆水行舟不做,自然倒退,如果不克服必经的困难,自然也就毫无存进,甚至返回开始处。要走的路,都是必经之路,畏难并不能让自己换一条路前行,否则就不再是广义上完全相同的自己了(这里赞同了决定论或者说因果)

    成败之数,视此而已。

    进,吾往也,是本文的核心。也是我一直说的核心。坚持,而不是放弃,孰轻孰重,自己抉择。

    进,吾往也

    原文:

    天下古今成败之林,若是其莽然不一途也。要其何以成?何以败?曰:“有毅力者成,反是者败。”盖人生历程,大抵逆境居十六七,顺境亦居十三四。而顺逆两境,又常相间以迭乘。无论事之大小,而必有数次乃至十数次之阻力。

    其阻力虽或大或小,而要之必无可逃避者也。其在志力薄弱之士,始固曰吾欲云云,吾欲云云。其意以为天下事固易易也。及骤尝焉,而阻力猝来,颓然丧矣。其次弱者,乘一时之客气,透过此第一关,遇再挫而退。稍强者,遇三四挫而退。更稍强者,遇五六挫而退。其事愈大者,其遇挫愈多,其不退也愈难。非至强之人,未有能善于其终者也。

    夫苟其挫而不退矣,则小逆之后必有小顺,大逆之后必有大顺。盘根错节之既破,而遂有应刃而解之一日。旁观者徒艳羡其功之成,以为是殆幸运儿,而天有以宠彼也。又以为我蹇于遭逢,故所就不彼若也。庸讵知所谓蹇焉幸焉者,彼皆与我之所同,而其能征服此蹇焉,利用此幸焉与否,即彼成我败所由判也。更譬诸操舟,如以兼旬之期行千里之地者,其间风潮之或顺或逆,常相参伍。彼以坚苦忍耐之力,冒其逆而突过之,而后得从容以容度其顺。我则或一日而返焉,或二三日而返焉,或五六日而返焉;故彼岸终不可得达也。

    孔子曰:“譬如为山,未成一箦,止,吾止也。譬如平地,虽覆一篑,进,吾往也。”孟子曰:“有为者譬若掘井,掘井九仞而不及泉,犹为弃井也。”成败之数,视此而已。

  • 还在篮子里

    劝学 · 语雀

    《劝学》荀子

    劝学精读-Abser

    君子曰:学不可以已。

    学而不已,三日当刮目相看

    青,取之于蓝而青于蓝;冰,水为之而寒于水。

    优秀的人也是慢慢蜕变的

    木直中绳,輮(左应为“车”,原字已废除)以为轮,其曲中规。虽有槁暴,不复挺者,輮使之然也。

    学会的,会刻在骨子里,影响你的每一个行为,优秀的人也正是不断改造自己的各个方面,使自己举手投足有道法自然之感。浑然天成的感觉不是先天成就的,而是后天打磨进骨髓中。

    故木受绳则直,金就砺则利,君子博学而日参省乎己,则知明而行无过矣。

    博学自省,改造自己的同时规范自己,像木受绳一般,像金属器不断磨砺同时也观察磨砺哪里一般,君子推进自己的同时更要为自己确立边界。寻求自我的界限。是故知行合一,没有歪曲自己学到的知识。

    故不登高山,不知天之高也;不临深溪,不知地之厚也;不闻先王之遗言,不知学问之大也。

    知识学的越多,才知道不知道的越多。坐井观天无可厚非,但是想要提高却闭门造车,好否?

    干、越、夷、貉之子,生而同声,长而异俗,教使之然也。诗曰:“嗟尔君子,无恒安息。靖共尔位,好是正直。神之听之,介尔景福。”神莫大于化道,福莫长于无祸。

    教育资源的不同我认为是现在人们不同情况的较大因素。编程和教育一样,不同地区有非常大的教育倾斜,虽然网上有着无比多混杂的资源(哈哈)。

    吾尝终日而思矣,不如须臾之所学也;

    这个我深有体会,只有看书,只有学习,才能触发有效的思考!!

    吾尝跂而望矣,不如登高之博见也。登高而招,臂非加长也,而见者远;顺风而呼,声非加疾也,而闻者彰。假舆马者,非利足也,而致千里;假舟楫者,非能水也,而绝江河。君子生非异也,善假于物也。

    站在巨人的肩膀上!君不见,逍遥游尚须同风起,跃龙门亦须待时机。互联网又有谚语“风口猪都能飞起来”。顺势而为,不可谓不智

    南方有鸟焉,名曰蒙鸠,以羽为巢,而编之以发,系之苇苕,风至苕折,卵破子死。巢非不完也,所系者然也。

    然也,君子不立危墙之下。

    西方有木焉,名曰射干,茎长四寸,生于高山之上,而临百仞之渊,木茎非能长也,所立者然也。

    系统的思考,也像在高山之上临百丈之渊,从系统外思考,更理性,更客观,仿佛自己一下子就达到一个很高的境界之后来看自己,能看到更多。

    蓬生麻中,不扶而直;白沙在涅,与之俱黑。兰槐之根是为芷,其渐之滫,君子不近,庶人不服。其质非不美也,所渐者然也。故君子居必择乡,游必就士,所以防邪辟而近中正也。

    愚见:此同-观测者效应,同-当你在凝视深渊时,深渊也在凝视你

    物类之起,必有所始。荣辱之来,必象其德。肉腐出虫,鱼枯生蠹。怠慢忘身,祸灾乃作。强自取柱,柔自取束。邪秽在身,怨之所构。施薪若一,火就燥也,平地若一,水就湿也。草木畴生,禽兽群焉,物各从其类也。是故质的张,而弓矢至焉;林木茂,而斧斤至焉;树成荫,而众鸟息焉。醯酸,而蚋聚焉。故言有招祸也,行有招辱也,君子慎其所立乎!

    一饮一啄,君子慎独

    积土成山,风雨兴焉;积水成渊,蛟龙生焉;积善成德,而神明自得,圣心备焉。故不积跬步,无以至千里;不积小流,无以成江海。骐骥一跃,不能十步;驽马十驾,功在不舍。锲而舍之,朽木不折;锲而不舍,金石可镂。蚓无爪牙之利,筋骨之强,上食埃土,下饮黄泉,用心一也。蟹六跪而二螯,非蛇鳝之穴无可寄托者,用心躁也。

    所以我们做的事,最好是从初中开始教能学的孩子,教大学生或者工作者是无奈之举,但是任何时候重新开始都不算晚,做事畏难必不成。

    是故无冥冥之志者,无昭昭之明;无惛惛之事者,无赫赫之功。

    非淡泊无以明志,非宁静无以致远。如果没有高的志向,燕雀安知鸿鹄之志,自驱动可行否?如果没有专一的理想,自省又可行否?

    行衢道者不至,事两君者不容。目不能两视而明,耳不能两听而聪。螣蛇无足而飞,鼫鼠五技而穷。《诗》曰:“尸鸠在桑,其子七兮。淑人君子,其仪一兮。其仪一兮,心如结兮!”故君子结于一也。

    归一便是均衡

    昔者瓠巴鼓瑟,而流鱼出听;伯牙鼓琴,而六马仰秣。故声无小而不闻,行无隐而不形 。玉在山而草润,渊生珠而崖不枯。为善不积邪?安有不闻者乎?

    积善成德,而神明自得!此举此时不闻,安有后不闻者?

    学恶乎始?恶乎终?曰:其数则始乎诵经,终乎读礼;其义则始乎为士,终乎为圣人, 真积力久则入,学至乎没而后止也。故学数有终,若其义则不可须臾舍也。为之,人也;舍 之,禽兽也。故书者,政事之纪也;诗者,中声之所止也;礼者,法之大分,类之纲纪也。 故学至乎礼而止矣。夫是之谓道德之极。礼之敬文也,乐之中和也,诗书之博也,春秋之微 也,在天地之间者毕矣。

    微言大义,愚见:此处更适合抽象的看待,不要落在实际的诗书上,这样容易着相

    君子之学也,入乎耳,着乎心,布乎四体,形乎动静。端而言,蝡而动,一可以为法则。小人之学也,入乎耳,出乎口;口耳之间,则四寸耳,曷足以美七尺之躯哉!古之学者为己,今之学者为人。君子之学也,以美其身;小人之学也,以为禽犊。故不问而告谓之傲,问一而告二谓之囋。傲、非也,囋、非也;君子如向矣。

    完善自己为学习目标,而我是为了自由

    学莫便乎近其人。礼乐法而不说,诗书故而不切,春秋约而不速。方其人之习君子之说,则尊以遍矣,周于世矣。故曰:学莫便乎近其人。

    学莫便乎近其人

    学之经莫速乎好其人,隆礼次之。上不能好其人,下不能隆礼,安特将学杂识志,顺诗书而已耳。则末世穷年,不免为陋儒而已。将原先王,本仁义,则礼正其经纬蹊径也。若挈裘领,诎五指而顿之,顺者不可胜数也。不道礼宪,以诗书为之,譬之犹以指测河也,以戈舂黍也,以锥餐壶也,不可以得之矣。故隆礼,虽未明,法士也;不隆礼,虽察辩,散儒也。

    现在的年轻人,就是知识太过抽象,读了一些浮躁的杂书,拿了成果就走。万物之理只记得个感觉,空洞的话随口就能长篇大论,一边说着所谓的大道理听的太多,都是鸡汤,不能指导实际行为,一边从浮躁的上层思想中得到虚浮的大道理,这做什么学问呢?修身尚且无法做到,与散儒有什么区别(这里我不是很推崇儒家思想,但是总有一些东西是相通的。)这样的人啊,

    趁早追求一些别的人生意义吧,何来求学习能得到的幸福呢?

    问楛者,勿告也;告楛者,勿问也;说楛者,勿听也。有争气者,勿与辩也。故必由其道至,然后接之;非其道则避之。故礼恭,而后可与言道之方;辞顺,而后可与言道之理;色从而后可与言道之致。故未可与言而言,谓之傲;可与言而不言,谓之隐;不观气色而言,谓瞽。故君子不傲、不隐、不瞽,谨顺其身。诗曰:“匪交匪舒,天子所予。”此之谓也。

    看清楚自己交流的目标,每一次交流实际上是损耗自己。讨论能得到什么东西,辩驳能得到什么东西,教导能得到什么东西,这里不止是指自己得到什么,而是你期望的得到者,可能是你,可能是身边人,可能是众生。

    思考,代价是什么!

    百发失一,不足谓善射;千里蹞步不至,不足谓善御;伦类不通,仁义不一,不足谓善学。学也者,固学一之也。一出焉,一入焉,涂巷之人也;其善者少,不善者多,桀纣盗跖也;全之尽之,然后学者也。

    学一阵子,停一阵子,学你🐎呢?

    君子知夫不全不粹之不足以为美也,故诵数以贯之,思索以通之,为其人以处之,除其害者以持养之。使目非是无欲见也,使耳非是无欲闻也,使口非是无欲言也,使心非是无欲虑也。及至其致好之也,目好之五色,耳好之五声,口好之五味,心利之有天下。是故权利不能倾也,群众不能移也,天下不能荡也。生乎由是,死乎由是,夫是之谓德操。德操然后能定,能定然后能应。能定能应,夫是之谓成人。天见其明,地见其光,君子贵其全也。

    故诵数以贯之,思索以通之,为其人以处之,除其害者以持养之

    有了坚定不移操守才能随机应对!

    有才俊的人最容易走到歪路,更加需要注意本文中的劝诫啊。这里分享一个

  • 还在篮子里

    《原则》 · 语雀

    《原则》

    • 原则=决策模型?
    • Ray Dalio的第一条原则:独立思考并决定

    1.你想要什么;2.事实的愿望是什么;3.面对事实,你如何实现自己的愿望······

    • 增长黑客的理论像是原则的不断更新和学习
    • “换言之,我的目标只是让自己正确—-我并不关心正确的答案是不是来源于我”
      • “我学到了一种很好的恐惧犯错的意识,这把我的思维定式从认为”我是对的“变成了问自己“我怎么知道我是对的””
      • 回答这个问题的最好方式是找到其他的独立思考者,他们与我肩负共同的使命,但对问题的看法与我不同。通过以一种经过深思熟虑的辩论方式与他们交流,我就能理解他们的推理,并让他们对我的推理进行压力测试。我们都可以通过这种方式降低自己犯错的可能性
    1. 找到与我观点不同的最聪明的人,以便自己能够努力理解他们的推理
    2. 知道自己在什么时候不能有明确的意见,不急于下结论
    3. 逐步归纳永恒和普适的原则,对其进行测试,将其系统化(这里我认为可以做一个原则测试的软件,可以联系我)
    4. 通过平衡风险来保持较大的回报,并降低下行波动
    • 桥水是一个创意择优的机构
      • 不是一个专制机构,由我领导,其他人服从;
      • 也不是一个民主机构,每个人都有平等的投票权;
      • 而是一个创意择优的机构,鼓励经过深思熟虑的意见不一致,根据不同人的相对长处分析和权衡他们的观点。
    • 在面对两个你都需要但看起来相互矛盾的东西时,你需要耐心地做出选择。几乎总是存在着一条你还没有发现的有利的道路,所以你需要不断找下去,直到找到它,而不是满足于最初对你显而易见的那种选择。
    • 是否能以数学方式表达世界所有不同部分之间的所有关系
    • 重要的不是预知未来,而是知道每一个时间点上如何针对可获得的信息做出合理的回应
    • 如果你以勤奋和有创造性的方式工作,你几乎可以得到你想要的任何东西,但你不可能同时得到所有东西。成熟意味着你可以放弃一些好的选择,从而追求更好的选择。

    来源: 《原则》 · 语雀