redis--问到你哭

256 阅读6分钟

你问我记住这些问题没? 我会很自豪的回答你,我没有。

回答有的是手写,可能有错别字。花花世界,别太当真

打** 是我认为比较重要的

** Redis的数据类型?

string,hash,list,set,zset(有序集合)

Redis相比Memcached有哪些优势?

  1. 速度快,数据存内存中。
  2. 支持丰富的数据类型
  3. 支持事务,操作都是原子性的

Memcached 断电后会数据被干掉

注释:有啥优势跟我有鸡毛关系

** Redis是单进程单线程的吗?为何它那么快那么高效?

redis是单进程单线程的

1.完全基于内存的

2.采用单线程,避免不必要的上下文切换可竞争条件

3.数据简单,数据操作也相对简单

4.使用多路I/O复用模型,非阻塞IO (多路由 相当于前台接客 ----> 逻辑是内存处理)

注释:单生狗 手速快吧

一个字符串类型的值能存储最大容量是多少?

512M

** Redis的持久化机制是什么?各自的优缺点?

  1. rdb
  2. aof

首先明白持久化是啥玩意?

即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)

rdb:

在夜黑风高的时间,将数据偷偷存在一个临时文件,等持久化完毕之后,用这个临时文件替换上次持久化的文件,达到数据恢复的目的

优点:

  1. 只有一个dump.rdb文件,方便持久化
  2. 容灾性好,文件可以保存到一个非常安全的磁盘去
  3. 性能最大化 fork 子进程来完成写操作,让主进程继续处理命令 单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis的高性能。
  4. 相对于数据集大时,比 AOF 的启动效率更高

缺点: 数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候

aof:

所有redis命令的纪录,完全持久化存储。保存为aof文件

优点:

  1. 数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每进行一次命令操作就记录到 aof 文件中一次。

  2. 通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof工具解决数据一致性问题。

  3. AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的 flushall))

缺点:

(1)AOF 文件比 RDB 文件大,且恢复速度慢。

(2)数据集大的时候,比 rdb 启动效率低。

Redis常见性能问题和解决方案有哪些?

(1)Master 最好不要写内存快照,如果 Master 写内存快照,save 命令调度 rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务

(2)如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一

(3)为了主从复制的速度和连接的稳定性,Master 和 Slave 最好在同一个局域网

(4)尽量避免在压力很大的主库上增加从

(5)主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1<- Slave2 <- Slave3…这样的结构方便解决单点故障问题,实现 Slave 对 Master的替换。如果 Master 挂了,可以立刻启用 Slave1 做 Master,其他不变。

注释:自己记忆

Redis过期键的删除策略?

(1)定时删除:在设置键的过期时间的同时,创建一个定时器 timer). 让定时器在键的过期时间来临时,立即执行对键的删除操作。

(2)惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。

(3)定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。

注释:记不住

Redis的回收策略(淘汰策略)?

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据 注意这里的 6 种机制, volatile 和 allkeys 规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的 lru、ttl 以及 random 是三种不同的淘汰策略,再加上一种 no-enviction 永不回收的策略。

使用策略规则:

(1)如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用 allkeys-lru

(2)如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random

注释:记不住

为什么Redis需要把所有数据放到内存中?

为了达到最快的读写,异步将数据写到磁盘

Redis的同步机制了解么?

Redis 可以使用主从同步,从从同步

第一次同步时,主节点做一次 bgsave,并同时将后续修改操作记录到内存 buffer,待完成后将 rdb 文件全量同步到复制节点,复制节点接受完成后将 rdb 镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

注释:记不住

Pipeline有什么好处,为什么要用Pipeline?

pipeline的概念可以从这里抽象出来:将一件需要重复做的事情 切割成各个不同的阶段

可以将多次 IO 往返的时间缩减为一次,前提是 pipeline 执行的指令之间没有因果相关性。

使用 redis-benchmark 进行压测的时候可以发现影响 redis 的 QPS峰值的一个重要因素是 pipeline 批次指令的数目

注释:记不住

是否使用过Redis集群,集群的原理是什么?

(1)Redis Sentinal 着眼于高可用,在 master 宕机时会自动将 slave 提升为master,继续提供服务。

(2)Redis Cluster 着眼于扩展性,在单个 redis 内存不足时,使用 Cluster 进行分片存储。

Redis集群方案什么情况下会导致整个集群不可用?

有 A,B,C 三个节点的集群,在没有复制模型的情况下,如果节点 B 失败了,那么整个集群就会以为缺少 5501-11000 这个范围的槽而不可用

注释:记不住

Redis支持的Java客户端都有哪些?官方推荐用哪个?

Redisson、Jedis、lettuce 等等,官方推荐使用 Redisson

Jedis与Redisson对比有什么优缺点?

Jedis 是 Redis 的 Java 实现的客户端,其 API 提供了比较全面的 Redis 命令的支持;

Redisson 实现了分布式和可扩展的 Java 数据结构,和 Jedis 相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等 Redis 特性。

Redisson 的宗旨是促进使用者对 Redis 的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。

注释:记不住

Redis如何设置密码及验证密码?

设置密码:config set requirepass 123456 授权密码:auth 123456

** 说说Redis哈希槽的概念?

Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽,集群的每个节点负责一部分 hash 槽。

注释:记不住

Redis集群的主从复制模型是怎样的?

为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有 N-1 个复制品.

注释:记不住

