搜索_哔哩哔哩-bilibili
1. 什么是redis
Redis是C语言开发的一个开源的(遵从BSD协议)高性能键值对(key-value)的内存数据库,可以用作数据库、缓存、消息中间件等。它是一种NoSQL(not-only sql,泛指非关系型数据库)的数据库
- 性能优秀, 数据在内存中,读写速度非常快,支持并发10W QPS
- 单进程单线程,是线程安全的,采用IO多路复用机制
- 丰富的数据类型,支持字符串(strings)、(哈希)散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等;
- 支持数据持久化。可以将内存中数据保存在磁盘中,重启时加载;
- 多种架构模式 单体, 主从复制,哨兵,集群
- 可以用作分布式锁
2. Redis支持的数据类型?
- String(字符串)
string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
string类型是Redis最基本的数据类型,一个键最大能存储512MB。
set key value get key
2. Hash(哈希)
Redis hash 是一个键值(key=>value)对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
hmset name key1 value1 key2 value2
hget name key2
hgetall name
hdel name key1
3. list (列表) (value值 可以相同)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
在 key 对应 list 的头部添加字符串元素
lpush name value
在 key 对应 list 的尾部添加字符串元素
rpush name value
返回 key 对应 list 的长度
llen name
读取列表
lrange name 0 10
4. Set(集合)
Redis的Set是string类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
sadd name value
获取key 的全部成员
smembers name
5. zset(sorted set:有序集合)
Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复。
可以做排行榜功能
zadd name score value
zadd code 1 java
zadd code 2 css
zadd code 3 html
zrange code 0 2 WITHSCORES
3. Redis和Memcached的区别
- 存储方式上:memcache会把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。redis有部分数据存在硬盘上,这样能保证数据的持久性
- 数据支持类型上:memcache对数据类型的支持简单,只支持简单的key-value,,而redis支持五种数据类型
- value的大小:redis可以达到1GB,而memcache只有1MB
4. redis的持久化机制
数据缓存在了内存中,但是会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件中,以保证数据的持久化
Redis的持久化策略有两种:
- RDB:RDB是一种快照存储持久化方式,具体就是将Redis某一时刻的内存数据保存到硬盘的文件当中,默认保存的文件名为dump.rdb,而在Redis服务器启动时,会重新加载dump.rdb文件的数据到内存当中恢复数据。
- AOF: AOF持久化方式会记录客户端对服务器的每一次变更操作命令,并将这些操作以Redis协议追加保存到以后缀为aof文件末尾,在Redis服务器重启时,会加载并运行aof文件的命令,以达到恢复数据的目的。(日志形式)
5. RDB 优缺点
RDB的几个优点
- 与AOF方式相比,通过rdb文件恢复数据比较快。
- rdb文件非常紧凑,适合于数据备份。
- 通过RDB进行数据备,由于使用子进程生成,所以对Redis服务器性能影响较小。
RDB的几个缺点
- 如果服务器宕机的话,采用RDB的方式会造成某个时段内数据的丢失,比如我们设置10分钟同步一次或5分钟达到1000次写入就同步一次,那么如果还没达到触发条件服务器就死机了,那么这个时间段的数据会丢失。
- 使用save命令会造成服务器阻塞,直接数据同步完成才能接收后续请求。
- 使用bgsave命令在forks子进程时,如果数据量太大,forks的过程也会发生阻塞,另外,forks子进程会耗费内存。虽然fork时,子进程不会复制父进程的数据空间,但是会复制内存页表(页表相当于内存的索引、目录);父进程的数据空间越大,内存页表越大,fork时复制耗时也会越多。
6. AOF
与RDB存储某个时刻的快照不同,AOF持久化方式会记录客户端对服务器的每一次变更操作命令,并将这些写操作以Redis协议追加保存到以后缀为aof文件末尾
Redis默认不开启AOF持久化方式
# 开启aof机制
appendonly yes
# aof文件名
appendfilename "appendonly.aof"
# 写入策略,always表示每个写操作都保存到aof文件中,也可以是everysec或no
appendfsync always
# 默认不重写aof文件
no-appendfsync-on-rewrite no
# 保存目录 dir ~/redis/
三种写入策略
appendfsync always
# appendfsync everysec
# appendfsync no
1. always
客户端的每一个写操作都保存到aof文件当,这种策略很安全,但是每个写请注都有IO操作,所以也很慢。
2. everysec
appendfsync的默认写入策略,每秒写入一次aof文件,因此,最多可能会丢失1s的数据。
3. no
Redis服务器不负责写入aof,而是交由操作系统来处理什么时候写入aof文件。更快,但也是最不安全的选择,不推荐使用。
AOF的优点
- AOF只是追加日志文件,因此对服务器性能影响较小,速度比RDB要快,消耗的内存较少。
- 数据更加完整
AOF的缺点
- AOF方式生成的日志文件太大,即使通过AFO重写,文件体积仍然很大。
- 恢复数据的速度比RDB慢。
7. 内存淘汰策略
redis 4 之前
Redis有六种淘汰策略
去掉 noeviction 可以分为两类三种
两类: 从已设置过期的中 和 所有的
三种: 最近最少使用, 随机淘汰, 剩余时间短
策略 | 描述 |
volatile-lru | 从已设置过期时间的KV集中优先对最近最少使用(less recently used)的数据淘汰 |
volitile-ttl | 从已设置过期时间的KV集中优先对剩余时间短(time to live)的数据淘汰 |
volitile-random | 从已设置过期时间的KV集中随机选择数据淘汰 |
allkeys-lru | 从所有KV集中优先对最近最少使用(less recently used)的数据淘汰 |
allKeys-random | 从所有KV集中随机选择数据淘汰 |
noeviction | 不淘汰策略,若超过最大内存,返回错误信息 |
8. 过期键删除
- 定时删除:在设置键的过期时间的同时,创建定时器,让定时器在键过期时间到来时,即刻执行键值对的删除;
- 定期删除:每隔特定的时间对数据库进行一次扫描,检测并删除其中的过期键值对;
- 惰性删除:键值对过期暂时不进行删除,至于删除的时机与键值对的使用有关,当获取键时先查看其是否过期,过期就删除,否则就保留;
三种策略都有各自的优缺点:定时删除对内存使用率有优势,但是对CPU不友好,惰性删除对内存不友好,如果某些键值对一直不被使用,那么会造成一定量的内存浪费,定期删除是定时删除和惰性删除的折中。
Reids采用的是惰性删除和定时删除的结合
9. 主从复制
只要主从服务器之间的网络连接正常,主从服务器两者会具有相同的数据,主服务器就会一直将发生在自己身上的数据更新同步 给从服务器,从而一直保证主从服务器的数据相同。
主从复制的作用
主从复制的作用主要包括:
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
- 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础
10. redis 集群
- 数据分区
采用 虚拟槽分区算法, 这个槽是用来存放缓存信息的单位,在 Redis 中将存储空间分成了 16384 个槽, 卡槽均匀分布在主节点上. 集群会对 Key 进行 CRC16算法得出一个值,使用这个值对卡槽(16384)数量取余数, 算出key 应该位于哪个槽上.
通过 CRC16(key)%16383 计算出 Slot 的值,假设计算的结果是 5002。
2. 缓存节点之间的通讯
- Redis Cluster 的每个缓存节点都会开通一个独立的 TCP 通道,用于和其他节点通讯。
- 有一个节点定时任务,每隔一段时间会从系统中选出“发送节点”。这个“发送节点”按照一定频率,例如:每秒 5 次,随机向最久没有通讯的节点发起 Ping 消息。
- 接受到 Ping 消息的节点会使用 Pong 消息向“发送节点”进行回复。
3. 缓存节点的扩展和收缩
上线
由于每个节点中保存着槽数据,因此当缓存节点数出现变动时,这些槽数据会根据对应的虚拟槽算法被迁移到其他的缓存节点上
下线
下线操作正好和上线操作相反,将要下线缓存节点的槽数据分配到其他的缓存主节点中
11. 缓存雪崩
由于原有缓存失效(或者数据未加载到缓存中),新缓存未到期间所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机,造成系统的崩溃。
解决方式:
- 不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀, 防止同一时间redis 的key失效, 导致查询都请求到数据库
- 做集群, 高可用, 防止redis宕机导致的查询都请求到数据库
12. 缓存穿透
缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
解决方式:
- 如果查询数据库也为空,直接设置一个默认值存放到缓存,到期时间设置的特别短,这样第二次到缓冲中获取就有值了,而不会继续访问数据库
13. 缓存击穿 热点数据
key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
- 设置热点数据永远不过期
- 加锁
14. Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?
SCAN cursor [MATCH pattern] [COUNT count]
// 查询以wang开头
scan 0 MATCH wang*
15. 缓存和数据库数据一致性问题
双写模式
修改完数据库就把缓存中的也修改
A线程改完数据库,在要修改缓存时,B线程也修改了数据库,同时B线程先把缓存修改了,这导致A线程再修改缓存,使得缓存的数据库是旧数据了,导致脏数据了。需要等缓存过期,或者数据又被修改了。
解决脏数据问题
修改数据库和修改缓存放到同一个锁里,这样只有修改完缓存,锁才会释放,下一个线程才能修改这条数据。 因为加锁,降低了性能。
失效模式
修改完数据库就把缓存删除
A线程改完数据库,并删除完缓存,B线程也在修改数据库时,C线程来读取缓存,这时候缓存因为删除了,会直接读取数据库并往缓存中更新(拿到的是A的值),如果C更新缓存是在B删除缓存之后,将导致C把A的值更新到缓存中。
解决脏数据问题
修改数据库和修改缓存放到同一个锁里,这样只有修改完缓存,锁才会释放,下一个线程才能修改这条数据。 因为加锁,降低了性能。
3w字深度好文|Redis面试全攻略,读完这个就可以和面试官大战几个回合了
3w字深度好文|Redis面试全攻略,读完这个就可以和面试官大战几个回合了
作者:张君鸿
链接:https://juejin.im/post/5d09a9ff51882577eb133aa9
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
作者:坚持就是胜利
链接:https://juejin.im/post/5dccf260f265da0bf66b626d
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。