翼度科技»论坛 编程开发 .net 查看内容

【Redis】.Net Core 面试破冰

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
目录

1.Redis简介

Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API。
Redis 支持丰富的数据类型,常用的如下:

  • string(字符串) // 是 redis 最基本的类型, string 可以包含任何数据,一个键最大能存储 512MB
  • hash(哈希) // hash 特别适合用于存储对象
  • list(列表) // 列表是简单的字符串列表,按照插入顺序排序。可以用作队列。
  • set(集合) //集合内元素的唯一性
  • zset (sorted set:有序集合)
2.Redis 的性能极高 – Redis 能读的速度是 110000 次/s,写的速度是 81000 次/s 。
3.Redis 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用
4.Redis 支持数据的高并发、高可用,如主从模式、哨兵模式、cluster 集群模式
2.使用场景


  • 字符串(String): 最基本的数据类型,可以存储字符串、整数或浮点数。场景:缓存 Session 会话,计数器,流水号等。
  • 哈希/散列/字典(Hash):键值对的集合,可以在一个哈希数据结构中存储多个字段和值。场景:存电商的购物车信息
  • 列表(List):按照插入顺序存储一组有序的值,可以在列表的两端执行插入、删除和访问操作。场景:用作简单的消息队列。
  • 集合(Set):无序的唯一值集合。场景:实现抽奖,文章的点赞、评论。
  • 有序集合(Sorted Set):可以根据分数对成员进行排序,同时保持唯一性。场景:实现体育赛事排行榜,游戏积分榜,热销商品排行榜。
  • Windows 下安装 redis 参考教程

3.C# 具体使用介绍(Nuget)

StackExchange.Redis

StackExchange.Redis 是一个.NET 平台上的高性能、异步的 Redis 客户端库,由 StackExchange 团队开发。
仓库地址:https://github.com/StackExchange/StackExchange.Redis
文档地址:https://stackexchange.github.io/StackExchange.Redis/Basics
  1. using StackExchange.Redis;
  2. ...
  3. ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
  4. IDatabase db = redis.GetDatabase();
  5. string value = "abcdefg";
  6. db.StringSet("mykey", value);
  7. ...
  8. string value = db.StringGet("mykey");
  9. Console.WriteLine(value); // writes: "abcdefg"
  10. ...
复制代码
小插曲:StackExchange.Redis Timeout 确实很烦...... 但是可以使用下面两款 redis Nuget 包
FreeRedis

是基于 .NET 的 redis 客户端,  CSRedisCore 是 .NETFramework 4.0 及以上 访问 redis-server 的客户端组件,也是 FreeSql 作者早年发布的 nuget 版本。后来重构了更简易的 FreeRedis,目前推荐大家使用 FreeRedis,支持几乎所有 .NET 平台和 AOT。
官方文档:https://freesql.net/guide/freeredis.html
仓库地址:https://github.com/2881099/FreeRedis
  1. public static RedisClient cli = new RedisClient("127.0.0.1:6379,password=123,defaultDatabase=13");
  2. cli.Serialize = obj => JsonConvert.SerializeObject(obj);
  3. cli.Deserialize = (json, type) => JsonConvert.DeserializeObject(json, type);
  4. cli.Notice += (s, e) => Console.WriteLine(e.Log); //print command log
  5. cli.Set("key1", "value1");
  6. cli.MSet("key1", "value1", "key2", "value2");
  7. string value1 = cli.Get("key1");
  8. string[] vals = cli.MGet("key1", "key2");
复制代码
NewLife.Redis

是一个 Redis 客户端组件,以高性能处理大数据实时计算为目标。Redis 协议基础实现 Redis/RedisClient 位于[X 组件],本库为扩展实现,主要增加列表结构、哈希结构、队列等高级功能。
推荐用法
  1. public static class RedisHelper
  2. {
  3.     /// <summary>
  4.     /// Redis实例
  5.     /// </summary>
  6.     public static FullRedis redisConnection { get; set; } = new FullRedis("127.0.0.1:6379", "123456", 4);
  7. }
  8. Console.WriteLine(RedisHelper.redisConnection.Keys);
