【Redis】.Net Core 面试破冰

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

using StackExchange.Redis;
...
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
IDatabase db = redis.GetDatabase();

string value = "abcdefg";
db.StringSet("mykey", value);
...
string value = db.StringGet("mykey");
Console.WriteLine(value); // writes: "abcdefg"
...

小插曲: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

public static RedisClient cli = new RedisClient("127.0.0.1:6379,password=123,defaultDatabase=13");
cli.Serialize = obj => JsonConvert.SerializeObject(obj);
cli.Deserialize = (json, type) => JsonConvert.DeserializeObject(json, type);
cli.Notice += (s, e) => Console.WriteLine(e.Log); //print command log

cli.Set("key1", "value1");
cli.MSet("key1", "value1", "key2", "value2");

string value1 = cli.Get("key1");
string[] vals = cli.MGet("key1", "key2");
NewLife.Redis

是一个 Redis 客户端组件,以高性能处理大数据实时计算为目标。Redis 协议基础实现 Redis/RedisClient 位于[X 组件],本库为扩展实现,主要增加列表结构、哈希结构、队列等高级功能。

推荐用法

public static class RedisHelper
{
    /// <summary>
    /// Redis实例
    /// </summary>
    public static FullRedis redisConnection { get; set; } = new FullRedis("127.0.0.1:6379", "123456", 4);
}
Console.WriteLine(RedisHelper.redisConnection.Keys);

基础用法(增,删,改,查)

// 实例化Redis,默认端口6379可以省略,密码有两种写法
//var rds = new FullRedis("127.0.0.1", null, 7);
var rds = new FullRedis("127.0.0.1:6379", "pass", 7);
//var rds = new FullRedis();
//rds.Init("server=127.0.0.1:6379;password=pass;db=7");
rds.Log = XTrace.Log;

var rds = new FullRedis("127.0.0.1", null, 7);
rds.Log = XTrace.Log;
rds.ClientLog = XTrace.Log; // 调试日志。正式使用时注释
var user = new User { Name = "NewLife", CreateTime = DateTime.Now };
rds.Set("user", user, 3600);
var user2 = rds.Get<User>("user");
XTrace.WriteLine("Json: {0}", user2.ToJson());
XTrace.WriteLine("Json: {0}", rds.Get<String>("user"));
if (rds.ContainsKey("user")) XTrace.WriteLine("存在!");
rds.Remove("user");

//----------------执行结果:----------------------------------------
14:14:25.990  1 N - SELECT 7
14:14:25.992  1 N - => OK
14:14:26.008  1 N - SETEX user 3600 [53]
14:14:26.021  1 N - => OK
14:14:26.042  1 N - GET user
14:14:26.048  1 N - => [53]
14:14:26.064  1 N - GET user
14:14:26.065  1 N - => [53]
14:14:26.066  1 N - Json: {"Name":"NewLife","CreateTime":"2018-09-25 14:14:25"}
14:14:26.067  1 N - EXISTS user
14:14:26.068  1 N - => 1
14:14:26.068  1 N - 存在!
14:14:26.069  1 N - DEL user
14:14:26.070  1 N - => 1
ServiceStack.Redis (收费)

是一个简单、高性能且功能丰富的 Redis C# 客户端,具有原生支持和 高级抽象,用于序列化 POCO 和支持原生同步和异步 API 的复杂类型。

会有一些破解版本~ 嘿嘿
官方地址:https://docs.servicestack.net/redis/
破解版本文章参考链接:https://www.cnblogs.com/nxahkm/p/14242143.html

基础用法

