Documentation
¶
Overview ¶
Package redisx 提供 Redis 客户端封装。
所有 enjoydream 后端服务共享此包,统一封装:key 前缀管理。 零可观测性副作用:构造不注入任何 tracing;需要 OpenTelemetry span 时 用 github.com/oekit/tracing/redisotel 装饰器显式接入。
核心特性 ¶
- key 前缀自动添加(prefix:key 格式)
- 支持 redis.UniversalClient(兼容单机、集群、哨兵模式)
- 提供 Client() 方法获取原生客户端(用于 distlock、tracing 接入等场景)
快速接入 ¶
从地址创建:
client, err := redisx.NewClientFromAddr("localhost:6379", "", 0,
redisx.WithPrefix("myservice"),
)
if err != nil {
log.Fatal(err)
}
defer client.Close()
从配置创建(支持集群):
opt := &redis.UniversalOptions{
Addrs: []string{"redis-1:6379", "redis-2:6379"},
Password: "password",
DB: 0,
}
client, err := redisx.NewClient(opt, redisx.WithPrefix("myservice"))
Key 前缀 ¶
所有操作的 key 会自动添加 "prefix:" 前缀:
client.Set(ctx, "user:123", value, 0) // 实际存储的 key 为 "myservice:user:123"
原生客户端 ¶
需要原生 redis.UniversalClient 时(如 distlock、cache、tracing 接入等包):
locker := distlock.NewRedisLocker(client.Client(), distlock.WithPrefix("myservice"))
_ = redisotel.InstrumentTracing(client.Client()) // github.com/oekit/tracing/redisotel
Index ¶
- type Batch
- func (b *Batch) Del(ctx context.Context, keys ...string)
- func (b *Batch) Discard()
- func (b *Batch) Exec(ctx context.Context) ([]redis.Cmder, error)
- func (b *Batch) Exists(ctx context.Context, keys ...string) *redis.IntCmd
- func (b *Batch) Expire(ctx context.Context, key string, ttl time.Duration)
- func (b *Batch) Get(ctx context.Context, key string) *redis.StringCmd
- func (b *Batch) HGet(ctx context.Context, key, field string) *redis.StringCmd
- func (b *Batch) HSet(ctx context.Context, key string, values ...any)
- func (b *Batch) Incr(ctx context.Context, key string) *redis.IntCmd
- func (b *Batch) IncrBy(ctx context.Context, key string, value int64) *redis.IntCmd
- func (b *Batch) Len() int
- func (b *Batch) Set(ctx context.Context, key string, value any, ttl time.Duration)
- type BloomFilter
- func (bf *BloomFilter) Add(ctx context.Context, item string) (bool, error)
- func (bf *BloomFilter) Exists(ctx context.Context, item string) (bool, error)
- func (bf *BloomFilter) Info(ctx context.Context) (map[string]any, error)
- func (bf *BloomFilter) MAdd(ctx context.Context, items ...string) ([]bool, error)
- func (bf *BloomFilter) MExists(ctx context.Context, items ...string) ([]bool, error)
- type Client
- func NewClient(opt *redis.UniversalOptions, opts ...RedisClientOptionFunc) (*Client, error)
- func NewClientFromAddr(addr, password string, db int, opts ...RedisClientOptionFunc) (*Client, error)
- func NewClientFromClient(c redis.UniversalClient, opts ...RedisClientOptionFunc) *Client
- func NewClientFromConfig(ctx context.Context, cfg RedisConfig) (*Client, error)
- func (c *Client) Client() redis.UniversalClient
- func (c *Client) Close() error
- func (c *Client) Decr(ctx context.Context, key string) *redis.IntCmd
- func (c *Client) DecrBy(ctx context.Context, key string, value int64) *redis.IntCmd
- func (c *Client) Del(ctx context.Context, keys ...string) *redis.IntCmd
- func (c *Client) Eval(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd
- func (c *Client) EvalRO(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd
- func (c *Client) EvalSha(ctx context.Context, sha1 string, keys []string, args ...any) *redis.Cmd
- func (c *Client) EvalShaRO(ctx context.Context, sha1 string, keys []string, args ...any) *redis.Cmd
- func (c *Client) Exists(ctx context.Context, keys ...string) *redis.IntCmd
- func (c *Client) Expire(ctx context.Context, key string, expiration time.Duration) *redis.BoolCmd
- func (c *Client) Get(ctx context.Context, key string) *redis.StringCmd
- func (c *Client) GetDel(ctx context.Context, key string) *redis.StringCmd
- func (c *Client) GetDelCompat(ctx context.Context, key string) *redis.StringCmd
- func (c *Client) HDel(ctx context.Context, key string, fields ...string) *redis.IntCmd
- func (c *Client) HGet(ctx context.Context, key, field string) *redis.StringCmd
- func (c *Client) HGetAll(ctx context.Context, key string) *redis.MapStringStringCmd
- func (c *Client) HSet(ctx context.Context, key string, values ...any) *redis.IntCmd
- func (c *Client) Incr(ctx context.Context, key string) *redis.IntCmd
- func (c *Client) IncrBy(ctx context.Context, key string, value int64) *redis.IntCmd
- func (c *Client) LLen(ctx context.Context, key string) *redis.IntCmd
- func (c *Client) LPop(ctx context.Context, key string) *redis.StringCmd
- func (c *Client) LPush(ctx context.Context, key string, values ...any) *redis.IntCmd
- func (c *Client) LRange(ctx context.Context, key string, start, stop int64) *redis.StringSliceCmd
- func (c *Client) NewBatch() *Batch
- func (c *Client) NewBloomFilter(ctx context.Context, key string, errorRate float64, capacity int64) (*BloomFilter, error)
- func (c *Client) NewCounter(key string) *Counter
- func (c *Client) Ping(ctx context.Context) *redis.StatusCmd
- func (c *Client) Pipeline() *Pipeline
- func (c *Client) Publish(ctx context.Context, channel string, message any) *redis.IntCmd
- func (c *Client) RPop(ctx context.Context, key string) *redis.StringCmd
- func (c *Client) RPush(ctx context.Context, key string, values ...any) *redis.IntCmd
- func (c *Client) SAdd(ctx context.Context, key string, members ...any) *redis.IntCmd
- func (c *Client) SIsMember(ctx context.Context, key string, member any) *redis.BoolCmd
- func (c *Client) SMembers(ctx context.Context, key string) *redis.StringSliceCmd
- func (c *Client) SRem(ctx context.Context, key string, members ...any) *redis.IntCmd
- func (c *Client) ScriptExists(ctx context.Context, hashes ...string) *redis.BoolSliceCmd
- func (c *Client) ScriptLoad(ctx context.Context, script string) *redis.StringCmd
- func (c *Client) Set(ctx context.Context, key string, value any, expiration time.Duration) *redis.StatusCmd
- func (c *Client) SetNX(ctx context.Context, key string, value any, expiration time.Duration) *redis.BoolCmd
- func (c *Client) Subscribe(ctx context.Context, channels ...string) *redis.PubSub
- func (c *Client) TxPipeline() *Pipeline
- func (c *Client) ZAdd(ctx context.Context, key string, members ...redis.Z) *redis.IntCmd
- func (c *Client) ZCard(ctx context.Context, key string) *redis.IntCmd
- func (c *Client) ZRange(ctx context.Context, key string, start, stop int64) *redis.StringSliceCmd
- func (c *Client) ZRangeWithScores(ctx context.Context, key string, start, stop int64) *redis.ZSliceCmd
- func (c *Client) ZRem(ctx context.Context, key string, members ...any) *redis.IntCmd
- type Counter
- func (ct *Counter) Decr(ctx context.Context, delta int64) (int64, error)
- func (ct *Counter) DecrFloor(ctx context.Context, delta int64, floor int64) (int64, error)
- func (ct *Counter) Get(ctx context.Context) (int64, error)
- func (ct *Counter) Incr(ctx context.Context, delta int64) (int64, error)
- func (ct *Counter) IncrWithTTL(ctx context.Context, delta int64, ttl time.Duration) (int64, error)
- func (ct *Counter) Reset(ctx context.Context) error
- type Pipeline
- func (p *Pipeline) Decr(ctx context.Context, key string) *redis.IntCmd
- func (p *Pipeline) DecrBy(ctx context.Context, key string, value int64) *redis.IntCmd
- func (p *Pipeline) Del(ctx context.Context, keys ...string) *redis.IntCmd
- func (p *Pipeline) Discard()
- func (p *Pipeline) Eval(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd
- func (p *Pipeline) EvalRO(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd
- func (p *Pipeline) EvalSha(ctx context.Context, sha1 string, keys []string, args ...any) *redis.Cmd
- func (p *Pipeline) EvalShaRO(ctx context.Context, sha1 string, keys []string, args ...any) *redis.Cmd
- func (p *Pipeline) Exec(ctx context.Context) ([]redis.Cmder, error)
- func (p *Pipeline) Exists(ctx context.Context, keys ...string) *redis.IntCmd
- func (p *Pipeline) Expire(ctx context.Context, key string, expiration time.Duration) *redis.BoolCmd
- func (p *Pipeline) Get(ctx context.Context, key string) *redis.StringCmd
- func (p *Pipeline) GetDel(ctx context.Context, key string) *redis.StringCmd
- func (p *Pipeline) HDel(ctx context.Context, key string, fields ...string) *redis.IntCmd
- func (p *Pipeline) HGet(ctx context.Context, key, field string) *redis.StringCmd
- func (p *Pipeline) HGetAll(ctx context.Context, key string) *redis.MapStringStringCmd
- func (p *Pipeline) HSet(ctx context.Context, key string, values ...any) *redis.IntCmd
- func (p *Pipeline) Incr(ctx context.Context, key string) *redis.IntCmd
- func (p *Pipeline) IncrBy(ctx context.Context, key string, value int64) *redis.IntCmd
- func (p *Pipeline) LLen(ctx context.Context, key string) *redis.IntCmd
- func (p *Pipeline) LPop(ctx context.Context, key string) *redis.StringCmd
- func (p *Pipeline) LPush(ctx context.Context, key string, values ...any) *redis.IntCmd
- func (p *Pipeline) LRange(ctx context.Context, key string, start, stop int64) *redis.StringSliceCmd
- func (p *Pipeline) Len() int
- func (p *Pipeline) Publish(ctx context.Context, channel string, message any) *redis.IntCmd
- func (p *Pipeline) RPop(ctx context.Context, key string) *redis.StringCmd
- func (p *Pipeline) RPush(ctx context.Context, key string, values ...any) *redis.IntCmd
- func (p *Pipeline) Raw() redis.Pipeliner
- func (p *Pipeline) SAdd(ctx context.Context, key string, members ...any) *redis.IntCmd
- func (p *Pipeline) SIsMember(ctx context.Context, key string, member any) *redis.BoolCmd
- func (p *Pipeline) SMembers(ctx context.Context, key string) *redis.StringSliceCmd
- func (p *Pipeline) SRem(ctx context.Context, key string, members ...any) *redis.IntCmd
- func (p *Pipeline) Set(ctx context.Context, key string, value any, expiration time.Duration) *redis.StatusCmd
- func (p *Pipeline) SetNX(ctx context.Context, key string, value any, expiration time.Duration) *redis.BoolCmd
- func (p *Pipeline) ZAdd(ctx context.Context, key string, members ...redis.Z) *redis.IntCmd
- func (p *Pipeline) ZCard(ctx context.Context, key string) *redis.IntCmd
- func (p *Pipeline) ZRange(ctx context.Context, key string, start, stop int64) *redis.StringSliceCmd
- func (p *Pipeline) ZRangeWithScores(ctx context.Context, key string, start, stop int64) *redis.ZSliceCmd
- func (p *Pipeline) ZRem(ctx context.Context, key string, members ...any) *redis.IntCmd
- type RedisClientOption
- type RedisClientOptionFunc
- type RedisConfig
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Batch ¶
type Batch struct {
// contains filtered or unexported fields
}
Batch 封装 Redis Pipeline 批量操作。 将多个命令打包发送,减少网络往返次数。 Batch 自动处理 key 前缀转换(与 Pipeline 代理同口径)。
type BloomFilter ¶
type BloomFilter struct {
// contains filtered or unexported fields
}
BloomFilter 是基于 RedisBloom 的布隆过滤器封装。 需要 Redis 服务器安装 RedisBloom 模块。
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client 是带 key 前缀的 Redis 客户端封装。
func NewClient ¶
func NewClient(opt *redis.UniversalOptions, opts ...RedisClientOptionFunc) (*Client, error)
NewClient 从 UniversalOptions 创建 Redis 客户端。
零可观测性副作用:不自动注入任何 tracing。需要 OpenTelemetry span 时 用装饰器显式接入(无 SDK 时 noop):
c, _ := redisx.NewClient(opt) _ = redisotel.InstrumentTracing(c.Client()) // github.com/oekit/tracing/redisotel
func NewClientFromAddr ¶
func NewClientFromAddr(addr, password string, db int, opts ...RedisClientOptionFunc) (*Client, error)
NewClientFromAddr 从地址字符串创建 Redis 客户端(便捷方法)。
func NewClientFromClient ¶ added in v0.10.5
func NewClientFromClient(c redis.UniversalClient, opts ...RedisClientOptionFunc) *Client
NewClientFromClient **包住既有** go-redis 客户端,不新建连接、不改连接参数。
为什么需要:消费方(pigeonbox/core)自己管连接池与超时(不想让 redisx 改写 它们的连接语义),但又想用 redisx 的原语(典型是 Counter.IncrWithTTL: 「计数 + 首次命中才设 TTL」的原子版)。 与 NewClient 的关键区别:
- NewClient 会**改写**调用方未设置的连接默认参数(DialTimeout/ReadTimeout/ WriteTimeout/PoolTimeout/MinIdleConns)⇒ 连接语义会变;
- NewClientFromClient 一个参数都不动 ⇒ 纯包装,调用方的连接行为完全不变。
传入 nil 会得到一个不可用的 Client(调用其方法会 panic)——调用方应自行保证非 nil。
func NewClientFromConfig ¶
func NewClientFromConfig(ctx context.Context, cfg RedisConfig) (*Client, error)
NewClientFromConfig 从配置创建 Redis 客户端(构造后立即 Ping 验证连通性)。
用法:
client, err := redis.NewClientFromConfig(ctx, redis.RedisConfig{
Host: cfg.Redis.Host, Port: cfg.Redis.Port,
Password: cfg.Redis.Password, DB: cfg.Redis.DB,
})
func (*Client) Client ¶
func (c *Client) Client() redis.UniversalClient
Client 返回底层 redis.UniversalClient(用于 distlock 等需要原生客户端的场景)。
func (*Client) GetDel ¶ added in v0.11.0
GetDel 原子返回 key 的值并删除该键(Redis GETDEL,要求 Redis >= 6.2)。
一次性凭据(OAuth state、授权 code、短信/图形验证码)的「恰好一次核销」 语义:GETDEL 单命令原子取删,并发下也只有一个调用方拿得到值——GET+DEL 两步在并发下会双核销。与其它单发命令同口径:convertKey 套 key 前缀, 超时/观测随底层客户端配置;key 不存在时返回 redis.Nil。
部署在 Redis < 6.2(无 GETDEL)时用 GetDelCompat(自动原子回退)。
func (*Client) GetDelCompat ¶ added in v0.11.0
GetDelCompat 语义与 GetDel 相同,额外兼容 Redis < 6.2(无 GETDEL): GETDEL 返回 unknown command 时回退到 Lua「GET+DEL」单次原子执行—— 不是两步 GET+DEL(并发下会双核销),核销语义不降级。
为什么不直接全部走 Lua:GETDEL 是服务端单命令,成本低于 EVAL; 回退只在老 Redis 上触发一次(后续命令仍会先试 GETDEL)。 管道版不提供 Compat:unknown command 只在 Exec 时暴露,无法在排队期 选择回退路径(见 Pipeline 类型文档)。
func (*Client) NewBloomFilter ¶
func (c *Client) NewBloomFilter(ctx context.Context, key string, errorRate float64, capacity int64) (*BloomFilter, error)
NewBloomFilter 创建布隆过滤器。 如果 key 不存在,会自动创建。
⚠️ key 已存在时**复用既有过滤器**:errorRate 与 capacity 以既有实例为准, 本次传入的参数不生效(Redis 的 BF.RESERVE 对已存在的 key 不覆盖)。 需要按新参数重建,请先 DEL 该 key。
「已存在」的判定**不做错误文本匹配**(#112):任何含 "exists" 的错误消息 (连接/代理/集群客户端的错误文案恰好带该词)此前都会被误判成「已存在,复用」, 后续 BF.ADD 打在不存在的过滤器上必然失败且错误点远离根因。RESERVE 报错后 改用 BF.INFO 探测区分——过滤器确实存在时 INFO 必成功(与 RedisBloom 各版本 的措辞无关,miniredis 与真实 Redis 通吃);INFO 也失败则 RESERVE 的失败另有 原因(连接故障/模块缺失等),原始错误上抛不吞。
参数:
- ctx: 上下文(支持超时/取消)
- key: 布隆过滤器的 key 名
- errorRate: 误判率(如 0.01 表示 1%);key 已存在时不生效
- capacity: 预计元素数量;key 已存在时不生效
func (*Client) Pipeline ¶
Pipeline 创建管道代理(批量发送命令,减少网络往返)。
与单发命令同口径:管道上排队的命令同样经 convertKey 应用 key 前缀。 此前直接返回裸 redis.Pipeliner,管道命令绕过前缀——配了前缀的部署里 同一逻辑键在「管道」与「单发」落到不同物理键,静默分叉(redisx#79)。 覆盖面与裸管道逃生舱见 Pipeline 类型文档。
func (*Client) ScriptExists ¶
ScriptExists 检查脚本是否已缓存。
func (*Client) ScriptLoad ¶
ScriptLoad 将脚本加载到脚本缓存。
func (*Client) Set ¶
func (c *Client) Set(ctx context.Context, key string, value any, expiration time.Duration) *redis.StatusCmd
Set 设置 key 的值。
func (*Client) SetNX ¶
func (c *Client) SetNX(ctx context.Context, key string, value any, expiration time.Duration) *redis.BoolCmd
SetNX 仅当 key 不存在时设置。
func (*Client) TxPipeline ¶
TxPipeline 创建事务管道(MULTI/EXEC),键转换口径与 Pipeline 相同。
type Counter ¶
type Counter struct {
// contains filtered or unexported fields
}
Counter 是分布式原子计数器。 基于 Redis INCR/DECR 实现,支持原子增减和过期。
func (*Counter) IncrWithTTL ¶
IncrWithTTL 原子增加并设置过期时间。 如果 key 已有 TTL,只增加不影响原有 TTL;如果 key 无 TTL,设置指定的 TTL。
type Pipeline ¶ added in v0.11.0
type Pipeline struct {
// contains filtered or unexported fields
}
Pipeline 是 redis.Pipeliner 的键前缀代理:管道上排队的命令与单发命令同口径, 所有键经 convertKey 应用 key 前缀。
为什么需要(redisx#79):此前 Pipeline()/TxPipeline() 直接返回裸 redis.Pipeliner,管道命令不经 convertKey —— 配了 key 前缀的部署里, 同一逻辑键在「管道」与「单发」两条路径落到不同物理键(缓存击穿、计数分裂、 锁不住该锁的键),且两边各自自洽、静默无报错。键转换是 Client 的既有承诺, 管道不该是例外。
覆盖面:与 Client 单发封装一一对应的全部键命令(string/key/hash/list/set/ zset/Lua/Publish)。未覆盖的命令(MGet、Geo、Stream…)不会静默漏前缀—— 本类型不暴露它们(编译期即拦截);需要时用 Raw() 拿底层裸 Pipeliner (不做键转换,键名请自行处理),或在本仓补一层封装。
GetDelCompat 的 <6.2 回退不提供管道版:unknown command 只在 Exec 时才暴露, 无法在排队期选择回退路径;老 Redis 请用单发 Client.GetDelCompat。
func (*Pipeline) EvalRO ¶ added in v0.11.0
func (p *Pipeline) EvalRO(ctx context.Context, script string, keys []string, args ...any) *redis.Cmd
EvalRO 执行只读 Lua 脚本。
func (*Pipeline) EvalShaRO ¶ added in v0.11.0
func (p *Pipeline) EvalShaRO(ctx context.Context, sha1 string, keys []string, args ...any) *redis.Cmd
EvalShaRO 通过 SHA1 执行已缓存的只读 Lua 脚本。
func (*Pipeline) GetDel ¶ added in v0.11.0
GetDel 原子返回 key 的值并删除该键(Redis GETDEL,要求 Redis >= 6.2)。 与单发 Client.GetDel 同口径:convertKey 应用 key 前缀,缺失键回 redis.Nil。
func (*Pipeline) Publish ¶ added in v0.11.0
Publish 向频道发布消息(频道名经 convertKey 套前缀,与单发 Client.Publish 同口径)。
func (*Pipeline) Raw ¶ added in v0.11.0
Raw 返回底层裸 redis.Pipeliner——不做任何键转换。 仅当 Pipeline 未覆盖所需命令时使用;键名(含前缀)由调用方自行负责。
func (*Pipeline) Set ¶ added in v0.11.0
func (p *Pipeline) Set(ctx context.Context, key string, value any, expiration time.Duration) *redis.StatusCmd
Set 设置 key 的值。
func (*Pipeline) SetNX ¶ added in v0.11.0
func (p *Pipeline) SetNX(ctx context.Context, key string, value any, expiration time.Duration) *redis.BoolCmd
SetNX 仅当 key 不存在时设置。
type RedisClientOption ¶
type RedisClientOption struct {
Prefix string
}
RedisClientOption 配置 Client 的可选参数。
type RedisClientOptionFunc ¶
type RedisClientOptionFunc func(*RedisClientOption)
RedisClientOptionFunc 配置函数类型。
func WithPrefix ¶
func WithPrefix(prefix string) RedisClientOptionFunc
WithPrefix 设置 key 前缀。 所有操作的 key 会自动添加 "prefix:" 前缀。