Redis

Redis

redis是什么?

​ Redis是一个开源(BSD许可)的,使用C语言编写的,支持网络交互的,内存中的Key-Value数据结构存储结构,它可以用作数据库、缓存和消息中间件。

​ 它支持多种类型的数据结构,如字符串(strings),散列(hashs),列表(lists),集合(sets),有序集合(sorted sets)与范围查询,bitmaps,hyperloglogs和地理空间(geospatial)索引半径查询。

关系型数据库与菲关系性数据库

关系型数据库

​ 采用关系模型来组织数据的数据库,关系模型就是二维表格模型。一张二维表的表名就是关系,二维表中的一行就是一条记录,二维表中的一列就是一列就是一个字段。

优点

​ 容易理解

​ 使用方便,通用的sql语言

​ 易于维护,丰富的完整性(实体完整性、参照完整性和用户定义的完整性)大大降低了数据冗余和数据不一致的概率缺点

缺点

​ 磁盘I/O是并发的瓶颈

​ 海量数据查询效率低

​ 横向扩展困难,无法简单的通过添加硬件和服务节点来扩展性能和负载能力

​ 当需要对数据库进行升级和扩展时,需要停机维护和数据迁移

​ 多表的关联查询以及复杂的数据分析类型的复杂sql查询,性能欠佳。因为要保证ACID(原子性,一致性,隔离性,持久性)

非关系型数据库

​ 非关系型,分布性,一般不保证遵循ACID原则的数据存储系统。键值对系统,结构不稳定。

优点

​ 根据需要添加字段,不需要多表联查。仅需id取出对应的value

​ 严格上讲不是一种数据库,而是一种数据结构化存储方法的集合

缺点

​ 只适合存储一些较为简单的数据

​ 不适合复杂查询的数据

​ 不适合持久存储海量数据

redis数据类型

redis支持五种数据类型
string(字符串)
hash(哈希)
list(列表)
set(集合)
zset(sorted set:有序集合)

string(字符串)

string是redis最基本的类型,一个key对应一个value

string类型是二进制安全的,意思是redis的string可以包含任何数据。比如jpg图片或者序列化对象。

string类型是Redis最基本的数据类型,string类型的值最大能存储512MB。

实例

在这里插入图片描述

在以上实例中我们使用了Redis的set和get命令。键为name,对应的值为jim。

Hash(哈希)

redis hash是一个键值(key=>value)对集合。

redis hash是一个string类型的filed和value的映射,hash特别适合用于存储对象。

存的是字符串和字符串值之间的映射,比如一个用户要存储其全名、姓氏、年龄等等,就很适合使用哈希。

在这里插入图片描述

hmset设置了两个field=>value对,hget获取对应field对应的value。每个hash可以存储232-1键值对(40多亿)。

List(列表)

redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边lpush)或者尾部(右边rpush)。

在这里插入图片描述

列表最多存储232-1元素(4294967295,每个列表可存储40多亿)。

Set(集合)

Redis的set是string类型的无序集合。

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

sadd命令

添加一个string元素到key对应的set集合中,成功返回1,如果元素已经在集合中返回0.

sadd key member

在这里插入图片描述

以上实例中rabbitmq添加了两次,但根据集合内元素的唯一性,第二次插入的元素被忽略。最大的成员数为232-1(4294967295,每个集合可存储40多亿)。

zset(sorted set:有序集合)

redis zset和set一样是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

zset的成员是唯一的,但分数(score)却可以重复。

zadd命令

添加元素到集合,元素在集合中存在则更新对应的score

zadd key score member

在这里插入图片描述

设置失效时间

有时候我们并不希望redis的key一直存在。例如缓存,验证码等数据,我们希望它们能在一定时间内自动的被销毁。redis提供了一些命令,能够让我们对key设置过期时间,并且让key过期之后被自动删除。

设置值时直接设置有效时间

在这里插入图片描述

​ EX表示以秒为单位

​ PX表示以毫秒为单位 EX,PX不分大小写

​ set name jim ex 30 设置失效时间为30秒

​ ttl 键 查看剩余时间(秒)

​ pttl 键 查看剩余时间(毫秒)

设置值后设置有效时间

​ expire 键 时间(秒)

​ pexpire 键 时间(毫秒)

springBoot集成使用redis

