Redis实战入门

一. Redis 连接

1. 进入redis环境

docker run -d -p 6379:6379 --name redis redis:latest
docker exec -it redis redis-cli

2. 检测 Redis 是否正常

127.0.0.1:6379> ping
PONG

3. 退出连接

127.0.0.1:6379> quit
[root@VM-4-12-centos data]#

二. redis内容

1. key 操作命令

输入命令

含义

keys *

查看所有的key

dbsize

获取总key数

exists key

查询键是否存在

del key

删除key

type key

查询键类型(string,set..)

ttl key

查询key的生命周期(秒)

pttl key

查询key的生命周期(毫秒)

expire key

设置过期时间(秒)

pexpire key

设置过期时间(毫秒)

persist key

设置永不过期

rename key newkey

更改键名称

2. 字符串string 操作命令

输入命令

含义

举例

返回值

set key value

存放键值

set java1 test

OK

get key

获取键值

get java1

"test"

mset key1 value1 key2 value2

批量存放

mset java1 1 java2 2 java3 3

OK

mget key1 key2

批量获取

mget java1 java2

1) "test" 2) "2"

strlen key

获取值长度

strlen java1

4

append key value

追加内容

append java1 test2

(integer) 6

getrange key start end

获取部分内容

getrange java1 0 3

"1tes"

3. 无序集合Set 操作命令

相当于 Java 语言里面的 HashSet

语法

含义

举例

返回值

sadd key member...

存储值

sadd langs java php c++ go ruby python kotlin java

(integer) 7

smembers key

获取所有元素语法

smembers langs

1) "php"2) "kotlin"3)"c++"4) "go"5) "ruby"6) "python"7) "java"

srandmember key count

随机获取元素语法

srandmember langs 3

1)"kotlin"2) "python"3) "c++"

sismember key member

判断集合是否存在元素

sismember langs go

(integer) 1

scard key

获取集合元素个数

scard langs

(integer) 7

srem key member..

删除集合元素

srem langs java

(integer) 1

spop key count

弹出元素

spop langs 2

1) "python" 2) "go"

4. 有序集合zset

类似于 Java 的 SortedSet 和 HashMap 的结合体

和列表的区别:

1、列表使用链表实现,两头快,中间慢。有序集合是散列表和跳跃表实现的,即使读取中间的元素也比较快。

2、列表不能调整元素位置,有序集合能。

3、有序集合比列表更占内存。

输入命令

含义

举例

返回值

zadd key [NX|XX] [CH] [INCR] score member [score member ...]

存储值

zadd footCounts 16011 tid 20082 huny 2893 nosy

3

zscore key member

获取元素分数

zscore footCounts tid

"16011"

zincrby key increment member

增加指定元素分数

zincrby footCounts 2000 tid

"18011"

zcard key

获取集合元素个数

zcard footCounts

3

zrem key member [member ...]

删除指定元素

zrem footCounts huny

1

zrank key member

获取元素排名

zrank footCounts tid

2

zrangebyscore key min max [WITHSCORES] [LIMIT offset count]

获取指定分数范围排名语法

zrangebyscore footCounts 3000 30000 withscores limit 0 1

1) "tid"2) "16011"

zcount key min max

获取指定范围分数个数

zcount footCounts 2000 20000

2

5. 列表操作命令list

Redis 中的 listJava 中的 LinkedList 很像,底层都是一种链表结构, list 的插入和删除操作非常快,时间复杂度为 0(1),不像数组结构插入、删除操作需要移动数据。

输入命令

含义

举例

返回值

lpush key value ...

左端存值语法

lpush list lily sandy

2

rpush key value ...

右端存值语法

rpush list tom kitty

4

lset key index value

索引存值语法

lset list 3 uto

OK

lpop key

左端弹出语法

lpop list

"broad"

rpop key

右侧弹出语法

rpop list

"kitty"

llen key

获取元素个数

llen list

2

lrange key start top

获取列表元素

lrange list 0 -1

1) "sandy" 2) "lily"3)"uto"

index key index

索引获取语法

