Redis面试题

1.Redis是什么?为什么要用Redis (或noSql)?

NoSQL是Not only SQL的缩写,大意为“不只是SQL”,说明这项技术是传统关系型数据库的补充而非替代。在整个NoSQL技术栈中MemCache、Redis、MongoDB被称为NoSQL三剑客。
在这里插入图片描述

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

2.Redis的基本操作

启动/usr/local/redis/bin/redis-server

停止服务/usr/local/redis/bin/redis-cli shutdown

准备配置文件cp /opt/redis-4.0.2/redis.conf /usr/local/redis/

在这里插入图片描述

redis-server文件路径 redis.conf文件路径
/usr/local/redis/bin/redis-server /usr/local/redis/redis.conf

(1)连接数据库:切换数据库select、清空当前库、查看数据库个数、通杀数据库
(2)key的操作:Redis中以key-value键值对为基本存储方式,其中key为字符串
(3)常用五大数据类型:String、List、Set、hash、zset
字符串、可重复集合、不可重复集合、类似Map<String,String>、带分数的set
(4)String操作
Redis中最基本的类型,它是key对应的一个单一值。二进制安全,不必担心由于编码等问题导致二进制数据变化。所以redis的string可以包含任何数据,比如jpg图片或者序列化的对象。Redis中一个字符串值的最大容量是512M。

(5)List操作
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。说明它的底层是基于链表实现的,所以它操作时头尾效率高,中间效率低。
在这里插入图片描述
(6)Set操作
Redis的set是string类型的无序集合。它是基于哈希表实现的。

(7)Hash操作
本身就是一个键值对集合。可以当做Java中的Map<String,Object>对待。
(8)zset操作
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。

3.配置Redis

单位说明:1k和1kb是不同的:单位的大小写不敏感
include:可以将公共的配置放入到一个公共配置文件中,然后通过自配置文件引入父配置文件中的内容。将配置按照模块分开。

network:bind限定访问的主机地址

general:

其他:

4.什么是持久化?

Redis工作时数据都存储在内存中,万一服务器断电,则所有数据都会丢失。针对这种情况,Redis采用持久化机制来增强数据安全性。

5.RDB详解

RDB机制:每隔一定的时间把内存中的数据作为一个快照保存到硬盘上的文件中。Redis默认开启RDB机制。
在这里插入图片描述

6.AOF详解

AOF机制:根据配置文件中指定的策略,把生成数据的命令保存到硬盘上的文件中。
在这里插入图片描述
AOF重写
在这里插入图片描述

两组命令执行后对于count来说最终的值是一致的,但是进行AOF重写后省略了中间过程,可以让AOF文件体积更小。而Redis会根据AOF文件的体积来决定是否进行AOF重写。参考的配置项如下:

实际工作中不要进行频繁的AOF重写,因为CPU资源和硬盘资源二者之间肯定是CPU资源更加宝贵,所以不应该过多耗费CPU性能去节省硬盘空间。

7.RDB与AOF的取舍

①RDB
[1]优势
适合大规模的数据恢复,速度较快

[2]劣势
会丢失最后一次快照后的所有修改,不能绝对保证数据的高度一致性和完整性。Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑,但上述成立有条件,Linux也有优化手段

②AOF
[1]优势
选择appendfsync always方式运行时理论上能够做到数据完整一致,但此时性能又不好。文件内容具备一定可读性,能够用来分析Redis工作情况。

[2]劣势
持久化相同的数据,文件体积比RDB大,恢复速度比RDB慢。效率在同步写入时低于RDB,不同步写入时与RDB相同。

③RDB和AOF并存
Redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整

RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份)、快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。

④使用建议
如果Redis仅仅作为缓存可以不使用任何持久化方式。

其他应用方式综合考虑性能和完整性、一致性要求。

RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。
如果Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。
只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。如果不开启AOF,仅靠Master-Slave Replication 实现高可用性能也不错。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。新浪微博就选用了这种架构。

8.备份建议

如何看待数据“绝对安全”?
Redis 作为内存数据库从本质上来说,如果不想牺牲性能,就不可能做到数据的“绝对”安全。 RDB 和 AOF 都只是尽可能在兼顾性能的前提下降低数据丢失的风险,如果真的发生数据丢失问题,尽可能 减少损失。
在整个项目的架构体系中,Redis 大部分情况是扮演“二级缓存”角色。

二级缓存适合保存的数据 
经常要查询,很少被修改的数据。 
不是非常重要,允许出现偶尔的并发问题。 
不会被其他应用程序修改。
如果 Redis 是作为缓存服务器,那么说明数据在 MySQL 这样的传统关系型数据库中是有正式版本的。数据 最终以 MySQL 中的为准。