​ Jedis是Redis官方推出的一款面向Java的客户端,提供了很多接口供Java语言调用。可以在Redis官网上下载。

​ Spring-data-redis是spring大家族的一部分,提供了在spring应用中通过简单的配置访问redis服务,对redis底层开发包(Jedis,JRedis,and RJC)进行了高度封装,RedisTemplate提供了redis各种操作。

spring-data-redis针对jedis提供了如下功能:

1.连接池自动管理,提供了一个高度封装的“RedisTemplate”类;

2.针对jedis客户端中大量api进行了归并封装,将同一类型操作封装为operation接口;

​ ValueOperations:简单的K-V操作

​ SetOperations:set类型数据操作

​ ZSetOpreations:zset类型数据操作

​ HashOperations:针对map类型的数据操作

​ ListOperations:针对list类型的数据操作

3.将事务操作封装,有容器控制;

4.针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)

​ JdkSerializationRedisSerializer:POJO对象的存取场景,使用JDK本身序列化机制。

​ StringRedisSerializer:Key或者value为字符串的场景,根据指定的charset对数据的字节序列编码成string,是“new String(bytes,charset)”和“string.getBytes(charset)“直接封装。是最轻量级和高效的策略。

​ JacksonJsonRedisSerialszer:jackson-json工具提供了javabean与json之间的转换能力,可以将pojo实例化序列化成json格式存储在redis中,也可以将json格式的数据转化成pojo实例。

搭建

1.添加redis依赖

<dependency> 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId> 
</dependency>

2.配置连接redis

spring: 
	redis: 
		host: 192.168.31.100 
		port: 6379 
		password: 111 
		database: 0 
		pool: max-active: 8 # 连接池最大连接数(使用负值表示没有限制) 
		max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制) 
		max-idle: 8 # 连接池中的最大空闲连接 
		min-idle: 0 # 连接池中的最小空闲连接 
		timeout: 5000ms # 连接超时时间(毫秒)

3.注入RedisTemplate

@Autowired 
RedisTemplate redisTemplate;

4.测试

redisTemplate.opsForValue().set("name", "aa"); redisTemplate.opsForValue().set("users", users,10*1000, TimeUnit.MILLISECONDS);
redisTemplate.hasKey("name"); redisTemplate.opsForValue().get("name"); redisTemplate.delete("users");
//序列化键值 
redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<User>(User.class));

需要被Redis缓存的类,必须实现序列化接口。

主从复制

​ 主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从结点(slave),数据的复制是单向的,只能由主节点到从节点。

​ 使用一个Redis实例作为主机,其余的作为备份机。主机和备份机的数据完全一致,主机支持数据的写入和读取等操作,而从机则只支持与主机数据的同步和读取。也就是说,客户端可以将数据写入主机,由主机自动将数据的写入操作同步到从机。主从模式很好的解决了数据备份问题,并且由于主从服务数据几乎是一致的,因而可以将写入数据的命令发送给主机执行,而读取数据的命令发送给不同的从机执行,从而达到读写分离的目的。

主从复制的作用主要包括:

1.数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

2.故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复,实际上是一种服务的冗余;

3.负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高redis服务器的并发量。

4.高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是redis高可用的基础。

在这里插入图片描述

主从复制配置

主从复制时只需要配置从库即可,其默认为主库模式。

打开redis客户端登录,使用命令info replication查看

主从复制可以搭建真集群,也可以搭建伪集群,真集群就是有多台主机,每台主机安装一个redis,伪集群就是在一台主机上复制多份配置,修改其端口,运行多个redis实例,配置方式两者相同。

哨兵机制

概述

哨兵模式是一种特殊的模式,首先redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行,其原理是哨兵通过发送命令,等待redis服务器响应,从而监控运行的多个redis实例。

单哨兵

在这里插入图片描述

哨兵集群

在这里插入图片描述

哨兵配置

从安装包中赋值sentinel.conf文件到bin目录下

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

缓存处理流程

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

在这里插入图片描述

缓存穿透

​ key对应的数据在数据库中并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据库,从而可能压垮数据库。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

数据库没有,缓存没有

在这里插入图片描述

解决方法:

1.使用布隆过滤器(BloomFilter)或者压缩filter提前拦截。

