Go语言中的Redis应用—基础 | 青训营

767 阅读13分钟

Go语言中的Redis应用—基础

前置

笔者环境

  • macos 10.15.7
  • Redis 7.0.11
  • Golang 1.18
  • GoLand 2022.01

读完本文可以获得

  • 什么是Redis&为什么要使用redis
  • Redis的特性&应用场景
  • Redis的数据结构&基本操作
  • 使用go-redis执行 Redis 命令

一、什么是Redis

1.1 简介

Redis是一款高性能的开源内存数据库,拥有快速的数据读写能力和丰富的数据结构,弥补了传统数据库在处理缓存、实时计算和高并发需求上的不足,为系统提供了高效的数据存储与访问方案。

1.2 为什么需要Redis

当应用需要快速读写、访问热数据(经常被访问的数据)时,Redis相较于传统的关系型数据库如MySQL会有超高的效率。

Screen Shot 2023-08-20 at 4.31.20 PM.png

Redis和MySQL在处理热点数据时的读写性能:

对比项RedisMySQL
读写性能10万+ QPS,延迟毫秒级几千QPS
随机读写O(1)复杂度需要磁盘IO
内存缓存支持需要额外缓存
并发量10万+连接更低
数据结构丰富相对单一
可扩展性易于扩展扩展更复杂

从上表可以更直观地看出,Redis在读写性能、并发量、可扩展性等方面都明显优于MySQL。特别是在处理热点数据读写时,Redis的性能可以达到MySQL的几十至几百倍,所以Redis非常适合用于缓存热点数据,以减轻数据库压力。

1.3 应用场景

应用场景需求与特点
缓存需要快速读写和访问热点数据,减轻数据库压力
排行榜需要快速的增删改操作和有序数据结构
消息队列需要发布订阅、阻塞队列等功能
计数器需要高并发地增减数值
分布式锁需要基于Redis原子操作实现锁
会话缓存需要快速存取用户会话信息
实时数据需要快速发布和订阅数据

缓存和消息队列,已经成为Redis的两大核心应用场景。

1.4 安装

二、基本操作

2.1 数据类型

数据类型常用操作
字符串SET, GET, INCR, DECR 等
列表LPUSH, RPUSH, LPOP, RPOP等
集合SADD, SREM, SMEMBERS等
有序集合ZADD, ZRANGE, ZREM等
哈希HSET, HGET, HGETALL等

下列示例基于redis-cli操作

  1. 在terminal中执行:redis-server [redis.conf配置文件路径],启动redis服务端。
  2. 新建一个terminal并执行:redis-cli,进入redis客户端。

2.2字符串(String)

Redis 字符串命令用于管理 Redis 中的字符串值。

语法

  • SET key value: 设置指定 key 的值。
  • GET key: 获取指定 key 的值。
127.0.0.1:6379> SET key "value"
OK
127.0.0.1:6379> GET key
"value"

2.3 INCR(数值)

Redis INCR 命令将 key 中储存的数字值➕1。

  • 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。

  • 本操作的值限制在 64 位(bit)有符号数字表示之内。

Note: 本质上这是一个字符串操作,因为Redis没有专门的整数类型。存储在 key 中的字符串被转换为十进制有符号整数,在此基础上加1。

语法

  • INCR key :将value+1
  • INCRBY key val: 将value +val
127.0.0.1:6379> SET num 1970
OK
127.0.0.1:6379> INCR num
(integer) 1971
127.0.0.1:6379> GET num
"1971"
127.0.0.1:6379> INCRBY num -100
(integer) 1871
127.0.0.1:6379> INCRBY num 101
(integer) 1972
127.0.0.1:6379> GET num
"1972"

用途

  1. 抢购、秒杀、详情页、点赞、评论
  2. 规避并发下,对数据库事物的操作

2.4 列表(List)

Redis 列表是按插入顺序排序的字符串列表。

  • 可以在列表的头部(左边)或尾部(右边)添加元素。

  • 列表可以包含超过 40 亿 个元素 ( 2^32 - 1 )。

语法

  • LPUSH key value: 将一个或多个值插入到列表头部。
  • RPUSH key value: 将一个或多个值插入到列表尾部。
  • LPOP key: 移除并获取列表第一个元素。
  • RPOP key: 移除并获取列表最后一个元素。
  • LRANGE key START END: 获取列表指定范围内的元素。