官方推荐两个都用;
如果对数据不敏感,可以选单独用 RDB;不建议单独用 AOF,因为可能出现Bug;
如果只是 做纯内存缓存,可以都不用

9.事务控制相关的命令有哪些?

在这里插入图片描述

10.组队失败情况

(1)自作自受
运行时出错,哪条语句出错那条无法执行,其余全部执行,没有回滚机制
(2)殃及池鱼
编译过程中,Redis检测出错误的命令语法,则全部取消。

11.锁

在使用WATCH命令监控一个KEY后,当前队列中的命令会由于外部命令的执行而放弃,这是乐观锁的体现。Redis不支持悲观锁。

悲观锁

认为当前环境非常容易发生碰撞,所以执行操作前需要把数据锁定,操作完成后释放锁,其他操作才可以继续操作。

乐观锁

认为当前环境不容易发生碰撞,所以执行操作前不锁定数据,万一碰撞真的发生了,那么放弃自己的操作。

12.redis消息订阅

消息订阅是进程间的一种消息通讯方式,即发送者(pub)发送消息,订阅者(sub)接收消息。redis支持消息订阅机制。

subcribe[频道] 订阅频道
publish[频道][消息] 向制定频道发布消息

13.读写分离的好处

性能优化:主服务器专注于写操作,可以用更适合写入数据的模式工作;同样,从服务器专注于读操作,可以用更适合读取数据的模式工作。

强化数据安全,避免单点故障:由于数据同步机制的存在,各个服务器之间数据保持一致,所以其中某个服务器宕机不会导致数据丢失或无法访问。从这个角度说参与主从复制的Redis服务器构成了一个集群。

14.主从复制搭建与启动

(1)第一步:创建/usr/local/cluster-redis目录
(2)第二步:把原始未经修改的redis.conf复制到/usr/local/cluster-redis目录
(3)第三步:把/usr/local/cluster-redis目录下的redis.conf复制为redis6000.conf
(4)第四步:按照既定计划修改redis6000.conf中的相关配置项
daemonize yes、dir、port、dbfilename、logfile、pidfile
(5)第五步:复制redis6000.conf为redis7000.conf
(6)第六步:修改redis7000.conf中的相关配置项
port、dbfilename、logfile、pidfile
(7)第七步:复制redis6000.conf为redis8000.conf
(8)第八步:修改redis8000.conf中的相关配置项
port、dbfilename、logfile、pidfile
启动:
/usr/local/redis/bin/redis-server /usr/local/cluster-redis/redis6000.conf
/usr/local/redis/bin/redis-server /usr/local/cluster-redis/redis7000.conf
/usr/local/redis/bin/redis-server /usr/local/cluster-redis/redis8000.conf

使用redis-cli停止指定服务器的命令格式如下:
/usr/local/bin/redis-cli -h IP地址 -p 端口号 shutdown

主从关系的设置

(1)查看主从关系
127.0.0.1:6000> info replication
#Replication
role:master
connected_slaves:0
刚刚启动的集群服务器中每一个节点服务器都认为自己是主服务器。需要建立主从关系。
(2)设定主从关系(临时建立)
在从机上指定主机位置即可
SLAVEOF 127.0.0.1 6000

永久建立(修改配置文件,在从机上设置slaveof属性)

(3)取消主从关系
在从机上执行命令
SLAVEOF NO ONE

15.主从复制的常见问题

测试1:在主机写入数据,在从机查看
可以查看
测试2:在从机写入数据报错。配置文件中的依据是:slave-read-only yes
测试3:主机执行SHUTDOWN看从机状态
从机原地待命
测试4:主机恢复启动,看从机状态
可以读取文件
测试5:从机SHUTDOWN,此时主机写入数据,从机恢复启动查看状态。重新设定主从关系后看新写入的数据是否同步。
同步
测试6:从机shutdown后,重启是否还具有原来的主从关系?
看是永久建立还是临时建立。

16.哨兵模式的作用

作用:通过哨兵服务器监控master/slave实现主从复制集群的自动管理。
master状态检测,如果master异常,则会进行Master-Slave切换,将其中一个Slave作为Master

17.主观下线与客观下线

主观下线:
SDOWN:当前一个Sentinel实例对某个redis服务器做出的下线判断。
客观下线:
ODOWN:多个Sentinel实例在对Master Server做出SDOWN判断,并通过Sentinel is-master-down-by-addr 命令互相交流后,得出MasterServer下线的判断,然后开机failover。

18.哨兵模式的工作原理:

