1.Redis持久化机制
Redis是一个支持持久化的内存数据库,通过持久化机制把内存的数据同步到硬盘文件来保证数据的持久化。 当Redis重启后通过把硬盘文件重新加载到内存,达到回复数据的目的。
实现:单独创建fork()一个子进程,将当前父进程的数据库数据复制到子进程的内存中,然后由子进程写入到临时的文件中,持久化的过程就结束了。再用这个临时的文件替换上次的快照文件,然后子进程退出。内存释放。
RDB的Redis默认的持久化方式,按照一定的时间周期策略吧内存的数据以快照方式保存到硬盘的二进制文件。
AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件的最后,类似于MySQL的binlog
当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
当两种方式同时开启是,数据恢复Redis会优先选择AOF恢复。
2.缓存雪崩,缓存穿透,缓存预热、缓存更新,缓存降级
一.缓存雪崩:由于原有缓存失效,新的缓存未到期间。所有原本应该访问缓存的请求都去查数据库了,而对数据库cpu的内存造成巨大的压力,严重会造成数据库的宕机。从而形成一系列连锁反应,造成整个系统崩溃。
解决办法:大多数系统设计者考虑用加锁或者列队的方式保证不会有大量的线程对数据库一次性进行读写。从而避免失效时大量的并发请求落到底层存储系统上。简单的方案是将缓存失效时间的分散开。
二.缓存穿透:用户查询数据,在数据库没有,自然在缓存中自然也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库在查询一遍,然后返回空。这样请求就让过缓存直接查数据库,这也是经常提到的缓存命中率问题。
解决办法:
1.采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉。避免对底层存储系统的查询压力。
2.如果一个查询返回的数据为空,我们任然把这个空结果进行缓存,但他的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存。这样第二次到缓存中就有值了,而不会继续访问数据库。
三.缓存预热
是系统上线后,将相关的缓存数据直接加载到缓存系统。
作用: 避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的缓存数据。
解决方法:
1.直接写个缓存刷新页面,上线时手工操作下
2.数据量不大,可以在项目启动的时候自动进行加载
3.定时刷新缓存。
四.缓存更新
除了缓存服务器自带的缓存失效策略之外,我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
1.定时去清理过期的缓存;
2.当用户请求过来时,在判断这个请求所用到的缓存是否过期,过期的话就去底层得到新的数据并更新缓存。
第一种的缺点是维护大量缓存的key是比较麻烦的。
第二种的缺点是每次用户的请求发过来时都要判断缓存失效,逻辑比较复杂,
五.缓存降级
当访问量剧增,服务出现问题会非核心服务影响到核心流程的性能时,仍然需要保证服务是可用的,即使有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
降级的目的是保证核心的服务可用,而有些服务是无法降级的(如加入购物车、结算)。
3.热点数据和冷数据
对于冷数据而言,大部分数据可能还没有被访问到就已经被挤出内存,不仅占用内存,而且价值不高。
热点数据就是在数据更新之前至少读取两次,这个数据经常被读取。
4.Memecache与Redis的区别
1.存储方式:Memecache把数据全部存储到内存中,断电数据会消失。数据不能超过内存大小。Redis有部分数据存在硬盘上,Redis可以持久化数据。
2.数据支持的类型 memecache所有的值都是简单的字符串,Redis作为其替代者,支持丰富的数据类型
String,list,set,zset,hash等数据结构的存储。
3.使用底层模型不同,他们之间底层实现的方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
4.value值大小不同:Redis最大可以达到1gb;memcache只有1mb.
5.redis的速度比memcache快很多
6.Redis支持数据的备份,及master-slave模式的数据备份。
5.单线程的Redis为什么这么快
1.纯内存操作
2.单线程操作,避免了频繁的上下文切换
3.采用了非阻塞I/O多路复用机制
6.Redis的数据类型,以及每种数据类型的使用场景。
1.string
get/set操作,value可以是String页可以是数字。一般做一些复杂的计数功能的缓存。
2.hash
存放的结构化的对象,比较方便而定就是操作其中的某一个字段。
3.list
使用list的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用Irange命令,做基于Redis的分页功能,性能极佳,用户体检好。
4.set
set堆放的是一堆不重复值的集合,所以可以做全局去重的功能,为什么不用jvm自带的set进行去重,因为我们的系统一般都是集群部署,使用jvm自带的set,比较麻烦,难道为了一个做一个全局去重,在起一个公共服务,太麻烦了。
利用交集、并集、差集等操作,可以计算共同爱好,全部爱好,自己独有的喜好等功能。
5.sorted set
多了一个权重参数score,集合中的元素能够按照score进行排列,可以做排行榜应用,取top n操作。
7.Redis的过期策略以及内存淘汰机制。
Redis采用的是定期删除+惰性删除策略。
为什么不用定时删除策略?
定时删除,用一个定时器来负责监视key,过期自动删除,虽然内存及时释放,但是十分消耗cpu资源。在大量并发请求下,cup要将时间应用在处理请求,而不是删除key,因此没有采用这一策略。
定期删除+惰性删除是如何工作的?
定期删除,Redis默认每一个100ms检查,是否有过期的key,有过期的key则删除。需要说明的是,Redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查。因此,如果采用定期删除,会导致很多key到时间没有删除。
惰性删除派上用场。也就是说在你获取某一个key的时候,Redis检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
缺点:
如果定期删除没有删除key,然后你又没有及时请求key,惰性删除也没有生效。这样Redis的内存会越来越高。经应该采用内存淘汰机制。
maxmemory-policy volatile-lru
就是配置内存淘汰策略的。
volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
volatile-random:从已设置过期的数据集中任意选择数据淘汰。
allkeys-lru:从数据集中挑选最近最少使用的数据淘汰。
allkeys-random:从数据集中任意选择数据淘汰
no-enviction:禁止驱逐数据,新写入操作会报错。
如果没有设置expire的key,不满足先决条件;那么volatile-lru,volatile-random和volatile-ttl策略的行为,和noeviction基本上一致。
8.Redis为什么单线程的。
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络宽带。既然单线程容易实现,而且cup不会成为瓶颈,那就顺利成章的采用单线程的方案了。Redis运用队列技术将并发访问变为串行访问。
1.绝大部分请求是纯粹的内存操作
2.采用单线程,避免了不必要的上下文切换和竞争条件
3.非阻塞IO优点
速度快
支持丰富数据类型
支持事务,操作都是原子性,对数据的修改要么全部执行,要么全部不执行。
丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除。
9.Redis常见性能问题和解决方案
1.master最好不要做任何持久化工作,
2.如果数据比较重要,某个slave开启aof备份数据,策略设置为每秒同步一次
3.为了主从复制的速度和连接的稳定性,master和slave最好在同一个局域网内
4.尽量避免在压力很大的主库上增加从库
5.主从复制不要用图状结构,用单向链表结构更为稳定。
10.为什么Redis的操作是原子性的,怎么保证原子性。
对于Redis而言,命令的原子性指的是:一个操作的不可再分,操作要么执行,要么不执行
Redis的操作之所以是原子性的,是因为Redis是单线程的
Redis本身提供的所有API都是原子性操作,Redis中的事务其实是要保证批量操作的原子性。
11.Redis的事务
Redis的事务功能通过MULTI、EXEC、DISCARD、WATCH四个原语实现。
1.Redis不支持回滚,所以Redis的内存可以保持简单切快速。
2.一个事务中的命令出错,所有命令都不会执行
3.一个事务中运行出现错误,正确的命令会被执行
MULTI开启事务,返回值是ok。MULTI执行后,客户端可以继续向服务器发送任意多的命令,这些命令不会被立即执行,而是会存放在一个队列中,当调用EXEC命令时,所有队列中的命令才会被执行。
EXEC:执行所有事务模块中的命令,返回事务内所有命令的返回值,按命令的执行顺序排列。当操作被打断时,返回值为空。
DISCARD,清空消息队列,并放弃执行事务,客户端从事务状态中退出。
WATCH 为Redis事务提供check-and- set行为。可以监控一个或多个键,一旦其中有一个键被修改,之后事务就不会执行,监控会一直持续到EXEC命令。