复制代码
基础用法(增,删,改,查)
  1. // 实例化Redis,默认端口6379可以省略,密码有两种写法
  2. //var rds = new FullRedis("127.0.0.1", null, 7);
  3. var rds = new FullRedis("127.0.0.1:6379", "pass", 7);
  4. //var rds = new FullRedis();
  5. //rds.Init("server=127.0.0.1:6379;password=pass;db=7");
  6. rds.Log = XTrace.Log;
  7. var rds = new FullRedis("127.0.0.1", null, 7);
  8. rds.Log = XTrace.Log;
  9. rds.ClientLog = XTrace.Log; // 调试日志。正式使用时注释
  10. var user = new User { Name = "NewLife", CreateTime = DateTime.Now };
  11. rds.Set("user", user, 3600);
  12. var user2 = rds.Get<User>("user");
  13. XTrace.WriteLine("Json: {0}", user2.ToJson());
  14. XTrace.WriteLine("Json: {0}", rds.Get<String>("user"));
  15. if (rds.ContainsKey("user")) XTrace.WriteLine("存在!");
  16. rds.Remove("user");
  17. //----------------执行结果:----------------------------------------
  18. 14:14:25.990  1 N - SELECT 7
  19. 14:14:25.992  1 N - => OK
  20. 14:14:26.008  1 N - SETEX user 3600 [53]
  21. 14:14:26.021  1 N - => OK
  22. 14:14:26.042  1 N - GET user
  23. 14:14:26.048  1 N - => [53]
  24. 14:14:26.064  1 N - GET user
  25. 14:14:26.065  1 N - => [53]
  26. 14:14:26.066  1 N - Json: {"Name":"NewLife","CreateTime":"2018-09-25 14:14:25"}
  27. 14:14:26.067  1 N - EXISTS user
  28. 14:14:26.068  1 N - => 1
  29. 14:14:26.068  1 N - 存在!
  30. 14:14:26.069  1 N - DEL user
  31. 14:14:26.070  1 N - => 1
复制代码
ServiceStack.Redis (收费)

是一个简单、高性能且功能丰富的 Redis C# 客户端,具有原生支持和 高级抽象,用于序列化 POCO 和支持原生同步和异步 API 的复杂类型。
  1. 会有一些破解版本~ 嘿嘿
  2. 官方地址:https://docs.servicestack.net/redis/
  3. 破解版本文章参考链接:https://www.cnblogs.com/nxahkm/p/14242143.html
复制代码
基础用法
  1. var redis = new RedisManagerPool("localhost:6379");
  2. using (var client = redis.GetClient())
  3. {
  4.     client.Set("key", "value");
  5.     var result = client.Get<string>("key");
  6.     Console.WriteLine(result);
  7.     client.SetEntryInHash("hash", "field1", "value1");
  8.     client.SetEntryInHash("hash", "field2", "value2");
  9.     var hashResult = client.GetAllEntriesFromHash("hash");
  10.     foreach(var entry in hashResult)
  11.     {
  12.         Console.WriteLine(entry.Key + " : " + entry.Value);
  13.     }
  14. }