127.0.0.1:6379>  LPUSH lists a 123 4
(integer) 3
127.0.0.1:6379> RPUSH lists hello world
(integer) 5
127.0.0.1:6379> LRANGE lists 0 -1
1) "4"
2) "123"
3) "a"
4) "hello"
5) "world"
127.0.0.1:6379> LPOP lists
"4"
127.0.0.1:6379> RPOP lists
"world"
127.0.0.1:6379>  LRANGE lists 0 -1
1) "123"
2) "a"
3) "hello"
127.0.0.1:6379> 

2.5 集合(Set)

Redis 的 Set 是 string 类型的无序集合。

  • 集合成员是唯一的,这就意味着集合中没有重复的数据。
  • 在 Redis 中,添加、删除和查找的时间复杂都是 O(1)(不管 Set 中包含多少元素)。
  • 集合中最大的成员数为 2^32 – 1 (4294967295), 每个集合可存储 40 多亿个成员。

语法

  • SADD key member: 向集合添加一个或多个成员。
  • SMEMBERS key: 返回集合中的所有成员。
  • SISMEMBER key member: 判断 member 元素是否是集合 key 的成员。
  • SRANDMEMBER key count: 随机返回集合key中count个随机元素。
  • count>0:返回含有 count 个不同的元素的数组。
  • count<0:返回一个包含count的绝对值的个数元素的数组,会出现一个元素出现多次的情况。
127.0.0.1:6379> SADD sets ppt word excel
(integer) 3
127.0.0.1:6379> SMEMBERS sets
1) "word"
2) "excel"
3) "ppt"
127.0.0.1:6379> SISMEMBER sets ppt
(integer) 1
127.0.0.1:6379> SRANDMEMBER sets 2
1) "word"
2) "ppt"

2.6 有序集合(Sorted Set)

Redis Sorted Set 内部的每个元素都会关联一个 double 类型的分数,并按分数进行有序排列

  • Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员,

  • 有序集合的成员是唯一的,但分数 ( score ) 却可以重复。

语法

  • ZADD key socre member:向有序集合添加一个或多个成员,或者更新已存在成员的分数
  • ZRange key start stop:通过索引区间返回有序集合成指定区间内的成员
127.0.0.1:6379> ZADD class -1 174a6
(integer) 1
127.0.0.1:6379> ZADD class 2 174a3
(integer) 1
127.0.0.1:6379> ZADD class 4 174a2
(integer) 1
127.0.0.1:6379> ZRANGE class 0 -1
1) "174a6"
2) "174a3"
3) "174a2"

2.7 哈希(Hash)

Redis hash 是一个 string 类型的 field 和 value 的映射表。

  • hash 特别适合用于存储对象。

  • 每个哈希键中可以存储多达 40 亿个字段值对。

语法

  • HSET key field value [field value...]:将哈希表 key 中的字段 field 的值设为 value(可设置多个 field value)。如果Key和field不存在就创建,field存在则覆盖。
  • HGET Hash_KEY Hash_FIELD:获取存储在哈希表中指定字段的值。
  • HGETALL Hash_KEY:获取在哈希表中指定 Hash_KEY 的所有字段和值。
127.0.0.1:6379> HSET Person name "redis" url "http://www.redis.com.cn" rank 1 visitors 240000000
(integer) 4
127.0.0.1:6379> HGET Person name
"redis"
127.0.0.1:6379> HGETALL Person
1) "name"
2) "redis"
3) "url"
4) "http://www.redis.com.cn"
5) "rank"
6) "1"
7) "visitors"
8) "240000000"

2.9 位图(Bitmap)

Redis Bitmap 通过类似 map 结构存放 0 或 1 ( bit 位 ) 作为值。

语法

SETBIT key offset value:对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。

GETBIT key offset:对 key 所储存的字符串值,获取指定偏移量上的位 ( bit ),当 offset 比字符串值的长度大,或者 key 不存在时,返回 0

BITCOUNT key start end:计算给定字符串特定范围中,被设置为 1 的比特位的数量。

BITOP [AND 、 OR 、 NOT 、 XOR] destKey key1 key2 ... ]:对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。除了 NOT 操作之外,其他操作都可以接受一个或多个 key 作为输入。

127.0.0.1:6379> SETBIT space 0 1
(integer) 0
127.0.0.1:6379> SETBIT work 67 1
(integer) 0
127.0.0.1:6379> GETBIT space 0
(integer) 1
127.0.0.1:6379> BITCOUNT space 0 -1
(integer) 1
127.0.0.1:6379> BITOP OR  destkey space work
(integer) 9

用途

常用来统计状态,如日活是否浏览过某个东西。

统计用户登录天数

一共366天,每一天就是一个二进制位,用户登录二进制位为1

