前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >有关Redis时间复杂度优化测试报告

有关Redis时间复杂度优化测试报告

作者头像
西柚dzh
发布2022-06-09 16:59:44
4370
发布2022-06-09 16:59:44
举报
文章被收录于专栏:dcmickey小站dcmickey小站

场景介绍

权限筛查需要汇总用户计算,比如领导角色权限能看到该部门下的所有员工单据。

不要考虑有没有更好的权限功能架构设计方案,因为只得基于现有架构上优化性能。

优化接口概述

接口定义:请忽略这个返回值,反面教材

代码语言:javascript
复制
/// <summary>
/// 获取当前登录人、当前页面得权限
/// 将项目和部门人员、个人的权限数据组合起来
/// </summary>
/// <param name="currentRouterUrl">当前路由</param>
/// <returns>权限(item1:全部 item2:所属部门或者公司 item3:项目 item4:个人 item5:选择得项目,item6:当前登录人所属部门下的所有人员或者是选择的公司下的所有人员)) item7:所属部门下的所有部门</returns>
public Tuple<bool, bool, bool, bool, List<string>, List<string>> GetCurrentUserDataRight(string currentRouterUrl = "")

接口调用方

代码语言:javascript
复制
var right = _businessRoleService.GetCurrentUserDataRight(); // 权限
return _dbContext.Queryable<表名称>()
.WhereIF(right.Item2 || right.Item3 || right.Item4, t => SqlFunc.ContainsArray(right.Item5, t.ProjectCode) || SqlFunc.ContainsArray(right.Item5, t.CreateUserCode));

数据环境

有这样一个用户表,数据量为8848条记录,且只查询特定需求字段,不含大varchar和text类属性值字段

使用redis进行缓存,以减轻数据库访问压力,注意不是数据库单次查询压力

单次查询数据库8848条记录,妥妥的无压力毫秒级 但是由于这个数据是为了做底层服务用的(权限筛查需要汇总用户计算,比如领导角色权限能看到该部门下的所有员工单据,需要抓到符合条件的员工工号),所以并发访问特别高,所以数据库的请求次数压力会不小,必须放入缓存中 之前人设计的是string缓存,8k+的数据反序列化到一个string缓存上, 容量大概7~8M空间。典型的大value型缓存事故,首先每次获取就很慢,会导致Redis服务线程阻塞不说, 这样的数据正反序列化开销也不小

补充点

权限筛查需要汇总用户计算,比如领导角色权限能看到该部门下的所有员工单据

这个呢 就不要考虑为啥不能按照部门id等其他维度来代替做权限功能,系统历史遗留问题,想办法再现有架构上优化性能。

改进方案

考虑改用hash

但是这样的结构对于单个用户查缓存so easy。给底层服务(权限筛查汇总计算用)

上HashScan代码

代码语言:javascript
复制
public static List<T> HashScan<T>(string key, long count = 50)
        {
            return Catch<List<T>>(key, () =>
            {
                List<T> result = new List<T>();
                var rediscli = GetClient();
                long cursor = 0;
                TimeConsumingLogHelper log = new TimeConsumingLogHelper().Start().AddLog("1");
                do
                {
                    var scanResp = rediscli.HScan<T>(key, cursor, count: count);
                    log.AddLog("xxx1");
                    cursor = scanResp.Cursor;
                    if (scanResp.Items.Length > 0)
                    {
                        result.AddRange(scanResp.Items.ToList().Select(x => x.value).ToList());
                    }
                    log.AddLog("xxx2");
                }
                while (cursor > 0);
                var sw = log.CollectLog();
                return result;
            });
        }

传入count为500时

