一、Redis简介
Redis是开源的、高性能的key-value数据库。
Redis数据类型包括:String、Hash、List、Set、Zset、Bitmap、Geospatial、HyperLogLog、Stream。
Bitmaps是Redis2.2.0版本新增的,即位图,是一串连续的二进制数组(0和1),可以通过偏移量(offset)定位元素。表示某个元素的值或者状态,时间复杂度为O(1)。由于bit是计算机中最小的单位,使用它进行存储将非常节省空间,特别适合一些数据量大且使用二值统计的场景。它的底层是基于String类型实现的。应用场景例如签到打卡,每一个用户一天的签到用一个bit位就能表示,一个月的签到情况用31个bit位,一年用366个bit位,不用复杂的集合类型就可以。
HyperLogLog是Redis在2.8.9版本添加的,用来做基数统计算法的数据结构。如统计网站的UV(Unique Vister-独立访客)。HLL是从Loglog算法派生的概率算法,用于确定非常大的集合的基数,而不需要存储其所有值。当然有小于0.81%的误差,但是对于UV统计来说可以忽略。简单来说,HyperLogLog提供不精确的去重计数。
GEO是Redis在3.2版本新增的,地理位置定位(底层使用zset实现),用于存储地理位置信息(经度、维度、名称),并对存储的信息进行操作。
Stream是5.0版本新增的数据类型,Redis专门为消息队列设计的数据类型。支持消息持久化、支持自动生成全局唯一ID、支持ack确认消息模式、支持消费组模式等,让消息队列更加稳定和可靠。
二、Redis快的原因
1、基于内存的实现。
Redis 是基于内存的一个数据库,内存读写速度非常快,数据存放在内存中,响应时间大约是100纳秒,这是Redis 每秒万亿级别访问的重要基础。
2、合理的线程模型。
2.1、Redis 是单线程。
避免CPU不必要的上下文切换和竞争锁的消耗。当然缺点就是如果某个命令执行时间过长(例如hgetall),会造成阻塞。Redis 是面向快速执行场景的数据库,所以要慎用smembers和lrange、hgetall等命令。
Redis 6.0引入多线程提速,不过它的执行命令操作内存仍然是个单线程。
2.2、I/O多路复用。
Redis使用epoll机制。
3、合理的数据编程。Redis支持多种数据类型。
String:若存储数字,使用int类型编码;若存储非数字,小于等于39字节的字符串使用embstr;大于39字节的用raw编码。
List:若列表的元素个数小于512个,列表每个元素的值都小于64字节(默认),使用ziplist编码,否则使用linkedlist编码。
Hash:哈希类型元素个数小于512个,所有值小于64字节的话,使用ziplist编码,否则使用hashtable编码。
Set:如集合中的元素都是整数且元素个数小于512个,使用intset编码,否则使用hashtable编码。
Zset:当有序集合的元素个数小于128个,每个元素的值小于64字节时,使用ziplist编码,否则使用skiplist(跳跃表)编码。
4、高效的数据结构。
SDS简单动态字符串、哈希、跳跃表skiplist、压缩列表ziplist。
5、虚拟内存机制。
Redis直接自己构建VM机制。暂时把不经常放的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其他需要访问的数据(热数据)。通过VM功能可以实现冷热数据分离,使热数据仍在内存中,冷数据保存到磁盘中。
三、Redis优缺点
3.1、优点:
1、性能高。读速度是110000次/s,写速度是81000次/s。
2、原子性。所有操作都是原子性,且支持对多个操作合并后的原子执行。
3、丰富的数据类型。
4、丰富的特性。支持publish/subscribe、通知、key过期等等特性。
3.2、缺点:
1、受物理内存限制。可以通过集群模式弥补。
2、存在数据一致性问题。存在主从一致问题、缓存数据库一致性问题。解决主从一致性问题,可以使用分布式锁,Redisson提供multiLock方案解决主从一致性问题。解决缓存数据库一致性问题,可以在每次写操作时,更新缓存中数据。
3、在线扩容困难。Redis在集群容量达到上线时,在线扩容变得非常复杂,这限制了其他大规模数据处理场景的应用。
4、存在缓存穿透、击穿和雪崩问题。解决方案包括,使用布隆过滤器来预防缓存穿透、使用分布式锁来处理缓存击穿、错开缓存失效时间以避免缓存雪崩。
5、缺乏自动容错和恢复功能。增加哨兵自动恢复故障。
6、数据类型限制。Redis数据类型相对有限,不支持所有的数据类型,可能限制某些场景下的应用。
四、应用实例
我在网上找了一篇个人觉得很不错的博文,访问地址如下:【Redis从头学-13】Redis哨兵模式解析以及搭建指南_redis搭建哨兵模式-CSDN博客
五、pipeline和lua脚本
1、pipeline(管道)。
批量处理数据。一次性返回所有结果集,对这个结果集进行批量处理,考验处理结果集的能力。一个pipeline的命令集被redis服务器执行的时候,有可能redis服务器会执行其他客户端传来的redis命令。一些异常情况尽可能发生,所以说使用pipeline一般会做一些异常捕获和处理。因此事实上管道不具有原子性。管道更适用于管道中的命令之间没有关系,不需要事务的原子性,但需要提高程序响应速度的场景。
pipeline通过减少客户端与redis的通信次数来实现降低往返延时时间,而且pipeline实现的原理是队列,队列原理是先进先出,这样可以保证数据的顺序性。
通俗来讲,pipeline就是把一组命令打包,然后一次性通过网络发送到redis,同时将执行的结果批量返回。
2、lua脚本。
lua脚本会将多个命令和操作当成一个命令在redis中执行,即执行该脚本期间,不会被其他命令或者脚本打断。因此lua脚本具有原子性,可以替代multi和exec的事务功能。当然,也正因如此,不宜在lua脚本中进行过大开销操作,避免影响后续其他请求的正常执行。因此lua脚本适合于相对简单的事务场景。
lua脚本的好处如下:1) lua脚本作为一个整体执行,中间不会被打断,具有原子性。2)可以把多条命令一次性打包,可以减少网络开销。3)lua脚本可以常驻redis内存,使用时可以直接拿来复用,减少代码量。
六、Redis持久化方案及优缺点。
1、aof。
对每条写入命令进行日志记录。
优点:数据可靠,丢失较少;持久化过程代价较低。
缺点:aof文件过大,数据恢复慢。
2、rdb。
将某一时刻的内存快照,以二进制的方式写入磁盘。
优点:rdb文件小,数据恢复快。
缺点:数据丢失较多,持久化过程代价较高。
3、混合持久化。
rdb文件小加载快但丢失多,aof文件大加载慢但丢失少。混合持久化是吸收rdb和aof两者优点的一种持久化方案。aof-rewrite的时候实际持久化的内容是rdb,等持久化后,持久化期间修改的数据以aof的形式附加到文件的尾部。混合持久化实际上是在aof-rewrite基础上进行优化,所以需要先开启aof-rewrite。
七、高可用性方案
1、主从复制。可实现高并发(读),典型部署方案为一主二从。
2、哨兵模式。可实现高可用,典型部署方案为一主二从三哨兵。
3、cluster集群。可同时支持高可用(读和写)、高并发,典型部署方案为三主三从。
redis主从复制 | redis哨兵 | redis集群 | |
---|---|---|---|
主要目的 | 数据备份与读写分离 | 高可用性和故障自动切换 | 高并发和数据分散处理 |
架构 | 一个主节点和多个从节点 | 监控主从结构并自动切换 | 多个主节点,数据分片 |
数据复制 | 主节点到从节点 | 监控并管理主从复制 | 每个主节点管理自己的数据集 |
故障转移机制 | 手动或哨兵自动切换 | 自动故障转移 | 自动处理节点故障 |
可伸缩性 | 有限,依赖主节点 | 为主从结构增加高可用性 | 高,因为数据分布式处理 |
使用场景 | 数据备份和读扩展 | 关键应用的高可用性 | 大规模应用的高性能需求 |
设置复杂度 | 相对简单 | 中等,需要配置哨兵 | 复杂,需要规划数据分区 |
八、Redis数据淘汰策略
1、no-enviction。
永不回收的策略。当内存达到限制时返回错误,并且客户端尝试执行使用更多内存的命令。
2、allkeys-lru。
尝试从数据集(server.db[i].dict)中回收最近最少使用的数据,使得新添加的数据有空间存放。
3、volatile-lru。
从已设置过期时间的数据集(server.db[i].expires)中回收最近最少使用的数据。
4、allkeys-random。
从数据集(server.db[i].dict)中随机回收数据。
5、volatile-random。
从已设置过期时间的数据集(server.db[i].expires)中随机回收数据。
6、volatile-ttl。
从已设置过期时间的数据集(server.db[i].expires)中回收将要过期的数据。
九、注意事项
1、脑裂。
当某个主节点所在的机器突然脱离正常网络,不能与其他的从节点连接,但实际上主节点还在运行,这时候哨兵就会认为主节点宕机了,然后开启选举,将其他从节点推举为新节点,这时集群中会出现两个主机。
2、多哨兵模式下的下线。
主观下线(sdown),sentinel发现Redis节点未在规定时间(down-after-milliseconds)内做出响应,则认为该节点主观下线。适用于所有节点。
客观下线(odown),sentinel监控的master节点发生故障时,哨兵至今通过is-master-down-by-adddr命令相互交流,并进行投票,当超过一定票数(quorum)的sentinel都认为master下线,则为客观下线。只适应主节点。
3、IO多路复用的三种机制。
select:使用轮询方式,每次调用select时,都需要遍历所有的文件描述符,检查其状态是否就绪。有固定大小的文件描述符集合,有限制,通常默认为1024。select使用fd_set数据结构来存放文件描述符集合。
poll:select的改进方式。解决select文件描述符数量限制问题。poll使用了一个保存文件描述符和事件的数据结构。并且每次调用时只需要遍历就绪的文件描述符,这样效率比select高一些。但是当文件描述符数量较大时,仍然需要遍历整个文件描述符集合。
epoll:epoll使用内核事件回调机制,当有事件就绪时,内核会直接将就绪的事件通知给用户空间,避免了每次都遍历所有的文件描述符。