Redis
文章目录
NoSQL
NoSQL:(not only SQL)不仅仅是SQL语句,泛指非关系型的数据库,区别于关系数据库。随着互联网 web2.0 网站的兴起,传统的关系数据库在处理 web2.0网站,特别是超大规模和高并发动态网站已经显得力不从心,出现了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。
NoSQL的优势
易扩展,数据之间无关系,这样就非常容易扩展,无形之间也在架构的层面上带来了可扩展的能力。
高性能,NoSQL 数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。
灵活的数据模型 NoSQL 无须事先为要存储的数据建立字段,随时可以存储自定义的数据格式。
Redis
Redis(远程字典服务),是NoSQL的一种,是利用C语言编写的开源的,以Key---value数据结构存储系统。可以作为数据库、缓存和消息中间件。
它支持多种数据类型,如:字符串(String)、散列值(hash)、列表(list)、集合(set)、有序集合(sorted set),Key---value中key是string类型的数据。
key---value是利用dict结构体进行存储的。
dict :hash字典
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K1Ghp2R2-1691936074904)(C:\Users\wdy_1\AppData\Roaming\Typora\typora-user-images\1691844349338.png)]
dict中维护了一个dictht ht[2] 两个hash表,ht[1]主要用于发生冲突后进行rehash的时候使用,ht[0]是常用的hash表。
redis的rehash并非像我们传统的那样:一次性做完所有的数据迁移工作。Redis将扩容后的rehash操作分散到了它的CRUD操作中,并非一步到位,而且每次rehash遵循少量多次原则,在它的查找、添加、删除都有rehash的体现,称之为渐进式rehash。当ht[0]中的数据完全迁移完成之后,先让ht[0]hash数组清空,然后让ht[0]的指针指向ht[1],这样下次rehash的时候又可以重复操作。
Redis与其他的Key---value(Map)相比的区别?
- Redis支持数据的持久化,可以将内存中的数据保存到磁盘中,重启后,再次加载就可以使用。Map是内存对象,程序关闭后就清除了。
- Redis可以用几十G来做缓存,Map不行。
- Redis可以实现分布式的缓存,Map只能存在创建它的程序中。
- Redis可以处理每秒百万级的并发,是专业的缓存服务,Map只是普通的对象。
- Redis缓存有过期机制,Map本身没有这个功能。
关系型数据库和非关系型数据库
关系型数据库
采用关系模型来组织数据的数据库,关系模型就是二维表模型,二维表的名字就是关系,二维表中的一行数据就是一条记录,二维表中的一列就是一个字段。
优点:
- 容易理解;
- 使用方便,直接使用SQL语句
- 易于维护
缺点:
- 磁盘IO是并发的瓶颈
- 海量数据查询效率低
- 横向扩展困难
- 多张表关联查询的复杂SQL查询,性能欠佳。
非关系型数据库
非关系型,一般不保证ACID原则的数据存储系统。键值对存储。
优点:
- 性能好
- 结构简单
- 灵活的数据模型
缺点:
- 只适合存储一些简单的数据
- 不适合持久存储海量数据
Redis数据类型
常用的五种数据类型:字符串(String)、散列值(hash)、列表(list)、集合(set)、有序集合(sorted set)。
底层共有六种数据结构:简单动态字符串、双向链表、压缩列表、哈希表、跳表和整数数组。
跳表
是由有序链表优化而来,链表的查询效率不高。可以把每两个元素的第一个元素提上一层,建立一些冗余索引。进而可以支持快速的插入、删除、查找操作。
查找32,类似这般查找。
压缩表
类似数组,由一系列特殊编码的连续内存块组成的顺序型数据结构。
String(字符串)
string是Redis最基本的类型,一个key对应一个value。底层使用简单动态字符串(sds)实现。最大能存储512MB
是二进制安全的,即string可以包含任何数据类型,比如序列化的对象或者图片。
44字节一下:OBJ.ENCLUDING.EMBSTR(地层是连续的),44字节以上:OBJ.ENCLUDING.RAW(地层是不连续的),数值型:OBJ.ENCLUDING。INT
使用场景:
- 计数器功能,我们可以使用incr命令实现自增操作,来计数,例如统计文章阅读量,点赞量,粉丝数等。
- 存储Token信息。
- session共享,在分布式系统中,由于有多台服务器,请求分发每次发送到的目标服务器肯能不同,name此时就需要一个中间件来保存session的状态,可以把session保存在redis中
- 限流,就是有时候我们需要对某些用户、某些接口进行限流,即比如只能访问多少次等。
Hash(哈希)
底层使用压缩表和哈希表
Hash和String都可以存储对象,但是String是将对象转换称为json对象,适合用于不经常修改的字符串,如果是那种需要经常修改的,建议使用Hash存储。
哈希冲突较多时
使用场景:
- 存储对象信息
- 购物车。使用用户ID作为缓存key,使用商品编号作为hashKey,使用商品信息作为hashValue
List(列表)
是一个简单的列表,可以重复,保证添加顺序。
用于消息队列,评论中按照时间先后的评论记录
使用场景:
- 消息队列。可以使用lpush+brpop命令实现阻塞队列
- 文章评论按照时间先后排序
set
元素不重复,不保证添加顺序。
快速定位需要查找的元素,可以用来实现点赞收藏等功能,共同好友推荐,
使用场景:
- 社交类软件的共同好友
- 抽奖功能,返回随机数
- 统计访问网站的ip地址
Zset有序集合
有序集合,值是Map<Object , Double>,每一个元素都会有一个分值,值不能重复,但是分数可以重复。
使用场景:
- 排行榜功能
Redis为什么快?
- Redis运行在内存中
- 数据结构简单
- 使用多路IO复用技术
- 单线程实现,单线程避免了线程切换、锁等造成的性能开销。
Redis是单线程还是多线程?
Redis6之前是单线程,为什么呢?
简单来说,就是Redis官方认为没有必要,单线程的Redis的瓶颈通常在CPU的IO,而在使用Redis时几乎不存在CPU成为瓶颈的情况。使用Redis主要的瓶颈在于内存和网络,并且使用单线程也存在一些优点,比如避免了并发读写带来的一些列问题。
为什么6.0之后引入了多线程?
单线程的瓶颈在内存和网咯,Redis6.0引入多线程主要是为了解决网络IO读写这个瓶颈,执行命令还是单线程执行的,所以不存在线程安全问题。
Redis6.0默认是否开启了多线程?
没有,需要修改配置redis.conf: io-threads-do-redis no,中的no改为yes
Redis过期键的删除策略
- 立即删除。在设置键的过期时间时,创建一个回调事件,当过期时间达到时,有时间处理器自动执行键的删除操作。立即删除能保证内存中数据的最大新鲜度,因为它保证过期键值会在过期后马上被删除,其所占用的内存也会随之释放。但是立即删除对CPU不是最友好的,因为删除操作会占用CPU时间。
- 惰性删除。惰性删除是指,某个键值过期后,此键值不会马上被删除,而是等到下次使用的时候,才会被检查到过期,此时才能得到删除。所以惰性删除的缺点是浪费内存,如果大量过期的键没有被访问就会一直占用着内存。
- 定期删除。每隔一段时间,就会对键进行检查,删除其中过期的键,该策略是惰性删除和立即删除的一个折中,既避免了大量占用CPU资源又避免了出现大量过期键不被删除占用内存的情况。
Redis持久化
Redis是一个内存数据库,数据保存在内存中,但是我们都知道内存的数据变化很快,也容易丢失。Redis还为我们提供了持久化的机制,分别是RDB(Redis DataBase)和AOF(Append Only File)。
RDB方式
RDB持久化是指在指定的时间间隔内将内存数据中数据集快照写入磁盘。也就是默认的持久化方式,这种方式就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。恢复时是将快照读取到内存中。
触发快照的时机:
save的规则满足的情况下,会触发RDB规则。
save 20 10 就是在20秒之内对10个键值进行操作,会触发持久化;
也可以在关闭Redis的时候使用shutdown save命令持久化到RDB中。
优点:
- 适合对大规模的数据恢复
- 只有一个文件dump.rdb,方便持久化
- 性能最大化。
缺点:
- 数据安全性低,在一定时间间隔内做一次备份,如果redis突然宕机,会丢失最后一次快照之后的数据
AOF方式
AOF持久化以日志的形式记录服务器所处理的每一个写,删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
优点:
- 具备更高的安全性,Redis提供了三种同步策略,分别是每秒同步、每修改同步和不同步。相比RDB突然宕机丢失的数据会更少。
- 由于此机制中对日志文件的写入操作是append模式,因此即使在写的过程中出现宕机情况,也不会影响到已经保存的日志内容。
- AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作,可以通过该文件完成数据的重建。
缺点:
- 对于相同数量的数据而言,AOF文件通常要大于RDB文件。RDB在恢复大数据集时的速度更快。
- 根据AOF选择同步策略的不同,效率也不同,但AOF在运行效率上一般会慢与RDB。
Redis的事务
Redis事务本质是一组指令的集合,一个事务中的所有指令都会被序列化,在事务执行过程中,会按照顺序执行。Redis事务的主要作用就是串联多个指令防止别的指令插队。
所有的指令在事务中,并没有直接被执行。只有发起执行exec指令的时候才执行。
事务在执行的过程中,不会被其他客户端发送来的指令请求打断。
redis单条指令保证是原子性的。但是事务不保证同一事物多条指令执行的原子性,即使指令有错误也会添加到队列中,执行报错也不会影响其他指令的执行。
事务执行的三个阶段
Redis的事务操作:
-
开启事务(multi),
-
执行命令、命令入队,
-
执行事务(exec),
-
放弃事务(discard)。
在事务开启前还可以通过watch指令观测监听某个键的变化,如果在其他客户端将监听值修改,在事务中对某个键操作时,事务会失效。
Redis的集群、主从、哨兵
为什么要使用Redis集群?
Redis单机版有以下缺点:
- 不能保证该数据的可靠性,服务部署在一台服务器上,一旦服务器宕机服务就不可用。
- 性能瓶颈,内存容量有限,处理能力有限。
Redis集群就是为了解决Redis单机版的这些问题。
Redis集群的实现方案有哪些?
- 主从模式
- 哨兵模式
- Redis自研
主从模式
单机版通过RDB或者AOF持久化机制将数据持久化到磁盘上,但数据都存储在一台服务器上,并且读写都在同一台服务器(读写不分离),如果磁盘出现问题,则会导致数据不可用,为了避免这种问题,Redis提供了复制功能,即主从复制。
主从复制是指将一台Redis服务器的数据,复制到其他Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能有主节点到从节点。
使用一个Redis实例作为主机,其余的作为备份机。主机和备份机的数据完全一致,主机支持数据的写入和读取等各项操作,而从机只能支持与主机数据同步和读取。也就是说,客户端可以将数据写入到主机,由主机自动将数据的写入操作同步到从机。主从模式很好地解决了数据备份问题,并且由于主从服务数据几乎是一致的,因而可以将写入数据的指令发送给主机执行,读取数据的指令发送给不同的从机执行,达到读写分离。
主从复制的作用主要包括:
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用链接主节点,读Redis数据时应用链接从节点)分担服务器负载。
优点:
- 高可靠性。在主机数据出现故障后可以切换到从机。
- 读写分离。
缺点:
- 不具备自动容错和恢复能力,主节点故障,从节点需要手动升级为主节点。
哨兵机制
为了解决主从模式的Redis集群不具备自动容错和恢复能力的问题,Redis2.6之后提供了哨兵模式。
哨兵模式的核心还是主从复制,是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
单哨兵
哨兵集群
监听所有服务器是否正常运行:通过发送命令返回服务器的运行状态,除了监控主服务器、从服务器外,哨兵之间也相互监控。
故障切换:当哨兵检测到master宕机,会自动将slave切换称为master,然后通过发布订阅模式通知其他服务器修改配置文件,让它们切换master。同时那台有问题的旧主机也会变为新主机的从。
优点:
是基于主从模式的,解决主从模式中master故障不可以自动切换故障的问题。
缺点:
- 浪费资源,集群里所有的节点保存的都是全数据,数据量过大时,主从同步会严重影响性能;
- 只有一个master执行写请求,性能瓶颈。
缓存穿透、缓存击穿、缓存雪崩
缓存处理流程
在Redis缓存中,前台请求后,后台先从缓存中读取数据,取到就直接返回结果,取不到才会去数据库中查询,查询之后将结果更新到缓存中并且返回。
缓存穿透
穿透缓存和数据库。
key对应的数据在数据库中就不存在,缓存中肯定也没有,每次请求缓存中找不到,请求都会到数据库中,从而肯能压垮数据库。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有。
解决办法:
- 将这个空对象设置到缓存里边去,下次在请求的时候,就可以从缓存中获取了。这种情况我们一般会设置一个较短的过期时间。
- 对参数进行校验,不和法的参数进行拦截。
缓存击穿
击穿缓存
某key在缓存中,但是如果这个key刚好过期了,此时又刚好大量请求访问这个key,这些请求发现这个key已经过期了,都会去数据库中查询,数据库的压力就上升了。
解决办法:
- 热点数据设置永不过期;
- 加锁,上面的现象是因为多个线程同时访问同一个数据,那么可以用加锁的方式解决;
缓存雪崩
大量的击穿
缓存雪崩是指,在高并发下,大量的缓冲失效,或者缓存层出现故障。于是所有的请求都会访问数据库,数据库的调用就会暴增,造成数据库挂掉。
解决办法:
- 随机设置key的失效时间,避免大量key集体失效。
- 若是集群部署,可以将热点数据均匀分布在不同的Redis库中也可以避免key全部失效问题
- 不设置过期时间
- 定时任务,在缓存失效前刷新缓存