前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DotNetCore三大Redis客户端对比和使用心得

DotNetCore三大Redis客户端对比和使用心得

作者头像
有态度的马甲
发布2020-04-16 14:41:51
2.2K0
发布2020-04-16 14:41:51
举报
文章被收录于专栏:精益码农精益码农

前言

稍微复杂一点的互联网项目,技术选型都会涉及Redis,.NetCore的生态越发完善,支持.NetCore的Redis客户端越来越多,

下面三款常见的Redis客户端,相信大家平时或多或少用到一些,结合三款客户端的使用经历,有些心得体会。

先比较宏观的背景:

使用心得

三款客户端Redis支持的连接字符串配置基本相同

代码语言:javascript
复制
  "connectionstrings": {
    "redis": "localhost:6379,password=abcdef,connectTimeout=5000,writeBuffer=40960"
  }
1. StackExchange.Redis

定位是高性能、通用的Redis .Net客户端;方便地应用Redis全功能;支持Redis Cluster

  • 高性能的核心在于:多路复用连接(允许有效使用来自多个调用线程的共享连接), 服务器端操作使用ConnectionMultiplexer类
代码语言:javascript
复制
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("server1:6379,server2:6379");
// 日常应用的核心类库是IDatabase
IDatabase db = redis.GetDatabase();

// 支持Pub/Sub
ISubscriber sub = redis.GetSubscriber();
sub.Subscribe("messages", (channel, message) => {
    Console.WriteLine((string)message);
});
---
sub.Publish("messages", "hello");

也正是因为多路复用,StackExchange.Redis唯一不支持的Redis特性是 "blocking pops",这个特性是RedisMQ的关键理论。如果你需要blocking pops, StackExchange.Redis官方推荐使用pub/sub模型模拟实现。

  • 日常操作API请关注IDatabase接口,支持异步方法,这里我对【客户端操作Redis尽量不要使用异步方法】的说法不敢苟同,对于异步方法我认为还是遵守微软最佳实践:对于IO密集的操作,能使用异步尽量使用异步
代码语言:javascript
复制
// 对应redis自增api:DECR mykey
_redisDB0.StringDecrementAsync("ProfileUsageCap", (double)1)
// 对应redis api:HGET KEY field1
_redisDB0.HashGetAsync(profileUsage, eqidPair.ProfileId))           
// 对应redis哈希自增api:HINCRBY myhash field -1
_redisDB0.HashDecrementAsync(profileUsage, eqidPair.ProfileId, 1)
  • ConnectionMultiplexer 方式支持随时切换Redis DB,对于多个Redis DB的操作,我封装了一个常用的Redis DB 操作客户端。
代码语言:javascript
复制
public class RedisStore
    {
        private static Lazy<ConnectionMultiplexer> LazyConnection;
        private static string connectionRedis = "localhost:6379";

        public RedisStore(string connectiontring)
        {
            connectionRedis = connectiontring ?? "localhost:6379";
            LazyConnection = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(connectionRedis));
        }
        public static ConnectionMultiplexer Connection => LazyConnection.Value;
        public RedisDatabase RedisCache => new RedisDatabase(Connection);

    }

    public class RedisDatabase
    {
        private Dictionary<int, IDatabase> DataBases = new Dictionary<int, IDatabase>();
        
        public ConnectionMultiplexer RedisConnection { get; }

        public RedisDatabase(ConnectionMultiplexer Connection)
        {
            DataBases = new Dictionary<int, IDatabase>{ };
            for(var i=0;i<16;i++)
            {
                DataBases.Add(i, Connection.GetDatabase(i));
            }
            
            RedisConnection = Connection;
        }

        public IDatabase this[int index]
        {
            get
            {
                if (DataBases.ContainsKey(index))
                    return DataBases[index];
                else
                   return DataBases[0];
            }
        }
    }
2. Microsoft.Extensions.Caching.StackExchangeRedis

从nuget doc可知,该组件库依赖于 StackExchange.Redis 客户端;是.NetCore针对分布式缓存提供的客户端,侧重点在Redis的缓存特性

该库是基于 IDistributedCache 接口实现的,该接口为实现分布式缓存的通用性,缓存内容将以byte[] 形式读写 ;另外能使用的函数签名也更倾向于【通用的 增、查操作】

代码语言:javascript
复制
// add Redis cache service
services.AddStackExchangeRedisCache(options =>
{
  options.Configuration = Configuration.GetConnectionString("redis");
  options.InstanceName = "SampleInstance";
});