lindex list 2

"ketty "

lrem key count value

根据值删除语法

lrem userids 0 111 //count=0 删除所有

2

ltrim key start stop

范围删除语法

ltrim list 2 4

OK

6. 散列表hash

Redis 中的 Hash 和 Java的 HashMap 更加相似, 都是 数组+链表 的结构,当发生 hash 碰撞时将会把元素追加到链表上,值得注意的是在 RedisHashvalue 只能是字符串.

输入命令

含义

举例

返回值

hset key field value

存放键值

hset user name javastack

1

hmset key field value field value ...

2

hmset user name javastack age 20 address china

4

hsetnx key field value

不存在时语法

hsetnx user tall 180

0

hget key field

获取字段值

hget key field

"20"

hmget key field field ...

获取多个字段值

hmget user name age address

1) "javastack"2)"20"3) "china"

hgetall key

获取所有键与值语法

hgetall user

1) "name"2) "javastack"3) "age"4) "20"5) "address"6) "china"

hkeys key

获取所有字段语法

127.0.0.1:6379> hkeys user

1) "name"2) "address"3) "tall"4) "age"

hvals key

获取所有值语法

hvals user

1) "javastack"2) "china"3) "170"4) "20"

hexists key field

判断字段是否存在

hexists user address

1

hlen key

获取字段数量

hlen user

4

hincrby key field increment

递增/减

hincrby user tall -10

170

hdel key field field ...

删除字段

hdel user age

1

三. key的操作之get set

1. get、set去设置

// exampleClient redis连接客户端的代码
func exampleClient() {
   rdb := redis.NewClient(redis.Options{Addr: "localhost:6379", Password: "", DB: 0})
   err := rdb.Set(ctx, "key", "value", 0).Err()
   if err != nil {
      panic(err)
   }
   val, err := rdb.Get(ctx, "key").Result()
   if err != nil {
      panic(err)
   }
   fmt.Println("key", val)

   val2, err := rdb.Get(ctx, "key2").Result()
   if err == redis.Nil {
      fmt.Println("key2 does not exist")
   } else if err != nil {
      panic(err)
   } else {
      fmt.Println("key2", val2)
   }
}

2. 执行任意命令

go-redis 还提供了一个执行任意命令或自定义命令的 Do 方法,特别是一些 go-redis 库暂时不支持的命令都可以使用该方法执行。具体使用方法如下

// doCommand 基本的get set
func doCommand() {
   rdb := redis.NewClient(redis.Options{Addr: "localhost:6379", Password: "", DB: 0})
   ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
   defer cancel()

   // 执行命令获取结果
   val, err := rdb.Get(ctx, "key").Result()
   fmt.Println("test1", val, err)

   // 先获取到命令对象
   cmder := rdb.Get(ctx, "key")
   fmt.Println("test2", cmder.Val())
   fmt.Println("test3", cmder.Err())

   // 直接执行命令获取错误
   // rdb.Set(ctx, "key", 10, time.Hour).Err()
   err = rdb.Set(ctx, "key", 3000, 0).Err()

   // 直接执行命令获取值
   value := rdb.Get(ctx, "key").Val()
   fmt.Println("test4", value)
}

3. redis.Nil

go-redis 库提供了一个 redis.Nil 错误来表示 Key 不存在的错误。因此在使用 go-redis 时需要注意对返回错误的判断。在某些场景下我们应该区别处理 redis.Nil 和其他不为 nil 的错误。

// exampleClient redis连接客户端的代码
func exampleClient() {
   rdb := redis.NewClient(redis.Options{Addr: "localhost:6379", Password: "", DB: 0})
   err := rdb.Set(ctx, "key", "value", 0).Err()
   if err != nil {
      panic(err)
   }
   val, err := rdb.Get(ctx, "key").Result()
   if err != nil {
      panic(err)
   }
   fmt.Println("key", val)

   val2, err := rdb.Get(ctx, "key2").Result()
   if err == redis.Nil {
      fmt.Println("key2 does not exist")
   } else if err != nil {
      panic(err)
   } else {
      fmt.Println("key2", val2)
   }
}