2.将这个空对象设置到缓存里面去。下次再请求的时候,就可以从缓存里边获取了,这种情况我们一般会将空对象设置一个较短的过期时间。

3.拉黑该IP地址。

4.对参数进行效验,不合法参数进行拦截。

布隆过滤器

​ 布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般算法要好的多,缺点就是有一定误识别率和删除困难。

​ 如果想要判断一个元素是不是在一个集合中,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢。不过世界上还有一种叫做散列表(又叫哈希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。这就是布隆过滤器的基本思想。

在这里插入图片描述

算法:

1.首先需要k个hash函数,每个函数可以把key散列成为1个整数

2.初始化时,需要一个长度为n比特的数组,每个比特位初始化为0

3.某个key加入集合时,用k个hash函数计算出k个散列值,并把数字中对应的比特位置为1

4.判断某个key是否在集合时,用k个hash函数计算出k个散列值,并查询数组中对应的比特位,如果所有的比特位都是1,认为在集合中。

布隆过滤器数据结构

BloomFilter 是由一个固定大小的二进制向量或者位图(bitmap)和一系列映射函数组成的。

在初始状态时,对于长度为m的位数组,它的所有位都被置为0,如下图所示:

在这里插入图片描述

当有变量被加入集合时,通过K个hash函数将这个变量映射成位图中的K个点,把它们置为1(假定有两个变量都通过3个映射函数)。

在这里插入图片描述

查询某个变量的时候我们只要看看这些点是不是都是1就可以大概率知道集合中有没有它了。

​ 如果这些点有任何一个0,则被查询变量一定不在;

​ 如果都是1,则被查询变量很可能存在。

为什么说是可能存在,而不是一定存在?那是因为映射函数本身就是散列表函数,散列表函数是会有碰撞的。

优点:不需要存储key,节省空间。

缺点:

1.算法判断key在集合中时,有一定的概率key其实不在集合中

2.无法删除

缓存击穿

​ 某个key对应的数据库中存在,但在redis中的某个时间节点过期了,此时若有大量并发请求过来,这些请求发现缓存过期,都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

解决方法:

1.热点数据设置永不过期

2.加上互斥锁:上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它,其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后将数据放到redis缓存起来。后面的线程进来发现已经有缓存了,就直接走缓存。

在这里插入图片描述

缓存雪崩

​ 缓存雪崩是指,在高并发情况下,大量的缓存失效,或者缓存层出现故障。于是所有的请求都会达到数据库,数据库的调用量会暴增,造成数据库也会挂掉的情况。

在这里插入图片描述

解决方法:

1.随机设置key失效时间,避免大量key集体失效。

setRedis(Key,value,time+Math.random()*10000);

2.若是集群部署,可将热点数据均匀分布在不同的Redis库中也能避免key全部失效问题。

3.不设置失效时间。

4.跑定时任务,在缓存失效前刷进新的缓存。

总结

​ 雪崩是大面积的key缓存失效,穿透是redis里不存在这个缓存key,击穿是redis某一个热点key突然失效,最终受害者都是数据库。

对于“Redis宕机,请求全部走数据库”这种情况,我们可以有以下思路:

**事发前:**实现Redis的高可用(主从架构+Sentinel(哨兵),尽量避免Redis挂掉这种情况发生。

**事发中:**万一Redis真的挂了,我们可以设置本地缓存(ehcache)+限流,尽量避免我们的数据库被干掉(起码能保证我们的服务还是能正常工作的)。

**事发后:**redis持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。

群部署,可将热点数据均匀分布在不同的Redis库中也能避免key全部失效问题。

3.不设置失效时间。

4.跑定时任务,在缓存失效前刷进新的缓存。

总结

​ 雪崩是大面积的key缓存失效,穿透是redis里不存在这个缓存key,击穿是redis某一个热点key突然失效,最终受害者都是数据库。

对于“Redis宕机,请求全部走数据库”这种情况,我们可以有以下思路:

**事发前:**实现Redis的高可用(主从架构+Sentinel(哨兵),尽量避免Redis挂掉这种情况发生。

**事发中:**万一Redis真的挂了,我们可以设置本地缓存(ehcache)+限流,尽量避免我们的数据库被干掉(起码能保证我们的服务还是能正常工作的)。

**事发后:**redis持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值