重温Redis

Redis数据类型

redis有五种基本数据类型String(字符串)、Hash(哈希)、List(列表)、Set(集合)、ZSet(有序集合)。

在这里插入图片描述

Redis底层的数据结构一共有6种,分别是简单字符串、双向链表、压缩列表、哈希表、跳表和整数数组。其对应关系如下图所示:

在这里插入图片描述

全局哈希表

Redis中存在全局哈希表,用来存储所有的数据。redis中的存储的都是(key,value)格式,每个(key,value)都称为一个entry。redis可以以(O1)的时间复杂度计算出entry的位置,这是redis速度快的一个原因。

在这里插入图片描述

当redis中数据不断的变多时,不可避免的产生哈希冲突问题,解决方法就是哈希表中有一个哈希桶,如果有哈希冲突时,以链表的形式将每个entry依次连接在哈希桶中。

但是这种解决方法存在一个弊端,当一个哈希桶中的entry越来越多时,也就是哈希桶下方连接的链表越来越长,此时我们的查询性能必然下降。

解决此处的查询性能,redis设置了两个全局哈希表用于rehash操作。当一个哈希表中的数据变多时,进行rehash操作,在另一个全局哈希表中扩充哈希桶的数量,将原来哈希表中的数据拷贝到另一个哈希表中,最后释放原哈希表的空间。因为如果一次rehash操作直接将所有数据都映射,那么会产生哈希堵塞,我们选择每次处理用户请求的时候,从哈希表1的第一个索引位置开始,将这个位置的所有数据拷入哈希表2,这样分散了rehash的操作,避免了耗时阻塞。

String(字符串)

string是redis最基本的数据结构类型,是二进制安全的,这就代表string可以存储任何类型的数据(图片或者序列化对象),其最大的存储空间为512M。

单值缓存

set key value

get key

del key

对象缓存(不对对象中的数据操作时可以使用)

set user:1 value(json 格式数据)

计数器

set news_views:1 0 设置文章访问量

incr news_views:1 文章访问量+1

decr news_views:1 文章访问量-1

get news_views:1 获得值

Web 集群 session 共享

session + redis 实现 session 共享

String的底层和C语言的String底层不一样,C语言或者java的String底层都为char[],而Redis使用SDS来封装。

在这里插入图片描述

SDS可以以O(1)的时间复杂度就获取到字符串的长度。

Hash(哈希)

哈希是redis里的一种数据结构类型,redis本身就是(key,value)的数据结构,使用哈希时,entry里的value也将存储(key,value)的值。

因为是映射的关系,所以他多可以用于存储用户信息(购物车,基本信息等等)。

基本操作

hset key field value 存储一个哈希表 key 的键值

hmset key field value [field value …] 存储多个键值对

hget key field 获取哈希表 key 对应的 field 键值

hmget key field [field …] 批量获取哈希表 key 中多个 field 键值

hdel key field [field …] 删除哈希表 key 中的 field 键值

hlen key 返回哈希表 key 中的 field 的数量

hgetall key 返回哈希表 key 中所有的键值

hincrby key field 增加的值(减少给负数)

List(列表)

redis列表是简单的字符串列表,可以插入和取出数据都可以从列表的头和尾进行操作

基本操作

lpush key value[value…] 将一个或多个值插入到 key 列表的表头(最左边)

rpush key value[value…] 将一个或多个值插入到 key 列表的表尾(最右边)

lpop key 移除并返回 key 列表的头元素

rpop key 移除并返回 key 列表的尾元素

lrange key start stop 返回列表key中指定区间内的元素,区间以偏移量start 和 stop

应用场景

消息队列,文章列表。

Set(集合)

set集合也是用来存储多个字符串数据,其内容是无序的,而且不允许重复值。

基本操作

sadd key member[member…] 往集合 key 中存入元素,元素存在则忽略, 若 key 不存在则新建

srem key member[member…] 从集合 key 中删除元素

smembers key 获取集合 key 中所有元素

scard key 获取集合 key 的元素个数

应用场景

随机抽奖

Zset(有序集合)

Zset是已排序的字符串集合,同时元素不能重复。

基本操作

zadd key score member[[score member]…] 往有序集合 key 中加入带分值元 素

zrem key member[member…] 从有序集合 key 中删除元素

