Redis学习笔记

  • 概述:
  • 一、Redis五大数据类型
  • 1.1 Redis-key
  • 1.2 String
  • 1.3 List
  • 1.4 Set
  • 1.5 Hash
  • 1.6 Zset
  • 二、Redis三种特殊数据类型
  • 2.1 geospatial
  • 2.2 hyperloglog
  • 2.3 Bitmap
  • 三、Redis基本的事务操作
  • 四、Redis的乐观锁
  • 监视测试
  • 五、使用Jedis操作Redis
  • 1、导入对应的依赖
  • 2、编码测试
  • 3、常用API
  • 六、通过Jetis再次理解事务
  • 七、SpringBoot集成Redis
  • 八、自定义RedisTemplate
  • 九、RedisConfig配置文件详解
  • 十、Redis持久化
  • 1、RDB(Redis DataBase)
  • 2、AOF(Append Only File)
  • 十一、Redis订阅发布
  • 十二、Redis主从复制
  • 哨兵模式
  • 十三、Redis缓存穿透和雪崩
  • 缓存穿透
  • 布隆过滤器
  • 缓存过滤器
  • 缓存击穿
  • 设置热点数据永不过期
  • 加互斥锁
  • 缓存雪崩
  • Redis高可用
  • 限流降级
  • 数据预热


概述:

作为学习Redis的学习笔记,学习的课程时b站的up主,遇见狂神说,一边学代码,一边学做人!全体起立!配置redis这里不讲解了,教程喝多,本学习笔记是在linux配置redis后学习所记!
视频链接https://www.bilibili.com/video/BV1S54y1R7SB?p=1

狂神:只要学不死,就往死里学

一、Redis五大数据类型

1.1 Redis-key

1.2 String

1.3 List

1.4 Set

set(集合),其中的值不可以重复。

sadd myset "hello"     #set集合中添加元素
sadd myset "xwy"
sadd myset "love"
smembers myset         #查看值
sismember myset hello  #是否存在hello
scard myset  #元素个数
remove
srem myset "hello"   #移除set的元素 hello
#set是无序不重复集合 可以随机获取
srandmember myset #随机抽出一个元素
srandmember myset 2 # 随机抽出俩元素
#随机移除key
spop myset  #随机删除一些set集合的元素
#将一个指定的值,移动到另外一个key中
smove myset myset2 "xwy" # 将xwy移动到myset2
微博、b站,共同关注功能(并集)
数字集合类:
- 并集 sunion key1 key2
- 交集 sinter key1 key2
- 差集 sdiff key1 key2 # key1 key2不同的元素

微博 A用户将所有关注的人放在一个set集合中!将他的粉丝也放在一个集合里
共同关注、共同爱好,二度好友

1.5 Hash

Hash(哈希)

Map集合: key-Map集合!相当于key - <key-value>,value是一个Map集合,本质和string没有太大区别

hset myhash filed1 hello 
hget myhash filed1 
hmset myhash filed1 hello filed2 world # set多个key-vlaue
hget myhash filed1 filed2
hgetall myhash  # 获取全部的hash值
hdel myhash filed1 # 删除指定的hash的key字段,对用的value也就消失了
hlen myhash # 获取长度
hexists myhash filed1 # 判断是否存在
hkeys myhash # 只获取key
hvals myhash # 只获取values
incr  decr
hincr myhash filed3 1  # 加一
hdecr myhash filed3 1
hsetnx myhash filed4 hello # 如果不存在则可以设置,存在则不可以设置

hash存储变更的数据,尤其是用户信息的保存,经常变动的信息,hash适合对象的存储,string适合字符串的存储。

1.6 Zset

Zset(有序集合),在set的基础上增加了一个值。set k1 v1 ; zset k1 score v1

zadd myset 1 one
zadd myset 2 two 3 three  # 添加多个值
zrange myset 0 -1
#排序如何实现?
zadd salary 2500 xwy
zadd salary 599 xxx
zadd salary 8000 xsk
zrangebyscore salary -inf +inf #从无穷小到无穷大(相当于遍历所有的)
zrangebyscore salary -inf +inf with scores # 带上score