4. 扫描或遍历所有key

你可以使用 KEYS prefix:* 命令按前缀获取所有 key

func ScanKeysDemo2() {
   rdb := redis.NewClient(redis.Options{Addr: "localhost:6379", Password: "", DB: 0})
   ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
   defer cancel()
   // 按前缀扫描key
   iter := rdb.Scan(ctx, 0, "prefix:*", 0).Iterator()
   // 此外,对于 Redis 中的 set、hash、zset 数据类型,go-redis 也支持类似的遍历方法。
   // iter := rdb.SScan(ctx, "set-key", 0, "prefix:*", 0).Iterator()
   // iter := rdb.HScan(ctx, "hash-key", 0, "prefix:*", 0).Iterator()
   // iter := rdb.ZScan(ctx, "sorted-hash-key", 0, "prefix:*", 0).Iterator()

   for iter.Next(ctx) {
      fmt.Println("[key]:", iter.Val())
   }
   if err := iter.Err(); err != nil {
      panic(err)
   }
}

四. zset命令

func ZSetDemo() {
   rdb := redis.NewClient(redis.Options{Addr: "localhost:6379", Password: "", DB: 0})
   zsetKey := "language_rank"
   languages := []*redis.Z{
      {Score: 90.0, Member: "Golang"},
      {Score: 98.0, Member: "Java"},
      {Score: 95.0, Member: "Python"},
      {Score: 97.0, Member: "JavaScript"},
      {Score: 99.0, Member: "C/C++"},
   }
   ctx, cancel := context.WithTimeout(context.Background(), 50000*time.Microsecond)
   defer cancel()

   // ZADD
   err := rdb.ZAdd(ctx, zsetKey, languages...).Err()
   if err != nil {
      fmt.Printf("zadd failed, err:%v\n", err)
      return
   }
   fmt.Println("zadd success!")

   // Goland分数加10
   newScore, err := rdb.ZIncrBy(ctx, zsetKey, 10.0, "Golang").Result()
   if err != nil {
      fmt.Printf("zincrby failed, err:%v\n", err)
      return
   }
   fmt.Printf("Goland's score is %f now.\n", newScore)

   // 取分数最高的3个
   ret := rdb.ZRevRangeWithScores(ctx, zsetKey, 0, 2).Val()
   for _, z := range ret {
      fmt.Println(z.Member, z.Score)
   }
   fmt.Println("=================")

   // 取95~100分的
   op := redis.ZRangeBy{
      Min: "95",
      Max: "100",
   }
   ret, err = rdb.ZRangeByScoreWithScores(ctx, zsetKey, op).Result()
   if err != nil {
      fmt.Printf("zrangebyscore failed, err:%v\n", err)
      return
   }
   for _, z := range ret {
      fmt.Println(z.Member, z.Score)
   }
}

执行上面的函数将得到如下输出结果。

zadd success
Golang's score is 100.000000 now.
Golang 100
C/C++ 99
Java 98
Python 95
JavaScript 97
Java 98
C/C++ 99
Golang 100

五. Pipeline

Redis Pipeline 允许通过使用单个 client-server-client 往返执行多个命令来提高性能。区别于一个接一个地执行100个命令,你可以将这些命令放入 pipeline 中,然后使用1次读写操作像执行单个命令一样执行它们。这样做的好处是节省了执行命令的网络往返时间(RTT)。

1. 写法一. pipeline

func PipelineWithNoExecDemo() {
   rdb := redis.NewClient(redis.Options{Addr: "localhost:6379", Password: "", DB: 0})
   ctx, _ := context.WithTimeout(context.Background(), time.Hour)

   var incr *redis.IntCmd
   _, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
      incr = pipe.Incr(ctx, "pipeline_counter")
      pipe.Expire(ctx, "pipeline_counter", time.Hour)
      return nil
   })
   if err != nil {
      panic(err)
   }
   // 在执行pipe.Exec之后才能获取到结果
   fmt.Println(incr.Val())
}