Redis集群会有写操作丢失吗?为什么?

Redis 并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作

Redis集群之间是如何复制的?

异步复制

Redis集群最大节点个数是多少?

16384 个。

注释:沙雕问这个问题

怎么测试Redis的连通性?

使用 ping 命令。

** 怎么理解Redis事务?

(1)事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

2)事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

** Redis事务相关的命令有哪几个?

discard 取消事务,放弃执行事务里面所有命令

exec 执行事务里面所有命令

multi 标记一个事务的开始

watch 监听一个或多个key,如果这个key别其他干扰了,就不执行

注释:记不住

Redis key的过期时间和永久有效分别怎么设置?

expire ɪkˈspaɪə(r)

persist pəˈsɪst

** Redis如何做内存优化?

尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小

Redis回收进程如何工作的?

1.一个客户端运行了新的命令,添加了新的数据。 2.redis 检查内存使用情况,如果大于 maxmemory 的限制, 则根据设定好的策略进行回收。 3.一个新的命令被执行,等等。所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地回收回到边界以下。如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。

都有哪些办法可以降低Redis的内存使用情况呢?

好好利用 Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的 Key-Value 可以用更紧凑的方式存放到一起

Redis的内存用完了会发生什么?

如果达到设置的上限,Redis 的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以将 Redis 当缓存来使用配置淘汰机制,当 Redis 达到内存上限时会冲刷掉旧的内容。

一个Redis实例最多能存放多少的keys?List、Set、Sorted Set他们最多能存放多少元素?

Redis 可以处理多达 232 的 keys

Redis 的存储极限是系统中的可用内存值。

MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据?

Redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。

相关知识:Redis 提供 6 种数据淘汰策略:

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据

注释:记不住

假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?

使用 keys 指令可以扫出指定模式的 key 列表。

对方接着追问:如果这个 redis 正在给线上的业务提供服务,那使用 keys 指令会有什么问题?

这个时候你要回答 redis 关键的一个特性:

redis 的单线程的。

keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。

这个时候可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用 keys 指令长。

** 如果有大量的key需要设置同一时间过期,一般需要注意什么?

如果大量的 key 过期时间设置的过于集中,到过期的那个时间点, redis 可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。

** 使用过Redis分布式锁么,它是什么回事?

通过 setnx(在指定的 key 不存在时,为 key 设置指定的值。) 来争抢锁,抢到之后,再用 expire 给锁加一个过期时间防止锁忘记了释放。

Redis 分布式锁其实就是在系统里面占一个“坑”,其他程序也要占“坑”的时候,占用成功了就可以继续执行,失败了就只能放弃或稍后重试。 占坑一般使用 setnx(set if not exists)指令,只允许被一个程序占有,使用完调用 del 释放锁。

** 缓存穿透

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

解决方案:

  1. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;

  2. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击

3.使用布隆过滤器 (二进制的 0 1 0 1 0 1 ) 拦截器的概念 (代码复杂度变大,还需要去维护这个集合) 建议用2

** 缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

解决方案: 设置热点数据永远不过期。 加互斥锁,互斥锁参考代码如下:

image.png 说明:

  1. 缓存中有数据,直接走上述代码13行后就返回结果了

  2. 缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。

  3. 当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这点

** 缓存雪崩

缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是, 缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

解决方案:

  1. 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

  2. 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。

  3. 设置热点数据永远不过期。

简述redis的哨兵模式

  哨兵是对redis进行实时的监控,主要有两个功能。

 1.监测主数据库和从数据库是否正常运行。

2.当主数据库出现故障的时候,可以自动将一个从数据库转换为主数据库,实现自动切换。

** 怎么保证缓存和数据库数据的一致性?

合理设置缓存的过期时间。

新增、更改、删除数据库操作时同步更新 Redis,可以使用事物机制来保证数据的一致性。

multiGet 批量查询优化

/**
 * 批量查询,对应mget
 * @param keys
 * @return
 */
public List<String> mget(List<String> keys) {
	return redisTemplate.opsForValue().multiGet(keys);
}

pipeline 批量查询优化

/**
 * 批量查询,管道pipeline  (一次会话)
 * nginx -> keepalive
 * redis -> pipeline
 * @param keys
 * @return
 */
public List<Object> batchGet(List<String> keys) {

	List<Object> result = redisTemplate.executePipelined(new RedisCallback<String>() {
		@Override
		public String doInRedis(RedisConnection connection) throws DataAccessException {
			StringRedisConnection src = (StringRedisConnection)connection;

			for (String k : keys) {
				src.get(k);
			}
			return null;
		}
	});

	return result;
}

redis 使用场景

Redis的五个常见使用场景

1、会话缓存(Session Cache)

最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗? 幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。

2、全页缓存(FPC)

除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。 再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。 此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis, 这个插件能帮助你以最快速度加载你曾浏览过的页面。

3、队列(秒杀,抢购,12306)list数据类型的使用

Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。 如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。

4、排行榜/计数器(set数据类型的使用)

Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可: 当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行: ZRANGE user_scores 0 10 WITHSCORES Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。

5、发布/订阅

最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。 Redis提供的所有特性中,我感觉这个是喜欢的人最少的一个,虽然它为用户提供如果此多功能。

GEO

GEO功能在Redis3.2版本提供,支持存储地理位置信息用来实现诸如附近位置、摇一摇这类依赖于地理位置信息的功能.geo的数据类型为zset.