①每个 Sentinel 以每秒钟一次的频率向它所知的 Master,Slave 以及其他 Sentinel 实例发送一个 PING 命 令 ;
②如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所 指定的值, 则这个实例会被 Sentinel 标记为主观下线;
③如果一个 Master 被标记为主观下线,则正在监视这个 Master 的所有 Sentinel 要以每秒一次的频率确认 Master 的确进入了主观下线状态;
④当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认 Master 的确进入了主观下线状态, 则 Master 会被标记为客观下线 ;
⑤在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有 Master,Slave 发送 INFO 命令
⑥当 Master 被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的 频率会从 10 秒一次改为每秒一次 ;
⑦若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除; 若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除;

19.哨兵模式的配置与启动

创建一个哨兵服务器运行所需要的配置文件。
vim /usr/local/cluster-redis/sentinel.conf

格式 sentinel monitor 为主机命名 主机IP 主机端口号 将主机判定为下线时需要Sentinel同意的数量
例子 sentinel monitor mymaster 127.0.0.1 6000 1

启动哨兵
/usr/local/redis/bin/redis-server /usr/local/cluster-redis/sentinel.conf --sentinel

+sdown master mymaster 127.0.0.1 6379 【主观下线】
+odown master mymaster 127.0.0.1 6379 #quorum 1/1【客观下线】
……
+vote-for-leader 17818eb9240c8a625d2c8a13ae9d99ae3a70f9d2 1【选举leader】
……
+failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379【把一个从机设置为主机】

-------------挂掉的主机又重新启动---------------------
-sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381【离开主观下线状态】
+convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381【转换为从机】

20.哨兵模式的选择主机机制

新主登基:从下线的主服务的所有从服务中挑选一个从服务,将它转乘主服务,
条件1.选择优先级靠前的从服务
条件2.选择偏移量最大的从服务
条件3.选择runid最小的从服务
群仆俯首:挑选出新的主服务之后,sentinel向原主服务的从服务发送slaveof 新服务的命 令,复制新的master
旧主俯首:当原来的主服务重新上显示,sentinel会向其发送slaveof命令,让其成为新的主 服务的从服务

21.Jedis

Jedis指通过java连接Redis客户端,需要搭配如相关的jar包。
redis与mysql的对比
MySQL Redis
连接 Connection Jedis
连接池 C3P0等等 JedisPool
操作完成 关闭连接 关闭连接

22.Jedis配置与使用

1.导包

普通redis.clients
JedisPool:commosn-pool2-2.4.2.jar jedis-2.9.0.jar

2.配置

在所连接的 Redis 客户端的配置文件中:
 注释掉 bind 127.0.0.1
 关闭保护模式,将 ptotect-mode 设置为 no
 禁用 Linux 防火墙,执行 service iptables stop

连接池参数以及含义
(1)MaxTotal 控制一个 pool 可分配多少个 jedis 实例,通过 pool.getResource()来获取;如果赋值为 -1,则表示不限制;如果 pool 已经分配了 MaxTotal 个 jedis 实例,则此时 pool 的状态 为 exhausted。
(2)maxIdle 控制一个 pool 最多有多少个状态为 idle(空闲)的 jedis 实例。
(3)MaxWaitMillis 表示当 borrow 一个 jedis 实例时,最大的等待毫秒数,如果超过等待时间,则直接抛 JedisConnectionException。
(4)testOnBorrow 获得一个 jedis 实例的时候是否检查连接可用性(ping());如果为 true,则得到的 jedis 实例均是可用的。

3.测试

(1)建立连接

public void testConnect() { 
//连接指定的redis,需要ip地址和端口号 
	Jedis jedis=new Jedis("192.168.162.128", 6379); 
	String ping = jedis.ping(); 
	System.out.println(ping); 
}

(2)测试key
(3)测试String
(4)测试list
(5)测试set
(6)测试hash
(7)测试zset
(8)测试redisPool

public void testPool(){ 
//默认的连接池配置 
	GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); 
	System.out.println(poolConfig); 
	JedisPool jedisPool=new JedisPool(poolConfig, "192.168.4.128", 6379,60000); 
	Jedis jedis = jedisPool.getResource(); 
	String ping = jedis.ping(); 
	System.out.println(ping); 
	//如果是从连接池中获取的,那么执行close方法只是将连接放回到池中 
	jedis.close(); 
	jedisPool.close();
}

23.Redis Cluster

24.Redis常用命令?

Keys pattern

*表示区配所有以bit开头的
查看Exists key是否存在

Set

设置 key 对应的值为 string 类型的 value。

setnx

