Redis中灵活结合SET和SETEX的方法及多语言工具库实现
本文介绍如何结合Redis的SET
和SETEX
命令实现灵活的键值操作,并提供Python、C++和Golang的封装工具库源码。
一、设计思路
通过创建统一的set
函数整合两种操作:
- 支持永不过期(使用SET)和可过期数据(使用SETEX)
- 支持批量操作和异步执行
- 封装连接池管理
- 支持标准JSON序列化
二、Python实现
# redis_tool.py
import json
import redis
from typing import Union, Any
class RedisTool:
def __init__(self, host='localhost', port=6379, db=0, pool_size=10):
self.pool = redis.ConnectionPool(
host=host, port=port, db=db, max_connections=pool_size
)
def _get_conn(self):
return redis.Redis(connection_pool=self.pool)
def set(
self,
key: str,
value: Union[dict, list, str, bytes],
expire: int = 0 # 0表示永不过期
) -> bool:
"""存储数据(支持JSON和二进制)"""
conn = self._get_conn()
# 序列化处理
if isinstance(value, (dict, list)):
value = json.dumps(value)
elif not isinstance(value, (str, bytes)):
raise TypeError("Unsupported value type")
# 选择命令
if expire > 0:
return conn.setex(key, expire, value)
return conn.set(key, value)
def get(self, key: str, parse_json: bool = False) -> Any:
"""获取数据(支持JSON解析)"""
conn = self._get_conn()
result = conn.get(key)
if result and parse_json:
return json.loads(result)
return result
def batch_set(self, items: dict, expire: int = 0):
"""批量设置键值"""
conn = self._get_conn()
pipe = conn.pipeline()
for key, value in items.items():
if isinstance(value, (dict, list)):
value = json.dumps(value)
if expire > 0:
pipe.setex(key, expire, value)
else:
pipe.set(key, value)
pipe.execute()
三、C++实现(基于hiredis)
// redis_tool.hpp
#include <hiredis/hiredis.h>
#include <string>
#include <unordered_map>
#include <nlohmann/json.hpp>
class RedisTool {
public:
RedisTool(const std::string& host = "127.0.0.1", int port = 6379)
: context_(redisConnect(host.c_str(), port)) {}
~RedisTool() {
redisFree(context_);
}
bool set(const std::string& key, const std::string& value, int expire = 0) {
redisReply* reply = nullptr;
if (expire > 0) {
reply = (redisReply*)redisCommand(
context_, "SETEX %s %d %b", key.c_str(), expire, value.data(), value.size()
);
} else {
reply = (redisReply*)redisCommand(
context_, "SET %s %b", key.c_str(), value.data(), value.size()
);
}
bool success = reply && reply->type == REDIS_REPLY_STATUS;
freeReplyObject(reply);
return success;
}
bool set_json(const std::string& key, const nlohmann::json& value, int expire = 0) {
return set(key, value.dump(), expire);
}
std::string get(const std::string& key) {
redisReply* reply = (redisReply*)redisCommand(context_, "GET %s", key.c_str());
std::string result;
if (reply && reply->type == REDIS_REPLY_STRING) {
result = std::string(reply->str, reply->len);
}
freeReplyObject(reply);
return result;
}
template<typename T>
T get_json(const std::string& key) {
auto str = get(key);
return nlohmann::json::parse(str).get<T>();
}
private:
redisContext* context_;
};
四、Golang实现
// redis_tool.go
package redis_tool
import (
"context"
"encoding/json"
"time"
"github.com/go-redis/redis/v8"
)
type RedisClient struct {
client *redis.Client
ctx context.Context
}
func NewRedisClient(addr string, db int) *RedisClient {
return &RedisClient{
client: redis.NewClient(&redis.Options{
Addr: addr,
DB: db,
}),
ctx: context.Background(),
}
}
func (rc *RedisClient) Set(key string, value interface{}, expiration time.Duration) error {
// 自动序列化JSON
var val interface{}
switch v := value.(type) {
case string, []byte:
val = v
default:
data, err := json.Marshal(v)
if err != nil {
return err
}
val = data
}
return rc.client.Set(rc.ctx, key, val, expiration).Err()
}
func (rc *RedisClient) Get(key string) (string, error) {
return rc.client.Get(rc.ctx, key).Result()
}
func (rc *RedisClient) GetJSON(key string, v interface{}) error {
data, err := rc.client.Get(rc.ctx, key).Bytes()
if err != nil {
return err
}
return json.Unmarshal(data, v)
}
func (rc *RedisClient) BatchSet(items map[string]interface{}, expiration time.Duration) {
pipe := rc.client.Pipeline()
for k, v := range items {
pipe.Set(rc.ctx, k, v, expiration)
}
pipe.Exec(rc.ctx)
}
五、使用示例
Python:
tool = RedisTool()
tool.set("user:1", {"name": "John", "age": 30}, expire=3600) # 带过期时间
user = tool.get("user:1", parse_json=True)
C++:
RedisTool rt;
rt.set_json("config:app", {{"theme", "dark"}, {"lang", "en"}}, 86400);
auto config = rt.get_json<nlohmann::json>("config:app");
Golang:
client := NewRedisClient("localhost:6379", 0)
client.Set("cache:homepage", "<html>...</html>", 10*time.Minute)
var user User
client.GetJSON("user:100", &user)
六、设计优势
- 智能序列化:自动处理JSON转换
- 过期策略统一:
expire=0
表示永不过期 - 连接复用:内置连接池管理
- 批量操作:减少网络开销
- 类型安全:C++模板强化类型检查
通过此设计,开发者可以灵活选择存储策略,简化了缓存管理和持久化存储的统一操作,同时保证了多语言环境下API的一致性。
https://github.com/0voice