代码语言:javascript
复制
当前毫秒数:30=>1
当前毫秒数:338=>xxx1
当前毫秒数:338=>xxx2
当前毫秒数:395=>xxx1
当前毫秒数:395=>xxx2
当前毫秒数:459=>xxx1
当前毫秒数:459=>xxx2
当前毫秒数:518=>xxx1
当前毫秒数:518=>xxx2
当前毫秒数:578=>xxx1
当前毫秒数:578=>xxx2
当前毫秒数:649=>xxx1
当前毫秒数:649=>xxx2
当前毫秒数:704=>xxx1
当前毫秒数:704=>xxx2
当前毫秒数:774=>xxx1
当前毫秒数:774=>xxx2
当前毫秒数:831=>xxx1
当前毫秒数:831=>xxx2
当前毫秒数:885=>xxx1
当前毫秒数:885=>xxx2
当前毫秒数:937=>xxx1
当前毫秒数:937=>xxx2
当前毫秒数:984=>xxx1
当前毫秒数:984=>xxx2
当前毫秒数:1030=>xxx1
当前毫秒数:1030=>xxx2
当前毫秒数:1080=>xxx1
当前毫秒数:1080=>xxx2
当前毫秒数:1135=>xxx1
当前毫秒数:1135=>xxx2
当前毫秒数:1184=>xxx1
当前毫秒数:1184=>xxx2
当前毫秒数:1219=>xxx1
当前毫秒数:1219=>xxx2
总耗时:1219毫秒

HMset复杂度

1.5s左右, 有时候会3s甚至6s的,不稳定 比for循环hset 性能好,减少网络和连接损耗等干扰

代码语言:javascript
复制
TimeConsumingLogHelper log = new TimeConsumingLogHelper().Start().AddLog("1");
            CSRedisHelper.HMSet(UserHashKey, mods);
            var sw = log.AddLog("xxx2").CollectLog()
代码语言:javascript
复制
当前毫秒数:0=>1
当前毫秒数:3503=>xxx2
总耗时:3503毫秒

当前毫秒数:0=>1
当前毫秒数:2196=>xxx2
总耗时:2196毫秒

当前毫秒数:0=>1
当前毫秒数:1303=>2
当前毫秒数:1972=>2.5
当前毫秒数:1973=>2.8
当前毫秒数:1975=>3
当前毫秒数:2879=>4
总耗时:2879毫秒

当前毫秒数:0=>1
当前毫秒数:1335=>2
当前毫秒数:3337=>2.5
当前毫秒数:3337=>2.8
当前毫秒数:3348=>3
当前毫秒数:6296=>4
总耗时:6296毫秒

Hset

代码语言:javascript
复制
public List<RedisUser> UpdateOrAddUser()
        {
            // var redisclient = RedisClient.GetDataBase();
            TimeConsumingLogHelper log = new TimeConsumingLogHelper().Start().AddLog("1");
            var usrslist = _dbContext.Boost.Queryable<USRUser>().Where(t => t.Status == 1)
               .ToList();
            log.AddLog("2");
            // string value = JsonConvert.SerializeObject(usrslist);
            // _redis.Client.StringSet("users", value);
            // CSRedisHelper.SetStr("users", value, -1);
            var mods = new Dictionary<string, RedisUser>();

            usrslist?.ForEach(u =>
            {
                mods.Add(u.UserID, u);
                //_redis.Client.HashSetAsync("users-hash", u.UserID, JsonConvert.SerializeObject(u));
                CSRedisHelper.SetHash(UserHashKey, u.UserID, u);
            });
            // 给单个用户查缓存用
            log.AddLog("3");
            CSRedisHelper.HMSet(UserHashKey, mods);
            var sw = log.AddLog("4").CollectLog();
            // HashSet(UserHashKey, mods);
            return usrslist;
        }
代码语言:javascript
复制
当前毫秒数:0=>1
当前毫秒数:1676=>2
当前毫秒数:187895=>3
当前毫秒数:188473=>4
总耗时:188473毫秒

stringset耗时

代码语言:javascript
复制
log.AddLog("2");
            string value = JsonConvert.SerializeObject(usrslist);
            // _redis.Client.StringSet("users", value);
             CSRedisHelper.SetStr("users", value, -1);
            log.AddLog("2.5");
            var mods = new Dictionary<string, RedisUser>();

            usrslist?.ForEach(u =>
            {
                mods.Add(u.UserID, u);
                //_redis.Client.HashSetAsync("users-hash", u.UserID, JsonConvert.SerializeObject(u));
                // CSRedisHelper.SetHash(UserHashKey, u.UserID, u); // 180多秒 不要用,网络连接损耗等干扰
            });
            // 给单个用户查缓存用
            log.AddLog("3");
            CSRedisHelper.HMSet(UserHashKey, mods);
            var sw = log.AddLog("4").CollectLog();
