news 2026/5/14 6:00:55

Redis高级数据结构:超越String的Redis世界

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis高级数据结构:超越String的Redis世界

Redis高级数据结构:超越String的Redis世界

引言

Redis不仅仅是"一个KV存储",它提供了丰富的数据结构,是现代应用架构中不可或缺的组件。深入理解Redis的数据结构,能够帮助我们设计出更高效、更优雅的解决方案。本文将全面解析Redis的五大基础类型和五种扩展类型。

一、String类型及其应用

1.1 String基础操作

package redis import ( "context" "fmt" "time" "github.com/redis/go-redis/v9" ) type StringOperations struct { client *redis.Client } func NewStringOperations(client *redis.Client) *StringOperations { return &StringOperations{client: client} } func (r *StringOperations) SetWithExpiry(ctx context.Context, key, value string, expiry time.Duration) error { return r.client.Set(ctx, key, value, expiry).Err() } func (r *StringOperations) Get(ctx context.Context, key string) (string, error) { return r.client.Get(ctx, key).Result() } func (r *StringOperations) MSet(ctx context.Context, values map[string]interface{}) error { return r.client.MSet(ctx, values).Err() } func (r *StringOperations) MGet(ctx context.Context, keys ...string) ([]interface{}, error) { return r.client.MGet(ctx, keys...).Result() } func (r *StringOperations) SetNX(ctx context.Context, key, value string, expiry time.Duration) (bool, error) { return r.client.SetNX(ctx, key, value, expiry).Result() }

1.2 分布式锁实现

package redis import ( "context" "errors" "fmt" "time" "github.com/redis/go-redis/v9" ) var ( ErrLockNotAcquired = errors.New("lock not acquired") ErrLockNotHeld = errors.New("lock not held") ) type DistributedLock struct { client *redis.Client key string value string expiry time.Duration } func NewDistributedLock(client *redis.Client, key string, expiry time.Duration) *DistributedLock { return &DistributedLock{ client: client, key: key, value: fmt.Sprintf("%d", time.Now().UnixNano()), expiry: expiry, } } func (dl *DistributedLock) Acquire(ctx context.Context) (bool, error) { result, err := dl.client.SetNX(ctx, dl.key, dl.value, dl.expiry).Result() if err != nil { return false, err } return result, nil } func (dl *DistributedLock) Release(ctx context.Context) error { script := redis.NewScript(` if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end `) result, err := script.Run(ctx, dl.client, []string{dl.key}, dl.value).Int64() if err != nil { return err } if result == 0 { return ErrLockNotHeld } return nil } func (dl *DistributedLock) Extend(ctx context.Context, expiry time.Duration) error { script := redis.NewScript(` if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("pexpire", KEYS[1], ARGV[2]) else return 0 end `) result, err := script.Run(ctx, dl.client, []string{dl.key}, dl.value, expiry.Milliseconds()).Int64() if err != nil { return err } if result == 0 { return ErrLockNotHeld } return nil }

二、Hash类型:对象存储

2.1 Hash基本操作

package redis import ( "context" "encoding/json" "time" "github.com/redis/go-redis/v9" ) type HashOperations struct { client *redis.Client } func NewHashOperations(client *redis.Client) *HashOperations { return &HashOperations{client: client} } func (r *HashOperations) HSet(ctx context.Context, key string, values map[string]interface{}) error { return r.client.HSet(ctx, key, values).Err() } func (r *HashOperations) HGet(ctx context.Context, key, field string) (string, error) { return r.client.HGet(ctx, key, field).Result() } func (r *HashOperations) HGetAll(ctx context.Context, key string) (map[string]string, error) { return r.client.HGetAll(ctx, key).Result() } func (r *HashOperations) HIncrBy(ctx context.Context, key, field string, incr int64) (int64, error) { return r.client.HIncrBy(ctx, key, field, incr).Result() } func (r *HashOperations) HExists(ctx context.Context, key, field string) (bool, error) { return r.client.HExists(ctx, key, field).Result() }

2.2 对象缓存实战

