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会有超高的效率。
Redis和MySQL在处理热点数据时的读写性能:
| 对比项 | Redis | MySQL |
|---|---|---|
| 读写性能 | 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操作
- 在terminal中执行:redis-server [redis.conf配置文件路径],启动redis服务端。
- 新建一个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"
用途
- 抢购、秒杀、详情页、点赞、评论
- 规避并发下,对数据库事物的操作
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)
四、总结
-
Redis 是一种高性能的 NoSQL 数据库,通过内存存储和数据结构设计,能够提供超高的读写性能、丰富的数据类型和可扩展性。
-
Redis 支持多种数据类型,包括字符串、哈希、列表、集合、有序集合等,可以灵活地应对不同的应用场景。
-
Golang 通过 go-redis 客户端,可以非常便捷地调用 Redis 的各种命令,实现缓存、排行榜、消息队列等功能。
-
文中展示了 Redis 的常见命令及 Golang 中的用法,包括 GET/SET、INCR、LPUSH/LRANGE、HSET/HGET、ZADD/ZRANGEBYSCORE 等,涵盖了字符串、哈希、列表、有序集合多种数据结构的操作。
-
Redis 非常适合处理热点数据的高效读写,可以显著降低数据库负载。Golang 通过 go-redis 客户端也可以便捷地使用 Redis 的强大功能。