设置 key 对应的值为 string 类型的 value。如果 key 已经存在,返回 0,nx 是 not exist 的意思。
删除某个key
第一次返回1 删除了 第二次返回0

Expire

设置过期时间(单位秒)

TTL

查看剩下多少时间
返回负数则key失效,key不存在了

Setex

设置 key 对应的值为 string 类型的 value,并指定此键值对应的有效期。

Mset

一次设置多个 key 的值,成功返回 ok 表示所有的值都设置了,失败返回 0 表示没有任何值被设置。

Getset

设置 key 的值,并返回 key 的旧值。

Mget

一次获取多个 key 的值,如果对应 key 不存在,则对应返回 nil。

Incr

对 key 的值做加加操作,并返回新的值。注意 incr 一个不是 int 的 value 会返回错误,incr 一个不存在的 key,则设置 key 为 1

incrby

同 incr 类似,加指定值 ,key 不存在时候会设置 key,并认为原来的 value 是 0

Decr

对 key 的值做的是减减操作,decr 一个不存在 key,则设置 key 为-1

Decrby

同 decr,减指定值。

Append

给指定 key 的字符串值追加 value,返回新字符串值的长度。

Strlen

取指定 key 的 value 值的长度。

persist xxx(取消过期时间)

选择数据库(0-15库)

Select 0

//选择数据库

move age 1

//把age 移动到1库

Randomkey

随机返回一个key

Rename

重命名

Type

返回数据类型

25.使用过Redis分布式锁么,它是怎么实现的?

先拿setnx来争抢锁,
抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。

如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?

set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!

26.使用过Redis做异步队列么,你是怎么用的?有什么缺点?

一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
缺点:
在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。

能不能生产一次消费多次呢?

使用pub/sub主题订阅者模式,可以实现1:N的消息队列。

27.什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免?

缓存穿透

一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。

如何避免?
1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。

缓存雪崩

当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。

如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决思路:
1、直接写个缓存刷新页面,上线时手工操作下;
2、数据量不大,可以在项目启动的时候自动进行加载;
3、定时刷新缓存;

缓存更新

除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),

我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
(1)定时去清理过期的缓存;
(2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

两者各有优劣,
第一种的缺点是维护大量缓存的key是比较麻烦的,
第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!
具体用哪种方案,大家可以根据自己的应用场景来权衡。

缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
以参考日志级别设置预案:
(1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
(2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
(3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
(4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
28.热点数据和冷数据是什么
热点数据,缓存才有价值
对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。频繁修改的数据,看情况考虑使用缓存

对于上面两个例子,寿星列表、导航信息都存在一个特点,就是信息修改频率不高,读取通常非常高的场景。
对于热点数据,比如我们的某IM产品,生日祝福模块,当天的寿星列表,缓存以后可能读取数十万次。再举个例子,某导航产品,我们将导航信息,缓存以后可能读取数百万次。
**数据更新前至少读取两次,**缓存才有意义。
这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太大价值了。

那存不存在,修改频率很高,但是又不得不考虑缓存的场景呢?
有!比如,这个读取接口对数据库的压力很大,但是又是热点数据,这个时候就需要考虑通过缓存手段,减少数据库的压力,
比如我们的某助手产品的,点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到Redis缓存,减少数据库压力。

29.Memcache与Redis的区别都有哪些?

1)、存储方式 Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,redis可以持久化其数据
2)、数据支持类型 memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型 ,提供list,set,zset,hash等数据结构的存储
3)使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。 Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
4)value 值大小不同:Redis 最大可以达到 1gb;memcache 只有 1mb。
5)redis的速度比memcached快很多
6)Redis支持数据的备份,即master-slave模式的数据备份。

30.单线程的redis为什么这么快

(一)纯内存操作
(二)单线程操作,避免了频繁的上下文切换
(三)采用了非阻塞I/O多路复用机制

31.Redis 内部结构

dict 本质上是为了解决算法中的查找问题(Searching)是一个用于维护key和value映射关系的数据结构,与很多语言中的Map或dictionary类似。 本质上是为了解决算法中的查找问题(Searching)
sds sds就等同于char * 它可以存储任意二进制数据,不能像C语言字符串那样以字符’\0’来标识字符串的结 束,因此它必然有个长度字段。
skiplist (跳跃表) 跳表是一种实现起来很简单,单层多指针的链表,它查找效率很高,堪比优化过的二叉平衡树,且比平衡树的实现,
quicklist
ziplist 压缩表 ziplist是一个编码后的列表,是由一系列特殊编码的连续内存块组成的顺序型数据结构,

32.redis的过期策略以及内存淘汰机制

redis采用的是定期删除+惰性删除策略。

