To apply to different scenarios, TiKV provides two types of APIs for developers: the Raw Key-Value API and the Transactional Key-Value API. This document uses two examples to guide you through how to use the two APIs in TiKV. The usage examples are based on multiple nodes for testing. You can also quickly try the two types of APIs on a single machine.

Try the Raw Key-Value API

To use the Raw Key-Value API in applications developed in the Go language, take the following steps:

  • Install the necessary packages.
  1. export GO111MODULE=on
  2. go mod init rawkv-demo
  3. go get github.com/pingcap/tidb@master
  • Import the dependency packages.
  1. import (
  2. "fmt"
  3. "github.com/pingcap/tidb/config"
  4. "github.com/pingcap/tidb/store/tikv"
  5. )
  • Create a Raw Key-Value client.
  1. cli, err := tikv.NewRawKVClient([]string{"192.168.199.113:2379"}, config.Security{})

Description of two parameters in the above command:

  • string: a list of PD servers’ addresses
  • config.Security: used to establish TLS connections, usually left empty when you do not need TLS
    • Call the Raw Key-Value client methods to access the data on TiKV. The Raw Key-Value API contains the following methods, and you can also find them at GoDoc.
  1. type RawKVClient struct
  2. func (c *RawKVClient) Close() error
  3. func (c *RawKVClient) ClusterID() uint64
  4. func (c *RawKVClient) Delete(key []byte) error
  5. func (c *RawKVClient) Get(key []byte) ([]byte, error)
  6. func (c *RawKVClient) Put(key, value []byte) error
  7. func (c *RawKVClient) Scan(startKey, endKey []byte, limit int) (keys [][]byte, values [][]byte, err error)

Usage example of the Raw Key-Value API

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/pingcap/tidb/config"
  5. "github.com/pingcap/tidb/store/tikv"
  6. )
  7. func main() {
  8. cli, err := tikv.NewRawKVClient([]string{"192.168.199.113:2379"}, config.Security{})
  9. if err != nil {
  10. panic(err)
  11. }
  12. defer cli.Close()
  13. fmt.Printf("cluster ID: %d\n", cli.ClusterID())
  14. key := []byte("Company")
  15. val := []byte("PingCAP")
  16. // put key into tikv
  17. err = cli.Put(key, val)
  18. if err != nil {
  19. panic(err)
  20. }
  21. fmt.Printf("Successfully put %s:%s to tikv\n", key, val)
  22. // get key from tikv
  23. val, err = cli.Get(key)
  24. if err != nil {
  25. panic(err)
  26. }
  27. fmt.Printf("found val: %s for key: %s\n", val, key)
  28. // delete key from tikv
  29. err = cli.Delete(key)
  30. if err != nil {
  31. panic(err)
  32. }
  33. fmt.Printf("key: %s deleted\n", key)
  34. // get key again from tikv
  35. val, err = cli.Get(key)
  36. if err != nil {
  37. panic(err)
  38. }
  39. fmt.Printf("found val: %s for key: %s\n", val, key)
  40. }

The result is like:

  1. INFO[0000] [pd] create pd client with endpoints [192.168.199.113:2379]
  2. INFO[0000] [pd] leader switches to: http://127.0.0.1:2379, previous:
  3. INFO[0000] [pd] init cluster id 6554145799874853483
  4. cluster ID: 6554145799874853483
  5. Successfully put Company:PingCAP to tikv
  6. found val: PingCAP for key: Company
  7. key: Company deleted
  8. found val: for key: Company

RawKVClient is a client of the TiKV server and only supports the GET/PUT/DELETE/SCAN commands. The RawKVClient can be safely and concurrently accessed by multiple goroutines, as long as it is not closed. Therefore, for one process, one client is enough generally.

Possible Error

  • If you see this error:
  1. build rawkv-demo: cannot load github.com/pingcap/pd/pd-client: cannot find module providing package github.com/pingcap/pd/pd-client

You can run GO111MODULE=on go get -u github.com/pingcap/tidb@master to fix it.

  • If you got this error when you run go get -u github.com/pingcap/tidb@master:
  1. go: github.com/golang/lint@v0.0.0-20190409202823-959b441ac422: parsing go.mod: unexpected module path "golang.org/x/lint"

You can run go mod edit -replace github.com/golang/lint=golang.org/x/lint@latest to fix it. Refer Link

Try the Transactional Key-Value API

The Transactional Key-Value API is more complicated than the Raw Key-Value API. Some transaction related concepts are listed as follows. For more details, see the KV package.

  • Storage

Like the RawKVClient, a Storage is an abstract TiKV cluster.

  • Snapshot

A Snapshot is the state of a Storage at a particular point of time, which provides some readonly methods. The multiple times read from a same Snapshot is guaranteed consistent.

  • Transaction

Like the transactions in SQL, a Transaction symbolizes a series of read and write operations performed within the Storage. Internally, a Transaction consists of a Snapshot for reads, and a MemBuffer for all writes. The default isolation level of a Transaction is Snapshot Isolation.