复制代码
4.Redis 常用面试问题以及回答


  • 什么是Redis?

    • Redis是一个开源的基于内存的数据结构存储系统,可以用作数据库、缓存和消息代理。它支持多种数据结构,包括字符串、哈希、列表、集合、有序集合等。

  • Redis与Memcached之间的区别是什么?
    Redis支持更多的数据结构(例如哈希、列表、集合、有序集合)而不仅仅是简单的键值对。

    • Redis支持持久化,可以将数据保存到磁盘上,而Memcached通常只存储在内存中。
    • Redis支持复制和高可用性功能,而Memcached不支持。
    • Redis提供了更多的功能,例如事务、Lua脚本、发布/订阅等。

  • Redis的数据结构有哪些?

    • 字符串(String)
    • 哈希(Hash)
    • 列表(List)
    • 集合(Set)
    • 有序集合(Sorted Set)

  • Redis如何实现持久化?

    • Redis支持两种持久化方式
    • 快照(Snapshotting):将内存中的数据保存到磁盘上,以快照的形式保存。
    • 日志(Journaling):通过持续记录操作日志的方式来保证数据的持久性。

  • Redis的过期策略是什么?

    • Redis使用定期删除和惰性删除两种策略来处理过期键:
    • 定期删除:Redis默认每秒检查一定数量的过期键,并删除其中的过期键。
    • 惰性删除:当访问一个过期键时,Redis会先删除该过期键,然后返回给客户端一个错误。

  • Redis的主从复制是什么?如何设置?

    • 主从复制是指将一个Redis服务器的数据复制到其他Redis服务器的过程。其中一个Redis服务器作为主节点(master),负责处理写操作,而其他服务器作为从节点(slave),负责复制主节点的数据。这样可以实现数据的备份、读写分离以及提高系统的可用性。
    • 要设置主从复制,首先需要在从节点的配置文件中指定主节点的地址和端口,然后启动从节点。从节点会连接到主节点并发送SYNC命令,主节点收到SYNC命令后将数据发送给从节点,从节点接收到数据后进行初始化,然后开始进行增量复制。

  • Redis的数据淘汰策略有哪些?

    • Redis提供以下几种数据淘汰策略:
    • LRU(Least Recently Used):删除最近最少使用的键。
    • LFU(Least Frequently Used):删除最不经常使用的键。
    • TTL(Time To Live):设置键的过期时间,当键过期时自动删除。

  • Redis事务是什么?

    • Redis事务是一组命令的集合,这些命令会按照顺序执行,且在执行事务期间不会被其他客户端的命令所打断。Redis事务使用MULTI、EXEC、DISCARD和WATCH命令来实现。

  • Redis的发布与订阅是如何工作的?

    • Redis的发布与订阅功能允许客户端订阅一个或多个频道,当有消息发布到被订阅的频道时,所有订阅了该频道的客户端都会接收到该消息。

  • Redis的哨兵(Sentinel)是什么?它的作用是什么?

    • Redis的哨兵是用于监控和管理Redis集群的组件。它的作用是监控主节点和从节点的状态,并在主节点宕机时自动进行故障转移,确保集群的高可用性和容错性。

  • Redis的持久化机制中,RDB(快照)和AOF(日志)两种方式各有什么优缺点?

    • RDB:

      • 优点:RDB快照方式将整个数据库的状态保存在磁盘上,适合用于备份和恢复。由于是将整个数据库保存在一个文件中,恢复速度较快。
      • 缺点:RDB方式可能会丢失最后一次持久化之后的数据,因为数据只在指定的时间间隔内进行持久化。

    • AOF:

      • 优点:AOF日志记录了每次写操作,可以确保即使Redis宕机,也可以通过重新执行AOF文件来恢复数据。这样可以更好地保证数据的完整性。
      • 缺点:AOF日志文件可能会变得非常大,因为每个写操作都会被记录下来,可能会影响性能。此外,AOF恢复的速度可能会比RDB方式慢一些。


  • Redis的集群模式是如何工作的?

    • Redis集群模式通过分片(Sharding)来实现数据的水平扩展。在Redis集群中,数据被分成多个槽(slot),每个槽可以存储一个键值对。客户端根据键的哈希值将键值对映射到相应的槽中,然后将命令发送到负责该槽的节点上进行处理。通过这种方式,Redis集群可以实现数据的分布式存储和负载均衡。

  • Redis的主从复制过程中可能出现的数据丢失问题如何解决?

    • 在Redis的主从复制中,可能会出现主节点宕机导致数据丢失的情况。为了解决这个问题,可以使用Redis的持久化机制来确保数据不会丢失。通过配置主节点和从节点的持久化方式(如AOF方式),可以在主节点宕机时,从节点仍然可以通过AOF文件来恢复数据。此外,还可以通过设置主从节点之间的复制延迟来减少数据丢失的风险。

  • Redis的Lua脚本是如何执行的?

    • Redis支持通过Lua脚本来执行一组原子操作。客户端可以通过EVAL命令将Lua脚本发送到Redis服务器执行。Redis服务器会将Lua脚本加载到内存中编译成字节码,并执行脚本中的命令。在执行Lua脚本期间,Redis会将脚本作为一个原子操作进行处理,确保脚本中的所有命令要么全部执行成功,要么全部执行失败。

