目录
基本介绍
Redis的适用场景
Redis的缺点
Redis支持五种数据类型
八种底层数据结构
遵守BSD协议
Redis相比memcached有哪些优势?
为什么Redis需要把所有数据放到内存中?
Redis是单进程单线程的
Redis的删除过期键
Redis有哪几种数据淘汰策略?
Redis的更新
缓存【失效】
缓存【命中】
缓存【更新】
为什么不采取更新后删除缓存?
为什么不删除缓存后再更新数据库?
Redis能否将数据持久化,如何实现?
RDB持久化原理Redis DataBase
AOF持久化原理Append-only file
Redis的三种集群方式:主从复制,哨兵模式和集群
主从复制
哨兵模式:
Redis-Cluster集群
RESP
缓存穿透
解决办法
缓存雪崩
解决办法
缓存击穿
解决办法
redis是一个基于内存的高性能key-value数据库,是非关系型数据库,适用于存储缓存用的数据,也适合需要高速读/写的场合使用它快速读/写。
1)会话缓存(Session Cache)
2)全页缓存(FPC)
3)队列
4)排行榜/计数器
5)发布/订阅
缓存穿透、缓存击穿、缓存雪崩
long类型的整数、整数集合 、embstr编码的简单动态字符串 、简单动态字符串 、双向链表 、压缩列表 、字典 (保存键值对的一种数据结构)、跳跃表+字典。
允许使用者修改和重新发布代码,也允许使用或在BSD代码基础上开发商业软件发布和销售,因此是适用于商业软件的。使用者使用时还必须做到满足三个条件:
1)如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议。
2)如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议。
3)不可以用开源代码的作者/机构名字和原来产品的名字做市场推广。
1)memcached所有的值均是简单的字符串,redis支持更为丰富的数据类型
2)redis的速度比memcached快很多
3)redis可以持久化其数据
4)存储方式 Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,这样能保证数据的持久性。
Redis为了达到最快的读写速度,所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上。所以redis具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度为严重影响redis的性能。在内存越来越便宜的今天,redis将会越来越受欢迎。
redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销
redis采用的是定期删除+惰性删除策略。
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
当前已用内存超过最大值时,触发主动清理策略。
主动清理策略主要有一下六种:
volatile-lru从已设置过期的数据集中挑选最近最少使用的淘汰volatile-ttl从已设置过期的数据集中挑选将要过期的数据淘汰volatile-random从已设置过期的数据集中任意挑选数据淘汰allkeys-lru从数据集中挑选最近最少使用的数据淘汰allkeys-random从数据集中任意挑选数据淘汰noenviction禁止淘汰数据客户端请求数据先从缓存中查询,如果没有再查询数据库,最后将数据放入缓存
客户端从缓存中直接取到数据,返回结果
客户端写入数据到数据库,成功之后,让缓存失效(下次请求时从缓存中拿不到,则查询数据库,再放入缓存)
防止并发写操作导致脏数据。
两个并发请求,一个读操作,一个写操作,如果先删除缓存,读操作会将【旧数据】写入缓存,写操作【更新数据】后也不会更新缓存,导致脏数据一直存在。
能,将内存中的数据异步写入硬盘中,两种方式:RDB(默认)和AOF。
指定的时间间隔内保存数据快照。实际操作过程是fork一个子进程出来,子进程将数据写入临时的rdb快照文件中,完成rdb快照文件的生成之后,就替换之前的旧的快照文件。是周期性的持久化
优点:Redis加载RDB恢复数据远远快于AOF的方式。
缺点:由于每次生成RDB开销较大,非实时持久化,有可能会丢失数据
以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。Redis通过rewrite的机制,让AOF文件不至于太庞大。就是大概是数据量多的时候,重写一个新的,把不需要的剔除。
优点:实时持久化。
缺点:所以AOF文件体积逐渐变大,需要定期执行重写操作来降低文件体积,加载慢。
Slave从节点服务启动并连接到Master之后,它将主动发送一个SYNC命令。Master服务主节点收到同步命令后将启动后台存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕后,Master将传送整个数据库文件到Slave,以完成一次完全同步。而Slave从节点服务在接收到数据库文件数据之后将其存盘并加载到内存中。此后,Master主节点继续将所有已经收集到的修改命令,和新的修改命令依次传送给Slaves,Slave将在本次执行这些数据修改命令,从而达到最终的数据同步。
使用一个或者多个哨兵(Sentinel)实例组成的系统,对redis节点进行监控,在主节点出现故障的情况下,能将从节点中的一个升级为主节点,进行故障转义,保证系统的可用性。
监控: 哨兵通过gossip 协议会不断地检查你的Master和Slave是否运作正常;
提醒:当被监控的某个Redis节点出现问题时, 哨兵可以通过 API 向管理员或者其他应用程序发送通知;
自动故障迁移:当一个Master不能正常工作时,哨兵通过投票协议会开始一次自动故障迁移操作,它会将其他一个Slave升级为新的Master,当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用现在的Master替换失效Master。Master和Slave服务器切换后,Master的配置文件的内容都会发生相应的改变,即,Master主服务器的redis.conf配置文件中会多一行slaveof的配置,sentinel.conf的监控目标会随之调换。
redis的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台redis服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了cluster模式,实现的redis的分布式存储,也就是说每台redis节点上存储不同的内容。
Redis-Cluster采用无中心结构,它的特点如下:
所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
节点的fail是通过集群中超过半数的节点检测失效时才生效。
客户端与redis节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
工作方式:
在redis的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的key到达的时候,redis会根据crc16的算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。
为了保证高可用,redis-cluster集群引入了主从模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点。当其它主节点ping一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。如果主节点A和它的从节点A1都宕机了,那么该集群就无法再提供服务了。
RESP 是redis客户端和服务端之前使用的一种通讯协议;
RESP 的特点:实现简单、快速解析、可读性好。
是指查询一个数据库一定不存在的数据。
程序在处理缓存时,一般是先从缓存查询,如果缓存没有这个key获取为null,则会从DB中查询,并设置到缓存中去。按这种做法,那查询一个一定不存在的数据值,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
最好对于每一个缓存key都有一定的规范约束,这样在程序中对不符合parttern的key 的请求可以拒绝。(但一般key都是通过程序自动生成的)
将可能出现的缓存key的组合方式的所有数值以hash形式存储在一个很大的bitmap中<布隆过滤器>(需要考虑如何将这个可能出现的数据的hash值之后同步到bitmap中, eg. 后端每次新增一个可能的组合就同步一次,或者 穷举),一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
常用: 如果对应在数据库中的数据都不存在,我们将此key对应的value设置为一个默认的值,比如“NULL”,并设置一个缓存的失效时间。当然这个key的时效比正常的时效要小的多。
指的是大量缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。
这个没有完美解决办法,但可以分析用户行为,尽量让失效时间点均匀分布,设置不同的过期时间。
用加分布式锁或者分布式队列的方式保证缓存的单线程(进程)写 (eg. redis的 SETNX),从而避免失效时大量的并发请求落到底层存储系统上。在加锁方法内先从缓存中再获取一次(防止另外的线程优先获取锁已经写入了缓存),没有再查DB写入缓存。 (当然也可以: 在没有获取锁(tryLock)的线程中一直轮询缓存,至超限时)
指的是热点key在某个特殊的场景时间内恰好失效了,恰好有大量并发请求过来了,造成DB压力。
与缓存雪崩的解决方法类似: 用加锁或者队列的方式保证缓存的单线程(进程)写,在加锁方法内先从缓存中再获取一次,没有再查DB写入缓存。
还有一种比较好用的(针对缓存雪崩与缓存击穿):
物理上的缓存是不设置超时时间的(或者超时时间比较长), 但是在缓存的对象上增加一个属性来标识超时时间(此时间相对小)。 当获取到数据后,校验数据内部的标记时间,判定是否快超时了,如果是,异步发起一个线程(控制好并发)去主动更新该缓存。这种方式会导致一定时间内,有些请求获取缓存会拿到过期的值,看业务是否能接受而定。