package redis import ( "context" "encoding/json" "fmt" "time" "github.com/redis/go-redis/v9" ) type UserCache struct { client *redis.Client prefix string } func NewUserCache(client *redis.Client) *UserCache { return &UserCache{ client: client, prefix: "user:", } } type User struct { ID int64 `json:"id"` Name string `json:"name"` Email string `json:"email"` Status string `json:"status"` CreatedAt int64 `json:"created_at"` } func (uc *UserCache) Get(ctx context.Context, userID int64) (*User, error) { key := fmt.Sprintf("%s%d", uc.prefix, userID) data, err := uc.client.HGetAll(ctx, key).Result() if err != nil { return nil, err } if len(data) == 0 { return nil, nil } user := &User{ ID: userID, Name: data["name"], Email: data["email"], Status: data["status"], CreatedAt: time.Now().Unix(), } return user, nil } func (uc *UserCache) Set(ctx context.Context, user *User, expiry time.Duration) error { key := fmt.Sprintf("%s%d", uc.prefix, user.ID) fields := map[string]interface{}{ "name": user.Name, "email": user.Email, "status": user.Status, } pipe := uc.client.Pipeline() pipe.HSet(ctx, key, fields) pipe.Expire(ctx, key, expiry) _, err := pipe.Exec(ctx) return err } func (uc *UserCache) Delete(ctx context.Context, userID int64) error { key := fmt.Sprintf("%s%d", uc.prefix, userID) return uc.client.Del(ctx, key).Err() } func (uc *UserCache) UpdateField(ctx context.Context, userID int64, field string, value interface{}) error { key := fmt.Sprintf("%s%d", uc.prefix, userID) return uc.client.HSet(ctx, key, field, value).Err() } func (uc *UserCache) GetField(ctx context.Context, userID int64, field string) (string, error) { key := fmt.Sprintf("%s%d", uc.prefix, userID) return uc.client.HGet(ctx, key, field).Result() } func (uc *UserCache) IncrementCounter(ctx context.Context, userID int64, field string) (int64, error) { key := fmt.Sprintf("%s%d", uc.prefix, userID) return uc.client.HIncrBy(ctx, key, field, 1).Result() }

三、List类型:队列与栈

3.1 阻塞队列实现

package redis import ( "context" "encoding/json" "time" "github.com/redis/go-redis/v9" ) type BlockingQueue struct { client *redis.Client name string } func NewBlockingQueue(client *redis.Client, name string) *BlockingQueue { return &BlockingQueue{ client: client, name: name, } } func (bq *BlockingQueue) Enqueue(ctx context.Context, value interface{}) error { data, err := json.Marshal(value) if err != nil { return err } return bq.client.LPush(ctx, bq.name, data).Err() } func (bq *BlockingQueue) Dequeue(ctx context.Context, timeout time.Duration) ([]byte, error) { result, err := bq.client.BRPop(ctx, timeout, bq.name).Result() if err != nil { return nil, err } if len(result) < 2 { return nil, nil } return []byte(result[1]), nil } func (bq *BlockingQueue) DequeueWithContext(ctx context.Context) ([]byte, error) { result, err := bq.client.BRPop(ctx, 0, bq.name).Result() if err != nil { return nil, err } if len(result) < 2 { return nil, nil } return []byte(result[1]), nil } func (bq *BlockingQueue) Length(ctx context.Context) (int64, error) { return bq.client.LLen(ctx, bq.name).Result() }

3.2 延迟队列实现

package redis import ( "context" "encoding/json" "fmt" "time" "github.com/redis/go-redis/v9" ) type DelayedQueue struct { client *redis.Client name string zsetName string } func NewDelayedQueue(client *redis.Client, name string) *DelayedQueue { return &DelayedQueue{ client: client, name: name, zsetName: name + ":delayed", } } type DelayedMessage struct { ID string Payload interface{} ExecuteAt time.Time } func (dq *DelayedQueue) Schedule(ctx context.Context, msg *DelayedMessage) error { data, err := json.Marshal(msg.Payload) if err != nil { return err } score := float64(msg.ExecuteAt.Unix()) pipe := dq.client.Pipeline() pipe.ZAdd(ctx, dq.zsetName, redis.Z{ Score: score, Member: fmt.Sprintf("%s:%s", msg.ID, string(data)), }) pipe.LPush(ctx, dq.name, msg.ID) _, err = pipe.Exec(ctx) return err } func (dq *DelayedQueue) Process(ctx context.Context, handler func(msg *DelayedMessage) error) error { now := float64(time.Now().Unix()) result, err := dq.client.ZPopMin(ctx, dq.zsetName, 1).Result() if err != nil { return err } if len(result) == 0 { return nil } msgID := result[0].Member.(string) payload, err := dq.client.LPop(ctx, dq.name).Result() if err != nil { return err } msg := &DelayedMessage{ ID: msgID, ExecuteAt: time.Unix(int64(result[0].Score), 0), } if err := json.Unmarshal([]byte(payload), &msg.Payload); err != nil { return err } return handler(msg) } func (dq *DelayedQueue) Cancel(ctx context.Context, msgID string) error { pattern := msgID + ":*" result, err := dq.client.ZRangeByScore(ctx, dq.zsetName, &redis.ZRangeBy{ Min: "-inf", Max: "+inf", }).Result() if err != nil { return err } for _, member := range result { if len(member) > len(msgID)+1 && member[:len(msgID)] == msgID { return dq.client.ZRem(ctx, dq.zsetName, member).Err() } } return nil }

四、Set类型:无序去重