也可以使用 Pipelined 方法,它会在函数退出时调用 Exec. 代码会更加简洁

2. 写法二. pipelined

func PipelineWithNoExecMulDemo() {
   rdb := redis.NewClient(redis.Options{Addr: "localhost:6379", Password: "", DB: 0})
   ctx, _ := context.WithTimeout(context.Background(), time.Hour)
   _, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
      for i := 0; i < 100; i++ {
         pipe.Set(ctx, fmt.Sprintf("key%d", i), fmt.Sprintf("key%d", i), time.Hour)
      }
      return nil
   })
   if err != nil {
      panic(err)
   }

   cmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
      for i := 0; i < 100; i++ {
         pipe.Get(ctx, fmt.Sprintf("key%d", i))
      }
      return nil
   })
   if err != nil {
      panic(err)
   }

   for _, cmd := range cmds {
      fmt.Println(cmd.(*redis.StringCmd).Val())
   }
}

六. 加锁

Redis 是单线程执行命令的,因此单个命令始终是原子的,但是来自不同客户端的两个给定命令可以依次执行,例如在它们之间交替执行。但是, Multi/exec 能够确保在 multi/exec 两个语句之间的命令之间没有其他客户端正在执行命令。

在这种场景我们需要使用 TxPipeline 或 TxPipelined 方法将 pipeline 命令使用 MULTIEXEC 包裹起来。

// ZTransactionTxPipeDemo 这里只是相当于加锁,TxPipeline/TxPipelined
func ZTransactionTxPipeDemo() {
   rdb := redis.NewClient(redis.Options{Addr: "localhost:6379", Password: "", DB: 0})
   ctx, cancel := context.WithTimeout(context.Background(), 50000*time.Microsecond)
   defer cancel()

   // TxPipeline demo
   pipe := rdb.TxPipeline()
   pipe.Incr(ctx, "tx_pipeline_counter")
   pipe.Expire(ctx, "tx_pipeline_counter", time.Hour)
   _, err := pipe.Exec(ctx)
   if err != nil {
      panic(err)
   }

   // TxPipelined demo
   var incr2 *redis.IntCmd
   _, err = rdb.TxPipelined(ctx, func(pipeliner redis.Pipeliner) error {
      incr2 = pipe.Incr(ctx, "tx_pipeline_counter")
      pipe.Expire(ctx, "tx_pipeline_counter", time.Hour)
      return nil
   })

   fmt.Println(incr2.Val(), err)
}

七. 事务-watch

我们通常搭配 WATCH 命令来执行事务操作。从使用 WATCH 命令监视某个 key 开始,直到执行 EXEC 命令的这段时间里,如果有其他用户抢先对被监视的 key 进行了替换、更新、删除等操作,那么当用户尝试执行 EXEC 的时候,事务将失败并返回一个错误,用户可以根据这个错误选择重试事务或者放弃事务。

Watch方法接收一个函数和一个或多个key作为参数

// WatchDemo 配合TxPipeline/TxPipelined完成增删改查
func WatchDemo() error {
   rdb := redis.NewClient(redis.Options{Addr: "localhost:6379", Password: "", DB: 0})
   ctx, cancel := context.WithTimeout(context.Background(), 50000*time.Microsecond)
   defer cancel()
   key := "key"
   return rdb.Watch(ctx, func(tx *redis.Tx) error {
      n, err := tx.Get(ctx, key).Int()
      if err != nil; err != redis.Nil {
         return err
      }
      // 假设操作耗时5秒
      // 5秒内我们通过其他的客户端修改key,当前事务就会失败
      time.Sleep(5 * time.Second)
      _, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
         pipe.Set(ctx, key, n+1, time.Hour)
         return nil
      })
      return err
   }, key)
}

参考文章

  1. Go语言操作Redis
  2. https://github.com/go-redis/redis
  3. Redis 常用操作命令,非常详细!
  4. Redis基础系列短视频《2. Redis简单实战》
  5. redis中文官网
  6. Redis 常用命令

原文地址:https://cloud.tencent.com/developer/article/2140959

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340