文章目录
- 1. redis的list数据结构
- 1.1. list结构的特性
- 1.2. 常用命令
- 2. 常见业务场景
- 2.1 消息队列
- 案例讲解
- 背景
- 优势
- 解决方案
- 代码实现
- 2.2 排行榜
- 案例讲解
- 背景
- 优势
- 解决方案
- 代码实现
- 3. 注意事项:
1. redis的list数据结构
参考链接:https://mp.weixin.qq.com/s/srkd73bS2n3mjIADLVg72A
Redis 中的 List 数据结构是一个简单的字符串列表,可以在两端快速推入和弹出元素。List 的实现是双向链表,这使得它在插入和删除操作上非常高效。List 的元素可以是字符串类型,且可以重复。
1.1. list结构的特性
- 有序:List 中的元素有顺序,元素按照插入的顺序进行排列。
- 支持重复:同一个元素可以出现多次。
- 双向操作:可以在两端进行插入和删除操作。
1.2. 常用命令
以下是一些常用的 Redis List 命令:
- LPUSH key value:在列表的左侧(头部)推入元素。
- RPUSH key value:在列表的右侧(尾部)推入元素。
- LPOP key:从列表的左侧弹出元素。
- RPOP key:从列表的右侧弹出元素。
- LRANGE key start stop:获取列表中指定范围的元素。
- LLEN key:获取列表的长度。
- LREM key count value:移除列表中指定数量的某个元素。
- LINSERT key BEFORE|AFTER pivot value:在列表中指定元素之前或之后插入一个新元素。
- LSET key index value:通过索引设置列表中的元素。
- LTRIM key start stop:修剪列表,只保留指定范围内的元素。
XXXXXX:6379> LPUSH user:1001:orders "order_1" #用户1001创建新订单order_1
(integer) 1
XXXXXX:6379> LPUSH user:1001:orders "order_2" #用户1001创建新订单order_2
(integer) 2
XXXXXX:6379> LPUSH user:1001:orders "order_3" #用户1001创建新订单order_3
(integer) 3
XXXXXX:6379> LRANGE user:1001:orders 0 -1 #用户1001查询所有订单
1) "order_3"
2) "order_2"
3) "order_1"
XXXXXX:6379> LLEN user:1001:orders #用户1001查询所有订单的数量
(integer) 3
XXXXXX:6379> LPOP user:1001:orders #用户1001取消最新的订单
"order_3"
XXXXXX:6379> LRANGE user:1001:orders 0 -1 #用户1001查询所有订单
1) "order_2"
2) "order_1"
XXXXXX:6379> LSET user:1001:orders 1 "order_1_completed" #订单order_1完成
OK
XXXXXX:6379> LRANGE user:1001:orders 0 -1 #用户1001查询所有订单
1) "order_2"
2) "order_1_completed"
XXXXXX:6379> LTRIM user:1001:orders 0 9 #用户修剪订单列表,只保留最近的 10 个订单
OK
XXXXXX:6379> LRANGE user:1001:orders 0 -1 #用户1001查询所有订单
1) "order_2"
2) "order_1_completed"
XXXXXX:6379> LREM user:1001:orders 1 "order_2" #用户移除订单order_2
(integer) 1
XXXXXX:6379> LRANGE user:1001:orders 0 -1 ##用户1001查询所有订单
1) "order_1_completed"
XXXXXX:6379> LINSERT user:1001:orders BEFORE "order_1_completed" "order_4" #用户在order_1前插入order_4
(integer) 2
XXXXXX:6379> LRANGE user:1001:orders 0 -1 ##用户1001查询所有订单
1) "order_4"
2) "order_1_completed"
2. 常见业务场景
2.1 消息队列
消息队列:List类型常用于实现消息队列,用于异步处理任务,如邮件发送队列、任务调度等。
案例讲解
背景
在一个电商平台中,用户下单后,系统需要执行多个异步任务,如订单处理、库存更新、发送确认邮件等
优势
- 异步处理:使用List作为消息队列,可以将任务异步化,提高用户体验和系统响应速度。
- 任务管理:方便地对任务进行管理和监控,如重试失败的任务、监控任务处理进度等。
- 系统解耦:各个任务处理模块可以独立运行,降低系统间的耦合度。
解决方案
使用Redis List类型存储和管理任务消息队列。
代码实现
package main
import (
"context"
"encoding/json"
"fmt"
"github.com/go-redis/redis/v8"
"log"
"time"
)
var ctx = context.Background()
// Redis 客户端初始化
var redisClient = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
type Order struct {
ID string
Amount float64
}
func (o Order) ToString() string {
orderJSON, _ := json.Marshal(o)
return string(orderJSON)
}
func addOrderToQueue(order Order) {
// 将新订单添加到订单处理队列
redisClient.LPush(ctx, "order_queue", order.ToString())
}
func getNextOrder() (Order, error) {
// 从订单处理队列中获取待处理的订单
orderJSON, err := redisClient.RPop(ctx, "order_queue").Result()
if err != nil {
return Order{}, err
}
var order Order
err = json.Unmarshal([]byte(orderJSON), &order)
if err != nil {
return Order{}, err
}
return order, nil
}
func addInventoryUpdateToQueue(order Order) {
// 将库存更新任务添加到库存更新队列
redisClient.LPush(ctx, "inventory_update_queue", order.ToString())
}
func getNextInventoryUpdate() (Order, error) {
// 从库存更新队列中获取待处理的更新
updateJSON, err := redisClient.RPop(ctx, "inventory_update_queue").Result()
if err != nil {
return Order{}, err
}
var order Order
err = json.Unmarshal([]byte(updateJSON), &order)
if err != nil {
return Order{}, err
}
return order, nil
}
func addEmailToQueue(order Order) {
// 将邮件发送任务添加到邮件发送队列
redisClient.LPush(ctx, "email_queue", order.ToString())
}
func getNextEmail() (Order, error) {
// 从邮件发送队列中获取待发送邮件的订单
emailJSON, err := redisClient.RPop(ctx, "email_queue").Result()
if err != nil {
return Order{}, err
}
var order Order
err = json.Unmarshal([]byte(emailJSON), &order)
if err != nil {
return Order{}, err
}
return order, nil
}
func processOrder(order Order) {
// 处理订单逻辑
fmt.Printf("Processing order: %s\n", order.ID)
// 添加库存更新任务
addInventoryUpdateToQueue(order)
// 添加邮件发送任务
addEmailToQueue(order)
// 模拟处理时间
time.Sleep(1 * time.Second)
}
func updateInventory(order Order) {
// 更新库存逻辑
fmt.Printf("Updating inventory for order: %s\n", order.ID)
// 模拟更新库存的操作
time.Sleep(1 * time.Second)
}
func sendEmail(order Order) {
// 发送确认邮件逻辑
fmt.Printf("Sending confirmation email for order: %s\n", order.ID)
// 模拟发送邮件的操作
time.Sleep(1 * time.Second)
}
2.2 排行榜
排行榜:使用List类型,可以存储和管理如游戏得分、文章点赞数等排行榜数据。
案例讲解
背景
在一个社交平台中,用户发表的文章根据点赞数进行排名,需要实时更新和展示排行榜。
优势
- 实时性:能够快速响应用户的点赞行为,实时更新排行榜。
- 排序功能:利用LRANGE命令,可以方便地获取指定范围内的排行榜数据。
解决方案
使用Redis List类型存储用户的得分或点赞数,并根据需要对List进行排序。
代码实现
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
"log"
)
var ctx = context.Background()
// Redis 客户端初始化
var redisClient = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
type Article struct {
ID string
Score int64
}
// 为文章点赞,更新排行榜
func likeArticle(articleID string) {
// 假设每个文章都有一个对应的得分,使用 Sorted Set 来维护
redisClient.ZIncrBy(ctx, "article_rankings", 1, articleID)
}
// 获取文章排行榜
func getArticleRankings() ([]Article, error) {
// 使用 ZREVRANGE 获取得分最高的文章
result, err := redisClient.ZRevRangeWithScores(ctx, "article_rankings", 0, -1).Result()
if err != nil {
return nil, err
}
articles := []Article{}
for _, z := range result {
articles = append(articles, Article{
ID: z.Member.(string),
Score: int64(z.Score),
})
}
return articles, nil
}
3. 注意事项:
List类型在列表元素数量较大时,操作可能会变慢,需要考虑性能优化。
- 在使用List实现队列时,要注意处理消息的顺序和丢失问题。
- 可以使用BRPOP或BLPOP命令在多个列表上进行阻塞式读取,适用于多消费者场景。