zrevrange salary 0 -1 #从大到小
zrem salary xwy #移除
zcard salary #获取数量
zcount salary 1 3 #获取指定区间的成员数量

案例:

set 排序,存储班级成绩表,工资表排序

普通消息:1 重要消息 2 带权重判断

排行榜应用,取top N

二、Redis三种特殊数据类型

2.1 geospatial

朋友圈定位,附近的人,打车距离计算

Redis的Geo在Redis3.2版本加入,可以推算地理位置的信息,比如两地的距离,方圆几里的人

#getadd
	#两极无法添加,实际我们是使用java程序一次性倒入所有
	
	geoadd china:city 116.40 39.90 beijing
	geoadd china:city 121.47 31.23 shanghai
	geoadd china:city 106.50 29.53 chongqing
	geoadd china:city 114.05 22.52 shenzhen
	geoadd china:city 120.16 30.24 hangzhou
# geopos
#获取指定城市的经纬度
#获取当前定位:一定是一个坐标值

geopos china:city beijing
geopos china:city beijing chongqing
#geodist
#m 米 、 km 千米 、 mi 英里 、 ft 英尺
geodist china:city beijing shanghai km #计算距离
#附近的人?(获取附近的人定位),通过半径来查询
#georadius

georadius china:city 110 30 1000 km withdist  #显示到中间的距离
georadius china:city 110 30 1000 km withcoord  #结显示他人的定位信息
georadius china:city 110 30 1000 km withdist withcoord count 200  # 以 110,30 为中心,半径 1000km 的 200 个城市
#georadiusbymember
#找出位于指定元素周围的其他元素
georadiusbymember china:city beijing 1000 km
#geohash
#返回11位的字符的geohash字符串
#将二维的经纬度转换成一维的字符串,如果两个字符串越相近,距离越近
#不常用
geohash china:city beijing chongqing

geo的底层实现原理就是zset,我们可以使用zset命令操作geo!

2.2 hyperloglog

什么事基数?

A{1,3,5,7,9}

B{1,3,5,7,8}

基数(不重复的元素) =5 ,可以接受误差!

简介

Redis 2.8.9更新了Hyperloglog数据结构。

Redis Hyperloglog基数统计的算法。

优点:占用内存的固定的,2^64不同元素的技术,只需要12kb内存!

网页的UV(一个人访问一个网站多次,但是还是算做一个人)

传统的方式是使用set,保存用户id,然后统计set的元素作为标准判断。

这个方式如果保存大量用户id,就会比较麻烦!

0.81%错误率!可以接受!统计uv任务,可以忽略不计!如果不允许容错,就使用set或者自己的数据类型!

pfadd mykey a b c d e f g h i j
pfcount mykey        #统计基数数量
pfmerge mykey mykey2 mykey3 # 合并两组元素到mykey3!

2.3 Bitmap

位存储

统计疫情感染人数: 0 0 0 1 0

统计粉丝活跃、用户登陆未登陆、365天打卡!

只要是两个状态的都可以使用bitmap!

都是操作二进制位来进行记录,只有0和1两个状态

#周一到周六的打卡记录
setbit sign 0 1
setbit sign 1 1
setbit sign 2 0
setbit sign 3 1
setbit sign 4 1
setbit sign 5 0
#查看某一天是否打卡
getbit sign 3
#统计打卡天数
bitcount sign

三、Redis基本的事务操作

MySQL: ACID!

Redis单条命令保证原子性,但是事务不保证原子性

**Redis事务没有隔离级别的概念!**所有的命令在事务中,并没有直接被执行,只有发起执行命令才会执行!

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

一次性、顺序性、排他性!

redis的事务:

  • 开启事务:Multi
  • 命令入队:…
  • 执行事务:exec
multi 
set k1 v1
set k2 v2
set k3 v3
exec
#放弃事务
multi
set k1 v1
set k2 v2
set k3 v3
discard
#编译型异常(代码有问题,命令有错),事务中的所有命令都不会被执行!
multi
set k1 v1
set k2 v2
set k3 v3
getset k3 #错误语句
exec
#不会执行所有的命令