缓存雪崩
缓存雪崩是指在缓存中大量的键在同一时间失效或者被删除,导致大量请求直接访问数据库,从而造成数据库负载过高的情况。以下是处理缓存雪崩问题的一些常见方法:

  • 设置不同的过期时间
    将缓存中的键的过期时间设置成随机的,避免大量键在同一时间失效,从而分散了缓存失效时对数据库的访问压力。
  • 使用热点数据预热
    在系统启动或者低峰期,提前加载热点数据到缓存中,避免在高峰期由于缓存未命中而直接访问数据库。
  • 缓存数据异步更新
    当缓存中的数据过期时,不要立即删除缓存,而是异步地更新缓存数据,避免因为缓存过期而导致大量请求直接访问数据库。
  • 限流措施
    对于缓存失效时的请求进行限流,限制每秒的请求量,避免大量请求同时涌入导致数据库负载过高。
  • 使用多级缓存
    使用多级缓存(例如本地缓存、分布式缓存)来减轻对数据库的访问压力。当一级缓存失效时,可以先查询二级缓存,避免直接访问数据库。
  • 数据库容灾备份
    在数据库层面采取容灾备份措施,如读写分离、主从复制等,确保即使在缓存雪崩时数据库也能正常提供服务。
  • 使用锁机制
    在更新缓存时,可以使用锁机制保证同一时间只有一个线程更新缓存,避免出现并发更新导致的数据库访问压力过高。
缓存击穿
缓存击穿是指在缓存中缓存了一个过期时间非常短的数据,当有大量请求同时访问这个数据时,会导致大量请求都穿过缓存直接访问数据库,从而造成数据库负载过高的情况。以下是处理缓存击穿问题的一些常见方法:

  • 使用互斥锁(Mutex)
    在获取缓存数据时,先尝试获取一个互斥锁,如果获取成功则从缓存中读取数据,如果获取失败则说明缓存中没有数据,此时再去查询数据库,并将查询结果写入缓存。这样可以确保只有一个线程去查询数据库,避免了大量线程同时穿透缓存直接访问数据库。
  • 设置热点数据永不过期
    对于一些热点数据,可以将其永不过期,或者设置一个较长的过期时间,以确保即使缓存失效,也能够保持一段时间内的可用性,避免了缓存击穿的问题。
  • 缓存预热
    在系统启动或者低峰期,提前将热点数据加载到缓存中,避免在高峰期由于缓存未命中而导致大量请求直接访问数据库。
  • 使用缓存穿透保护机制
    在业务逻辑层对请求进行拦截和检查,如果检测到查询不存在的数据的请求,可以直接拒绝或者返回预设的空值,避免请求直接访问数据库。
  • 限制查询频率
    对于查询不存在的数据的请求,可以限制其查询频率,例如设置一个时间窗口内只允许查询一次。这样可以有效地减少恶意请求的影响,并且降低数据库负载。
  • 使用熔断机制
    当缓存击穿情况发生时,可以使用熔断机制暂时关闭缓存,直接让请求访问数据库,避免数据库负载过高。
