总结归纳redis的核心技术点如下:
1、redis是基于C语言开发、纯内存运行的一个key-value型数据库,redis是单线程实现的。
2、利用队列技术,将并发访问转换为了串行访问,减少了传统数据库串行控制的开销。
3、redis支持的语言由:C、C++、java、php、Node.js、GO等。
string
list
set
sorted set
hash
redis是基于内存进行操作的,CPU则不是redis的性能瓶颈,他的性能瓶颈很有可能是机器的内存大小和网络的带宽。能用单线程实现,且CPU又不是性能瓶颈,自然也不会使用更麻烦的多线程实现了。
至于redis为什么速度这么快呢,主要有一下几个方面:
1、完全基于内存,绝大部分操作都是对内存进行操作,速度非常快。数据在内存中以类似hashmap的方式存储,而hashmap的优势就是查询和操作的时间复杂度都是O(1)。
2、数据结构简单,redis这五种数据结构都是专门进行设计的,因此速度更快。
3、采用单线程,避免了多进程、多线程之间的切换,不用考虑上下文切换和竞争,不存在加锁释放锁等操作。
4、使用了I/O多路复用和非阻塞IO的方式。
5、redis自己设计了一套VM通信机制,使用起来更加快捷。
优点:主从复制实现了数据的多机备份,对于读操作实现了负载均衡和故障恢复。
缺点:无法做到写操作负载均衡,集群的存储能力受到单机的限制。
1、redis的主从复制,能很大程度上解决单点故障对整个系统所造成的影响,redis的集群关系为:一主多从。
2、主节点启动后,从节点可以通过slaveof 主节点IP地址 主节点redis端口
的方式来向主节点注册从节点。
3、从节点注册完成后,会自动向主节点发送sync
命令来全量复制主节点的数据信息。
4、主节点执行gbsave
命令,将生成的快照发送给从节点,从节点进行数据同步。
5、上述操作执行完后,如果主节点收到新的写入操作,主节点将把新的操作发给从节点,从节点将进行增量复制,更新自己的数据信息。
优点:在主从复制的基础上,哨兵实现了自动化的故行恢复。
缺点:写操作无法时间负载均衡,存储能力受到单机的限制。
1、主从集群启动。
2、哨兵集群启动,监测主从集群中的每一台redis的状态,检测的方式为哨兵向集群中每一台机器每秒发送一个PING指令,如果响应时间超过了预定的范围,则判断该节点下线。
3、如果leader主节点宕机,哨兵能立马相应,在剩下的从节点中选出主节点对外服务。
我们都知道,redis默认的持久化方式是RDB,这种持久化方式当数据量比较大的时候进行持久化,将严重影响redis的各种性能。原因:redis在使用rdb的方式进行持久化的时候,会fork子进程来完成,而fork操作的时间跟数据量是成正相关的,并且fork操作的时候会阻塞主线程。 因此数据量越大,RDB持久化时间越长,整个集群的综合性能将变得很差,所以我们必须找出一种办法来解决这种问题。
解决办法:增加redis实例的个数。在面向百万、千万级别的用户规模时,横向扩展的redis切片集群会是一个非常好的选择。但是我们需要解决两个问题:
1、数据切片和实例的对应分布关系
在Redis 3.0后,官方提供了一个叫redis cluster的方案,来实现切片集群,其中就规定了数据和实例的对应关系。具体来说,redis cluster中定义了一个叫哈希槽的东西,来处理数据和实例之间的对应关系,一个切片集群一共有16384(2^14)个哈希槽,这些哈希槽类似于数据分区,每个键值对会根据他的key,被映射到一个哈希槽中。
具体映射过程如下:首先根据key值,按照CRC16算法得出一个16bit的值,然后用这个值对16384(2^14)取模运算,得到一个0~16383之间的值,即得到哈希槽的编号。其次,redis会自动把这些哈希槽均匀的分布在集群实例上。例如:集群一共有N台实例,那么每台实例上大概有16384/N个哈希槽。当然我们也可以通过参数进行指定。
2、客户端如何定位数据所在的实例?
客户端在定位数据的时候,他所处的哈希槽是可以在客户端通过计算得出来的。但是要进一步定位到实例,还需要知道哈希槽分布在哪个实例上。
当集群刚开始启动的时候,每个实例只知道自己分配的哈希槽信息,并不知道其他实例分配的哈希槽信息。那么韦森么客户端可以在访问任意一个实例的时候获取所有哈希槽信息呢?这是因为redis实例回把自己的哈希槽信息发给和它相连的其他实例,来完成哈希槽分配信息的扩散。当所有实例之间完成互通后,每个实例就有所有哈希槽的映射关系了。
客户端收到哈希槽的消息后,在本地进行缓存。客户端在请求键值对信息时,先计算哈希槽的值,然后再根据缓存的映射关系去请求相应的实例。
总结:在面对数据增加的时候,虽然增加内存这种纵向扩展的方法比较简单直接,但是会造成数据库的内存增加而导致性能降低。而redis的切片集群提供的横向扩展模式,也就是使用多个实例,并给每个实例配置一定数量的哈希槽,数据可以通过键的哈希值映射到哈希槽,再通过哈希槽分散保存在不同的实例上。这样做的好处是扩展性更强,不管有多少数据,切片集群都能轻松应对。
1、RDB方式,为redis默认开启持久化方式。该方式每隔一定时间进行一次持久化,输入shutdown
命令下线的时候,会进行一次持久化,输入bgsave
也会进行一次持久化。
优点:RDB的文件紧凑,体积小,适合网络传输以及全量复制。速度比AOF快,对性能影响较小。
缺点:兼容性差,需要使用特定格式的文件,不同版本的RDB文件不适用。
2、AOF方式,为redis默认关闭持久化方式。该方式默认每秒进行一次持久化。
优点:兼容性好,数据更安全。
缺点:文件大,恢复速度慢,对性能影响大。
1、Master主节点最好不要做任何持久化工作,如果数据比较重要,则让某个slave从节点进行每秒一次的AOF备份策略。
2、为了主从复制的速度以及整个架构的稳定性,所有机器尽量安排在同一个局域网内。
3、避免在压力很大的主节点上增加从节点,以免造成主节点意外下线。
1、定时删除,在设置健的同时设定定时任务,在规定时刻删除该键。
优点:对内存比较友好,能及时清楚不需要的数据,节省空间。
缺点:对cpu不友好,如果同一时间过期的键比较多,将会大量占用cpu的使用,影响该机器的响应时间和吞吐量。
2、惰性删除,程序不管过期的键,但每次在获取该键信息的时候,进行判断看是否过期。过期则删,否则返回。
优点:对CPU比较友好,不会占用cpu太多的资源。
缺点:由内存溢出的可能。
3、定期删除:由于定时删除会占用太多CPU的时间,影响服务的整体性能;惰性删除会浪费太多内存空间还有内存泄漏的风险,于是出现了一种整合折中这两种策略的定期删除策略。
每隔一段时间,程序对数据库进行一次检查,删除里面过期的键,至于检查多少、删多少则由专门的算法根据当前的实际情况进行决定,并通过限制删除操作的时长和频率来减少对CPU的影响。
1、缓存穿透:某些请求查询的数据不存在,因此redis中并为缓存。然后又去mysql中查询,当然也查不到。大量这样的请求穿过redis进入mysql造成mysql崩溃的现象叫缓存穿透。
2、缓存击穿:单个热点数据过期被删除,紧接着有大量请求访问该数据,于是redis转发到mysql,导致mysql崩溃的现象叫缓存击穿。
3、缓存雪崩:一大批数据几乎同时过期被删除,然后又有大量请求访问redis,这次的规模比缓存击穿的规模更大,造成mysql崩溃的现象叫缓存雪崩。
缓存穿透的解决办法:布隆过滤器,擅长在超大数据集中查找需要的数据是否存在。如果返回不存在,则一定会不存在,如果返回存在则有可能存在也有可能不存在。
缓存击穿和缓存雪崩的解决办法:设置热点数据永不过期。