#运行时异常(1/0),如果事务队列中存在语法性错误!其他命令正常,错误命令报错!
multi
set k1 “vvvv”
incr k1
exec
#只有错误语句没有被执行!

四、Redis的乐观锁

监控

悲观锁:

  • 悲观,什么时候都出问题,无论做什么都会加锁

乐观锁:

  • 很乐观,认为什么时候都不会出问题,不回上锁,更新数据的时候才判断一下,在此期间有没有修改过数据
  • 获取version
  • 更新的时候比较version

监视测试

set money 100
set out 0
watch money #监视money
multi      #事务正常结束,数据期间没有发生变动,这个时候正常执行
decrby money 20
decrby out 20
exec
#再开一个客户端测试多线程修改值,watch当作redis的乐观锁操作
set money 100
set out 0
watch money #监视money
multi      
decrby money 20
decrby out 20

#重新开一个客户端
set money 200

#回第一个客户端
exec #错误
unwatch #事务失败,先解锁
watch   #再加锁
multi
decrby money 10
exec #成功

五、使用Jedis操作Redis

我们使用java操作Redis

什么是Jedis,是Redis官方推荐的Java连接工具,是java操作Redis的中间件

1、导入对应的依赖

<dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
    </dependencies>

2、编码测试

  • 连接数据库
  • 操作命令
  • 断开连接
package com.xwy.test;
import redis.clients.jedis.Jedis;
/**
 * @author levi
 * @create 2020/7/25 12:57 下午
 */
public class TestPing {
    public static void main(String[] args){
        Jedis jedis = new Jedis("ip",6379);
        System.out.println(jedis.ping());
    }
}

3、常用API

所有代码类似命令!

六、通过Jetis再次理解事务

事务

JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","xwy");
Transaction multi = jedis.multi(); //开启事务
String result = jsonObject.toJSONString();
try{
    multi.set("user1",result);
    multi.set("user2",result);
    multi.exec();
}catch (Exception e){
    multi.discard();
    e.printStackTrace();//放弃事务;
}finally {
    System.out.println(jedis.get("user1"));
    jedis.close();
}

七、SpringBoot集成Redis

springboot2.x后,原来的jedis被替换成了lettuce

jedis:采用的直连,多个线程操作是不安全的,想要避免不安全,使用jetis pool连接池,更像BIO模式

lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况,可以减少线程数据,更像NIO模式:

整合测试

  • 导入依赖
  • 配置连接
spring.redis.host=39.97.184.172
spring.redis.port=6379
  • 测试
package com.xwy;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class SpringbootO2ApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    void contextLoads() {

        //opsForValue 操作字符串 类似string
        //opsForList 操作List
        //ops。。。

//        //获取redis连接对象
//        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
//        connection.flushAll();
//        connection.flushDb();

        redisTemplate.opsForValue().set("mykey","xwy");
        System.out.println(redisTemplate.opsForValue().get("mykey"));
    }

}

八、自定义RedisTemplate

首先测试:

redis命令查询set型的key redis查看set长度_java

关于对象的保存:

需要讲对象序列化!

redis命令查询set型的key redis查看set长度_redis_02

可以成功获取值!

不同的方式的配置类:(写好的固定模版,企业中拿去直接使用!!!)

redis命令查询set型的key redis查看set长度_redis_03


redis命令查询set型的key redis查看set长度_redis命令查询set型的key_04

在真实的开发中,一般都自己封装RedisUtil

由于代码太多,先留个坑

九、RedisConfig配置文件详解

#1、配置文件 unit单位对大小写不敏感!

2、bind ip  #绑定的IP

3、protected-mode #保护模式

#通用

4、daemonize yes #以守护进程方式运行,默认是no 需要改为yes

5、pidfile /var/run/redis_6379.pid # 如果后台运行,需要制定pid文件

6、loglevel notice #日志
   logfile ""  #输出的日志文件位置名

