Redis Hash查询-单个key存多个对象和多个key存整个对象的差异

使用的是.NET CORE的StackExchange.Redis客户端程序操作redis
以下是个人见解

单个key存多个对象

redis结构

优点:获取多个数据或全部数据的时候比较方便,操作简单
// 获取全部
var list= await _redisClient.HashGetAllAsync<MemberLive>($"liveRpc:memberLiveListTemp");
// 指定ID获取
var list= await _redisClient.HashGetAsync<MemberLive>($"liveRpc:memberLiveListTemp", new StackExchange.Redis.RedisValue[] { 0, 1, 2, 3 });
缺点:

1、修改value的json字符对象里面的某一个值,需要序列化为对象,修改后在存入hash,并发下会出现数据不一致问题
2、不可以单独对某一条数据设置过期时间
3、占用内存较大

二、多个key存整个对象

redis结构图

优点:可以直接对单个字段进行原子操作、设置过期时间、存在内存小
缺点:存储、获取比较复杂、特别是批量获取数据比较麻烦

1、使用Batch命令指定key获取数据,数量多不建议

    var batch = _redisClient.CreateBatch();
    var tasks = new List<Task<StackExchange.Redis.HashEntry[]>>();
    var list = new List<MemberLive>();
    for (int i = 0; i < 10000; i++)
    {
        var task = batch.HashGetAllAsync($"liveRpc:memberLiveList:{i}");
        tasks.Add(task);
    }
    batch.Execute();

    foreach (var task in tasks)
    {
        var hashEntries = await task;	// 数据多这里容易出现redis超时
        list.Add(hashEntries.ToModel<MemberLive>());
    }

2、使用Lua命令,比Batch强

	// 模糊查询获取Redis的keys:
	var redisResult01 = await _redisClient.ScriptEvaluateAsync(
	   StackExchange.Redis.LuaScript.Prepare($"local ks = redis.call('KEYS', @keypattern) return ks "),
	   new { @keypattern = "liveRpc:memberLiveList*" });
	
	// 查询全部
	var redisResult02 = await _redisClient.ScriptEvaluateAsync(
	   StackExchange.Redis.LuaScript.Prepare("local ks = redis.call('KEYS', @keypattern) local rst ={ }; for i, v in pairs(ks) do rst[i] = redis.call('hgetall', v) end; return rst "),
	   new { @keypattern = "liveRpc:memberLiveList*" });
	
	
	// 指定key
	var keys = new List<StackExchange.Redis.RedisKey>();
	for (int i = 0; i < 2000; i++)
	{
	    keys.Add($"liveRpc:memberLiveList:{i}");
	}
	var sum = 0L;
	for (int i = 0; i < 1; i++)
	{
	    var ts1 = DateTime.Now.D2L();
		
		// 指定key
	    var redisResult03 = await _redisClient.ScriptEvaluateAsync("local rst ={ }; for i, v in pairs(KEYS) do rst[i] = redis.call('hgetall', v) end; return rst", keys.ToArray());
	
		// 指定key和field
	    var redisResult04 = await _redisClient.ScriptEvaluateAsync("local rst ={ }; for i, v in pairs(KEYS) do rst[i] = redis.call('hmget', v, 'Id', 'GiftTotalMoney') end; return rst", keys.ToArray());
	    var ts2 = DateTime.Now.D2L();
	
	    var tt = ts2 - ts1;
	
	    sum = Interlocked.Add(ref sum, tt);
	}

3、扩展方法

	private static readonly ConcurrentDictionary<int, PropertyInfo[]> _propertysDic = new ConcurrentDictionary<int, PropertyInfo[]>();

    public static HashEntry[] GetHashEntryArray<T>(this T t) where T : class, new()
    {
        List<HashEntry> listHashEntry = new List<HashEntry>();

        var properties = t.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
        if (!properties.Any()) return listHashEntry.ToArray();

        foreach (PropertyInfo prop in properties)
        {
            string name = prop.Name; // 获得属性的名字,后面就可以根据名字判断来进行些自己想要的操作
            var value = prop.GetValue(t, null);  // 用pi.GetValue获得值
            listHashEntry.Add(new HashEntry(name, value.ToString()));
        }

        return listHashEntry.ToArray();
    }

    public static T ToModel<T>(this HashEntry[] entrys) where T : class, new()
    {
        T obj = Activator.CreateInstance<T>();

        if (entrys == null) return obj;

        var properties = _propertysDic.GetOrAdd(obj.GetType().FullName.GetHashCode(), (value) =>
        {
            var properties = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
            return properties;
        });

        // var properties = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);

        foreach (HashEntry item in entrys)
        {
            var prop = properties.FirstOrDefault(t => t.Name == item.Name);
            if (prop == null) continue;

            var value = System.ComponentModel.TypeDescriptor.GetConverter(prop.PropertyType).ConvertFromString(item.Value);
            prop.SetValue(obj, value, null);
        }
        return obj;
    }

    public static List<T> HashToModel<T>(this RedisResult redisResult) where T : class, new()
    {
        var r = new List<T>();

        var redisResultArray = (RedisResult[])redisResult;
        foreach (var item in redisResultArray)
        {
            string[] preval = (string[])item;
            if (preval.Length == 0) continue;

            T obj = Activator.CreateInstance<T>();
            var properties = _propertysDic.GetOrAdd(objobj.GetType().FullName.GetHashCode(), (value) =>
            {
                var properties = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
                return properties;
            });

            foreach (PropertyInfo prop in properties)
            {
                int index = Array.IndexOf(preval, prop.Name);
                if (index < 0) continue;

                var value = System.ComponentModel.TypeDescriptor.GetConverter(prop.PropertyType).ConvertFromString(preval[index + 1]);
                prop.SetValue(obj, value, null);
            }

            r.Add(obj);
        }

        return r;
    }
结论:

1、数据修改不频繁、内容比较固定建议用第一种
2、数据修改频繁、需要设置过期时间建议使用第二种

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值