zoukankan      html  css  js  c++  java
  • redis大幅性能提升之使用管道(PipeLine)和批量(Batch)操作

      

           前段时间在做用户画像的时候,遇到了这样的一个问题,记录某一个商品的用户购买群,刚好这种需求就可以用到Redis中的Set,key作为productID,value

    就是具体的customerid集合,后续的话,我就可以通过productid来查看该customerid是否买了此商品,如果购买了,就可以有相关的关联推荐,当然这只是系统中

    的一个小业务条件,这时候我就可以用到SADD操作方法,代码如下:

            static void Main(string[] args)
            {
                ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("192.168.23.151:6379");
    
                var db = redis.GetDatabase();
    
                var productID = string.Format("productID_{0}", 1);
    
                for (int i = 0; i < 10; i++)
                {
                    var customerID = i;
    
                    db.SetAdd(productID, customerID);
                }
            }

    一:问题

        但是上面的这段代码很明显存在一个大问题,Redis本身就是基于tcp的一个Request/Response protocol模式,不信的话,可以用wireshark监视一下:

    从图中可以看到,有很多次的192.168.23.1 => 192.168.23.151 之间的数据往返,从传输内容中大概也可以看到有一个叫做productid_xxx的前缀,

    那如果有百万次局域网这样的round trip,那这个延迟性可想而知,肯定达不到我们预想的高性能。

    二:解决方案【Batch】

         刚好基于我们现有的业务,我可以定时的将批量的productid和customerid进行分组整合,然后用batch的形式插入到某一个具体的product的set中去,

    接下来我可以把上面的代码改成类似下面这样:

     1         static void Main(string[] args)
     2         {
     3             ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("192.168.23.151:6379");
     4 
     5             var db = redis.GetDatabase();
     6 
     7             var productID = string.Format("productID_{0}", 1);
     8 
     9             var list = new List<int>();
    10 
    11 
    12             for (int i = 0; i < 10; i++)
    13             {
    14                 list.Add(i);
    15             }
    16 
    17             db.SetAdd(productID, list.Select(i => (RedisValue)i).ToArray());
    18         }

    从截图中传输的request,response可以看到,这次我们一次性提交过去,极大的较少了在网络传输方面带来的尴尬性。。

    三:再次提出问题

      product维度的画像我们可以解决了,但是我们还有一个customerid的维度,也就是说我需要维护一个customerid为key的set集合,其中value的值为

    该customerid的各种平均值,比如说“总交易次数”,“总交易金额”。。。等等这样的聚合信息,然后推送过来的是批量的customerid,也就是说你需要定时

    维护一小嘬set集合,在这种情况下某一个set的批量操作就搞不定了。。。原始代码如下:

     1         static void Main(string[] args)
     2         {
     3             ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("192.168.23.151:6379");
     4 
     5             var db = redis.GetDatabase();
     6 
     7 
     8             //批量过来的数据: customeridlist, ordertotalprice,具体业务逻辑省略
     9             var orderTotalPrice = 100;
    10 
    11             var customerIDList = new List<int>();
    12 
    13             for (int i = 0; i < 10; i++)
    14             {
    15                 customerIDList.Add(i);
    16             }
    17 
    18             //foreach更新每个redis 的set集合
    19             foreach (var item in customerIDList)
    20             {
    21                 var customerID = string.Format("customerid_{0}", item);
    22 
    23                 db.SetAdd(customerID, orderTotalPrice);
    24             }
    25         }

    四:解决方案【PipeLine】

        上面这种代码在生产上当然是行不通的,不过针对这种问题,redis早已经提出了相关的解决方案,那就是pipeline机制,原理还是一样,将命令集整合起来通过

    一条request请求一起送过去,由redis内部fake出一个client做批量执行操作,代码如下:

     1         static void Main(string[] args)
     2         {
     3             ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("192.168.23.151:6379");
     4 
     5             var db = redis.GetDatabase();
     6 
     7 
     8             //批量过来的数据: customeridlist, ordertotalprice,具体业务逻辑省略
     9             var orderTotalPrice = 100;
    10 
    11             var customerIDList = new List<int>();
    12 
    13             for (int i = 0; i < 10; i++)
    14             {
    15                 customerIDList.Add(i);
    16             }
    17 
    18             var batch = db.CreateBatch();
    19 
    20             foreach (var item in customerIDList)
    21             {
    22                 var customerID = string.Format("customerid_{0}", item);
    23 
    24                 batch.SetAddAsync(customerID, orderTotalPrice);
    25             }
    26 
    27             batch.Execute();
    28         }

    然后,我们再看下面的wireshark截图,可以看到有很多的SADD这样的小命令,这就说明有很多命令是一起过去的,大大的提升了性能。

    最后可以再看一下redis,数据也是有的,是不是很爽~~~

    192.168.23.151:6379> keys *
     1) "customerid_0"
     2) "customerid_9"
     3) "customerid_1"
     4) "customerid_3"
     5) "customerid_8"
     6) "customerid_2"
     7) "customerid_7"
     8) "customerid_5"
     9) "customerid_6"
    10) "customerid_4"

    好了,先就说到这里了,希望本篇对你有帮助。

  • 相关阅读:
    gcc5.2版本安装详解
    Java的各种加密算法
    Response.ContentType 详细列表
    使用C#选择文件夹、打开文件夹、选择文件
    C#从SQL server数据库中读取l图片和存入图片
    GridView导出成Excel字符"0"丢失/数字丢失的处理方式 收藏
    只能在执行Render() 的过程中调用 RegisterForEventValidation;
    维护删除订单后,清空安装和售后信息;条码打印软件补充打印问题
    Bind("入库日期", "{0:yyyy-MM-dd}") 关于asp.net格式化数据库日期字符串
    特别注意: range.Text.ToString(); 和 range.Value2.ToString(); 的区别
  • 原文地址:https://www.cnblogs.com/huangxincheng/p/6212406.html
Copyright © 2011-2022 走看看