7、databse 16 # 默认是16个数据库

# 快照
   #持久化,在规定的时间内,执行了多少次操作,则会持久化到文件.rdb.aof
   #redis是内存数据库,如果没有持久化,数据断电即失

8、#如果900s内,如果至少有一个key进行了修改,就需要持久化操作
save 900 1

9、stop-writes-on-bgsave-error yes # 持久化出错还要不要继续执行

10、rdbcompression yes # 是否压缩rdb文件,需要小号一些cpu资源

11、rdbchecksum yes #保存rdb文件的时候,是否要进行错误检查校验

12、dir ./ #rdb 文件保存的目录

#安全

13、密码

#限制

14、maxclients 1000 #设置能连接redis的最大客户端数量

15、maxmemory <bytes> #设置redis配置的最大内存容量

16、maxmemory-policy noeviction #内存达到上限之后的处理策略
    #移除过期的key
    #报错
    #。。。。

#APPEND ONLY模式 aof配置

17、appendonly no #默认是不开启aof模式 默认是rdb方式持久化,在大部分情况下,rdb完全够用

18、appendfilename “appendonly.aof“ #持久化文件的名字

19 #appendfsync always   #每次修改都会sync 消耗性能
   #appendfsync everysec  #每秒执行一次,可能会丢失这一秒的数据
   #appendfsync no     #不执行sync

十、Redis持久化

Redis是一个内存数据库,不提供持久化那么数据断电即失!

1、RDB(Redis DataBase)

触发机制

  • save的规则满足的情况下,出发rdb规则
  • 执行flushall,也会触发
  • 推出redis,也会产生一个rdb文件
    备份自动生成一个dump.rdb

如果恢复rdb文件

1、只需要将rdb文件放在redis启动目录下,redis启动自动检查dump.rdb回复其中的数据

2、查看需要存在的位置

config get dir

优点:

  • 适合大规模的数据恢复
  • 对数据完整性的要求并不高,

缺点

  • 需要一定的时间间隔进行进程操作,如果redis意外宕机,这个最后一次修改的数据就没有了。
  • fork进程的时候,会占用一定的内容空间。

2、AOF(Append Only File)

将所有命令都记录下来,history,回复的时候就把这个文件全部执行一遍!

AOF保存的文件时appendonly.aof!!

默认是不开启的,需要手动配置!!appendonly on改为yes!!!重启redis就可以开启!!

如果aof文件有错误,redis启动不起来!我们需要修复!

redis给我们提供了一个工具,叫redis-check-aof!!!

redis-check-aof --fix appendonly.aof

优点和缺点

优点

  • 每一次修改都同步,文件完整性更好
  • 每秒同步一次可能会丢失一秒数据
  • 从不同步效率最高

缺点

  • 相对于数据文件来说,aof远远大于rdb,修复的速度比rdb慢
  • 运行效率比rdb慢,所有redis默认的持久化策略是rdb!

十一、Redis订阅发布

通信 队列 发送者 订阅者

Redis发布订阅(pub/sub)是一种消息通信模式。微信、微博、关注系统

Redis可以订阅任意数量的频道。

角色

消息发送者、频道、接受者

测试

subscribe kuangshenshuo #建立频道

#去另一个终端!
publish kuangshenshuo "hello kuangshen" #发送消息

使用场景:

  • 实时消息系统
  • 实时聊天(频道作为聊天室,将信息回显给所有人)
  • 订阅、关注系统

稍微复杂的场景使用消息中间件MQ做

十二、Redis主从复制

数据复制是单向的,只能从主节点到从节点!!

主从复制,读写分离!80%的情况下都是独操作!减缓服务器压力,架构中常用!一主二从!

作用

  • 数据冗余
  • 故障恢复
  • 负载均衡
  • 高可用(集群)

一般来说,Redis运用于工程,只使用一台是万万不可的!!!

  • 结构上,单台redis服务器会发生单点故障,并且一台服务器要处理所有的负载,压力较大
  • 容量上,单台redis内存容量有限,一般来说,单台redis最大使用内存不要超过20G

