【Redis】.Net Core 面试破冰 【Redis】. Redis 面试破冰 Core Net
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。
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 常用面试问题以及回答
-
什么是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.: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.建议及经验分享
建议
-
缓存热点数据:
使用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”,精选的内容和深入的分析,共同探讨、学习、创造,建立有意义的连接,致力于成为您的信息灯塔。