logo头像
Snippet 博客主题

redis:一个神奇且强大的数据库

简介

Redis是一个高性能的key-value数据库。
支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
安装及入门查看:https://www.jianshu.com/p/56999f2b8e3b

使用命令参考

官方文档:https://redis.io/commands
和 中文翻译版:http://redisdoc.com/
https://www.redis.net.cn/order/3528.html

redis为什么这么快

1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);

2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;

3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

4、使用多路I/O复用模型,非阻塞IO;只有IO这里是多线程,redis的业务均是多线程的。

5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?

雪崩、穿透、击穿

雪崩

定义:Redis 的 key 同时大面积失效,直接访问数据库,导致数据库压力
解决: 处理缓存雪崩简单,在批量往Redis存数据的时候,把每个Key的失效时间都加个随机值就好了,这样可以保证数据不会在同一时间大面积失效。

穿透

定义:缓存中查不到,每次你去数据库里查,也查不到,请求每次都“视缓存于无物”,直接查询数据库。
解决:
1、在接口层增加校验,比如用户鉴权校验,参数做校验,不合法的参数直接代码Return。
2、每次系统 从数据库中只要没查到,就写一个空值到缓存里去,比如 set -999 UNKNOWN。然后设置一个过期时间,这样的话,下次有相同的 key 来访问的时候,在缓存失效之前,都可以直接从缓存中取数据。
3、布隆过滤器(Bloom Filter)在查询的时候先去 BloomFilter 去查询 key 是否存在,如果不存在就直接返回,存在再走查缓存 -> 查 DB。

击穿

指一个Key非常热点,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。
解决:1、设置热点数据永远不过期。
2、或者加上互斥锁就能搞定了,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据。

避免以上情况事前事中事后的解决方案

  • 事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。
  • 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
  • 事后:redis 持久化RDB + AOF,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

三种集群方式

主从复制

Redis主从复制主要有两个角色,主机(master)对外提供读写功能,从机(slave)对外只提供读功能,主机定期把数据同步到从机上保证数据一致性。
Redis主机数据同步到从机上有两种方式,一种是全量同步,另一种是增量同步。

主从同步
你启动一台slave 的时候,他会发送一个sync命令给master ,如果是这个slave第一次连接到master,他会触发一个全量复制。master就会启动一个线程,生成RDB快照,还会把新的写请求都缓存在内存中,RDB文件生成后,master会将这个RDB发送给slave的,slave拿到之后做的第一件事情就是写进本地的磁盘,然后加载进内存,然后master会把内存里面缓存的那些新命名都发给slave。

如果主节点发生故障怎么办?
答:哨兵机制

哨兵机制(sentinel)

功能:

  • 集群监控:负责监控 Redis master 和 slave 进程是否正常工作。
  • 消息通知:如果某个 Redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
  • 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
  • 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。

一个健壮的部署至少需要三个哨兵实例

集群(cluster)

redis的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台redis服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了cluster模式,实现的redis的分布式存储,也就是说每台redis节点上存储不同的内容。
或者,当数据量过大到一台服务器存放不下的情况时,主从模式或sentinel模式就不能满足需求了,这个时候需要对存储的数据进行分片,将数据存储到多个Redis实例中。

布隆过滤器

优点:是空间效率和查询时间都远远超过一般的算法。
缺点:是有一定的误识别率和删除困难。

布隆过滤器检索时,使用相同的哈希函数进行计算出对应的bit位置,只要看这些位置的值,如果这些位置有任何一个0,则被检元素一定不在;如果都是1,则被检元素可能存在。一句话概率就是全0一定不存在、全1不一定存在。

布隆过滤器的误判是指多个输入经过哈希之后在相同的bit位置1了,这样就无法判断究竟是哪个输入产生的,因此误判的根源在于相同的bit位被多次映射且置1。
这种情况也造成了布隆过滤器的删除问题,因为冲突的存在无法确定有多少输入映射到这个bit位了,当然这是个优化方向。

过期策略

定期删除:
默认100s就随机抽一些(而非全部)设置了过期时间的key,去检查是否过期,过期了就删了。
但是,定期删除可能会导致很多过期key到了时间并没有被删除掉,所以就得靠惰性删除了。

惰性删除:等查询的时候再看看过期了没,过期了就删了不给返回。
但是,如果定期删除漏掉了很多过期key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了,怎么办?
答:内存淘汰机制

内存淘汰机制

当内存不足以容纳新写入数据时,有以下几个机制

  • noeviction:新写入操作会报错(这个一般没人用吧)

  • allkeys-lru:在键空间中,移除最近最少使用的key(这个是最常用的)

  • allkeys-random:在键空间中,随机移除某个key(这个一般没人用吧)

  • volatile-lru:在设置了过期时间的键空间中,移除最近最少使用的key(这个一般不太合适)

  • volatile-random:在设置了过期时间的键空间中,随机移除某个key

  • volatile-ttl:在设置了过期时间的键空间中,有更早过期时间的key优先移除

zset的底层实现原理

跳表

参考文献
面了BAT,我总结了他们会问的Redis基础知识
阿里面试Redis最常问的三个问题:缓存雪崩、击穿、穿透
大厂都喜欢这样问Redis,哨兵、持久化、主从、手撕LRU
面了6家大厂,我把问烂了的Redis常见面试题总结了一下
白话布隆过滤器BloomFilter
Redis的过期策略和内存淘汰机制
Redis|主从复制与哨兵机制
Redis哨兵机制
redis的三种集群方式