代码语言:javascript
复制
当前毫秒数:1275=>2
当前毫秒数:4310=>2.5


当前毫秒数:1270=>2
当前毫秒数:3301=>2.5

当前毫秒数:0=>1
当前毫秒数:1358=>2
当前毫秒数:2187=>2.5
当前毫秒数:2189=>3
当前毫秒数:2959=>4

stringget耗时

两个redis组件耗时差不多,都是700ms。 并且反映出==getstring获取7~8M左右的大value耗时还可以接受==,欣慰,只是set进去耗时长点2s多点

代码语言:javascript
复制
List<RedisUser> result = new List<RedisUser>();
            TimeConsumingLogHelper log = new TimeConsumingLogHelper().Start().AddLog("1");
            result = ServiceHelperProvider.Instance.RedisHelper.Get<List<RedisUser>>("users");
            log.AddLog("2");
            var result2 = CSRedisHelper.GetStr<List<RedisUser>>("users");
            var sw = log.AddLog("3").CollectLog();
代码语言:javascript
复制
当前毫秒数:0=>1
当前毫秒数:722=>2
当前毫秒数:1432=>3
总耗时:1432毫秒

==最终我的方案是用内存缓存~==

写入8k多条的集合只要10ms不到,读取也是2ms甚至不耗时,贼快

这玩意本身就是要到内存中进行后续计算的,直接放内存不是更好!注意内存的坑即可 使用本地缓存极有可能导致严重的线程安全问题,并发考虑严重 根据MSDN的描述:MemoryCache是线程安全的。那么说明,在操作MemoryCache中的缓存项时,MemoryCache保证程序的行为都是原子性的,而不会出现多个线程共同操作导致的数据污染等问题。

附录内存缓存测试打样

代码语言:javascript
复制
public List<RedisUser> UpdateOrAddUser()
        {
            // var redisclient = RedisClient.GetDataBase();
            TimeConsumingLogHelper log = new TimeConsumingLogHelper().Start().AddLog("1");
            var usrslist = _dbContext.Boost.Queryable<USRUser>().Where(t => t.Status == 1)
                .ToList();
            log.AddLog("2");
            string value = JsonConvert.SerializeObject(usrslist);
            // _redis.Client.StringSet(UserFullLoadKey, value);
            CSRedisHelper.SetStr(UserFullLoadKey, value, -1);
            log.AddLog("2.5");
            MemoryCacheCoreHelper.SetCache<List<RedisUser>>(UserFullLoadKey, usrslist, 24 * 60 * 60 - 500);
            log.AddLog("2.8");
            var mods = new Dictionary<string, RedisUser>();

            usrslist?.ForEach(u =>
            {
                mods.Add(u.UserID, u);
                //_redis.Client.HashSetAsync("users-hash", u.UserID, JsonConvert.SerializeObject(u));
                // CSRedisHelper.SetHash(UserHashKey, u.UserID, u); // 180多秒 不要用,网络连接损耗等干扰
            });
            // 给单个用户查缓存用
            log.AddLog("3");
            CSRedisHelper.HMSet(UserHashKey, mods);
            var sw = log.AddLog("4").CollectLog();
            // HashSet(UserHashKey, mods);
            return usrslist;
        }
代码语言:javascript
复制
当前毫秒数:0=>1
当前毫秒数:1656=>2
当前毫秒数:3877=>2.5
当前毫秒数:3883=>2.8
当前毫秒数:3886=>3
当前毫秒数:8681=>4
总耗时:8681毫秒

版权属于:dingzhenhua

本文链接:https://cloud.tencent.com/developer/article/2019331

转载时须注明出处及本声明

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-10-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 场景介绍
  • 数据环境
  • 改进方案
    • 上HashScan代码
    相关产品与服务
    文件存储
    文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档