var redis = new RedisManagerPool("localhost:6379");
using (var client = redis.GetClient())
{
    client.Set("key", "value");
    var result = client.Get<string>("key");
    Console.WriteLine(result);

    client.SetEntryInHash("hash", "field1", "value1");
    client.SetEntryInHash("hash", "field2", "value2");
    var hashResult = client.GetAllEntriesFromHash("hash");
    foreach(var entry in hashResult)
    {
        Console.WriteLine(entry.Key + " : " + entry.Value);
    }
}
4.Redis 常用面试问题以及回答
  1. 什么是Redis?

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

    Redis支持更多的数据结构(例如哈希、列表、集合、有序集合)而不仅仅是简单的键值对。

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

    • 字符串(String)
    • 哈希(Hash)
    • 列表(List)
    • 集合(Set)
    • 有序集合(Sorted Set)
  4. Redis如何实现持久化?

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

    • Redis使用定期删除和惰性删除两种策略来处理过期键:
    • 定期删除:Redis默认每秒检查一定数量的过期键,并删除其中的过期键。
    • 惰性删除:当访问一个过期键时,Redis会先删除该过期键,然后返回给客户端一个错误。
  6. Redis的主从复制是什么?如何设置?

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

    • Redis提供以下几种数据淘汰策略:

    • LRU(Least Recently Used):删除最近最少使用的键。

    • LFU(Least Frequently Used):删除最不经常使用的键。

    • TTL(Time To Live):设置键的过期时间,当键过期时自动删除。

  8. Redis事务是什么?

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

    • Redis的发布与订阅功能允许客户端订阅一个或多个频道,当有消息发布到被订阅的频道时,所有订阅了该频道的客户端都会接收到该消息。
  10. Redis的哨兵(Sentinel)是什么?它的作用是什么?

    • Redis的哨兵是用于监控和管理Redis集群的组件。它的作用是监控主节点和从节点的状态,并在主节点宕机时自动进行故障转移,确保集群的高可用性和容错性。
  11. Redis的持久化机制中,RDB(快照)和AOF(日志)两种方式各有什么优缺点?

    • RDB:

      • 优点:RDB快照方式将整个数据库的状态保存在磁盘上,适合用于备份和恢复。由于是将整个数据库保存在一个文件中,恢复速度较快。

      • 缺点:RDB方式可能会丢失最后一次持久化之后的数据,因为数据只在指定的时间间隔内进行持久化。

    • AOF:

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

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

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

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

缓存雪崩

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

  1. 设置不同的过期时间

    将缓存中的键的过期时间设置成随机的,避免大量键在同一时间失效,从而分散了缓存失效时对数据库的访问压力。

  2. 使用热点数据预热

    在系统启动或者低峰期,提前加载热点数据到缓存中,避免在高峰期由于缓存未命中而直接访问数据库。

  3. 缓存数据异步更新

    当缓存中的数据过期时,不要立即删除缓存,而是异步地更新缓存数据,避免因为缓存过期而导致大量请求直接访问数据库。

  4. 限流措施

    对于缓存失效时的请求进行限流,限制每秒的请求量,避免大量请求同时涌入导致数据库负载过高。

  5. 使用多级缓存

    使用多级缓存(例如本地缓存、分布式缓存)来减轻对数据库的访问压力。当一级缓存失效时,可以先查询二级缓存,避免直接访问数据库。

  6. 数据库容灾备份

    在数据库层面采取容灾备份措施,如读写分离、主从复制等,确保即使在缓存雪崩时数据库也能正常提供服务。

  7. 使用锁机制

    在更新缓存时,可以使用锁机制保证同一时间只有一个线程更新缓存,避免出现并发更新导致的数据库访问压力过高。

缓存击穿

缓存击穿是指在缓存中缓存了一个过期时间非常短的数据,当有大量请求同时访问这个数据时,会导致大量请求都穿过缓存直接访问数据库,从而造成数据库负载过高的情况。以下是处理缓存击穿问题的一些常见方法:

  1. 使用互斥锁(Mutex)

    在获取缓存数据时,先尝试获取一个互斥锁,如果获取成功则从缓存中读取数据,如果获取失败则说明缓存中没有数据,此时再去查询数据库,并将查询结果写入缓存。这样可以确保只有一个线程去查询数据库,避免了大量线程同时穿透缓存直接访问数据库。

  2. 设置热点数据永不过期

    对于一些热点数据,可以将其永不过期,或者设置一个较长的过期时间,以确保即使缓存失效,也能够保持一段时间内的可用性,避免了缓存击穿的问题。

  3. 缓存预热

    在系统启动或者低峰期,提前将热点数据加载到缓存中,避免在高峰期由于缓存未命中而导致大量请求直接访问数据库。

  4. 使用缓存穿透保护机制

    在业务逻辑层对请求进行拦截和检查,如果检测到查询不存在的数据的请求,可以直接拒绝或者返回预设的空值,避免请求直接访问数据库。

  5. 限制查询频率

    对于查询不存在的数据的请求,可以限制其查询频率,例如设置一个时间窗口内只允许查询一次。这样可以有效地减少恶意请求的影响,并且降低数据库负载。

  6. 使用熔断机制

    当缓存击穿情况发生时,可以使用熔断机制暂时关闭缓存,直接让请求访问数据库,避免数据库负载过高。