4.1 标签系统实现

package redis import ( "context" "fmt" "github.com/redis/go-redis/v9" ) type TagSystem struct { client *redis.Client } func NewTagSystem(client *redis.Client) *TagSystem { return &TagSystem{client: client} } func (ts *TagSystem) AddTags(ctx context.Context, entityType string, entityID string, tags ...string) error { key := fmt.Sprintf("entity:%s:%s:tags", entityType, entityID) return ts.client.SAdd(ctx, key, tags).Err() } func (ts *TagSystem) RemoveTags(ctx context.Context, entityType string, entityID string, tags ...string) error { key := fmt.Sprintf("entity:%s:%s:tags", entityType, entityID) return ts.client.SRem(ctx, key, tags).Err() } func (ts *TagSystem) GetTags(ctx context.Context, entityType string, entityID string) ([]string, error) { key := fmt.Sprintf("entity:%s:%s:tags", entityType, entityID) return ts.client.SMembers(ctx, key).Result() } func (ts *TagSystem) HasTag(ctx context.Context, entityType string, entityID string, tag string) (bool, error) { key := fmt.Sprintf("entity:%s:%s:tags", entityType, entityID) return ts.client.SIsMember(ctx, key, tag).Result() } func (ts *TagSystem) GetEntitiesByTag(ctx context.Context, entityType string, tag string) ([]string, error) { key := fmt.Sprintf("tag:%s:%s:%s", entityType, tag, "entities") return ts.client.SMembers(ctx, key).Result() } func (ts *TagSystem) AddEntityToTag(ctx context.Context, entityType string, entityID string, tag string) error { entityKey := fmt.Sprintf("entity:%s:%s:tags", entityType, entityID) tagKey := fmt.Sprintf("tag:%s:%s:%s", entityType, tag, "entities") pipe := ts.client.Pipeline() pipe.SAdd(ctx, entityKey, tag) pipe.SAdd(ctx, tagKey, entityID) _, err := pipe.Exec(ctx) return err } func (ts *TagSystem) GetIntersection(ctx context.Context, entityType string, tags ...string) ([]string, error) { if len(tags) == 0 { return nil, nil } keys := make([]string, len(tags)) for i, tag := range tags { keys[i] = fmt.Sprintf("tag:%s:%s:%s", entityType, tag, "entities") } return ts.client.SInter(ctx, keys...).Result() } func (ts *TagSystem) GetUnion(ctx context.Context, entityType string, tags ...string) ([]string, error) { if len(tags) == 0 { return nil, nil } keys := make([]string, len(tags)) for i, tag := range tags { keys[i] = fmt.Sprintf("tag:%s:%s:%s", entityType, tag, "entities") } return ts.client.SUnion(ctx, keys...).Result() }

五、ZSet类型:有序集合

5.1 排行榜实现

package redis import ( "context" "fmt" "github.com/redis/go-redis/v9" ) type Leaderboard struct { client *redis.Client key string } func NewLeaderboard(client *redis.Client, name string) *Leaderboard { return &Leaderboard{ client: client, key: fmt.Sprintf("leaderboard:%s", name), } } func (l *Leaderboard) UpdateScore(ctx context.Context, member string, score float64) error { return l.client.ZAdd(ctx, l.key, redis.Z{ Score: score, Member: member, }).Err() } func (l *Leaderboard) IncrementScore(ctx context.Context, member string, increment float64) (float64, error) { return l.client.ZIncrBy(ctx, l.key, increment, member).Result() } func (l *Leaderboard) GetRank(ctx context.Context, member string) (int64, error) { rank, err := l.client.ZRevRank(ctx, l.key, member).Result() if err != nil { return -1, err } return rank + 1, nil } func (l *Leaderboard) GetScore(ctx context.Context, member string) (float64, error) { return l.client.ZScore(ctx, l.key, member).Result() } func (l *Leaderboard) GetTopN(ctx context.Context, n int64) ([]redis.Z, error) { return l.client.ZRevRangeWithScores(ctx, l.key, 0, n-1).Result() } func (l *Leaderboard) GetRange(ctx context.Context, start, stop int64) ([]redis.Z, error) { return l.client.ZRevRangeWithScores(ctx, l.key, start, stop).Result() } func (l *Leaderboard) GetRankedMembers(ctx context.Context, start, stop int64) ([]string, error) { return l.client.ZRevRange(ctx, l.key, start, stop).Result() } func (l *Leaderboard) RemoveMember(ctx context.Context, member string) error { return l.client.ZRem(ctx, l.key, member).Err() } func (l *Leaderboard) GetCount(ctx context.Context) (int64, error) { return l.client.ZCard(ctx, l.key).Result() } func (l *Leaderboard) GetMembersByScore(ctx context.Context, min, max float64) ([]redis.Z, error) { return l.client.ZRevRangeByScoreWithScores(ctx, l.key, &redis.ZRangeBy{ Min: fmt.Sprintf("%f", min), Max: fmt.Sprintf("%f", max), }).Result() }

