Redis面试专题(未完待续。。。)
什么是 Redis?
Redis是一个开源的key-value存储系统,支持多种数据结构,包括字符串、列表、集合和有序集合等
Redis 与其他key - value 缓存产品有以下三个特点?
Redis与其他key-value缓存产品相比,具有以下三个特点:
a. 高性能:Redis采用单线程模型,能够处理高并发请求,同时也支持异步I/O操作。
b. 可扩展性:Redis支持集群模式,可以水平扩展到多个节点,提高系统的容错性和可扩展性。
c. 数据持久化:Redis支持RDB和AOF两种持久化方式,可以将内存中的数据定期写入磁盘,保证数据的安全性。
Redis 优势?
Redis的优势包括:
a. 高性能:Redis采用单线程模型,能够处理高并发请求,同时也支持异步I/O操作。
b. 可扩展性:Redis支持集群模式,可以水平扩展到多个节点,提高系统的容错性和可扩展性。
c. 数据持久化:Redis支持RDB和AOF两种持久化方式,可以将内存中的数据定期写入磁盘,保证数据的安全性。
d. 支持丰富的数据结构:Redis支持字符串、列表、集合和有序集合等多种数据结构,满足不同场景的需求
Redis 与其他key-value 存储有什么不同?
Redis与其他key-value存储的不同之处在于,
Redis支持更多的数据结构,并且支持数据持久化,可以在重启后恢复数据。
2、Redis 的数据类型?
90%的人知道Redis 5种最基本的数据结构;
只有不到10%的人知道8种基本数据结构,
https://mp.weixin.qq.com/s?__biz=Mzg2ODA3NjA1MA==&mid=2247484999&idx=1&sn=348f4cb64a05590dbc206d11659c094b&chksm=ceb0993af9c7102c9cc7c84a8b257aaa163c2e941968441ee06cafd3163de94578a11a6b7155&token=495069624&lang=zh_CN#rd
3、使用 Redis 有哪些好处?
使用 Redis 的好处包括:
高性能:Redis 是一种内存数据库,它使用单线程模型来处理请求,因此可以提供非常高的读写性能。此外,Redis 还支持数据缓存和消息队列等功能,这些功能也可以提高应用程序的性能。
高可用性:Redis 支持主从复制和哨兵机制,这些机制可以帮助确保 Redis 集群的高可用性。如果一个节点出现故障,其他节点可以接管它的工作,从而保持系统的可用性。
可扩展性:Redis 支持分布式架构,可以通过增加更多的节点来扩展系统规模。此外,Redis 还支持数据分片和集群模式,这些模式可以帮助更好地利用系统资源,提高系统的可扩展性。
数据结构丰富:Redis 支持多种数据结构,包括字符串、哈希表、列表、集合和有序集合等。这些数据结构可以帮助满足不同的应用需求,例如存储用户信息、存储商品信息、计算排名等。
简单易用:Redis 提供了简单的 API,易于学习和使用。此外,Redis 还支持命令行工具和客户端库,这些工具可以帮助开发人员更方便地与 Redis 进行交互。
总之,使用 Redis 可以提高应用程序的性能、可用性和可扩展性,同时还提供了丰富的数据结构和简单易用的 API,是一款非常优秀的 NoSQL 数据库。
4、Redis 相比 Memcached 有哪些优势?
Redis 相比 Memcached 的优势包括:
内存数据结构:Memcached 是基于内存的缓存系统,而 Redis 不仅支持内存存储,还支持多种数据结构,例如字符串、哈希表、列表、集合和有序集合等。这使得 Redis 可以更灵活地满足不同的应用需求。
持久化机制:Memcached 不支持持久化,即在重启服务器时会丢失所有缓存数据。而 Redis 支持 RDB(快照)和 AOF(追加文件)两种持久化机制,可以保证数据的可靠性和安全性。
事务支持:Redis 支持事务操作,可以保证多个命令的原子性执行。这对于需要保证数据的一致性和完整性的应用非常重要。
高级功能:Redis 支持更多的高级功能,例如发布/订阅模式、Lua 脚本、管道和消息队列等。这些功能可以帮助应用程序更好地利用 Redis 的功能。
可扩展性:Redis 支持分布式架构,可以通过增加更多的节点来扩展系统规模。此外,Redis 还支持数据分片和集群模式,这些模式可以帮助更好地利用系统资源,提高系统的可扩展性。
Redis 相比 Memcached 具有更丰富数据结构、更强的持久化机制、更多的高级功能和更好的可扩展性,是一款更加全面和强大的缓存系统。
5、Memcache 与 Redis 的区别都有哪些?
Memcached是一种基于内存的key-value缓存系统,
而Redis则是一种基于内存的数据存储系统,两者的主要区别在于数据持久化的方式和适用场景
6、Redis 是单进程单线程的?
Memcached是一种基于内存的key-value缓存系统,
而Redis则是一种基于内存的数据存储系统,两者的主要区别在于数据持久化的方式和适用场景
7、一个字符串类型的值能存储最大容量是多少?
一个字符串类型的值能存储的最大容量取决于字符串的长度和字符集,
一般来说,单个字符串类型的值最大能存储的容量为5-10GB左右
8、Redis 的持久化机制是什么?
Redis的持久化机制有两种:RDB和AOF。
RDB是将内存中的数据定期写入磁盘,
AOF则是将每个写命令追加到一个文件中,最后再将整个文件刷入磁盘
1、RDBRedis DataBase)持久化方式?
2、AOFAppend-only file)持久化方式?
Redis的持久化方式有RDB和AOF两种,其中RDB是默认的持久化方式,AOF是可选的持久化方式。
9、Redis 常见性能问题和解决方案?
Redis的持久化方式有RDB和AOF两种,其中RDB是默认的持久化方式,AOF是可选的持久化方式。
11、Redis 的回收策略(淘汰策略)?
Redis的回收策略是淘汰策略,它会根据一定的规则来判断哪些键需要被删除,以达到内存空间的最优化利用
12、为什么 Redis 需要把所有数据放到内存中?
Redis需要把所有数据放到内存中是因为它的数据结构比较复杂,
而且需要快速响应客户端的请求,所以需要将数据尽可能地加载到内存中。
13、Redis 的同步机制了解么?
Redis的同步机制是通过主从复制来实现的,
其中一个节点作为主节点,负责接收客户端的写命令,
另一个节点作为从节点,负责接收主节点的读命令,并将结果返回给客户端
14、Pipeline 有什么好处,为什么要用pipeline?
Redis的Pipeline机制可以一次性发送多个命令,减少了网络开销和命令执行的时间,提高了性能。
15、是否使用过 Redis 集群,集群的原理是什么?
Redis集群方案的原理是将数据分散到多个节点上,通过分片和负载均衡来实现高可用性和可扩展性
16、Redis 集群方案什么情况下会导致整个集群不可用?
当 Redis 集群中的某个节点发生故障时,可能会导致整个集群不可用。
这可能是由于网络问题、硬件故障或软件错误等原因引起的。
为了避免这种情况的发生,可以采取以下措施:
使用高可用性(HA)解决方案,
如 Sentinel 或 Cluster 模式;定期备份数据以防止数据丢失;监控集群状态并及时处理故障。
public class RedisCluster {
private String host;
private int port;
private int password;
private JedisPool jedisPool;
public RedisCluster(String host, int port, String password) {
this.host = host;
this.port = port;
this.password = password;
}
public void setPassword(String password) {
this.password = password;
}
public JedisPool getJedisPool() {
return jedisPool;
}
public void close() {
if (jedisPool != null) {
jedisPool.close();
}
}
}
17、Redis 支持的 Java 客户端都有哪些?
Redis 支持多种 Java 客户端,
包括官方提供的 Redisson 和第三方库 Lettuce 等。
此外,还有其他一些流行的 Java 客户端,例如 Spring Data Redis、Jedis、lettuce-redis 等。
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.connection.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
public class RedisClient {
private static final String REDIS_HOST = "localhost";
private static final int REDIS_PORT = 6379;
private static final String REDIS_PASSWORD = "password";
public static RedisTemplate<String, Object> getRedisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(new LettuceConnectionFactory(new RedisStandaloneConfiguration(REDIS_HOST, REDIS_PORT, REDIS_PASSWORD)));
return template;
}
public static RedisOperations getRedisOperations() {
RedisOperations operations = new RedisOperations(getRedisTemplate());
return operations;
}
}
18、Jedis 与 Redisson 对比有什么优缺点?
Jedis 是 Redis 官方提供的 Java 客户端,而 Redisson 是一个基于 Redis 的分布式对象和服务框架。它们各有优缺点,具体如下:
优点:
Jedis:
轻量级,易于使用和集成。
提供了丰富的操作方法,支持常见的 Redis 命令和数据结构。
可以与其他 Java 库无缝集成。
Redisson:
提供了更多的功能,如分布式锁、分布式对象、分布式队列等。
支持更多的数据结构,如 List、Set、Map 等。
提供了更灵活的配置方式,可以通过注解或 XML 配置文件进行配置。
缺点:
Jedis:
不支持事务操作。
不支持持久化存储。
性能相对较低。
Redisson:
学习曲线较陡峭,需要一定的学习成本。
配置较为复杂,需要了解其内部实现原理。
对于一些简单的应用场景,可能过于复杂。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.connection.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
@Component
public class RedisUtils {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void doSomethingWithRedis(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
public RedisOperations getRedisOperations() {
return redisTemplate.getConnection();
}
}
19、Redis 如何设置密码及验证密码?
在 Redis 中,可以使用以下两种方式来设置密码和验证密码:
在创建 Redis 连接时,通过 URI() 方法指定密码参数。例如:
Jedis jedis = new Jedis("redis://localhost");
jedis.auth("password"); // 设置密码
如果密码验证失败,则会抛出异常。
需要注意的是,为了安全起见,建议不要将密码明文存储在程序中,而是将其作为加密后的字符串存储在数据库或其他安全的地方。
20、说说 Redis 哈希槽的概念?
Redis 哈希槽(Hash Slot)是 Redis 中一种数据结构,用于将键值对存储在不同的槽位中。每个槽位可以存储一个键值对,其中键和值都是字符串类型。
哈希槽的概念源于哈希表(Hash Table),它是一种基于哈希函数的数据结构,通过哈希函数将键映射到对应的槽位上,从而实现快速的查找、插入和删除操作。Redis 中的哈希槽也是通过哈希函数将键映射到对应的槽位上,从而实现高效的数据存储和访问。
Redis 中的哈希槽有两种类型:散列槽(Hash Slot)和列表槽(List Slot)。散列槽主要用于存储键值对,而列表槽主要用于存储列表类型的数据,例如有序集合(sorted set)和跳跃表(skiplist)等。
Redis 中的哈希槽分配策略是动态的,即根据当前系统的负载情况和内存使用情况来动态调整哈希槽的数量和大小。这样可以保证系统能够充分利用可用的资源,提高系统的性能和可靠性。
21Redis的过期策略和内存淘汰机制
Redis的过期策略和内存淘汰机制是Redis 中非常重要的概念,它们可以保证Redis 的高可用性和性能。
1.过期策略
Redis 中的过期策略是指在缓存数据过期后如何处理这些数据。Redis 支持两种过期策略:
*TTL(Time-to-Live):该策略会为每个键设置一个过期时间,当缓存数据超过指定的时间后,Redis 会自动删除这个键值对。TTL 策略适用于大多数场景,因为它简单易用,而且能够很好地控制缓存数据的生命周期。
*LRU(Least Recently Used):该策略会将最近最少使用的缓存数据删除,以释放内存空间。当Redis 的内存不足时,它会自动执行LRU 策略来清理缓存数据。LRU 策略适用于需要频繁访问的数据,因为它可以确保最近最少使用的缓存数据不会被删除。
2.内存淘汰机制
Redis 中的内存淘汰机制是指在Redis 内存不足时如何处理缓存数据。Redis 支持两种内存淘汰机制:
*LRU(Least Recently Used):该机制会将最近最少使用的缓存数据删除,以释放内存空间。当 Redis 的内存不足时,它会自动执行LRU 策略来清理缓存数据。LRU 机制适用于需要频繁访问的数据,因为它可以确保最近最少使用的缓存数据不会被删除。
*Eviction by Staleness:该机制会根据缓存数据的过期时间来判断哪些缓存数据应该被删除。如果一个键的过期时间比当前时间更近,那么这个键对应的缓存数据就会被删除。Eviction by Staleness 机制适用于需要保留一定时间的历史数据的场景,因为它可以确保历史数据不会被误删。
1)noeviction:当内存不足以容纳新写入数据
时,新写入操作会报错。应该没人用吧。
2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最
近最少使用的key。推荐使用,目前项目在用这种。
3)allkeys-random:当内存不足以容纳新写入数据时,在键
空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。
4)volatile-lru:当内存不足以容
纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又
做持久化存储的时候才用。不推荐
5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键
空间中,随机移除某个key。依然不推荐
6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键
空间中,有更早过期时间的key优先移除。不推荐
ps:如果没有设置 expire 的key, 不满足先决条件 (prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。
渐进式ReHash
渐进式rehash是一种优化技术,用于减少Redis在扩容时重新哈希所有键值对的开销。当Redis的内存使用量超过预设阈值时,它会触发一次rehash操作,将所有键值对重新哈希到新的槽中。但是,如果所有的键值对都存储在同一组槽中,那么每次扩容都需要进行全量rehash,这会导致性能下降。
为了解决这个问题,Redis引入了渐进式rehash的概念。渐进式rehash是指在扩容时只重新哈希一部分键值对,而不是全部重新哈希。具体步骤如下:
1.首先,Redis会计算出需要扩容的槽的数量和大小。
2.然后,Redis会选择一部分键值对,将其重新哈希到新的槽中。这些键值对通常是按照键的哈希值排序后的前几个或者随机选择的一部分。
3.接着,Redis会将被选中的键值对从旧的槽中删除,并将其添加到新的槽中。
4.最后,Redis会继续选择下一轮要重新哈希的键值对,重复以上步骤,直到所有键值对都被重新哈希到新的槽中。
使用Jedis库实现渐进式rehash
public class RedisRehashExample {
private static Jedis jedis = new Jedis("localhost");
public staticvoid main(String[] args) throws Exception {
//设置最大内存使用量为1GB
jedis.configSet("maxmemory", "1024");
//初始化Jedis连接池
jedis.poolConfig(new PoolConfig());
//获取当前可用槽的数量和大小
int totalSlots = jedis.getInteger("total_slots");
long totalSize = jedis.getLong("total_size");
//计算需要扩容的槽的数量和大小
int newSlots = totalSlots * 2;
long newSize = (totalSize + newSlots) >> 1;
//开始渐进式rehash操作
for (int i = 0; i < totalSlots; i++) {
long hash = i % totalSlots;
long key = "key" + i;
long value = "value" + i;
if (jedis.exists(key)) {
long oldKey = "oldkey" + i;
long oldValue = "oldvalue" + i;
if (jedis.exists(oldKey)) {
//将旧的键值对从旧的槽中删除,并添加到新的槽中
jedis.del(oldKey);
jedis.set(key, value);
jedis.del(oldKey);
jedis.set(oldKey, oldValue);
} else {
//如果旧的键值对不在旧的槽中,则跳过该次rehash操作
continue;
}
} else {
//如果该键值对不在任何槽中,则跳过该次rehash操作
continue;
}
}
}
}
- 点赞
- 收藏
- 关注作者
评论(0)