127.0.0.1:6379> SETBIT idx123  0 1    第1天登录
(integer) 0
127.0.0.1:6379> SETBIT idx123  23 1   第24天登录
(integer) 0
127.0.0.1:6379> SETBIT idx123  365 1  第366天登录
(integer) 0
127.0.0.1:6379> BITCOUNT idx123 0 -1  统计从0位到365位一共有几个1,也就是有几天
(integer) 3



2.10 基础操作

基础操作描述
KEYS查找键值
EXPIRE设置键的过期时间
TTL查看键的剩余过期时间
DEL删除键值对

KEYS - 查找键:

127.0.0.1:6379> KEYS * 
1) "key"
2) "list"
3) "set"

EXPIRE - 设置键的过期时间:

127.0.0.1:6379> EXPIRE key 60
(integer) 1

TTL - 查看剩余过期时间:

127.0.0.1:6379> TTL key 
(integer) 56

DEL - 删除键:

127.0.0.1:6379> DEL key
(integer) 1

2.11 高级功能

高级功能描述
发布订阅实现消息队列
事务原子执行多个命令
Lua脚本编写脚本完成复杂操作
pipeline批量发送命令减少RTT

事务操作:

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> INCR counter
QUEUED
127.0.0.1:6379> INCR counter
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 1
2) (integer) 2

发布订阅:

# 订阅端
127.0.0.1:6379> SUBSCRIBE channel

# 发布端
127.0.0.1:6379> PUBLISH channel "hello"

Lua脚本:

127.0.0.1:6379> EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

pipeline:

127.0.0.1:6379> PIPELINE
127.0.0.1:6379> INCR counter1
127.0.0.1:6379> INCR counter2
127.0.0.1:6379> EXEC
1) (integer) 1
2) (integer) 1

三、go-redis

使用Redis作为数据库,并用go-redis库来操作它。

3.1 导包

import (
	"github.com/go-redis/redis/v9"
)

github.com/go-redis/redis/v9 是一个 Redis 客户端库,它提供了一系列的方法和类型,让您可以方便地执行 Redis 命令

3.2 连接redis

RedisClient := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "your_password", // 如果没有Password,可以删掉改行
})

3.2 GET/SET/DEL/INCR/SETNX

下列命令都是用来操作字符串类型的键值对的。

  • GET命令用来获取一个键的值,如果键不存在则返回nil。
  • SET命令用来设置一个键的值,可以指定过期时间或其他选项。
  • DEL命令用来删除一个或多个键,返回被删除的键的数量。
  • INCR命令用来将一个键的值增加1,如果键不存在则先设置为0再增加1,如果键的值不是整数则返回错误。
  • SETNX命令用来设置一个键的值,只有当键不存在时才生效,返回1表示成功,0表示失败。
// 设置一个键foo的值为bar
err := RedisClient.Set(ctx, "foo", "bar", 0).Err()
if err != nil {
panic(err)
}

// 获取一个键foo的值
val, err := RedisClient.Get(ctx, "foo").Result()
if err != nil {
panic(err)
}
fmt.Println("foo", val)

// 删除一个键foo
n, err := RedisClient.Del(ctx, "foo").Result()
if err != nil {
panic(err)
}
fmt.Println("deleted", n)

// 将一个键counter的值增加1
n, err := RedisClient.Incr(ctx, "counter").Result()
if err != nil {
panic(err)
}
fmt.Println("counter", n)

// 设置一个键lock的值为1,只有当键不存在时才生效
ok, err := RedisClient.SetNX(ctx, "lock", 1, 0).Result()
if err != nil {
panic(err)
}
fmt.Println("set lock", ok)

3.3 HSET/HGET/HINCRBY

下列命令都是用来操作哈希类型的键值对的。哈希类型的键可以存储多个字段和值,类似于Go中的map。

  • SET命令用来设置一个哈希类型键的一个或多个字段和值,返回被设置的字段的数量。
  • HGET命令用来获取一个哈希类型键的一个字段的值,如果字段不存在则返回nil。
  • HINCRBY命令用来将一个哈希类型键的一个字段的值增加指定的整数,如果字段不存在则先设置为0再增加,如果字段的值不是整数则返回错误。
// 设置一个哈希类型键user:1的name和age字段
n, err := RedisClient.HSet(ctx, "user:1", "name", "Alice", "age", 20).Result()
if err != nil {
panic(err)
}
fmt.Println("set fields", n)

// 获取一个哈希类型键user:1的name字段
val, err := RedisClient.HGet(ctx, "user:1", "name").Result()
if err != nil {
panic(err)
}
fmt.Println("name", val)