六、Geospatial:地理位置

package redis import ( "context" "fmt" "github.com/redis/go-redis/v9" ) type GeoOperations struct { client *redis.Client } func NewGeoOperations(client *redis.Client) *GeoOperations { return &GeoOperations{client: client} } func (g *GeoOperations) AddLocation(ctx context.Context, key string, longitude, latitude float64, member string) error { return g.client.GeoAdd(ctx, key, &redis.GeoLocation{ Name: member, Longitude: longitude, Latitude: latitude, }).Err() } func (g *GeoOperations) GetPosition(ctx context.Context, key string, member string) (*redis.GeoPos, error) { pos, err := g.client.GeoPos(ctx, key, member).Result() if err != nil { return nil, err } if len(pos) == 0 || pos[0] == nil { return nil, fmt.Errorf("member not found") } return pos[0], nil } func (g *GeoOperations) GetDistance(ctx context.Context, key, member1, member2, unit string) (float64, error) { return g.client.GeoDist(ctx, key, member1, member2, unit).Result() } func (g *GeoOperations) SearchNearby(ctx context.Context, key string, longitude, latitude, radius float64, unit string, count int) ([]redis.GeoLocation, error) { return g.client.GeoSearchLocation(ctx, key, &redis.GeoSearchLocationQuery{ GeoSearchQuery: redis.GeoSearchQuery{ Longitude: longitude, Latitude: latitude, Radius: radius, Unit: unit, WithCoord: true, WithDist: true, Count: count, Sort: "ASC", }, }).Result() }

七、总结

Redis的丰富数据结构为开发者提供了强大的工具集:

  1. String:适用于简单的KV缓存、计数器、分布式锁
  2. Hash:适用于对象存储,适合字段级别的更新
  3. List:适用于消息队列、任务队列、栈
  4. Set:适用于标签系统、去重场景、交集运算
  5. ZSet:适用于排行榜、有序事件、延迟队列
  6. Geospatial:适用于附近的人、地理位置服务

合理选择数据结构,能够显著提升应用性能和代码可读性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/14 6:00:53

PostgreSQL高级特性:JSON、全文搜索与高级索引

PostgreSQL高级特性&#xff1a;JSON、全文搜索与高级索引 引言 PostgreSQL是功能最强大的开源关系型数据库&#xff0c;它的许多高级特性让开发者能够在保持SQL优势的同时&#xff0c;处理半结构化数据、实现复杂的搜索功能。本文将深入探讨PostgreSQL的JSON数据类型、全文搜索…

作者头像 李华
网站建设 2026/5/14 6:00:52

利用 API 实现多账号协同运营,私域增长更轻松

一、 场景描述&#xff1a;多账号管理的“规模化陷阱” 对于中大型私域团队或代运营机构&#xff0c;往往拥有成百上千个企业微信账号。如果没有 API 的介入&#xff0c;团队会陷入以下困境&#xff1a; • 登录灾难&#xff1a;需要准备大量手机和电脑&#xff0c;人工切换账号…

作者头像 李华
网站建设 2026/5/14 6:00:49

OpenClaw AI Agent:模块化智能体框架的设计原理与应用实践

1. 项目概述&#xff1a;OpenClaw AI Agent是什么&#xff1f;最近在AI圈子里&#xff0c;OpenClaw这个名字开始被频繁提及。它不是某个新发布的模型&#xff0c;也不是一个具体的工具&#xff0c;而是一个概念&#xff0c;或者说&#xff0c;是一种构建AI智能体的新范式。简单…

作者头像 李华
网站建设 2026/5/14 5:58:05

python系列【仅供参考】:Pycharm 给 python 程序打包EXE的配置和方法

Pycharm 给 python 程序打包EXE的配置和方法 Pycharm 给 python 程序打包EXE的配置和方法 前言: 1 工具介绍 1.1 PyInstaller 一、核心功能与优势 二、基本使用流程 2.1.安装 2.2.基础打包命令 2.3 生成文件说明 三、关键参数与配置 四、.spec 文件的作用 五、常见问题与解决 …

作者头像 李华
网站建设 2026/5/14 5:55:11

AI工具桥接实战:基于MCP协议构建自定义AI助手扩展

1. 项目概述&#xff1a;连接AI与工具的桥梁 最近在折腾AI应用开发&#xff0c;特别是想让大语言模型&#xff08;LLM&#xff09;能更“接地气”地操作各种外部工具和服务时&#xff0c;遇到了一个挺有意思的项目&#xff1a; AIWerk/openclaw-mcp-bridge 。简单来说&#x…

作者头像 李华