概述
谈谈你对Redis 的理解
redis是一种高性能的NoSQL 键值对存储型数据库,他支持多种数据结构
它是将数据存储在内存中,所以它的效率非常高,读写速度很快
redis还支持事务,我们一般都是将redis作为缓存来使用。
除此之外,redis也经常用来做分布式锁、持久化、多种集群方案
Redis的优缺点
redis优点:
-
redis将数据存储在内存,速度快
-
相比于memcache,redis还支持数据持久化
-
支持多种数据结构,有String、List、Set、ZSet、Hash等五大数据结构还有其他的数据结构
-
支持主从复制,主机会自动将数据同步在从机,进行读写分离
缺点:
- 因为redis将数据存储在内存中,它会收到物理内存的限制,不太适合海量的高性能数据读写
- redis宕机时,不具备自动容错和恢复功能
数据类型
Redis有多少种数据类型
String、List、Set、ZSet、Hash
还有bitmaps、hyperloglogs 、geospatial
- Redis五种数据类型的应用场景
数据类型 | 存储值 | 应用场景 |
---|---|---|
String | 字符串、整数或者浮点数 | 做简单的键值对缓存、计数器 |
List | 列表 | 存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的数据 |
Set | 无序集合 | 交集、并集、差集的操作,比如交集,可以把两个人的粉丝列表整一个交集 |
ZSet | 有序集合 | 去重但可以排序,如获取排名前几名的用户 |
Hash | 包含键值对的无序散列表 | 结构化的数据,比如一个对象 |
Redis持久化
什么是Redis持久化
持久化就是把内存的数据保存在磁盘中,防止宕机时,数据丢失
Redis的持久化机制
redis提供了两种持久化机制RDB(默认)和AOF
RDB
RDB是将支持当前数据的快照存成一个数据文件的持久化机制
优点
- 只有一个文件dump.rdb,容灾行好,一个文件可以保存0到安全的磁盘
- 性能最大化,他会fork一个子进程来完成数据库的持久化,主进程继续处理命令,保证的redis 的高性能
- 恢复速度更快
缺点
-
数据安全性较低。因为是间隔一段时间进行持久化,如果在持久化期间发生事故,会丢失数据
(适合对数据安全要求不是很苛刻的情况下)
AOF
aof默认不开启,需要自己配置
优点
- 数据安全,每进行一次命令就会将其操作记录到aof文件中
缺点
- 因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢
如何选择合适的持久化方式
- 如果想要最安全的方式,两种都开启
- 如果数据安全性的问题不是很重要可以开启默认的RDB
Redis内存相关
expire设置key 的过期时间,如何处理过期的数据
- 定时删除,定时去清理过期的缓存 ttl
- 惰性删除,当请求过来时,再判断这个key是否已经过期,是则去底层数据库获取数据
- 定期删除,每隔一段时间就去检查内存中是否存在过期的key,将其删除
Redis的过期键的删除策略
①、定时删除
在设置某个key 的过期时间同时,我们创建一个定时器,让定时器在该过期时间到来时,立即执行对其进行删除的操作。
优点:定时删除对内存是最友好的,能够保存内存的key一旦过期就能立即从内存中删除。
缺点:对CPU最不友好,在过期键比较多的时候,删除过期键会占用一部分 CPU 时间,对服务器的响应时间和吞吐量造成影响。
②、惰性删除
设置该key 过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key。
优点:对 CPU友好,我们只会在使用该键时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查。
缺点:对内存不友好,如果一个键已经过期,但是一直没有使用,那么该键就会一直存在内存中,如果数据库中有很多这种使用不到的过期键,这些键便永远不会被删除,内存永远不会释放。从而造成内存泄漏。
③、定期删除
每隔一段时间,我们就对一些key进行检查,删除里面过期的key。
优点:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除,也能有效释放过期键占用的内存。
缺点:难以确定删除操作执行的时长和频率。
如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好。
如果执行的太少,那又和惰性删除一样了,过期键占用的内存不会及时得到释放。
另外最重要的是,在获取某个键时,如果某个键的过期时间已经到了,但是还没执行定期删除,那么就会返回这个键的值,这是业务不能忍受的错误。
Redis的内存淘汰策略
全局的键空间选择性移除
- no-eviction:当内存不足以容纳新写入数据时,新写入操作会报错。
- allkeys-lru(least ):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。(这个是最常用的)
- allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
设置过期时间的键空间选择性移除
- volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
总结
Redis的内存淘汰策略的选取并不会影响过期的key的处理。内存淘汰策略用于处理内存不足时的需要申请额外空间的数据;过期策略用于处理过期的缓存数据。
Redis的内存用完了怎么办
如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。
Redis如何做内存优化
好好利用基本数据类型,通常情况下可以将小的key-value使用更加紧凑的方式方一起,比如多个key-value优化成一个key-value
事务
什么是事务
事务就是一组不可分割的操作集合。当多个应用程序并发访问数据库时,事务可以在这些应用程序之间提供一个隔离方法,防止彼此的操作互相干扰
ACID概述
-
原子性(Atomicity ):
值事务是一个不可分割的工作单位,事务中的操作要么全成功,要么全失败
-
一致性(Consistency):
这个一致性依赖于开发者,以下为转账的例子
read(A); //开启事务
A = A – 100;
write(A);
read(B);
B = B + 50; //
write(B); //结束事务
//没有体现出事务的一致性
-
隔离性(Isolation):
多个事务并发执行时,事物之间不会相互影响
-
持久性(Durability):
事务一旦被提交,它对数据库中数据的改变就是永久的,不会因为数据库服务停止而丢失
Redis事务的三个阶段
事务开始:multi
命令入队
执行事务:exec
Redis事务相关命令
Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的
Redis会将一个事务中的所有命令序列化,然后按顺序执行。
- redis 不支持回滚,“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 所以 Redis 的内部可以保持简单且快速。
- 如果在一个事务中的命令出现错误,那么所有的命令都不会执行;
- 如果在一个事务中出现运行错误,那么正确的命令会被执行。
# 运行出错 正确的命令将会继续执行,如果是命令错误则事务失败
localhost:1>multi
"OK"
localhost:1>set k1 1
"QUEUED"
localhost:1>incr k1
"QUEUED"
localhost:1>set k2 QWE
"QUEUED"
localhost:1>incr k2
"QUEUED"
localhost:1>get k1
"QUEUED"
localhost:1>exec
1) "OK"
2) "2"
3) "OK"
4) "ERR value is not an integer or out of range"
5) "2"
- WATCH 命令是一个乐观锁(在multi之前执行),可以为 Redis 事务提供 CAS行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。
- UNWATCH命令可以取消watch对所有key的监控。
- MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
- EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。
- DISCARD:放弃事务,并且客户端会从事务状态中退出。
Redis事务支持隔离性吗?
Redis是单线程程序,它保证在执行事务的时候,不会将事务其中断,事务运行直到执行完事务队列中所有命令为止
因此Redis的事务总是带有隔离性
Redis事务保证原子性吗,支持回滚吗?
因为Redis是单线程的程序,他的单条命令是原子性的,但是他的事务不支持原子性,如果有一条命令执行失败(不是语法错误),剩下的命令还是会继续完成,所以不保证原子性,也不支持回滚
Redis事务的其他实现(空)
集群方案
Redis的主从架构
主从架构 -> 读写分离 -> 水平扩容支撑读高并发
Redis主从复制原理(不熟悉)
主从复制的几种模式:
- 一主一从:最简单的主从模型,主机将数据同步给从机,从机提供查询,另外从机可以开启aof,既能保证效率,又可靠。
- 一主多从:一个主机,多个从机,主机提供数据写入,从机提供数据查询
- 主从从结构(树形扩展):一主多从的架构会导致master的数据同步压力过大,所以可以采用树形拓扑的方式,主机只需要同步给其中一台slave,由这台salve再同步给其它的从机。
集群之间异步复制
redis 主从复制的核心原理
当启动一个 slave node 的时候,它会发送一个 PSYNC
命令给 master node。
如果这是 slave node 初次连接到 master node,那么会触发一次 full resynchronization
全量复制。此时 master 会启动一个后台线程,开始生成一份 RDB
快照文件,
同时还会将从客户端 client 新收到的所有写命令缓存在内存中。RDB
文件生成完毕后, master 会将这个 RDB
发送给 slave,slave 会先写入本地磁盘,然后再从本地磁盘加载到内存中,
接着 master 会将内存中缓存的写命令发送到 slave,slave 也会同步这些数据。
slave node 如果跟 master node 有网络故障,断开了连接,会自动重连,连接之后 master node 仅会复制给 slave 部分缺少的数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZKKYPerI-1597901014138)(D:\图片\redis.png)]
哨兵模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4qYdCeRD-1597901014143)(D:\图片\redis2.png)]
- 集群监控:监控master和salve是否正常工作
- 消息通知:当一个哨兵发现master挂掉了,就会通知其他哨兵和其他redis服务,其他哨兵也会也检测master是否真的挂了
- 故障转移:如果半数以上的哨兵都觉得他挂了,他就真挂了,如何会选出一个哨兵当作代表,去salve中选择一个新的master
- 配置中心:如果故障转移发生了,会给客户端提高一个新master地址
注意:
- 哨兵模式需要多个实例来保证自己的健壮性
- 哨兵 + redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性
分布式问题
Redis实现互斥锁
public String get(String key) {
String value = redis.get(key);
if (value == null) {
if (redis.setnx(key_mutex, "1")) {
// 3 min timeout to avoid mutex holder crash
redis.expire(key_mutex, 3 * 60)
value = db.get(key);
redis.set(key, value);
redis.delete(key_mutex);
} else {
//其他线程休息50毫秒后重试
Thread.sleep(50);
get(key);
}
}
}
实现分布式锁(空)
缓存异常
缓存雪崩
缓存雪崩指大面积的缓存失效(缓存过期),后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉
解决方案–对症下药
-
如果是因为缓存过期导致的,可以在设置缓存过期时间的时候再加上一个随机数
-
如果是因为redis服务器挂掉,而导致请求全部走的数据库
- 事发前:可以实现Redis的高可用方案①主从架构+哨兵模式②使用Redis Cluster模式,来尽量避免这种情况的发生
- 事发后:万一Redis真的挂了,就使用设置本地缓存(ehcache)+限流+降级(hystrix)
-
并发量不是很大的情况下可以使用互斥锁(setnx),用来重建缓存
第一个用户发起请求
查找缓存,缓存中没有
setnx设置,设置一个缓存标记(设置其过期时间,不然可能会造成资源锁死)
- 然后第一个线程会从数据库中获取资源,获取成功后则更新缓存
- 其他的线程发现这个缓存标记后就进入等待(50ms的样子),然后再从缓存中查询一次
缓存穿透
缓存穿透指请求查询了缓存和数据库中都没有的数据,导致所有的请求都落在了数据库上,数据库因为短时间内承受大量请求而崩掉
解决方案
-
如果缓存中取不到,数据库中也不存在的话,就将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
-
使用布隆过滤器来过滤请求
什么是布隆过滤器
本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。
缓存击穿
缓存击穿指请求查询缓存中没有而数据库中有的数据,这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案
- 设置热点数据永不过期
- 使用setnx命令设置互斥锁,用来将重建缓存
缓存预热
- 数据量不大的情况下,可以在项目启动的时候自动将数据加载至缓存
- 定时刷新页面
缓存降级(空)
热点数据和冷数据(空)
缓存热点key(空)
其他
Redis和Memcache的区别
对比 | Redis | Memcache |
---|---|---|
数据存储类型 | 支持多种数据结构,String、List、Set、ZSet、Hash等 | 支持的数据结构单一 |
事务 | 支持事务 | 不支持 |
持久化 | 支持持久化,有RDB(默认)和AOF两种策略可选 | 不支持 |
集群 | 支持集群,有哨兵模式 | 没有原生的集群模式 |
网络IO模型 | 多路IO复用模型 | 多线程,非阻塞IO模式 |
Redis异步队列如何实现
使用List数据结构,rpush生产消息,lpop消费消息(或者lpush、rpop)
Redis回收采用的是什么算法
LRU(least recently used) 最近最少使用算法
Redis常见性能问题和解决方案?
- master最好不要做持久化工作(不明确原因)
- 为了主从复制的速度和连接稳定性,Slave和Master最好在同一个局域网内
- 尽量避免在压力较大的主库上增加从库