缓存穿透

缓存穿透是指恶意请求或者大量请求查询一个不存在的数据,导致请求都到达数据库,从而使数据库负载过高。以下是处理缓存穿透的一些常见方法:

  1. 缓存空值(缓存零值)

    当数据库中查询结果为空时,可以将这个空结果也缓存起来,但是设置一个较短的过期时间,避免缓存过期时间过长导致的缓存雪崩问题。这样可以确保相同的请求在短时间内都会被命中缓存,减少对数据库的访问压力。

  2. 布隆过滤器(Bloom Filter)

    布隆过滤器是一种数据结构,可以快速判断一个元素是否存在于集合中。可以在缓存层之前使用布隆过滤器过滤掉不存在的键,从而减少对数据库的查询请求。如果布隆过滤器判断请求的键不存在,可以直接返回一个预设的空值,而不必查询数据库。

  3. 使用缓存穿透保护机制

    在业务逻辑层对请求进行拦截和检查,如果检测到恶意请求或者大量查询不存在的数据的请求,可以直接拒绝或者返回预设的空值,避免请求直接访问数据库。

  4. 限制查询频率

    对于查询不存在的数据的请求,可以限制其查询频率,例如设置一个时间窗口内只允许查询一次。这样可以有效地减少恶意请求的影响,并且降低数据库负载。

  5. 使用热点数据预热

    针对业务中可能会频繁查询的热点数据,在系统启动或者定时任务中预先将这些数据加载到缓存中,避免因为缓存未命中而直接访问数据库。这样可以提高缓存命中率,减少对数据库的请求。

  6. C# Bloom Filter Code

参考文章1.:https://www.cnblogs.com/mushroom/p/4556801.html
   
参考文章2:https://www.cnblogs.com/eventhorizon/p/16414593.html
github学习地址:https://github.com/eventhorizon-cli/EventHorizon.BloomFilter
5.建议及经验分享
建议
  1. 缓存热点数据

    使用Redis来缓存常用且频繁访问的数据,例如用户会话信息、页面片段、常用查询结果等。这样可以减轻数据库的压力,提高系统的性能和响应速度。

  2. 分布式锁

    在分布式系统中,使用Redis的分布式锁机制来实现对共享资源的并发访问控制。通过SETNX命令或者Redlock算法等方式,确保同一时间只有一个线程可以对关键资源进行操作,避免并发冲突和数据不一致性问题。

  3. 消息队列

    使用Redis的发布/订阅功能或者列表数据结构来实现消息队列,用于异步处理任务、解耦系统各个组件之间的依赖关系,提高系统的可扩展性和稳定性。

  4. 会话管理

    使用Redis来存储用户会话信息,实现分布式会话管理,从而实现多台服务器之间的会话共享和负载均衡。通过设置会话过期时间和定期刷新会话,确保会话数据的安全性和一致性。

  5. 计数器和排行榜

    使用Redis的计数器和有序集合等数据结构来实现计数器和排行榜功能,用于统计访问量、点赞数、排名等信息。这样可以快速地获取热门数据和实时统计结果。

  6. 分布式缓存

    在分布式系统中,使用Redis作为分布式缓存,通过集群模式和主从复制等特性来实现数据的高可用性和水平扩展,提高系统的稳定性和可靠性。

  7. 持久化设置

    根据业务需求和数据重要性,选择合适的持久化方式(如RDB快照、AOF日志)和持久化策略,确保数据在发生故障时能够快速恢复并且不会丢失。

  8. 监控和调优

    定期监控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”,精选的内容和深入的分析,共同探讨、学习、创造,建立有意义的连接,致力于成为您的信息灯塔。
ShareFlow

posted @ 2024-04-03 16:45  雨太阳  阅读(200)  评论(0编辑  收藏  举报