// 将一个哈希类型键user:1的age字段增加10
n, err := RedisClient.HIncrBy(ctx, "user:1", "age", 10).Result()
if err != nil {
panic(err)
}
fmt.Println("age", n)

3.4 LPUSH/RPOP/LRANGE

下列命令都是用来操作列表类型的键值对的。列表类型的键可以存储多个元素,类似于Go中的slice。

  • LPUSH命令用来将一个或多个元素插入到列表类型键的左端,返回列表长度。
  • RPOP命令用来移除并返回列表类型键的右端元素,如果列表为空则返回nil。
  • LRANGE命令用来获取列表类型键指定范围内的元素,范围可以用索引或负数表示,例如0表示第一个元素,-1表示最后一个元素。
// 将a,b,c三个元素插入到列表类型键mylist的左端
n, err := RedisClient.LPush(ctx, "mylist", "a", "b", "c").Result()
if err != nil {
panic(err)
}
fmt.Println("list length", n)

// 移除并返回列表类型键mylist的右端元素
val, err := RedisClient.RPop(ctx, "mylist").Result()
if err != nil {
panic(err)
}
fmt.Println("pop value", val)

// 获取列表类型键mylist的所有元素
vals, err := RedisClient.LRange(ctx, "mylist", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println("list values", vals)

• ZADD/ZRANGEBYSCORE/ZREVRANGE/ZINCRBY/ZSCORE

下列命令都是用来操作有序集合类型的键值对的。有序集合类型的键可以存储多个元素和分数,类似于Go中的map,但是元素是按照分数从小到大排序的。

  • ZADD命令用来将一个或多个元素和分数添加到有序集合类型键中,返回被添加的元素的数量。
  • ZRANGEBYSCORE命令用来获取有序集合类型键中指定分数范围内的元素,可以指定是否返回分数和排序方式。
  • ZREVRANGE命令用来获取有序集合类型键中指定索引范围内的元素,按照分数从大到小排序,可以指定是否返回分数。
  • ZINCRBY命令用来将有序集合类型键中指定元素的分数增加指定的浮点数,如果元素不存在则先添加再增加,返回新的分数。
  • ZSCORE命令用来获取有序集合类型键中指定元素的分数,如果元素不存在则返回nil。
// 将a,b,c三个元素和对应的分数添加到有序集合类型键myscore中
n, err := RedisClient.ZAdd(ctx, "myscore", &redis.Z{Score: 1.0, Member: "a"}, &redis.Z{Score: 2.0, Member: "b"}, &redis.Z{Score: 3.0, Member: "c"}).Result()
if err != nil {
panic(err)
}
fmt.Println("added members", n)

// 获取有序集合类型键myscore中分数在1.5到2.5之间的元素,不返回分数,按照分数从小到大排序
vals, err := RedisClient.ZRangeByScore(ctx, "myscore", &redis.ZRangeBy{Min: "1.5", Max: "2.5"}).Result()
if err != nil {
panic(err)
}
fmt.Println("members by score", vals)

// 获取有序集合类型键myscore中索引在0到-1之间的元素,返回分数,按照分数从大到小排序
vals, err := RedisClient.ZRevRangeWithScores(ctx, "myscore", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println("members by index", vals)

// 将有序集合类型键myscore中a元素的分数增加0.5
score, err := RedisClient.ZIncrBy(ctx, "myscore", 0.5, "a").Result()
if err != nil {
panic(err)
}
fmt.Println("new score of a", score)

// 获取有序集合类型键myscore中b元素的分数
score, err := RedisClient.ZScore(ctx, "myscore", "b").Result()
if err != nil {
panic(err)
}
fmt.Println("score of b", score)

四、总结

  1. Redis 是一种高性能的 NoSQL 数据库,通过内存存储和数据结构设计,能够提供超高的读写性能、丰富的数据类型和可扩展性。

  2. Redis 支持多种数据类型,包括字符串、哈希、列表、集合、有序集合等,可以灵活地应对不同的应用场景。

  3. Golang 通过 go-redis 客户端,可以非常便捷地调用 Redis 的各种命令,实现缓存、排行榜、消息队列等功能。

  4. 文中展示了 Redis 的常见命令及 Golang 中的用法,包括 GET/SET、INCR、LPUSH/LRANGE、HSET/HGET、ZADD/ZRANGEBYSCORE 等,涵盖了字符串、哈希、列表、有序集合多种数据结构的操作。

  5. Redis 非常适合处理热点数据的高效读写,可以显著降低数据库负载。Golang 通过 go-redis 客户端也可以便捷地使用 Redis 的强大功能。