Redis高频考点
引言
Redis(REmote Dictionary Server 远程字典服务)是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API的非关系型数据库。传统数据库遵循 ACID 规则。而 Nosql(Not Only SQL 的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称) 一般为分布式而分布式一般遵循 CAP 定理。
目录
Redis数据类型
Redis基本操作命令
Redis持久化
Redis事务
Redis架构模式
一致性哈希和哈希槽
Redis异步队列
缓存穿透和缓存雪崩
1.Redis支持的数据类型?
(1)string字符串
格式: set key value
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。string类型是Redis最基本的数据类型,一个键最大能存储512MB。
(2)hash(哈希)
格式: hmset name key1 value1 key2 value2
Redis hash 是一个键值(key=>value)对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
(3)list(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
格式: lpush name value
在 key 对应 list 的头部添加字符串元素
格式: rpush name value
在 key 对应 list 的尾部添加字符串元素
格式: lrem name index
key 对应 list 中删除 count 个和 value 相同的元素
格式: llen name
返回 key 对应 list 的长度
(4)set(集合)
格式: sadd name value
Redis的Set是string类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
(5)zset(sorted set:有序集合)
格式: zadd name score value
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
2.Redis常用的操作命令
https://www.runoob.com/redis/redis-keys.html
3.什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。Redis 提供了两种持久化方式:RDB(默认)和AOF。
RDB
RDB 是 Redis 默认的持久化方案。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件恢复数据。
相关配置
#指定本地数据库文件名,一般采用默认的 dump.rdb
dbfilename dump.rdb
#指定本地数据库存放目录,一般也用默认配置
dir ./
#默认开启数据压缩
#配置存储至本地数据库时是否压缩数据,默认为yes。
#Redis采用LZF压缩方式,但占用了一点CPU的时间。
#若关闭该选项,但会导致数据库文件变的巨大。建议开启。
rdbcompression yes
#若不想用RDB方案,可以把 save "" 的注释打开,下面三个注释
#save ""
#15min,有1个key发生改变,则保存
save 900 1
#5min,有10个key发生改变,则保存
save 300 10
#1min,有10000个key发生改变,则保存
save 60 10000
#另外,在使用SHOWDOWN命令后,也会dump到文件。
RDB快照触发机制
1 在指定的时间间隔内,执行指定次数的写操作
2 执行save(阻塞, 只管保存快照,其他的等待) 或者是bgsave (异步)命令
3 执行flushall 命令,清空数据库所有数据,意义不大。
4 执行shutdown 命令,保证服务器正常关闭且不丢失任何数据,意义...也不大。
优点
1 适合大规模的数据恢复。
2 如果业务对数据完整性和一致性要求不高,RDB是很好的选择。
缺点
1 数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。
2 备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍哦),最后再将临时文件替换之前的备份文件。所以Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。
AOF
Redis 默认不开启。它的出现是为了弥补RDB的不足(数据的不一致性),所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
相关配置
#redis 默认关闭,开启需要手动把no改为yes
appendonly yes
#指定本地数据库文件名,默认值为 appendonly.aof
appendfilename "appendonly.aof"
#指定更新日志条件
# appendfsync always
appendfsync everysec
# appendfsync no
#配置重写触发机制
#当前AOF文件比上次AOF重写后的文件大100%的时候进行AOF重写
auto-aof-rewrite-percentage 100
#当AOF文件小于64MB的时候不进行AOF重写 。一般都设置为3G,64M太小了
auto-aof-rewrite-min-size 64mb
AOF的保存策略
根据配置文件触发,可以是每次执行触发,可以是每秒触发,可以不同步。
根据AOF文件恢复数据
在实际开发中,可能因为某些原因导致appendonly.aof 文件格式异常,从而导致数据还原失败,可以通过命令进行修复 。
redis-check-aof --fix appendonly.aof
AOF的重写机制
随着命令不断从AOF缓存中写入到AOF文件中,AOF文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制来压缩AOF文件。AOF文件的压缩和RDB文件的压缩原理不一样,RDB文件的压缩是使用压缩算法将二进制的RDB文件压缩,而AOF文件的压缩主要是去除AOF文件中的无效命令,比如说:
- 同一个key的多次写入只保留最后一个命令
- 已删除、已过期的key的写命令不再保留
AOF重写的触发机制也分为手动触发和自动触发两种方式。手动触发,执行bgrewriteaof命令直接触发AOF重写
bgrewriteaof
另外,可以在redis.conf配置文件中配置以下参数来自动触发AOF重写,执行bgrewriteaof命令
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
AOF的存储结构(RESP格式):
内容是redis通讯协议(RESP )格式的命令文本存储。
RESP 是redis客户端和服务端之前使用的一种通讯协议;
RESP 的特点:实现简单、快速解析、可读性好
https://www.cnblogs.com/tommy-huang/p/6051577.html
AOF 的优缺点
优点:数据的完整性和一致性更高
缺点:因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢。
总结
- Redis 默认开启RDB持久化方式,在指定的时间间隔内,执行指定次数的写操作,则将内存中的数据写入到磁盘中。
- RDB 持久化适合大规模的数据恢复但它的数据一致性和完整性较差。
- Redis 需要手动开启AOF持久化方式,默认是每秒将写操作日志追加到AOF文件中。
- AOF 的数据完整性比RDB高,但记录内容多了,会影响数据恢复的效率。
- Redis 针对 AOF文件大的问题,提供重写的瘦身机制。
- 若只打算用Redis 做缓存,可以关闭持久化。
- 若打算使用Redis 的持久化。建议RDB和AOF都开启。其实RDB更适合做数据的备份,留一后手。AOF出问题了,还有RDB。
4.Redis 事务(部分支持事务)
是什么?
可以一次执行多个命令,本质是一组命令的集合,一个事务中的命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞。
能干嘛?
一个队列中,一次性、顺序性、排他性地执行一系列命令
怎么玩?
#标记一个事务地开始
MULTI
#执行事务内所有的命令
EXEC
#取消事务,放弃行事务内所有的命令
DISCARD
#监视一个或多个key,如果在事务执行(EXEC)之前,这个或这些key被其它命令所改动,那么事务将被打断(全体连坐),相当于乐观锁。
WATCH
#取消监视,在执行完一次事务后,会自动UNWATCH
UNWATCH
事务有五种执行情况:
Case1:正常执行
Case2:放弃事务(DISCARD)
Case3:全体连坐(在EXEC前就出现error时,一般是命令敲错了,可以理解为编译不通过)
Case4:冤头债主(在EXEC后才发现错误,可以理解为编译通过,但运行错误,例如对一个string类型的key执行INCR操作)
Case4:WATCH监控
5.Redis 有哪些架构模式?讲讲各自的特点?
单机版单机版
特点:简单
问题:
1、内存容量有限 2、处理能力有限 3、无法高可用。
主从复制读写分离主从复制
用法:
SLAVEOF 主节点IP 主节点端口
#使当前数据库停止与其它数据库的同步,转为主数据库
SLAVEOF on one
#查看当前节点的主从情况
info replication
特点:
- 只有主能写入,从只能读
- 主死,从默认待命,等主回来,从重新连接主
- 从死,需要重新执行SLAVEOF命令才能成为从节点。
常用的三招:
一主二仆
薪火相传
反客为主
问题:
无法保证高可用
没有解决 master 写的压力
哨兵模式哨兵模式(自动版的反客为主)
哨兵模式需要配置sentinel,conf文件
#sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 192.168.1.57 7000 1
#启动哨兵
redis-sentinel sentinel.conf
原理
Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性:
监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作。
特点
1、保证高可用
2、监控各个节点
3、自动故障迁移
4、如果原来的老大回来了,它会被哨兵设置为slave
缺点
主从模式,切换需要时间丢数据
没有解决 master 写的压力
Jedis sharding集群
Jedis Sharding可以说是在Redis cluster出来之前业界普遍的采用方式,其主要思想是采用hash算法将存储数据的key进行hash散列,这样特定的key会被定为到特定的节点上。
庆幸的是,Java Redis客户端驱动Jedis已支持Redis Sharding功能,即ShardedJedis以及结合缓存池的ShardedJedisPool
Jedis的Redis Sharding实现具有如下特点:
采用一致性哈希算法,将key和节点name同时hashing,然后进行映射匹配,采用的算法是MURMUR_HASH。采用一致性哈希而不是采用简单类似哈希求模映射的主要原因是当增加或减少节点时,不会产生由于重新匹配造成的rehashing。一致性哈希只影响相邻节点key分配,影响量小。
为了避免一致性哈希只影响相邻节点造成节点分配压力,ShardedJedis会对每个Redis节点根据名字(没有,Jedis会赋予缺省名字)会虚拟化出160个虚拟节点进行散列。根据权重weight,也可虚拟化出160倍数的虚拟节点。用虚拟节点做映射匹配,可以在增加或减少Redis节点时,key在各Redis节点移动再分配更均匀,而不是只有相邻节点受影响。
ShardedJedis支持keyTagPattern模式,即抽取key的一部分keyTag做sharding,这样通过合理命名key,可以将一组相关联的key放入同一个Redis节点,这在避免跨节点访问相关数据时很重要。
当然,Redis Sharding这种轻量灵活方式必然在集群其它能力方面做出妥协。比如扩容,当想要增加Redis节点时,尽管采用一致性哈希,毕竟还是会有key匹配不到而丢失,这时需要键值迁移。
作为轻量级客户端sharding,处理Redis键值迁移是不现实的,这就要求应用层面允许Redis中数据丢失或从后端数据库重新加载数据。但有些时候,击穿缓存层,直接访问数据库层,会对系统访问造成很大压力。
集群(proxy型)模式集群(proxy型)模式
Twemproxy 是一个 Twitter 开源的一个 redis 和 memcache 快速/轻量级代理服务器; Twemproxy 是一个快速的单线程代理程序,支持 Memcached ASCII 协议和 redis 协议。
特点
1、多种 hash 算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins
2、支持失败节点自动删除
3、后端 Sharding 分片逻辑对业务透明,业务方的读写方式和操作单个 Redis 一致
缺点
增加了新的 proxy,需要维护其高可用。
failover 逻辑需要自己实现,其本身不能支持故障的自动转移可扩展性差,进行扩缩容都需要手动干预
Redis官方 Cluster集群模式Redis官方 Cluster集群模式
即使使用哨兵,redis每个实例也是全量存储,每个redis存储的内容都是完整的数据,浪费内存且有木桶效应。为了最大化利用内存,可以采用cluster群集,就是分布式存储。即每台redis存储不同的内容。
采用redis-cluster架构正是满足这种分布式存储要求的集群的一种体现。redis-cluster架构中,被设计成共有16384个hash slot。每个master分得一部分slot,其算法为:hash_slot = crc16(key) mod 16384 ,这就找到对应slot。采用hash slot的算法,实际上是解决了redis-cluster架构下,有多个master节点的时候,数据如何分布到这些节点上去。key是可用key,如果有{}则取{}内的作为可用key,否则整个可以是可用key。群集至少需要3主3从,且每个实例使用不同的配置文件。
在redis-cluster架构中,redis-master节点一般用于接收读写,而redis-slave节点则一般只用于备份,其与对应的master拥有相同的slot集合,若某个redis-master意外失效,则再将其对应的slave进行升级为临时redis-master。
在redis的官方文档中,对redis-cluster架构上,有这样的说明:在cluster架构下,默认的,一般redis-master用于接收读写,而redis-slave则用于备份,当有请求是在向slave发起时,会直接重定向到对应key所在的master来处理。但如果不介意读取的是redis-cluster中有可能过期的数据并且对写请求不感兴趣时,则亦可通过readonly命令,将slave设置成可读,然后通过slave获取相关的key,达到读写分离。
例如,我们假设已经建立了一个三主三从的redis-cluster架构,其中A、B、C节点都是redis-master节点,A1、B1、C1节点都是对应的redis-slave节点。在我们只有master节点A,B,C的情况下,对应redis-cluster如果节点B失败,则群集无法继续,因为我们没有办法再在节点B的所具有的约三分之一的hash slot集合范围内提供相对应的slot。然而,如果我们为每个主服务器节点添加一个从服务器节点,以便最终集群由作为主服务器节点的A,B,C以及作为从服务器节点的A1,B1,C1组成,那么如果节点B发生故障,系统能够继续运行。节点B1复制B,并且B失效时,则redis-cluster将促使B的从节点B1作为新的主服务器节点并且将继续正确地操作。但请注意,如果节点B和B1在同一时间发生故障,则Redis群集无法继续运行。
注意
(1)redis-cluster最小配置为三主三从,当1个主故障,大家会给对应的从投票,把从立为主,若没有从数据库可以恢复则redis群集就down了。
(2)在这个redis cluster中,如果你要在slave读取数据,那么需要带上readonly指令。redis cluster的核心的理念,主要是用slave做高可用的,每个master挂一两个slave,主要是做数据的热备,当master故障时的作为主备切换,实现高可用的。redis cluster默认是不支持slave节点读或者写的,跟我们手动基于replication搭建的主从架构不一样的。slave node要设置readonly,然后再get,这个时候才能在slave node进行读取。对于redis -cluster主从架构,若要进行读写分离,官方其实是不建议的,但也能做,只是会复杂一些。具体见下面的章节。
(3)redis-cluster的架构下,实际上本身master就是可以任意扩展的,你如果要支撑更大的读吞吐量,或者写吞吐量,或者数据量,都可以直接对master进行横向扩展就可以了。也扩容master,跟之前扩容slave进行读写分离,效果是一样的或者说更好。
(4)可以使用自带客户端连接:使用redis-cli -c -p cluster中任意一个端口,进行数据获取测试。
6.什么是一致性哈希算法?什么是哈希槽?
一致性hash
https://www.cnblogs.com/lpfuture/p/5796398.html
哈希槽(Redis 集群没有使用一致性hash, 而是引入了哈希槽的概念。)
关系:cluster>node>slot>key
https://blog.csdn.net/z15732621582/article/details/79121213
用了哈希槽的概念,而没有用一致性哈希算法,不都是哈希么?这样做的原因是为什么呢?
Redis Cluster是自己做的crc16的简单hash算法,没有用一致性hash。Redis的作者认为它的crc16(key) mod 16384的效果已经不错了,虽然没有一致性hash灵活,但实现很简单,节点增删时处理起来也很方便。
一致性哈希和哈希槽对比
7.使用过Redis分布式锁么,它是怎么实现的?
先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。
如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?
set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!
8.使用过Redis做异步队列么,你是怎么用的?有什么缺点?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
缺点
在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
能不能生产一次消费多次呢?
使用pub/sub主题订阅者模式,可以实现1:N的消息队列。
9.什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免?
缓存穿透
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
如何避免?
1:不存在也缓存:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
2:布隆过滤器:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。
缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。
如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。