前言思考
整个网站的瓶颈是什么?
- 数据量如果太大、一台机器放不下了
- 数据的索引(B+ Tree),一个机器内存也放不下
- 访问量(读写混合),一个服务器承受不了
2、Memcached(缓存)+ MYSQL + 垂直拆分(读写分离)
3、分库分表 + 水平拆分 + MYSQL集群
redis入门
概述
Redis是什么?
Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API。
Redis 通常被称为数据结构服务器,因为值(value)可以是字符串(String)、哈希(Hash)、列表(list)、集合(sets)和有序集合(sorted sets)等类型。
Redis能干嘛?
- 内存储存、持久化,内存中是断电即失,所以说持久化很重要(rdb、aof)
- 效率高,可用于高速缓存
- 发布订阅
- 地图信息分析
- 计时器、计数器
- ………………
特性
- 多样的数据类型
- 持久化
- 集群
- 事务
通过指定的配置文件启动服务
redis默认安装目录
/usr/local/bin
redis-server kcongfig/redis.conf
redis是单线程
[图片上传失败...(image-aa8202-1633261064223)]
setex key 30 "hello" # 设置过期时间
setnx # 不存在,再设置(在分布式锁中会常常使用)
mset # 批量设置
mget # 批量获取
msetnx # 原子性操作,要么一起成功,要么一起失败
getset # 先get然后set
# set中的值是不能重复
sadd
SISMEMBER # 判断某一个值是不是在set中
SMEBERS # 查看指定set的所有值
scard myset # 获取set集合中内容元素的个数
srem # 移除set中指定的元素
SRANDNEMBER # 随机抽选出一个元素
smove # 将一个指定的值,移动到另外一个set集合
SDIFF # 查看两个set集合中的差集
SINTER # 查看两个set集合中的交集
SUNION # 查看两个set集合的并集
# Hash(哈希)
hset
hget
hmset
hmget
hdel # 删除hash指定key字段!对应的value值也就消失了
HEXISTS # 判断hash指定的字段是否存在
hkeys # 只获取所有的key
hvals # 只获取所有的value
# Zset(有序集合)
# 在set的基础上
Hyperloglog
什么是基数?
不重复的元素(个数)
网页的uv(一个人访问一个网站多次。但是还是算作一个人)
0.81%错误率!
PFadd # 创建一组元素
PFCOUNT # 统计元素的基数数量
PFMERGE # 合并两组 并集
如果不运行出错,那么使用set或者自己的数据类型即可
Bitmaps
位储存
统计用户信息,活跃,不活跃!登录、未登录!打卡! 两个状态的都可以使用Bitmaps
事务
Redis事务的本质:一组命令的集合!一个事务中的所有命令都会被序列化,再事务执行的过程中,会按照顺序执行!
==Redis单条命令是保证原子性的,但是事务不保证原子性!==
==redis事务没有隔离级别的概念==
redis事务
- 开启事务(multi)
- 命令入队(…..)
- 执行事务(exec)
Redis监视(乐观锁)
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 20
QUEUED
127.0.0.1:6379(TX)> INCRBY out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20
理解:watch监听状态下,开启事务,若中途有一个新的线程修改了监听的值,执行事务时失败。unwatch取消监听后,重新watch
通过Jedis操作redis
// maven导包
public static void main(String[] args ) {
// 1、new Jedis对象
Jedis jedis = new Jedis("127.0.01",6379);
System.out.pringIn(jedis.ping());
}
spring boot整合
说明:在springBoot2.x之后,原来使用的jedis被替换了lettuce
jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池! BIO
lettuce:采用netty,实例可以再多个线程中进行共享,不存在线程不安全的情况! 可以减少线程数据了,更像Nio模式
1、导入依赖
<dependency> <groupId>org,springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2、在application-properties中进行配置
spring.redis.host=127.0.0.0 spring.redis.port=6379
3、测试
@Autowired private RedisTemplate redisTemplate;
发现存在中文乱码的问题:因为没有配置序列化
@Configuration public class RedisConfig { // 编写我们自己的redisTemplate @Bean public RedisTemplate<String, Object> reidsTemplate(RedisConnectionFactory redisConnectionFactory) throw UnknowHostException { RedisTemplate<String, Object> template = new RedisTemplate<>(); Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(); // 配置具体的序列化方式 template.setKeySerializer(jackson2JsonRedisSerializer) ..................基本为固定模板.................. } }
配置工具类 redisUntil
Redis持久化
RDB 策略生成文件 AOF 将操作记录下来
1、相对于数据文件来说,aof远远大于rdb,修复的速度也比 rdb慢!
2、Aof运行效率也要比rdb慢,所以我们redis默认的配置就是rdb持久化!
发布订阅
Redis主从复制
概念
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。Master以写为主,Slave以读为主。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
主从复制的作用主要包括:
1、数据冗余∶主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2、故障恢复︰当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余.
3、负载均衡∶在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
4、高可用基石︰除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
一般来说,要将Redis运用于工程项目中,只使用一台Redis是万万不能的,原因如下︰
1、从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大;
2、从容量上,单个Redis服务器内存容量有限,就算一台Redis服务器内存容量为256G,也不能将所有内存用作Redis存储内存,一般来说,单台Redis最大使用内存不应该超过20G。
Redis缓存穿透和雪崩
缓存穿透(查不到)
概念
缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。
解决方案
布隆过滤器
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力;
[图片上传失败...(image-8162a3-1633261064224)]
缓存空对象
当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源﹔
image-20210817150041806但是这种方法会存在两个问题:
1、如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;
2、即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。
缓存击穿(量太大,缓存过期)
概念
这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导使数据库瞬间压力过大。
解决方案
设置热点数据永不过期
从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题。
加互斥锁
分布式锁∶使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。
缓存雪崩
概念
缓存雪崩,是指在某一个时间段,缓存集中过期失效。Redis宕机!
产生雪崩的原因之一,比如在写本文的时候,马上就要到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。
解决方案
redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis ,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。(异地多活!)
限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
网友评论