To use the Transactional Key-Value API in applications developed by golang, take the following steps:

  • Install the necessary packages.
  1. export GO111MODULE=on
  2. go mod init txnkv-demo
  3. go get github.com/pingcap/tidb@master
  • Import the dependency packages.
  1. import (
  2. "flag"
  3. "fmt"
  4. "os"
  5. "github.com/juju/errors"
  6. "github.com/pingcap/tidb/kv"
  7. "github.com/pingcap/tidb/store/tikv"
  8. "github.com/pingcap/tidb/terror"
  9. goctx "golang.org/x/net/context"
  10. )
  • Create Storage using a URL scheme.
  1. driver := tikv.Driver{}
  2. storage, err := driver.Open("tikv://192.168.199.113:2379")
  • (Optional) Modify the Storage using a Transaction.

The lifecycle of a Transaction is: begin → {get, set, delete, scan} → {commit, rollback}.

  • Call the Transactional Key-Value API's methods to access the data on TiKV. The Transactional Key-Value API contains the following methods:
  1. Begin() -> Txn
  2. Txn.Get(key []byte) -> (value []byte)
  3. Txn.Set(key []byte, value []byte)
  4. Txn.Iter(begin, end []byte) -> Iterator
  5. Txn.Delete(key []byte)
  6. Txn.Commit()

Usage example of the Transactional Key-Value API

  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "os"
  6. "github.com/juju/errors"
  7. "github.com/pingcap/tidb/kv"
  8. "github.com/pingcap/tidb/store/tikv"
  9. "github.com/pingcap/tidb/terror"
  10. goctx "golang.org/x/net/context"
  11. )
  12. type KV struct {
  13. K, V []byte
  14. }
  15. func (kv KV) String() string {
  16. return fmt.Sprintf("%s => %s (%v)", kv.K, kv.V, kv.V)
  17. }
  18. var (
  19. store kv.Storage
  20. pdAddr = flag.String("pd", "192.168.199.113:2379", "pd address:192.168.199.113:2379")
  21. )
  22. // Init initializes information.
  23. func initStore() {
  24. driver := tikv.Driver{}
  25. var err error
  26. store, err = driver.Open(fmt.Sprintf("tikv://%s", *pdAddr))
  27. terror.MustNil(err)
  28. }
  29. // key1 val1 key2 val2 ...
  30. func puts(args ...[]byte) error {
  31. tx, err := store.Begin()
  32. if err != nil {
  33. return errors.Trace(err)
  34. }
  35. for i := 0; i < len(args); i += 2 {
  36. key, val := args[i], args[i+1]
  37. err := tx.Set(key, val)
  38. if err != nil {
  39. return errors.Trace(err)
  40. }
  41. }
  42. err = tx.Commit(goctx.Background())
  43. if err != nil {
  44. return errors.Trace(err)
  45. }
  46. return nil
  47. }
  48. func get(k []byte) (KV, error) {
  49. tx, err := store.Begin()
  50. if err != nil {
  51. return KV{}, errors.Trace(err)
  52. }
  53. v, err := tx.Get(k)
  54. if err != nil {
  55. return KV{}, errors.Trace(err)
  56. }
  57. return KV{K: k, V: v}, nil
  58. }
  59. func dels(keys ...[]byte) error {
  60. tx, err := store.Begin()
  61. if err != nil {
  62. return errors.Trace(err)
  63. }
  64. for _, key := range keys {
  65. err := tx.Delete(key)
  66. if err != nil {
  67. return errors.Trace(err)
  68. }
  69. }
  70. err = tx.Commit(goctx.Background())
  71. if err != nil {
  72. return errors.Trace(err)
  73. }
  74. return nil
  75. }
  76. func scan(keyPrefix []byte, limit int) ([]KV, error) {
  77. tx, err := store.Begin()
  78. if err != nil {
  79. return nil, errors.Trace(err)
  80. }
  81. it, err := tx.Iter(kv.Key(keyPrefix), nil)
  82. if err != nil {
  83. return nil, errors.Trace(err)
  84. }
  85. defer it.Close()
  86. var ret []KV
  87. for it.Valid() && limit > 0 {
  88. ret = append(ret, KV{K: it.Key()[:], V: it.Value()[:]})
  89. limit--
  90. it.Next()
  91. }
  92. return ret, nil
  93. }
  94. func main() {
  95. pdAddr := os.Getenv("PD_ADDR")
  96. if pdAddr != "" {
  97. os.Args = append(os.Args, "-pd", pdAddr)
  98. }
  99. flag.Parse()
  100. initStore()
  101. // set
  102. err := puts([]byte("key1"), []byte("value1"), []byte("key2"), []byte("value2"))
  103. terror.MustNil(err)
  104. // get
  105. kv, err := get([]byte("key1"))
  106. terror.MustNil(err)
  107. fmt.Println(kv)
  108. // scan
  109. ret, err := scan([]byte("key"), 10)
  110. for _, kv := range ret {
  111. fmt.Println(kv)
  112. }
  113. // delete
  114. err = dels([]byte("key1"), []byte("key2"))
  115. terror.MustNil(err)
  116. }

The result is like:

  1. INFO[0000] [pd] create pd client with endpoints [192.168.199.113:2379]
  2. INFO[0000] [pd] leader switches to: http://192.168.199.113:2379, previous:
  3. INFO[0000] [pd] init cluster id 6563858376412119197
  4. key1 => value1 ([118 97 108 117 101 49])
  5. key1 => value1 ([118 97 108 117 101 49])
  6. key2 => value2 ([118 97 108 117 101 50])