redis是一个基于内存的高性能key-value数据库,将数据缓存在内存中,并周期性把更新的数据写入磁盘或者把修改操作写入追加的记录文件(与memcached区别),并且在此基础上实现了master-slave(主从)同步。此外单个value的最大限制是1GB,不像 memcached只能保存1MB的数据。
redis的过期数据的淘汰机制是怎样的?
1.惰性删除
Redis很懒,有些Key直到被查询的时候,才会拿去判断是否已经超过了有效期,如果已经超过了有效期,那么才会被删除。也就是说,Redis里面,有可能存在已经过期的数据。
2.主动删除
主动删除功能会定时来把过期的数据清除掉。Redis每秒都会进行10次下面这样的操作:
(1)随机抽取20个设置了有效期的key
(2)把其中已经过期了的key删除掉
(3)如果过期的key超过25%,那么重新进行第一次操作。
因为Redis是单线程的,为了避免资源都浪费在淘汰过期数据上,Redis限制了每次执行时间都是25毫秒,如果超过了这个时间,那么就直接退出。毕竟缓存的主要任务还是缓存数据,不能因小失大。如果Redis在某一段时期变得非常卡顿呢?原因就有可能是有大批量的key在用一个时间过期,那么就会有大量资源在删除过期数据。虽然每次只有25ms,十次也就是250ms了,但相当于1/4的资源都拿去删除过期数据而不是在提供服务,更要命的事,如果删除过多数据可能还会触发内存重新整理,进一步造成卡顿。所以,我们不要批量的设置一批数据在同一个时间过期,最好加个随机值,或者延后,在被缓存的数据上增加时间戳校验。
Redis内存不足的缓存淘汰策略,LRU(最久没有使用的)、LFU(使用频率最少的)
- noeviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键
- allkeys-lru:加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键
- volatile-lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键
- allkeys-random:加入键的时候如果过限,从所有key随机删除
- volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐
- volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键
- volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键
- allkeys-lfu:从所有键中驱逐使用频率最少的键
https://www.jianshu.com/p/c8aeb3eee6bc,https://blog.csdn.net/azurelaker/article/details/85045245
redis高可用集群
为了避免单个redis服务器挂掉,造成服务不可用,可以考虑以集群的方式保证服务的正常使用。比较常见有两种方式:
(1)主备的方式:在这种方式中采取1主1备或多备的方式,redis主服务器提供读写服务,从服务器提供备份的服务,使用keepalived向外提供统一的ip,当主服务出现故障时,keepalived会自动将服务路由到从服务器。优点是:对外ip由keepalived向外提供,服务出现故障时,客户端无感知;缺点:如果服务一直正常运行,备份服务则一直被浪费,没有被使用
(2)主从的方式:当请求量比较大时,可以考虑1主多从,甚至读写分离,主服务只提供写服务和少量的读服务,其余的操作全部分配到从服务器去完成。
redis缓存雪崩
即大量的请求没有走缓存,或缓存中没有该数据,导致,请求全部打在了数据库上,导致数据库崩溃,从而服务不可用。造成这种情况的原因有两个:(1)redis服务挂了(2)缓存中的大量数据在同一时间段内过期,从而导致查询缓存时找不到所需数据,从而查询数据库。
解决方式:(1)使用redis集群提供高用的服务(2)可以在设置过期时间时加上一个1到5分钟的随机数,避免大量缓存在同一时间点失效(3)当发现一个缓存失效后,在起一个线程,查询出该数据,存入缓存中,过期时间设置为原来的2倍
缓存穿透:
举一个简单的例子,数据库的每条数据均有1个id字段,它的值一般是从1开始递增大于0的,假如有人恶意发出请求查询id为-1的数据,缓存和数据库中都不会有这样的数据因此返回空,如果大量有这样的请求,每一次都会查询数据表,就有可能将数据库搞崩。
解决方式:(1)既然是非法参数,那么我们就需要加强对参数的校验规则,提前发现(2)一般我们缓存数据时,如果为返回结果为空是不会进行记录的,现在可以将这些为空的查询也进行存储。
redis缓存和数据库的数据一致性:
当数据库中的数据发生改变时,需要执行两步:更新数据库中的数据,删除缓存(更新缓存就算了,直接删除具体可用去查一下)。而不管这两步执行的先后顺序,都会有一致性问题,而现在的争论点也主要在这里。但结合实际情况一般采用的先更新数据库,再删除缓存的策略,facebook的论文也指出他们采用的也是这种方式,因为这种方式出现数据不一致的几率是最小的。
假如有A、B两个线程,A需要更新数据,B是查询数据,此时redis缓存与DB出现数据不一致的情况是:
(1)恰好查询的数据在缓存中不存在或正好失效
(2)恰好B在A更新之前将旧数据从数据库中查了出来
(3)A更新数据
(4)A删除缓存
(5)B将旧数据存入缓存
要造成这种情况需要满足缓存不存在、B在A之前查询了数据、B在A之后插入了数据,但我们知道查询是比更新要快的,因此一般是(5)在(4)之前,这种情况发生的几率是比较小的。如果有人说一定要解决怎么办,我们可以采用双重删除的方式,在A删除缓存之后,在启动一个线程,经过几百MS左右的延迟后再执行删除操作,如果失败则重复执行删除操作,直到成功。
而如果是先删除缓存再更新数据库,不一致的情况则会容易出现的多,还是假如有A、B两个线程,A需要更新数据,B是查询数据:
(1)请求A删除缓存
(2)请求B查询发现缓存不存在
(3)请求B去数据库查询得到旧值
(4)请求B将旧值写入缓存
(5)请求A更新数据
只需要B查询在A删除缓存和更新数据之间进行便可以,是不是概率大了很多。它也可以使用双重删除来进行处理。但还是更推荐先更新DB再删除缓存。
redis事务:redis命令可以由multi开启一个事务,插入命令,由exec执行操作,事务可以保证命令被有序的执行,且不会别其他线程的命令插入,但与mysql事务不同的是,当其中的某条命令执行失败时,并不会造成前面的命令回滚,并且其后的命令依然会执行。同时可以使用watch命令监控一个变量,它会一直监控到exec命令,如果在执行的过程中被监视的变量被其他线程修改,那么事务将被打断,不在执行。https://www.cnblogs.com/liuchuanfeng/p/7190654.html
redis持久化方式:数据持久化包括RDB和AOF两种方式,RDB会根据设置当内存中数据量到达一定程度或者一段时间后将数据持久化到磁盘中,而AOF是将修改操作日志实时的进行存储。官方建议是两种方式同时使用,没有数据持久化存储的redis和memcache一样,是一个单纯的内存数据库。
(1)RDB:该种方式会有一个子进程来专门的进行数据的保存工作,不会影响父进程的性能,且利用备份文件恢复速度较快;缺点也很明显,如果在数据保存之前出现故障,当redis恢复后会丢失一部分数据
(2)AOF:该种方式是默认关闭的,但可以根据设置每秒存储写入操作的历史记录,出现故障后会根据历史纪录进行数据恢复,数据丢失较少,缺点是保存的历史记录文件较大,且恢复速度比较慢
redis为什么那么快:
(1)redis大部分是内存操作,将数据存储在内存之中,速度很快
(2)redis是单线程,减少了线程间上下文切换和竞争的资源消耗
(3)redis采用了非阻塞,多路复用的IO方式,这里的多路复用表示redis使用单个线程监测多个网络连接,没有读写请求时会阻塞,当发现有请求到来时会唤醒线程,依次处理读写请求,因为redis是内存操作速度很快,且避免了轮询各个链接等大量无用操作。
(4)redis采用了跳跃表、压缩列表等数据结构对性能也有很大的提升
多线程访问redis:当多线程访问时,需要对访问的过程来加锁,因为redis虽然速度很快,但是它是单线程来依次处理各个线程的链接请求的,因此可能会造成,在后面的链接请求超时