redis简介
redis是使用c语开发的数据库,除了做缓存之外,也经常被用作分布式锁、甚至是消息队列
常用的三种缓存读写策略
- Cache Aside Pattern(旁路缓存模式)
这个是我们平时使用比较多的一个读写模式
写:
先更新DB,然后直接删除cache
读:
从cache中读取数据,读取到就直接返回
cache读取不到的话,就从DB中读取数据返回
再把数据放到cache中
在写数据的过程中,不可以先删除cache,否则会导致数据库和缓存数据不一致的问题(缓存写入的速度比数据库快很多)所以一般不存在写数据过程中,先更新DB,后删除cache出现数据不一致的问题
缺陷1:首次请求数据一定不在 cache 的问题
解决办法:可以将热点数据可以提前放入cache 中。
缺陷2:写操作比较频繁的话导致cache中的数据会被频繁被删除,这样会影响缓存命中率 。
解决办法:
数据库和缓存数据强一致场景 :更新DB的时候同样更新cache,不过我们需要加一个锁/分布式锁来保证更新cache的时候不存在线程安全问题。
可以短暂地允许数据库和缓存数据不一致的场景 :更新DB的时候同样更新cache,但是给缓存加一个比较短的过期时间,这样的话就可以保证即使数据不一致的话影响也比较小。
- Read/Write Through Pattern(读写穿透)
服务端主要是把cache作为主要数据存储
写:
- 先查 cache,cache 中不存在,直接更新 DB。
- cache 中存在,则先更新 cache,然后 cache 服务自己更新 DB(同步更新 cache 和 DB)。
读:
- 从 cache 中读取数据,读取到就直接返回 。
- 读取不到的话,先从 DB 加载,写入到 cache 后返回响应。
- Write Behind Pattern(异步缓存写入)
Write Behind Pattern 和 Read/Write Through Pattern 很相似,两者都是由 cache 服务来负责 cache 和 DB 的读写。
但是,两个又有很大的不同:Read/Write Through 是同步更新 cache 和 DB,而 Write Behind Caching 则是只更新缓存,不直接更新 DB,而是改为异步批量的方式来更新 DB。
很明显,这种方式对数据一致性带来了更大的挑战,比如cache数据可能还没异步更新DB的话,cache服务可能就就挂掉了。
说一下你在项目中遇到的redis应用场景
- 5大value数据类型(场景)
- 基本上就是缓存
- 为的是服务无状态,延申思考,看你的项目有那些数据结构和对象,在单机里需要单机锁,在多机上需要分布式锁,抽出来放到redis中:
- 无锁化
redis是单线程还是多线层
无论什么版本工作线层就是一个
6.x高版本出现了IO多线程
【去学一下系统的IO课】你要真正了解面向IO模式编程的时候,有内核的事,从内核把数据搬运到程序里这是第一步,然后搬运回来的数据做的计算式第二步,netty
单线程,满足redis的串行原子,只不过IO多线程后,把输入、输出放到更多的线程里去并行,好处如下:1.执行时间短,更快 2. 更好的压榨系统及硬件的资源(网卡能够高效的使用)
redis存在线程安全吗? 为什么?
单线程,满足redis串行原子
redis可以保证内部串行
外界使用的时候要保障,业务上要自行保障顺序
redis热key问题
所谓热key问题就是突然有几十万的请求去访问redis上的某个特定key。那么这样就会造成流量过于集中,达到物理网卡上线,从而导致这台redis的服务器宕机
解决办法:
利用二级缓存,比如利用ehcache,或者一个hashmap都可以,在你发现热key以后,把热key放到系统的JVM中。针对这种热key请求,会直接从JVM中取,而不会走redis层。
遇到过缓存穿透吗,详细描述一下
缓存穿透是指查询一个数据库一定不存在的数据
如果数据库查询的对象为空,也放入缓存,key为用于提交过来的主键值,value为null,只是设定的缓存过期时间比较短,比如设置为60秒,从而保护我们的数据库
使用布隆过滤器
如何避免缓存雪崩?
产生原因:key的数量是N 过期
缓存在同一时间大面积的失效,后面的请求都直接落到了数据库上,造成数据库短时间内承受大量请求。 这就好比雪崩一样,摧枯拉朽之势,数据库的压力可想而知,可能直接就被这么多请求弄宕机了。
可以将缓存的数据设置不同的失效时间,这样就可以避免缓存数据在某个时间段集中失效。对一些热门的数据可以缓存的时间长一些,对于冷门的数据可以缓存的时间短一些,对一些特别热门的数据设置永不过期
以上问题,核心就是避免DB无效、重复请求
缓存如何回收的?/redis是怎样删除过期key的
后台在轮询,分段分批的删除那些过期的key
请求的时候判断时候已经过期了
缓存击穿
缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿透缓存,直接请求到数据库上
描述一下redis持久化及原理
RDB、AOF:主从同步也算持久化
高版本:开启AOF,AOF是可以通过执行日志得到全部内存数据的方式,但是追求性能
体积变大,重复无效指令 重写,后台用线程吧内存的kv生成指令写个新的aof
4.x新增更有新增模式,把重写方式换成直接RDB放到aof文件的头部,比2.1的方法快了,再追加日志
redis持久化原理
当前线程阻塞服务
fork + cow
rdb和aof的区别
rdb是redis的一个快照方式,如果redis在某一个时刻进行一个快照,如果发生宕机,那么这个时刻之后的数据一定会丢失
aof 把写操作指令持续的写到一个类似的文件里这样会导致文件越来越大 重写操作会压缩这个文件
4.x版本以后采用rdb和aof共同使用
如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。
redis主从复制
一般只需要配置从机就好
-
slaveof 127.0.0.1 6379
slaveof no one 让自己当老大info replication
-
也可以在配置文件中修改 replication配置
主机中可以写,从机负责读
主机断开链接,从机依旧是可以连接到主机的,但是没有写操作
如果使用命令行配置的主从,这个时候重启,这个从机会变成主机,只要变为从机,立马就会从主机中获取值
哨兵模式
自动选举master模式
能够后台监控主机是否故障,如果故障了根据投票数自动将从库切换为主库
采用多哨兵模式防止哨兵挂掉
sentinel monitor myredis 127.0.0.1 6379 1
1 代表主机挂了,slave投票看让谁接替成为主机
如果主机回来之后。只能归并到新的主机下,当作从机,这就是哨兵模式
优点:
redis事物
redis事物中的所有命令都会被序列化,按顺序地执行。事物在执行过程中,不会被其他客户端发送来的命令请求所打断
如果组队过程中发现错误 任务都不会执行,如果在执行的时候有错误,除了报错的任务其余的都会执行。
multi开启事物
exec执行事物
multi结束事物
discard:事物添加队列中取消队列
事物冲突:使用乐观锁和悲观锁
使用watch监听key,如果事物执行之前这个key其他命令所改动,那么事物将被打断 unwatch取消监听key
分布式锁
setnx用来抢锁,expire给锁设置一个过期时间,防止锁忘记释放
这个方案不是原子操作,如果执行完加锁,正要设置过期时间,进程crash或重启维护,别的线程永远获取不到锁。
if(jedis.setnx(key_resource_id,lock_value) == 1){ //加锁
expire(key_resource_id,100); //设置过期时间
try {
do something //业务请求
}catch(){
}
finally {
jedis.del(key_resource_id); //释放锁
}
}
为了保证加锁和设置过期时间是原子性和锁过期释放而业务还没有执行完,最好的办法是使用Redisson框架和lua脚本
if redis.call('get',KEYS[1]) == ARGV[1] then
return redis.call('del',KEYS[1])
else
return 0
end;
redisson底层原理
只要线程一加锁成功,就会启动一个watch dog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了「锁过期释放,业务没执行完」问题。
高级分布式算法 redlock
搞多个Redis master部署,以保证它们不会同时宕掉。并且这些master节点是完全相互独立的,相互之间不存在数据同步。同时,需要确保在这多个master实例上,是与在Redis单实例,使用相同方法来获取和释放锁。
redlock实现步骤
- 按顺序向5个master节点请求加锁
- 根据设置的超时时间来判断,是不是要跳过该master节点。
- 如果大于等于3个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦。
- 如果获取锁失败,解锁!