缓存穿透
缓存穿透是指恶意请求或者大量请求查询一个不存在的数据,导致请求都到达数据库,从而使数据库负载过高。以下是处理缓存穿透的一些常见方法:

  • 缓存空值(缓存零值)
    当数据库中查询结果为空时,可以将这个空结果也缓存起来,但是设置一个较短的过期时间,避免缓存过期时间过长导致的缓存雪崩问题。这样可以确保相同的请求在短时间内都会被命中缓存,减少对数据库的访问压力。
  • 布隆过滤器(Bloom Filter)
    布隆过滤器是一种数据结构,可以快速判断一个元素是否存在于集合中。可以在缓存层之前使用布隆过滤器过滤掉不存在的键,从而减少对数据库的查询请求。如果布隆过滤器判断请求的键不存在,可以直接返回一个预设的空值,而不必查询数据库。
  • 使用缓存穿透保护机制
    在业务逻辑层对请求进行拦截和检查,如果检测到恶意请求或者大量查询不存在的数据的请求,可以直接拒绝或者返回预设的空值,避免请求直接访问数据库。
  • 限制查询频率
    对于查询不存在的数据的请求,可以限制其查询频率,例如设置一个时间窗口内只允许查询一次。这样可以有效地减少恶意请求的影响,并且降低数据库负载。
  • 使用热点数据预热
    针对业务中可能会频繁查询的热点数据,在系统启动或者定时任务中预先将这些数据加载到缓存中,避免因为缓存未命中而直接访问数据库。这样可以提高缓存命中率,减少对数据库的请求。
  • C# Bloom Filter Code
  1. 参考文章1.:https://www.cnblogs.com/mushroom/p/4556801.html
  2.    
  3. 参考文章2:https://www.cnblogs.com/eventhorizon/p/16414593.html
  4. github学习地址:https://github.com/eventhorizon-cli/EventHorizon.BloomFilter
复制代码
5.建议及经验分享

建议


  • 缓存热点数据
    使用Redis来缓存常用且频繁访问的数据,例如用户会话信息、页面片段、常用查询结果等。这样可以减轻数据库的压力,提高系统的性能和响应速度。
  • 分布式锁
    在分布式系统中,使用Redis的分布式锁机制来实现对共享资源的并发访问控制。通过SETNX命令或者Redlock算法等方式,确保同一时间只有一个线程可以对关键资源进行操作,避免并发冲突和数据不一致性问题。
  • 消息队列
    使用Redis的发布/订阅功能或者列表数据结构来实现消息队列,用于异步处理任务、解耦系统各个组件之间的依赖关系,提高系统的可扩展性和稳定性。
  • 会话管理
    使用Redis来存储用户会话信息,实现分布式会话管理,从而实现多台服务器之间的会话共享和负载均衡。通过设置会话过期时间和定期刷新会话,确保会话数据的安全性和一致性。
  • 计数器和排行榜
    使用Redis的计数器和有序集合等数据结构来实现计数器和排行榜功能,用于统计访问量、点赞数、排名等信息。这样可以快速地获取热门数据和实时统计结果。
  • 分布式缓存
    在分布式系统中,使用Redis作为分布式缓存,通过集群模式和主从复制等特性来实现数据的高可用性和水平扩展,提高系统的稳定性和可靠性。
  • 持久化设置
    根据业务需求和数据重要性,选择合适的持久化方式(如RDB快照、AOF日志)和持久化策略,确保数据在发生故障时能够快速恢复并且不会丢失。
  • 监控和调优
    定期监控Redis的性能指标和使用情况,如内存使用率、连接数、命中率等,并根据监控结果进行调优和优化,保证Redis的稳定运行和高效利用。
Redis 经验分享


  • 在 Linux 上多实例部署,实例个数等于处理器个数,各实例最大内存直接为本机物理内存,避免单个实例内存撑爆
  • 把海量数据(10 亿+)根据 key 哈希(Crc16/Crc32)存放在多个实例上,读写性能成倍增长
  • 采用二进制序列化,而非常见 Json 序列化
  • 合理设计每一对 Key 的 Value 大小,包括但不限于使用批量获取,原则是让每次网络包控制在 1.4k 字节附近,减少通信次数
  • Redis 客户端的 Get/Set 操作平均耗时 200~600us(含往返网络通信),以此为参考评估网络环境和 Redis 客户端组件
  • 使用管道 Pipeline 合并一批命令
  • Redis 的主要性能瓶颈是序列化、网络带宽和内存大小,滥用时处理器也会达到瓶颈
  • 其它可查优化技巧 以上经验,源自于 300 多个实例 4T 以上空间一年多稳定工作的经验,并按照重要程度排了先后顺序,可根据场景需要酌情采用!
ShareFlow

欢迎关注“ShareFlow”,精选的内容和深入的分析,共同探讨、学习、创造,建立有意义的连接,致力于成为您的信息灯塔。


来源:https://www.cnblogs.com/goodluckily/p/18113045
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
来自手机

举报 回复 使用道具