zscore key member 返回有序集合 key 中元素 member 的分值

zincrby key increment member 为有序集合 key 中元素 member 的分值

加上 increment zcard key 返回有序集合 key 中元素个数

zrange key start stop[withscores] 正序获取有序集合 keyastart 下标到 stop 下标的元素

应用场景

记录朋友圈点赞用户

过期策略

我们在set key的时候,可以给它设置一个过期时间,比如expire key 60。指定这key60s后过期,60s后,redis是如何处理的嘛?

定时过期:定时过期指的是在redis的过期时间结束后立即删除。此方法在使用的过程中占用大量的cpu资源去处理过期数据,从而影响redis的响应时间和吞吐量。

惰性过期:惰性过期指的是redis会在访问某一个key的时候,才会判读该key是否过期,如果过期则清除。该过期策略对cpu很友好,但是对内存不友好,多个不访问的entry会一直存在于内存中,造成内存的大量浪费。

定期过期:定期过期指的是每过一段时间,redis都会扫描expirse字典总的部分key,并清除过期的key。该策略是对前两中策略的折中,在不同的场景使用下可以设置相适应的扫描时间和扫描数量,达到最好的效果

注:expirse字典是保存了redis所有设置了过期时间的key和过期时间的数据,其value就是该key的过期时间。键空间是指该redis集群中保存的所有键。

redis中同时使用惰性过期和定期过期两种过期策略。

假设redis中存放了30w个设置了过期时间的键,如果每隔100ms采用定期过期策略清除,cpu的负载将会过高。所以是没100ms随机抽取一定数量的key删除。但是这样的情况下很多key还是没有删除掉,这时就是用惰性删除策略,每次获取key的时候都判断一下key是否设置了过期时间并且已过期,此时就会删除。

但是很多key没有访问,也没有在定期删除策略中删除的话,内存中还是会有很多key堆积。或者业务量变大后,redis的key被大量使用,内存直接不够,此时redis也不会直接挂掉。因为redis中有八种淘汰策略去保护他自己。

  • volatile-lru:当内存不足时,从已设置过期时间的key中删除最久没有使用过的一批数据
  • allkeys-lru:当内存不足是,从所有key中删除最久没有使用过的一批数据
  • volatile-lfu:当内存不足时,从已设置过期时间的key中删除使用次数最少的一批数据
  • allkeys-lfu:当内存不足时,从所有key中删除使用次数最少的一批数据
  • volatile-random:当内存不足时,从设置了过期时间的key中随机淘汰数据。
  • allkeys-random:当内存不足时,从所有key中随机淘汰数据。
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的key中,根据过期时间进行淘汰,越早过期的优先被淘汰;
  • noeviction:默认策略,当内存不足以容纳新写入数据时,新写入操作会报错。

线程模型

redis6.x之前是真正的单线程,无论是键值读写还是对外提供的数据访问都是单线程的。

redis6.x之后,redis的数据读写依旧是单线程的,在网络请求和数据持久化,集群同步数据都是多线程的。

为什么redis设计为单线程读取效率还是很高?

1、因为redis的数据大多存放在内存中,读取速度很快。

2、redis的内存结构为哈希表,可以以O(1)的时间复杂度寻找到对应的entry。

3、redis使用I/O多路复用监听多个socket连接客户端,这样就可以使用一个线程来处理多个情况,减少线程切换的开销,也避免了I/O操作。

4、redis的读取是单线程的,避免了不必要的上下文切换(多线程的切换),运行效率高,而且单线程不会造成死锁的发生。

持久化

redis是一个内存数据库,数据保存在内存中,但是我们都知道,内存是变化很快的,数据容易在内存中的丢失。

redis也提供了持久化机制,分别是RDB(Redis DataBase)和AOF(Append Only File)

RDB 方式

RDB持久化是指在指定的时间间隔内将内存中的数据写入到硬盘中,保存为 dump.rdb。

触发时机

  1. save

    这里是用来配置触发 Redis 的 RDB 持久化条件,也就是什么时

    候将内存中的数据保存到硬盘。比如"save m n"。表示 m 秒内数据集存在 n 次 修改时,自动触发 bgsave。

  2. flushall命令

    触发rdb规则存储到硬盘

  3. 退出redis

    退出redis也会触发rdb规则

优点

适合大规模恢复数据

