文章目录
- 1、Redis底层协议
- 2、Redis的热Key问题如何解决
- 3、Redis是单线程还是多线程
- 4、 什么是脑裂问题?
- 5、redis集群会有写操作丢失吗?
- 6、什么是 Redis 的 Pipeline?它有哪些优点?
- 7、redis主从复制、哨兵机制、集群的理解
- 8、缓存双写不一致的问题产生原因
- 9、Redis相比memcached有哪些优势?
- 10、Redis大key问题
- 11、高并发场景下我们如何保证幂等性?
- 12、Redis如何保证与数据库的双写一致性
1、Redis底层协议
Redis底层协议是一种轻量级、高效率的文本协议,称作RESP(Redis Serialization Protocol)。
RESP协议之所以快速,是因为它采用了二进制数据传输,将数据序列化并进行压缩。
所以可以减少网络传输时的数据大小,同时RESP协议还支持批量操作,可以一次性处理多个命令,从而提高了Redis的吞吐量。此外,RESP协议还支持管道技术,即将多个命令一起发送到Redis服务器,并且可以异步地获取结果,这可以进一步提高Redis的性能。
2、Redis的热Key问题如何解决
-
使用缓存预热:
在系统启动或者低峰期,可以通过缓存预热的方式将热 Key 提前加载到缓存中,减少系统高峰期对热 Key 的访问压力。 -
数据分片:
将热 Key 分散到多个节点上,采用数据分片的方式来减轻单个节点的负载。可以根据一致性哈希算法等方式,将 Key 均匀分布到多个节点上,避免热 Key 集中在单个节点导致性能瓶颈。 -
使用多级缓存:
在架构设计上,可以考虑使用多级缓存,将部分热 Key 存储在本地内存或者分布式缓存中,减轻 Redis 的压力。例如,可以将热 Key 存储在本地内存中,而冷 Key 存储在 Redis 中,通过本地内存进行快速响应,减少对 Redis 的访问。 -
限流和降级:
针对热 Key,可以采取限流和降级策略,对其访问进行限制或者降级处理,防止热 Key 的高访问频率对系统造成影响。例如,可以对热 Key 的请求进行限流,或者在负载过高时对热 Key 的访问进行降级,返回默认值或者错误信息。 -
监控和报警:
建立监控系统,实时监控 Redis 的状态和性能指标,及时发现热 Key 问题并采取相应的应对措施。可以通过设置合适的阈值和报警机制,对 Redis 的性能异常进行及时报警和处理。
3、Redis是单线程还是多线程
Redis 采用的是单线程模型
。通常说得单线程。主要指的是 Redis 对外提供的键值存储服务的主要流程是单线程的,即网络 I/O 和数据读写是由单个线程
来完成的。
这样设计可以避免多线程之间的竞争条件和锁开销,提高了访问共享数据的效率。然而,除了对外提供的键值存储服务,Pedis 在某些功能上会使用额外的线程来执行,比如持久化、异步删除和集群数据同步等。这些功能需要在后台执行,不参与主要的网络 I/0 和数据处理。因此,严格来说,Redis 并不是完全单线程。
Redis 6.x 版本引入了多线程的 I/O 处理
,但这并不改变其核心数据处理的单线程特性。多线程 I/O 处理主要用于处理网络 I/O,以提高网络吞吐量,而核心的数据操作仍然由单个主线程处理。
总的来说,Redis 的单线程模型是其设计的一个核心特点,它使得 Redis 在保持简单性的同时,能够提供极高的性能。
4、 什么是脑裂问题?
脑裂问题是指由于网络分区或节点故障,导致集群中的多个节点失去联系,并各自认为自己是主节点,继续接收和处理客户端的读写请求。这会导致数据的不一致和潜在的数据丢失。
Redis 脑裂(Split-Brain)问题通常发生在 Redis 集群(Cluster)或哨兵模式(Sentinel)中。
解决脑裂问题的方法:
-
配置 quorum:
在 Redis 哨兵模式中,可以通过配置 quorum 来减少脑裂问题的发生。quorum 是指在进行故障转移时,需要同意故障转移的最少哨兵数量。配置合理的 quorum 数量,可以减少不一致故障转移的风险。 -
增加故障检测和恢复时间:
通过增加故障检测和恢复时间,可以减少因临时网络故障导致的脑裂。例如,可以通过调整 down-after-milliseconds 和 failover-timeout 等参数来延长检测时间,从而减少不必要的故障转移。 -
自动故障恢复:
使用 Redis 的自动故障恢复机制,可以在脑裂发生后尽快恢复正常状态。例如,在 Redis 集群模式中,可以配置自动重新分片(resharding)和数据迁移(data migration)来恢复数据一致性。 -
使用集群管理工具:
使用 Redis 集群管理工具,如 Redis Cluster Manager(redis-trib),可以帮助监控和管理集群状态,及时发现和处理脑裂问题。 -
网络可靠性:
通过提高网络的可靠性,减少网络分区的发生,可以从根本上减少脑裂问题。可以考虑使用更加稳定和可靠的网络基础设施,或者配置冗余网络链路。 -
数据持久化和备份:
定期进行数据持久化和备份,可以在脑裂问题发生时,快速恢复数据,减少数据丢失和不一致的影响。
实践示例:配置 quorum
在 Redis 哨兵模式下,可以通过 sentinel.conf 文件配置 quorum 参数。例如:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
在上述配置中,monitor 参数中的最后一个数字 2 就是 quorum 值,表示至少需要 2 个哨兵同意才能进行故障转移。
通过合理配置和管理,可以有效减少 Redis 脑裂问题的发生,确保数据的一致性和系统的稳定性。
5、redis集群会有写操作丢失吗?
写操作丢失可能发生在以下几种情况下:
Redis 的默认复制方式是异步复制,
主节点在写操作后立即返回成功,而从节点在稍后时间进行同步。在主节点发生故障且从节点尚未完成同步之前,这部分写操作会丢失。
为了减少写操作丢失的可能性,可以采取以下措施:
- 定期监测集群状态,确保主从节点之间的复制正常进行;
- 设置合理的持久化策略,将数据写入磁盘或使用AOF模式以便数据恢复;
- 配置复制偏移量和 ACK:
使用 min-slaves-to-write 和 min-slaves-max-lag 配置参数,确保至少有一定数量的从节点完成复制并且延迟在允许范围内,才能继续接受写操作。
# 设置至少需要一个从节点同步完成,才能进行写操作
min-replicas-to-write 1
# 设置从节点同步延迟最大值为10秒
min-replicas-max-lag 10
通过上述配置,确保在集群环境下,只有在至少一个从节点同步完成且延迟在10秒以内时,主节点才会继续接受写操作,从而减少写操作丢失的风险。
重要注意的是、Redis集鞋的主从复制模型无法完全消除与提作丢失的风险,但通过配置和监控的合理手段,可以最大眼度地峰任写操作丢失的可能性,保道教相的安全性和可靠性。
6、什么是 Redis 的 Pipeline?它有哪些优点?
Redis 的 Pipeline 是一种通过减少网络通信延迟来提高性能的技术。它允许客户端在发送多个命令之前不等待每个命令的响应,而是将这些命令一次性发送到服务器,然后一次性接收所有响应。这种批处理技术
可以显著提高吞吐量,特别是在网络延迟较高的环境下。
优点包括:
- 减少网络延迟:通过减少网络往返次数,Pipeline 可以显著降低命令的整体执行时间。
- 提高吞吐量:由于减少了网络等待时间,服务器可以更快地处理更多的命令,提高系统的整体吞吐量。
在 Jedis 中,可以通过 Pipeline 和事务结合使用来确保命令的原子性:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
public class RedisPipelineWithTransactionExample {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
// 启用事务和 Pipeline
Pipeline pipeline = jedis.pipelined();
pipeline.multi();
// 批量添加命令
pipeline.set("key1", "value1");
pipeline.set("key2", "value2");
pipeline.set("key3", "value3");
pipeline.get("key1");
pipeline.get("key2");
pipeline.get("key3");
// 执行所有命令
pipeline.exec();
pipeline.syncAndReturnAll().forEach(System.out::println);
jedis.close();
}
}
7、redis主从复制、哨兵机制、集群的理解
- 主从复制:冗余备份、读分担 (一个主,并不高可用,主故障需人工干预)
- 哨兵机制:节点监听,自动故障转移,高可用 (哨兵集群是哨兵多个,主还是一个,不能解决单节点的写和存储能力)
- 集群:多个主从节点群组成的分布式服务器群,主节点负责读写,从节点提供冗余备份和故障恢复,自动故障转移,负载均衡、高可用、可线性扩展(奇数节点,至少3个主节点)
详解参看:Redis主从与哨兵架构详解
Redis集群之高可用可水平扩展
8、缓存双写不一致的问题产生原因
1)缓存和数据库之间的双写不一致问题主要由以下原因引起:
- 更新顺序问题:
如果在写请求时,先更新数据库再更新缓存,而在两次操作之间发生了读请求,读请求可能会获取到旧的缓存数据。
反之,如果先更新缓存再更新数据库,同样存在更新缓存后但数据库未更新时发生读请求,可能获取到旧的数据库数据。 - 网络延迟:
网络延迟可能导致更新操作未能及时完成,导致缓存和数据库中的数据不同步。 - 系统故障:
在更新操作进行中,如果系统故障或网络中断,可能会导致更新操作未能完成。
缓存双写不一致的解决方案
2)为了减少和解决缓存双写不一致问题,可以采用以下策略:
延时双删策略
:
在更新数据库前删除缓存,等待一段时间后再次删除缓存。
这种方式可以减少因为缓存和数据库不同步导致的不一致问题。
// 更新数据库
updateDatabase();
// 删除缓存
deleteCache();
// 延时删除缓存
Thread.sleep(delayTime);
deleteCache();
示例:延时双删策略
假设在 Java 应用中使用 Redis 作为缓存,MySQL 作为数据库,延时双删策略可以这样实现:
public class CacheService {
private RedisTemplate<String, Object> redisTemplate;
private JdbcTemplate jdbcTemplate;
// 更新数据的示例方法
public void updateData(String key, String value) {
// 删除缓存
redisTemplate.delete(key);
// 更新数据库
jdbcTemplate.update("UPDATE my_table SET value = ? WHERE key = ?", value, key);
// 延时后再次删除缓存
try {
Thread.sleep(100); // 100ms 的延时,具体时间根据实际情况调整
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
redisTemplate.delete(key);
}
// 读取数据的示例方法
public String getData(String key) {
// 先读取缓存
String value = redisTemplate.opsForValue().get(key);
if (value == null) {
// 缓存未命中,从数据库读取
value = jdbcTemplate.queryForObject("SELECT value FROM my_table WHERE key = ?", String.class, key);
// 更新缓存
redisTemplate.opsForValue().set(key, value);
}
return value;
}
}
在这个示例中,通过在更新数据库后延时删除缓存,可以减少缓存和数据库数据不一致的问题。延时的具体时间需要根据系统的实际情况进行调整,以确保大部分情况下数据能同步到缓存。
缓存双写不一致问题是分布式系统中常见的问题,通过合理的缓存更新策略、延时双删策略以及分布式事务等方法,可以有效减少不一致问题的发生。在实际应用中,选择合适的解决方案需要结合具体的业务场景和系统特点。
9、Redis相比memcached有哪些优势?
Redis 和 Memcached 都是流行的内存缓存系统,但它们有各自的特点和优势。以下是 Redis 相比于 Memcached 的一些主要优势:
-
数据类型丰富
Redis 支持多种数据类型,不仅仅是简单的字符串值,还包括:
字符串(String)
哈希(Hash)
列表(List)
集合(Set)
有序集合(Sorted Set)
位图(Bitmap)
HyperLogLog
地理空间索引(Geo)
流(Stream)
Memcached 仅支持简单的键值对存储(字符串类型),这使得 Redis 在处理复杂数据结构和操作时更为灵活和强大。
memcached的单value值容量只有1M,而redis则最大支持至512M -
持久化
Redis 提供数据持久化功能,可以将内存中的数据持久化到磁盘上,有以下两种持久化方式:
RDB(Redis Database)快照:定期将内存中的数据快照保存到磁盘。
AOF(Append Only File)日志:记录每个写操作,提供更高的数据安全性。
Memcached 是一个纯内存存储系统,不支持持久化,因此在系统重启或故障时,所有数据都会丢失。 -
主从复制和高可用性
Redis 支持主从复制(master-slave replication),可以将数据从主节点复制到一个或多个从节点,提供读扩展和数据冗余。此外,Redis 提供 Redis Sentinel 用于监控 Redis 实例并自动进行故障转移,确保系统的高可用性。
Memcached 不原生支持复制和高可用性,通常需要依赖外部工具和框架来实现这些功能。 -
集群模式
Redis 提供原生的集群模式(Redis Cluster),允许将数据分布在多个节点上,支持水平扩展和自动分片(sharding),并且具有内置的故障转移机制。
Memcached 通过客户端实现分布式存储,虽然也可以实现数据分片,但缺乏内置的集群管理和自动故障转移功能。 -
脚本支持
Redis 支持使用 Lua 脚本进行复杂的操作,允许将多个命令打包在一个事务中执行,确保操作的原子性。Lua 脚本在 Redis 中执行速度非常快,因为它们直接在 Redis 服务器端运行。
Memcached 不支持脚本功能,无法在服务器端执行复杂的操作。 -
事务支持
Redis 支持基本的事务功能,可以通过 MULTI、EXEC、DISCARD 和 WATCH 命令来实现事务操作,确保一组命令的原子性执行。
Memcached 不支持事务操作,只能执行简单的键值对操作。 -
发布/订阅功能
Redis 提供发布/订阅(Pub/Sub)功能,可以用于消息通知、实时消息系统等场景,增强了 Redis 的功能多样性。
Memcached 不支持发布/订阅功能。 -
内存效率和存储空间管理
Redis 提供更灵活的内存管理和压缩策略,例如通过 maxmemory 参数可以控制最大内存使用量,并支持多种内存淘汰策略(如 LRU、LFU 等)。此外,Redis 提供更紧凑的数据结构,可以更高效地利用内存。
Memcached 的内存管理较为简单,主要基于 slab 分配机制,内存利用率在某些场景下可能不如 Redis 高效。
总结
Redis 在功能和特性上比 Memcached 更加丰富和灵活,适用于更多复杂的场景。具体选择哪种缓存系统,取决于应用的需求和场景。如果只需要一个简单的内存缓存系统,Memcached 可能是一个很好的选择。但如果需要更多高级功能和特性,如持久化、高可用性、集群、复杂数据结构等,Redis 是更优的选择。
10、Redis大key问题
11、高并发场景下我们如何保证幂等性?
- 数据库唯一主键实现幂等性
其实现方式是使用分布式ID充当主键,不使用MySQL中的自增主键 - 乐观锁实现幂等性
在表中增加版本号标识,只有版本号标识一直才更新成功 - 分布式锁
简单来说就是分布式的排他锁,但是我们可以控制锁的粒度以提高程序的执行性能 - 获取token
a.服务端提供获取 Token 的接口,请求前客户端调用接口获取 Token
b.然后将该串存入 Redis 数据库中,以该 Token 作为 Redis 的键 (注意设置过期时间)
c.将 Token 返回到客户端,在执行业务请求带上该 Token
d.服务端接收到请求后根据 Token 到 Redis 中查找该 key 是否存在 (注意原子性)e.如果存在就将该 key 删除,然后正常执行业务逻辑。如果不存在就抛异常,返回重复提交的错误信息,
12、Redis如何保证与数据库的双写一致性
缓存可以提升性能。减轻数据库压力,在获取这部分好处的同时,它却带来了一些新的问题,缓存和数据库之间的数据一致性问题。
首先我们来看看一致性:
- 强一致性
- 弱一致性
解决双写一致性方案:
- 延迟双删
- 延迟双删策略是分布式系统中数据库存储和缓存数据保持一致性的常用策略,但它不是强一致。
- 实现思路: 也是非常简单的,先删除缓存然后更新DB在最后延迟 N 秒去再去执行一次缓存删除
- 弊端: 小概率会出现不一致情况、耦合程度高
- 通过MQ进行重试删除
- 更新完DB之后进行删除,如果删除失败则向MQ发送一条消息,然后消费者不断进行删除尝试。
- binlog异步删除
- 实现思路: 低耦合的解决方案是使用canal。canal伪装成mysql的从机,监听主机mysql的二进制文件,当数据发生变化时发送给MQ。最终消费进行删除。