// Set Cache Item (by byte[])
 lifetime.ApplicationStarted.Register(() =>
{
      var currentTimeUTC = DateTime.UtcNow.ToString();
      byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
      var options = new DistributedCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromMinutes(20));
      cache.Set("cachedTimeUTC", encodedCurrentTimeUTC, options);
});

// Retrieve Cache Item
[HttpGet]
[Route("CacheRedis")]
public async Task<string> GetAsync()
{
  var ret = "";
  var bytes = await _cache.GetAsync("cachedTimeUTC");
   if (bytes != null)
   {
      ret = Encoding.UTF8.GetString(bytes);
      _logger.LogInformation(ret);
   }
   return  await Task.FromResult(ret);
}

① 很明显,该Cache组件并不能做到自由切换 Redis DB, 目前可在redis连接字符串一次性配置项目要使用哪个Redis DB

② 会在指定DB(默认为0)生成key = SampleInstancecachedTimeUTC 的redis缓存项

③ 通用接口只支持bytes[] 形式传值,以上byte[] 实际是以Hash的形式存储

3. CSRedisCore

该组件是基于连接池模型,默认配置会预热50个redis连接。功能更灵活,针对实际Redis应用场景有更多玩法。

  • 普通模式
  • 官方集群模式 redis cluster
  • 分区模式(作者实现)

普通模式使用方法极其简单,这里要提示的是:该客户端也不支持随意切换Redis DB, 但是原作者给出一种缓解的方式:构造多客户端。

代码语言:javascript
复制
var redisDB = new CSRedisClient[16];                        // 多客户端
for (var a = 0; a < redisDB.Length; a++)
  redisDB[a] = new CSRedisClient(Configuration.GetConnectionString("redis") + ",defaultDatabase=" + a);
services.AddSingleton(redisDB);
// ----------------------------
_redisDB[0].IncrByAsync("ProfileUsageCap", -1)
_redisDB[0].HGetAsync(profileUsage, eqidPair.ProfileId.ToString())
_redisDB[0].HIncrByAsync(profileUsage, eqidPair.ProfileId.ToString(), -1);

内置的静态操作类RedisHelper, 与Redis-Cli 命令完全一致, 故能原生支持”blocking pops”。

代码语言:javascript
复制
// 实现后台服务,持续消费MQ消息
public class BackgroundJob : BackgroundService
    {
        private readonly CSRedisClient[] _redisDB;
        private readonly IConfiguration _conf;
        private readonly ILogger _logger;
        public BackgroundJob(CSRedisClient[] csRedisClients,IConfiguration conf,ILoggerFactory loggerFactory)
        {
            _redisDB = csRedisClients;
            _conf = conf;
            _logger = loggerFactory.CreateLogger(nameof(BackgroundJob));
        }
        
        //  Background 需要实现的后台任务
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _redisDB[0] = new CSRedisClient(_conf.GetConnectionString("redis") + ",defualtDatabase=" + 0);
            RedisHelper.Initialization(_redisDB[0]);

            while (!stoppingToken.IsCancellationRequested)
            {
                var key = $"eqidpair:{DateTime.Now.ToString("yyyyMMdd")}";
                // 阻塞式从右侧读取List首消息
                var eqidpair = RedisHelper.BRPop(5, key);
                // TODO Handler Message
                else
                    await Task.Delay(1000, stoppingToken);
            }
        }
    }

-----RedisMQ 生产者---
//  将一个或多个msg插入List头部
RedisHelper.LPush(redisKey, eqidPairs.ToArray());

以上三大客户端,Microsoft.Extensions.Caching.StackExchangeRedis 与其他两者的定位还是有很大差距的,单纯使用Redis缓存特性, 有微软出品,必属精品情结的可使用此客户端; StackExchange.Redis、CSRedisCore 对于Redis全功能特性支持的比较全

Redis的一点小经验

  • 对要使用的Redis API 的时间复杂度心里要有数,尽量不要使用长时间运行的命令如keys *,可通过redis.io SlowLog命令观测哪些命令耗时较长
  • Redis Key可按照“:”分隔定义成有业务意义的字符串,如NewUsers:202004:666(某些Redis UI可直观友好查看该键值)
  • 合适确定Key-Value的大小:Redis对于small value更友好, 如果值很大,考虑划分到多个key
  • 关于缓存穿透,面试的时候会问,自行搜索布隆过滤器。
  • redis虽然有持久化机制,但在实际中会将key-value持久化到关系型数据库,因为对于某些结构化查询,SQL更为有效。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 精益码农 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 使用心得
    • 1. StackExchange.Redis
      • 2. Microsoft.Extensions.Caching.StackExchangeRedis
        • 3. CSRedisCore
        • Redis的一点小经验
        相关产品与服务
        云数据库 Redis
        腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档