Redis 是一个高性能的开源内存数据库,常用于缓存、消息队列、会话存储等场景。它支持多种数据结构(如字符串、哈希、列表、集合、有序集合等)和丰富的操作命令,具有极高的性能和灵活性。
以下是 Redis 的基础知识、安装、常用命令,以及在 Go 中使用 Redis 的方法。
redis中文官方文档指南:redis中文文档
redis的中文官方文档学习笔记很全,推荐去官网学习
30天学会Go–第6天 GO语言 RESTful API 学习与实践:30天学会Go–第6天 GO语言 RESTful API 学习与实践-CSDN博客
wget http://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz
cd redis-stable
make
src/redis-server
redis-server.exe
启动服务。PATH
中:
Path
,点击 编辑。C:\Program Files\Redis
)docker pull redis
docker run -d --name redis -p 6379:6379 redis
连接 Redis:
redis-cli
设置键值对:
SET key value
获取键值:
GET key
删除键:
DEL key
检查键是否存在:
EXISTS key
设置过期时间(秒):
EXPIRE key seconds
查看剩余过期时间:
TTL key
字符串是 Redis 中最基本的数据结构,可以存储文本或二进制数据,支持数值操作。
增加值(适用于数值类型):
INCR key
:将键的值加 1。SET counter 10
INCR counter
# 输出: (integer) 11
INCRBY key increment
:将键的值增加指定的 increment
。INCRBY counter 5
# 输出: (integer) 16
DECR key
:将键的值减 1。DECR counter
# 输出: (integer) 15
DECRBY key decrement
:将键的值减少指定的 decrement
。DECRBY counter 3
# 输出: (integer) 12
SET key value
:设置键的值。SET greeting "Hello"
GET key
:获取键的值。GET greeting
# 输出: "Hello"
APPEND key value
:将 value
追加到键的现有值之后。APPEND greeting " World"
# 输出: (integer) 11
STRLEN key
:获取键值的长度。STRLEN greeting
# 输出: (integer) 11
哈希是一个键值对集合,适用于存储对象(如用户信息)。
设置哈希字段:
HSET key field value
:设置哈希表中的字段 field
为指定值 value
。HSET user:1 name "Alice"
HSET user:1 age 30
# 输出: (integer) 1
获取哈希字段值:
HGET key field
:获取哈希表中字段 field
的值。HGET user:1 name
# 输出: "Alice"
获取所有字段和值:
HGETALL key
:获取哈希表中所有字段和值。
HGETALL user:1
# 输出:
# 1) "name"
# 2) "Alice"
# 3) "age"
# 4) "30"
HMSET key field1 value1 field2 value2
:同时设置多个字段及其值。
HMSET user:2 name "Bob" age 25 city "New York"
HMGET key field1 field2
:获取多个字段的值。
HMGET user:2 name city
# 输出:
# 1) "Bob"
# 2) "New York"
HDEL key field
:删除哈希表中的一个或多个字段。
HDEL user:1 age
# 输出: (integer) 1
HLEN key
:获取哈希表中字段的数量。
HLEN user:2
# 输出: (integer) 3
HEXISTS key field
:检查字段是否存在。
HEXISTS user:2 city
# 输出: (integer) 1
列表是一个链表,支持从两端插入和弹出元素。
从左插入元素:
LPUSH key value1 value2
:将一个或多个值插入到列表的头部。LPUSH tasks "Task1" "Task2"
# 输出: (integer) 2
从右插入元素:
RPUSH key value1 value2
:将一个或多个值插入到列表的尾部。RPUSH tasks "Task3"
# 输出: (integer) 3
获取列表范围内的元素:
LRANGE key start stop
:获取指定范围的元素(支持负索引)。LRANGE tasks 0 -1
# 输出:
# 1) "Task2"
# 2) "Task1"
# 3) "Task3"
弹出最左边的元素:
LPOP key
:移除并返回列表的第一个元素。
LPOP tasks
# 输出: "Task2"
RPOP key
:移除并返回列表的最后一个元素。RPOP tasks
# 输出: "Task3"
LLEN key
:获取列表的长度。LLEN tasks
# 输出: (integer) 1
LINDEX key index
:获取列表中指定索引的元素。LINDEX tasks 0
# 输出: "Task1"
集合是一个无序的、唯一的元素集合。
添加元素到集合:
SADD key member1 member2
:将一个或多个元素添加到集合。SADD fruits "apple" "banana" "cherry"
# 输出: (integer) 3
获取集合中的所有元素:
SMEMBERS key
:获取集合中的所有元素。SMEMBERS fruits
# 输出:
# 1) "apple"
# 2) "banana"
# 3) "cherry"
检查元素是否存在:
SISMEMBER key member
:检查指定元素是否在集合中。
SISMEMBER fruits "banana"
# 输出: (integer) 1
SREM key member
:移除集合中的一个或多个元素。
SREM fruits "apple"
# 输出: (integer) 1
SCARD key
:获取集合中元素的数量。
SCARD fruits
# 输出: (integer) 2
有序集合是一个带有分数的集合,分数用于排序。
添加元素并设置分数:
ZADD key score1 member1 score2 member2
:添加一个或多个元素及其分数。ZADD leaderboard 100 "Alice" 200 "Bob" 150 "Charlie"
# 输出: (integer) 3
获取有序集合中的元素:
ZRANGE key start stop [WITHSCORES]
:按分数从低到高返回指定范围的元素。ZRANGE leaderboard 0 -1 WITHSCORES
# 输出:
# 1) "Alice"
# 2) "100"
# 3) "Charlie"
# 4) "150"
# 5) "Bob"
# 6) "200"
ZINCRBY key increment member
:为指定元素的分数增加 increment
。ZINCRBY leaderboard 50 "Alice"
# 输出: "150"
在 Go 中使用 Redis,通常使用第三方库 go-redis。
运行以下命令安装:
go get github.com/redis/go-redis/v9
以下是一个简单的示例,展示如何在 Go 中使用 Redis。
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background() // 一个通用的、无状态的上下文,用于程序的起点或测试场景
func main() {
// 1. 连接到 Redis
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Redis 地址
Password: "", // 如果没有设置密码,留空
DB: 0, // 使用默认数据库
})
// 2. 写入数据
err := rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
// 3. 读取数据
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key:", val)
// 4. 检查键是否存在
exists, err := rdb.Exists(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key exists:", exists)
// 5. 删除键
err = rdb.Del(ctx, "key").Err()
if err != nil {
panic(err)
}
fmt.Println("key deleted")
}
连接 Redis:
redis.NewClient
创建 Redis 客户端。写入数据:
Set
方法写入键值对。0
表示不过期。读取数据:
Get
方法读取键的值。检查键是否存在:
Exists
方法检查键是否存在。删除键:
Del
方法删除键。Redis 的发布/订阅(Pub/Sub)功能允许消息的发布者和订阅者通过频道(Channel)进行通信,适用于实时消息推送场景。
err := rdb.Publish(ctx, "channel1", "Hello, Redis!").Err()
if err != nil {
panic(err)
}
Publish
:将消息发布到指定的频道 channel1
。sub := rdb.Subscribe(ctx, "channel1")
ch := sub.Channel()
for msg := range ch {
fmt.Println("Received message:", msg.Payload)
}
Subscribe
:订阅指定的频道 channel1
。sub.Channel()
:返回一个通道(Channel),用于接收消息。msg.Payload
:获取消息内容。Subscribe
监听指定频道。Publish
向频道发送消息。分布式锁是一种用于在分布式系统中控制资源访问的机制。Redis 的原子操作(如 SETNX
和 DEL
)可以用来实现分布式锁。
ok, err := rdb.SetNX(ctx, "lock_key", "lock_value", 10*time.Second).Result()
if err != nil || !ok {
fmt.Println("Failed to acquire lock")
return
}
fmt.Println("Lock acquired, executing task...")
SetNX
:尝试设置键 lock_key
,如果键不存在,则设置成功并返回 true
;如果键已存在,则返回 false
。10\*time.Second
:锁的过期时间,防止因异常情况导致锁无法释放。ok
:表示加锁是否成功。rdb.Del(ctx, "lock_key")
Del
:删除键 lock_key
,释放锁。SetNX
确保锁的唯一性和原子性。简单的 Del
存在潜在问题:如果一个客户端意外删除了其他客户端的锁,可能会导致资源竞争。
可以使用 Lua 脚本确保解锁的原子性:
unlockScript := redis.NewScript(`
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
`)
_, err := unlockScript.Run(ctx, rdb, []string{"lock_key"}, "lock_value").Result()
if err != nil {
panic(err)
}
在某些任务耗时较长的情况下,可以通过定时任务自动续约锁的过期时间:
go func() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for range ticker.C {
_, err := rdb.Expire(ctx, "lock_key", 10*time.Second).Result()
if err != nil {
fmt.Println("Failed to renew lock")
return
}
fmt.Println("Lock renewed")
}
}()
以下是一个完整的分布式锁实现,包括加锁、续约和解锁:
package main
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
// 加锁
lockKey := "lock_key"
lockValue := "unique_value"
ok, err := rdb.SetNX(ctx, lockKey, lockValue, 10*time.Second).Result()
if err != nil || !ok {
fmt.Println("Failed to acquire lock")
return
}
fmt.Println("Lock acquired")
// 自动续约
go func() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for range ticker.C {
_, err := rdb.Expire(ctx, lockKey, 10*time.Second).Result()
if err != nil {
fmt.Println("Failed to renew lock")
return
}
fmt.Println("Lock renewed")
}
}()
// 模拟执行业务逻辑
time.Sleep(15 * time.Second)
// 解锁
unlockScript := redis.NewScript(`
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
`)
_, err = unlockScript.Run(ctx, rdb, []string{lockKey}, lockValue).Result()
if err != nil {
panic(err)
}
fmt.Println("Lock released")
}
Redis 是一个功能强大且灵活的内存数据库,在 Go 中使用 Redis 非常简单。通过学习 Redis 的基本命令和在 Go 中的集成,你可以轻松实现缓存、消息队列、分布式锁等功能。