环境配置

127.0.0.1:6379> info replication #查看当前库的信息
# Replication
role:master # 角色 
connected_slaves:0  #没有从机
master_replid:c35253ec819f702ff5d100cd6b88bebd5f897a15
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6379>
#在两个从机中配置!!
slaveof 127.0.0.0.1 6379

真实的配置应该是在配置文件中配置,这里使用的是命令,是暂时的!!

细节

  • 主机可以写,从机只能读不能写!!主机中的所有信息和数据都会被从机自动保存!!
  • 主机断开连接,从机依旧连接到主机,但是没有写操作了,如果住机会来了,从机仍然能获得主机写的信息!!
  • 如果是使用命令行配置的,从机重启会变成主机,重新变为主机,重新变为从机后,立马会从主机中获取值!

复制原理

redis命令查询set型的key redis查看set长度_redis命令查询set型的key_05

如果没有老大了,能不能选择一个老大出来呢?

slaveof no one #如果主机断开可以篡位,是自己变为主机!

其他节点手动变为我的从节点!手动!主机回来之后,只能重新配置,光杆司令!

哨兵模式

哨兵模式(自动选举老大的模式)

哨兵是一个独立的进程!

测试

#1 配置
#进入redis.conf所在的文件夹
vim sentinel.conf
#写入
sentinel monitor myredis 127.0.0.1 6379 1 #被监控的名称 主机 端口号 1
#后面的数字1 代表主机挂了,slave投票看让谁接替成为主机,票数最多的,成为主机!
#2 启动
#bin
redis-sentinel kconfig/sentinel.conf

如果主机master宕机,就会从从机中随机选一个作为主机!!主机回来后,自动现任主机的从机!

优缺点

优点:

  • 哨兵集群,基于主从复制模式,所有主从配置的优点都有
  • 主从可以切换,故障可以转移,可用性就会更好
  • 哨兵模式是主从模式爹升级,手冻到自动,更加健壮

缺点:

  • Redis不好在线扩容,集群容量达到上限,在线扩容十分麻烦
  • 实现哨兵模式的配置其实是很麻烦的,有很多选择

哨兵模式的全部配置

redis命令查询set型的key redis查看set长度_redis命令查询set型的key_06

redis命令查询set型的key redis查看set长度_spring boot_07


redis命令查询set型的key redis查看set长度_redis_08

十三、Redis缓存穿透和雪崩

缓存穿透

概念

用户想要查询一个数据,但是缓存内没有,数据库也没有,于是查询失败,但是用户很多的情况下,缓存都没有命中,于是都去请求数据库,这就给数据库造成很大压力,出现缓存穿透!!

解决方案

布隆过滤器

redis命令查询set型的key redis查看set长度_spring boot_09

缓存过滤器

redis命令查询set型的key redis查看set长度_redis_10

问题

  • 如果空值能够被缓存起来,就意味着缓存需要更多的空间,因为当中很多空值的键
  • 即使对空值设置了过期时间,但是还是会存在缓存层和存储层数据会有一段时间窗口的不一致,这对于需要保持数据一致性的业务会有影响

缓存击穿

火力全部被压在一个地方!

微博服务器宕机!

概述

redis命令查询set型的key redis查看set长度_java_11

解决方案

设置热点数据永不过期

从缓存层面看,没有设置过期时间,所以不会出现热点key过期后产生的问题

加互斥锁

分布式锁,保证每一个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可,这种发式将高并发的压力转移到了分布式锁,对分布式锁的考验很大!

缓存雪崩

概念

redis命令查询set型的key redis查看set长度_java_12

解决方案

Redis高可用

redis可能挂掉,那么多增加几台redis,一台挂掉其他可以继续工作,搭建集群,异地多活

限流降级

缓存失效,通过加锁或者队列空值数据库写缓存的线程数量

数据预热

正式部署前,先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中,在即将发生大并发访问前手动出发缓存不同的key,设置不同的过期时间,让缓存失效的时间尽量均匀。