为什么不用定时删除策略?
定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.

定期删除+惰性删除是如何工作的呢?
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。

采用定期删除+惰性删除就没其他问题了么?
不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。

在redis.conf中有一行配置
maxmemory-policy volatile-lru

该配置就是配内存淘汰策略的(什么,你没配过?好好反省一下自己)
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(驱逐):禁止驱逐数据,新写入操作会报错

ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。

33.Redis 为什么是单线程的

官方FAQ表示,因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。
既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程会有很多麻烦!)Redis利用队列技术将并发访问变为串行访问
1)绝大部分请求是纯粹的内存操作(非常快速)
2)采用单线程,避免了不必要的上下文切换和竞争条件
3)非阻塞IO优点:
1.速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
2. 支持丰富数据类型,支持string,list,set,sorted set,hash
3.支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
4. 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除如何解决redis的并发竞争key问题

同时有多个子系统去set一个key。这个时候要注意什么呢? 不推荐使用redis的事务机制。因为我们的生产环境,基本都是redis集群环境,做了数据分片操作。你一个事务中有涉及到多个key操作的时候,这多个key不一定都存储在同一个redis-server上。因此,redis的事务机制,十分鸡肋。
(1)如果对这个key操作,不要求顺序: 准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可
(2)如果对这个key操作,要求顺序: 分布式锁+时间戳。 假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。
(3) 利用队列,将set方法变成串行访问也可以redis遇到高并发,如果保证读写key的一致性
对redis的操作都是具有原子性的,是线程安全的操作,你不用考虑并发问题,redis内部已经帮你处理好并发的问题了。

34.Redis 集群方案应该怎么做?都有哪些方案?

1.twemproxy,大概概念是,它类似于一个代理方式, 使用时在本需要连接 redis 的地方改为连接 twemproxy, 它会以一个代理的身份接收请求并使用一致性 hash 算法,将请求转接到具体 redis,将结果再返回 twemproxy。
缺点: twemproxy 自身单端口实例的压力,使用一致性 hash 后,对 redis 节点数量改变时候的计算值的改变,数据无法自动移动到新的节点。

2.codis,目前用的最多的集群方案,基本和 twemproxy 一致的效果,但它支持在 节点数量改变情况下,旧节点数据可恢复到新 hash 节点

3.redis cluster3.0 自带的集群,特点在于他的分布式算法不是一致性 hash,而是 hash 槽的概念,以及自身支持节点设置从节点。具体看官方文档介绍。

35.有没有尝试进行多机redis 的部署?如何保证数据一致的?

主从复制,读写分离
一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的,并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。

对于大量的请求怎么样处理
redis是一个单线程程序,也就说同一时刻它只能处理一个客户端请求;
redis是通过IO多路复用(select,epoll, kqueue,依据不同的平台,采取不同的实现)来处理多个客户端请求的

36.Redis 常见性能问题和解决方案?

(1) Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件
(2) 如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即: Master <- Slave1 <- Slave2 <-Slave3…

37.讲解下Redis线程模型

文件事件处理器包括分别是套接字、 I/O 多路复用程序、 文件事件分派器(dispatcher)、 以及事件处理器。使用 I/O 多路复用程序来同时监听多个套接字, 并根据套接字目前执行的任务来为套接字关联不同的事件处理器。当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关闭(close)等操作时, 与操作相对应的文件事件就会产生, 这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
I/O 多路复用程序负责监听多个套接字, 并向文件事件分派器传送那些产生了事件的套接字。
工作原理:
1)I/O 多路复用程序负责监听多个套接字, 并向文件事件分派器传送那些产生了事件的套接字。
尽管多个文件事件可能会并发地出现, 但 I/O 多路复用程序总是会将所有产生事件的套接字都入队到一个队列里面, 然后通过这个队列, 以有序(sequentially)、同步(synchronously)、每次一个套接字的方式向文件事件分派器传送套接字: 当上一个套接字产生的事件被处理完毕之后(该套接字为事件所关联的事件处理器执行完毕), I/O 多路复用程序才会继续向文件事件分派器传送下一个套接字。如果一个套接字又可读又可写的话, 那么服务器将先读套接字, 后写套接字.

38.为什么Redis的操作是原子性的,怎么保证原子性的?

对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。
Redis的操作之所以是原子性的,是因为Redis是单线程的。
Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。
多个命令在并发中也是原子性的吗?
不一定, 将get和set改成单命令操作,incr 。使用Redis的事务,或者使用Redis+Lua==的方式实现.

  • 1
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

打赏
文章很值,打赏犒劳作者一下
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页

打赏

lgg要努力

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者