对数据完整性要求不高

缺点

因为是定时的,所以如果redis服务器宕机,那么最后一次修改的数据将无法存储

AOF方式

AOF是以日志的形式,每次的操作都记录到日志中(读操作不操作),只许追加文件,不许修改文件。redis重启之初会将该文件的数据。

开启AOF方式

修改 redis.conf 配置文件,开启 AOF 机制

appendon1y no #默认是不开启 aof 模式的,改为 yes 开启.

appendfilename appendonly.aof #默认的文件名是 appendonly.aof,可以通过 appendfilename 参数修改

AOF 同步机制

appendfsync always 每次set记录一次
appendfsync everysec 每秒记录一次

事务

当事务未开启的时候,redis的每一条命令,因为redis是单线程的,所以redis的每条命令都是原子性的。

原子性:原子性是一个事务中的多条命令,要么都执行,要么都不执行。如果其中存在异常,那么所有命令都将回滚,也就是说所有都未执行。

redis事务的特性:redis的事务支持原子性,但有隔离性(这个隔离性与mysql的隔离性不同,它没有隔离级别)

redis事务的本质是一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行的过程中会按照顺序的执行。

开启事务    mulit
命令入队    这里执行各种命令(例如get、set等)
执行事务    exec
放弃事务    discard

主从复制

主从复制是redis高可用的基础,是指一台redis服务器中的数据复制到其他redis服务器中。前者成为主节点(master)、后者成为从节点(solve)。数据的复制是单向的,只能由主节点到从节点。

在这里插入图片描述

主机和备份机的数据完全一致,主机支持数据的写入和读取等各项操作,而从机则只支持与主机数据的同步和读取。也就是说主从复制很好的解决了数据备份的问题,需要写入的操作交给主机,需要读取的数据分配给各个从机,达成读写分离的效果。

主要作用

数据冗余:将数据同步到各台从机上,实现数据的热备份。

故障恢复:当主机宕机时,因为从机中的数据与主机是一致的,由从节点提供服务也能达到一样的效果,实现快速的数据恢复。

负载均衡:在主从复制的基础上,搭配读写分离,由一台主机写,多台主机分配读操作,在写少读多的场景下大大的提高了redis服务器的并发量。

高可用基石:主从复制也是redis哨兵机制和集群的基础。

哨兵机制

哨兵是redis中一个独立的进程,其功能是向redis的服务器发送命令,等待服务器的响应,达到监控redis服务器的目的。

哨兵定期向服务器发送请求,如果时间过长没有收到响应,那么判断该服务器宕机,如果它是主机,那么投票选取一个从机替换主机。

当原来主机恢复后,又可以当做主机继续使用.

缓存穿透、缓存击穿、缓存雪崩

缓存处理流程

前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取, 数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果。

缓存穿透

指的是访问一个key值,但是这个key在缓存中和数据库中都不存在,每次压力都会给到数据库,从而可能压垮数据库。例如查询一个不存在的Id,来获取用户信息,无论缓存还是数据库都没有,黑客可以利用此进行攻击压垮数据库。

处理方式

1.对每次访问数据库的值进行判断,看是否符合规范

2.每次访问如果没有值,将空置存入缓存,这样下次就不会访问到数据库了。

缓存击穿

缓存击穿指的是一个请求访问缓存和数据库,缓存没有,但是数据库有,此时就访问数据库了,如果此时有大量的请求访问进来,那么就会造成数据库压力过大从而可能被压垮。

处理方式

1.设置热点缓存永不过期。

2.当一个键快要过期时,提前检测到它然后重新设置过期时间。

3.加锁,每次访问的请求加上锁,这样第一遍访问时没有数据,让其访问数据库,此时锁住该方法,其余线程都不让进,当数据库访问结束,将值存入缓存,缓存就有值了。

缓存雪崩

缓存雪崩实际上就是很多个缓存击穿,多个key都过期了,但是接收到大量的请求,这样请求都进入数据库,数据库压力暴增从而可能挂掉。

处理方式

1.设置热点键永不过期

2.随机设置key失效时间,避免大量key同时失效

3.若是集群部署,可将热点数据均匀分布在不同的 Redis 库中也能够避免 key

全部失效问题。

4.不设置过期时间

5.跑定时任务,当键过期前刷新缓存。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值