Those who are familiar with the Redis command can skip it because the library API names correspond to the commands one by one and are easy to remember

Redis database connection

Github.com/go-redis/re…

var rdb *redis.Client var ctx = context.Background() func initClient() (err error) { rdb = redis.NewClient(&redis.Options{ Addr: "localhost:6379", Password: "", DB: 0, }) _, err = rdb.Ping(ctx).Result() if err ! = nil { return err } return nil }Copy the code

If you’re a beginner and you declare an RDB, init must say RDB = and don’t say RDB := otherwise RDB is a local variable

External references are nil

There are also redis sentinel mode and cluster mode two connection methods that are not demonstrated here. If necessary, you can inquire by yourself

The basic use

func redisDemo() { err := rdb.Set(ctx, "score", 100, 0).Err() if err ! = nil { panic(err) } val, err := rdb.Get(ctx, "score").Result() if err ! = nil { panic(err) } fmt.Println("score:", val) val2, err := rdb.Get(ctx, FMT.Println("keytest does not exist").Result() if err == redis.Nil {// fmt.Println("keytest does not exist")} else if err ! = nil { fmt.Println("get keytest failed") } else { fmt.Println("keytest:", val2) } }Copy the code

In general, Redis is fairly simple and can be explored on its own, directly in Goland. I’m going to show you what the API is and I’m not going to show you what it is

Redis – leaderboard-like operation

This example will be slightly more complicated than the above example, many sites similar to the leaderboard operation is actually a zset, written like this:

func redisDemo2() { zsetkey := "language_rank" languages := []*redis.Z{ {Score: 90, Member: "java"}, {Score: 80, Member: "go"}, {Score: 70, Member: "js"}, {Score: 60, Member: "rust"}, {Score: 50, Member: "c++"}, } num, err := rdb.ZAdd(ctx, zsetkey, languages...) .Result() if err ! Println("num:", num) // newScore, err := rdb.ZIncrBy(CTX, zsetkey, 10) "go").Result() if err ! Println("newScore:", newScore) := rdb. zrevrangescore with scores (CTX, zsetkey, 0, 0); 2).Val() for _, z := range ret { fmt.Println("name:", z.Member, " score:", Z.core)} // set the score within the range of the op := redis. Z.core {Min: "80", Max: "110", } ret = rdb.ZRangeByScoreWithScores(ctx, zsetkey, &op).Val() for _, z := range ret { fmt.Println(z.Member," ",z.Score) } }Copy the code

pipeline

Pipeline is mainly network optimization, can save RTT, not transactions, do not make a mistake, for example, you want to execute 3 commands, then your normal operation is 3 RTT network time, but you can put these 3 into a pipeline that only needs 1 RTT network time

This is used when a large number of commands need to be executed at the same time

Pipeline is not a panacea. For example, if the following operation depends on the previous operation, it is not suitable. For example, if we want to take a value, and then decide to set a new value according to this value, the two operations set operation depends on the previous GET operation

There is no way to pipeline this scenario

Transaction TxPipeline

Redis is single-threaded, single command is atomic operation, but commands from different clients can be executed in sequence, at this time, we need to use TxPipeline to ensure that there will be no commands from other clients inserted between our two commands.

watch

This kind of scenario is also commonly used, for example, we order to buy graphics cards, graphics cards are now so few, how do you ensure that users must have inventory at the moment of ordering? That is actually the key that you watch the inventory when placing an order. If you find that the inventory is cruD in the process of placing an order, you can directly return it. The operation failed

Note that multiple keys can be passed

Here’s an example:

func watchDemo() { key := "watch_count" err := rdb.Watch(ctx, func(tx *redis.Tx) error { n, err := tx.Get(ctx, key).Int64() if err ! = nil && err ! = redis.Nil { return err } _, err = tx.TxPipelined(ctx, func(pipeliner redis.Pipeliner) error { pipeliner.Set(ctx, key, n+1, 0) return nil }) return err }, key) if err ! = nil { fmt.Println("tx exec failed:",err) return } fmt.Println("tx exec success") }Copy the code

This function can be executed by itself, no problem

So if I change it a little bit, sleep for a few seconds in this function

In the meantime, I will modify the value in redis-CLI

func watchDemo() { key := "watch_count" err := rdb.Watch(ctx, func(tx *redis.Tx) error { n, err := tx.Get(ctx, key).Int() if err ! = nil && err ! = redis.Nil { return err } _, err = tx.TxPipelined(ctx, func(pipeliner redis.Pipeliner) error { time.Sleep(5 * time.Second) pipeliner.Set(ctx, key, n+1, 0) return nil }) return err }, key) if err ! = nil { fmt.Println("tx exec failed:", err) return } fmt.Println("tx exec success") }Copy the code

Note that we added a time sleep operation to the transaction to ensure that we have enough time to set the key in redis-CLI. The result is obvious that this operation must fail

Tx.Pipelined is not a transaction, so this code is invalid. It has to be TX