mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-12-25 00:56:35 +08:00
refactor: redis操作界面重构
This commit is contained in:
@@ -9,7 +9,6 @@ import (
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/req"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -18,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
// scan获取redis的key列表信息
|
||||
func (r *Redis) Scan(rc *req.Ctx) {
|
||||
func (r *Redis) ScanKeys(rc *req.Ctx) {
|
||||
ri := r.getRedisIns(rc)
|
||||
|
||||
form := &form.RedisScanForm{}
|
||||
@@ -27,24 +26,60 @@ func (r *Redis) Scan(rc *req.Ctx) {
|
||||
cmd := ri.GetCmdable()
|
||||
ctx := context.Background()
|
||||
|
||||
kis := make([]*vo.KeyInfo, 0)
|
||||
keys := make([]string, 0)
|
||||
var cursorRes map[string]uint64 = make(map[string]uint64)
|
||||
|
||||
size, _ := cmd.DBSize(ctx).Result()
|
||||
|
||||
if form.Match != "" && !strings.ContainsAny(form.Match, "*") {
|
||||
// 精确匹配, 判断是否存在
|
||||
res, err := cmd.Exists(ctx, form.Match).Result()
|
||||
if err == nil && res != 0 {
|
||||
keys = append(keys, form.Match)
|
||||
}
|
||||
|
||||
rc.ResData = &vo.Keys{Cursor: cursorRes, Keys: keys, DbSize: size}
|
||||
return
|
||||
}
|
||||
|
||||
// 通配符或全匹配
|
||||
mode := ri.Info.Mode
|
||||
if mode == "" || mode == entity.RedisModeStandalone || mode == entity.RedisModeSentinel {
|
||||
redisAddr := ri.Cli.Options().Addr
|
||||
// 汇总所有的查询出来的键值
|
||||
var keys []string
|
||||
// 有通配符或空时使用scan,非模糊匹配直接匹配key
|
||||
if form.Match == "" || strings.ContainsAny(form.Match, "*") {
|
||||
cursorRes[redisAddr] = form.Cursor[redisAddr]
|
||||
cursorRes[redisAddr] = form.Cursor[redisAddr]
|
||||
for {
|
||||
ks, cursor := ri.Scan(cursorRes[redisAddr], form.Match, form.Count)
|
||||
cursorRes[redisAddr] = cursor
|
||||
if len(ks) > 0 {
|
||||
// 返回了数据则追加总集合中
|
||||
keys = append(keys, ks...)
|
||||
}
|
||||
// 匹配的数量满足用户需求退出
|
||||
if int32(len(keys)) >= int32(form.Count) {
|
||||
break
|
||||
}
|
||||
// 匹配到最后退出
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if mode == entity.RedisModeCluster {
|
||||
mu := &sync.Mutex{}
|
||||
// 遍历所有master节点,并执行scan命令,合并keys
|
||||
ri.ClusterCli.ForEachMaster(ctx, func(ctx context.Context, client *redis.Client) error {
|
||||
redisAddr := client.Options().Addr
|
||||
nowCursor := form.Cursor[redisAddr]
|
||||
for {
|
||||
ks, cursor := ri.Scan(cursorRes[redisAddr], form.Match, form.Count)
|
||||
ks, cursor, _ := client.Scan(ctx, nowCursor, form.Match, form.Count).Result()
|
||||
// 遍历节点的内部回调函数使用异步调用,如不加锁会导致集合并发错误
|
||||
mu.Lock()
|
||||
cursorRes[redisAddr] = cursor
|
||||
nowCursor = cursor
|
||||
if len(ks) > 0 {
|
||||
// 返回了数据则追加总集合中
|
||||
keys = append(keys, ks...)
|
||||
}
|
||||
mu.Unlock()
|
||||
// 匹配的数量满足用户需求退出
|
||||
if int32(len(keys)) >= int32(form.Count) {
|
||||
break
|
||||
@@ -54,95 +89,33 @@ func (r *Redis) Scan(rc *req.Ctx) {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 精确匹配
|
||||
keys = append(keys, form.Match)
|
||||
}
|
||||
|
||||
var keyInfoSplit []string
|
||||
if len(keys) > 0 {
|
||||
keyInfosLua := `local result = {}
|
||||
-- KEYS[1]为第1个参数,lua数组下标从1开始
|
||||
for i = 1, #KEYS do
|
||||
local ttl = redis.call('ttl', KEYS[i]);
|
||||
local keyType = redis.call('type', KEYS[i]);
|
||||
table.insert(result, string.format("%d,%s", ttl, keyType['ok']));
|
||||
end;
|
||||
return table.concat(result, ".");`
|
||||
// 通过lua获取 ttl,type.ttl2,type2格式,以便下面切割获取ttl和type。避免多次调用ttl和type函数
|
||||
keyInfos, err := cmd.Eval(ctx, keyInfosLua, keys).Result()
|
||||
biz.ErrIsNilAppendErr(err, "执行lua脚本获取key信息失败: %s")
|
||||
keyInfoSplit = strings.Split(keyInfos.(string), ".")
|
||||
}
|
||||
|
||||
for i, k := range keys {
|
||||
ttlType := strings.Split(keyInfoSplit[i], ",")
|
||||
ttl, _ := strconv.Atoi(ttlType[0])
|
||||
// 没有存在该key,则跳过
|
||||
if ttl == -2 {
|
||||
continue
|
||||
}
|
||||
ki := &vo.KeyInfo{Key: k, Type: ttlType[1], Ttl: int64(ttl)}
|
||||
kis = append(kis, ki)
|
||||
}
|
||||
} else if mode == entity.RedisModeCluster {
|
||||
var keys []string
|
||||
// 有通配符或空时使用scan,非模糊匹配直接匹配key
|
||||
if form.Match == "" || strings.ContainsAny(form.Match, "*") {
|
||||
mu := &sync.Mutex{}
|
||||
// 遍历所有master节点,并执行scan命令,合并keys
|
||||
ri.ClusterCli.ForEachMaster(ctx, func(ctx context.Context, client *redis.Client) error {
|
||||
redisAddr := client.Options().Addr
|
||||
nowCursor := form.Cursor[redisAddr]
|
||||
for {
|
||||
ks, cursor, _ := client.Scan(ctx, nowCursor, form.Match, form.Count).Result()
|
||||
// 遍历节点的内部回调函数使用异步调用,如不加锁会导致集合并发错误
|
||||
mu.Lock()
|
||||
cursorRes[redisAddr] = cursor
|
||||
nowCursor = cursor
|
||||
if len(ks) > 0 {
|
||||
// 返回了数据则追加总集合中
|
||||
keys = append(keys, ks...)
|
||||
}
|
||||
mu.Unlock()
|
||||
// 匹配的数量满足用户需求退出
|
||||
if int32(len(keys)) >= int32(form.Count) {
|
||||
break
|
||||
}
|
||||
// 匹配到最后退出
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
} else {
|
||||
// 精确匹配
|
||||
keys = append(keys, form.Match)
|
||||
}
|
||||
|
||||
// 因为redis集群模式执行lua脚本key必须位于同一slot中,故单机获取的方式不适合
|
||||
// 使用lua获取key的ttl以及类型,减少网络调用
|
||||
keyInfoLua := `local ttl = redis.call('ttl', KEYS[1]);
|
||||
local keyType = redis.call('type', KEYS[1]);
|
||||
return string.format("%d,%s", ttl, keyType['ok'])`
|
||||
for _, k := range keys {
|
||||
keyInfo, err := cmd.Eval(ctx, keyInfoLua, []string{k}).Result()
|
||||
biz.ErrIsNilAppendErr(err, "执行lua脚本获取key信息失败: %s")
|
||||
ttlType := strings.Split(keyInfo.(string), ",")
|
||||
ttl, _ := strconv.Atoi(ttlType[0])
|
||||
// 没有存在该key,则跳过
|
||||
if ttl == -2 {
|
||||
continue
|
||||
}
|
||||
ki := &vo.KeyInfo{Key: k, Type: ttlType[1], Ttl: int64(ttl)}
|
||||
kis = append(kis, ki)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
size, _ := cmd.DBSize(context.TODO()).Result()
|
||||
rc.ResData = &vo.Keys{Cursor: cursorRes, Keys: kis, DbSize: size}
|
||||
rc.ResData = &vo.Keys{Cursor: cursorRes, Keys: keys, DbSize: size}
|
||||
}
|
||||
|
||||
func (r *Redis) KeyInfo(rc *req.Ctx) {
|
||||
ri, key := r.checkKeyAndGetRedisIns(rc)
|
||||
cmd := ri.GetCmdable()
|
||||
ctx := context.Background()
|
||||
ttl, err := cmd.TTL(ctx, key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "ttl失败: %s")
|
||||
|
||||
ttlInt := -1
|
||||
if ttl != -1 {
|
||||
ttlInt = int(ttl.Seconds())
|
||||
}
|
||||
|
||||
typeRes, err := cmd.Type(ctx, key).Result()
|
||||
biz.ErrIsNilAppendErr(err, "获取key type失败: %s")
|
||||
|
||||
rc.ResData = &vo.KeyInfo{
|
||||
Key: key,
|
||||
Ttl: ttlInt,
|
||||
Type: typeRes,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Redis) TtlKey(rc *req.Ctx) {
|
||||
|
||||
Reference in New Issue
Block a user