什么!Redis不会用!看完这个你就是Redis大牛了!

redis

1.nosql

联系作者:扣扣  190557020

什么是nosql:

NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。

NoSQL用于超大规模数据的存储。(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。

常用的NoSQL数据库,Redis,MongoDB,Es,

大致分为以下几种:

  1. 键值数据库

  2. 列组数据库

  3. 文档数据库

  4. 图形数据库

2.安装redis

windos下安装redis

直接下载直接解压即可用

下载地址:链接: https://pan.baidu.com/s/1chv4tPBCI6tV3HHIb-92Tw 提取码: g9n3

配置一个快速启动的脚本

创建redis-start.bat文件,右击编辑,输入 redis-server redis.windows.conf 保存即可

双击启动

默认端口为6379

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LctcMVXJ-1627702088495)(H:\技术点\redis\redis-这一篇就够了.assets\1627566370994.png)]

redis-cli.exe 文件是命令行操作,输入keys * 查看所有键

在这里插入图片描述

出现以上界面,windows下的Redis就算安装完成了,

linux下本地安装redis

路灯

https://www.cnblogs.com/xsge/p/13841875.html

Docker下安装redis:

记得关闭防火墙/开放端口

centos7 下的 docker安装redis

#启动docker
systemctl start docker

#查找redis的镜像
docker search redis

# 下载指定版本,,
docker pull redis:6.0-rc

此处需要联网

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bV0UnDC6-1627702088498)(H:\技术点\redis\redis-这一篇就够了.assets\1627571504111.png)]

#查看docker里的镜像
docker images
#创建挂载目录,配置文件需要将下载一份配置上去,redis.conf,
mkdir -p /home/redis/myredis
#conf文件的目录

//创建data
cd /home/redis/myredis
mkdir data

redis各版本配置文件下载:https://redis.io/topics/config

安装的命令

docker run  -p 6379:6379 \
--name niuzi_redis -v /home/redis/myredis/myredis.conf:/etc/redis/redis.conf \
-v /home/redis/myredis/data:/data -d redis redis-server /etc/redis/redis.conf \
--appendonly yes \
redis:6.0-rc


#二选一,下面这个使用的默认的配置
docker run -itd --name redis6 -p 6379:6379 redis:6.0-rc

–restart=always 总是开机启动

-p 6379:6379 将6379端口挂载出去

–name 给这个镜像取一个名字

-v 数据卷挂载

/home/redis/myredis/myredis.conf:/etc/redis/redis.conf

这里是将 liunx 路径下的myredis.conf 和redis下的redis.conf 挂载在一起。
/home/redis/myredis/data:/data 这个同上

-d redis 表示后台启动redis

redis-server /etc/redis/redis.conf 以配置文件启动redis,加载容器内的conf文件,最终找到的是挂载的目录 /etc/redis/redis.conf 也就是liunx下的/home/redis/myredis/myredis.conf

–appendonly yes 开启redis 持久化

#查看是否安装成功
docker ps -a 

在这里插入图片描述

#运行redis
docker start niuzi_redis  #可以是名称,也可以是最前面的id

#打开redis命令测试
docker exec -it redis6-two redis-cli

#输入测试命令
keys *

出现以下界面则表示安装成功
在这里插入图片描述

3.redis相关知识

端口号默认是6379

一共有16个数据库,

redis是单线程

redis可以进行持久化操作

单线程+多路io复用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YKRJK6zT-1627702088502)(H:\技术点\redis\redis-这一篇就够了.assets\1627607784983.png)]

原子性 所谓原子操作是指不会被线程调度机制打断的操作

​ 这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。

(1)在单线程中, 能够在单条指令中完成的操作都可以认为是"原子操作",因为中断只能发生于指令之间。

(2)在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。

Redis单命令的原子性主要得益于Redis的单线程。

4.常用五大数据类型

1.对key进行操作
#查看所有key
keys *

#判断该key是否存在 返回1则存在,0则不存在
exists key 

#查看key是什么类型
type key

#删除key
del key

#根据value选择非阻塞删除
unlink key

#设置key的过期时间,10描
expire key 10

#查看还有多少秒过期  -1永不过期,-2表示已过期
ttl key 

#切换数据库
select 0/1/2/3/....

#查看当前数据库有多少数量的key
dbsize


!!!!!!慎用!!!!!

#清空当前库
flushdb

#通杀全部库
flushall


2.String类型

String是redis最基本的类型,是非常强大的

String类型是二进制安全的,意味着他可以保存任何数据,比如序列化

一个redis中字符串value最多可以是512M

#存入值,重复往key中存储会覆盖值
set key value

#获取值
get key

#追加值
append key value

#获取长度
strlen

#当key不存在的时候设置key的值
setnx key value
3. List类型

单键多值

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

它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差

#从表头添加或表尾
lpush/rpush  key value,key value

#从表头或表尾吐出一个值,
lpop/rpop key  #键在则在,键光则亡

#根据下标范围获取值  多用于 0 -1  获取全部
lrange key start stop

#在<value>的后面插入<newvalue>插入值
linsert <key>  before <value><newvalue>

#从左边删除n个value(从左到右)
lrem <key><n><value>

#将列表key下标为index的值替换成value
lset<key><index><value>
4.set类型

set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。

#将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略
sadd <key><value1><value2> ..... 

#取出该集合的所有值
smembers <key>

#判断集合<key>是否为含有该<value>值,有1,没有0
sismember <key><value>

#返回该集合的元素个数。
scard<key>

#删除集合中的某个元素。
srem <key><value1><value2> .... 

#随机从该集合中吐出一个值。
spop <key>

#随机从该集合中取出n个值。不会从集合中删除 。
srandmember <key><n>

#返回两个集合的交集元素。
sinter <key1><key2>

#返回两个集合的并集元素。
sunion <key1><key2>

#返回两个集合的差集元素(key1中的,不包含key2中的)
sdiff <key1><key2>

Java中HashSet的内部实现使用的是HashMap,只不过所有的value都指向同一个对象。Redis的set结构也是一样,它的内部也使用hash结构,所有的value都指向同一个内部值

5.hash类型

Redis hash 是一个键值对集合。

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

类似Java里面的Map<String,Object>

用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,

#给<key>集合中的  <field>键赋值<value>
hset <key><field><value>

#从<key1>集合<field>取出 value
hget <key1><field> 

#批量设置hash的值
hmset <key1><field1><value1><field2><value2>... 

#查看哈希表 key 中,给定域 field 是否存在。 
hexists<key1><field>

#列出该hash集合的所有field
hkeys <key>

#列出该hash集合的所有value
hvals <key>

Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。

6.zset有序集合

Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。

不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复了 。

因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。

访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表。

多用于,点赞之类的

#将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
zadd  <key><score1><value1><score2><value2>#返回有序集 key 中,下标在<start><stop>之间的元素
#带WITHSCORES,可以让分数一起和值返回到结果集。
zrange <key><start><stop>  [WITHSCORES]   

#返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
zrangebyscore key minmax [withscores] [limit offset count]
 
#同上,改为从大到小排列。 
zrevrangebyscore key maxmin [withscores] [limit offset count]               

#为元素的score加上增量
zincrby <key><increment><value>    

#删除该集合下,指定值的元素 
zrem  <key><value>

#统计该集合,分数区间内的元素个数
zcount <key><min><max> 

#返回该值在集合中的排名,从0开始
zrank <key><value>

SortedSet(zset)是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表

zset底层使用了两个数据结构

(1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。

(2)跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。

7.新数据类型Bitmaps

进行位操作,

合理地使用操作位能够有效地提高内存使用率和开发效率。

(1) Bitmaps本身不是一种数据类型, 实际上它就是字符串(key-value) , 但是它可以对字符串的位进行操作。

(2) Bitmaps单独提供了一套命令, 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一个以位为单位的数组, 数组的每个单元只能存储0和1, 数组的下标在Bitmaps中叫做偏移量。

#添加数据-设置Bitmaps中某个偏移量的值(0或1)
setbit key <key><offset><value>

#获取偏移量
get <key><offset>

统计**字符串**被设置为1的bit数。一般情况下,给定的整个字符串都会被进行计数,通过指定额外的 start 或 end 参数,可以让计数只在特定的位上进行。start 和 end 参数的设置,都可以使用负数值:比如 -1 表示最后一个位,而 -2 表示倒数第二个位,start、end 是指bit组的字节的下标数,二者皆包含

bitcount<key>[start end] #统计字符串从start字节到end字节比特值为1的数量

start和end代表起始和结束字节数

bitop是一个复合操作, 它可以做多个Bitmaps的and(交集) 、 or(并集) 、 not(非) 、 xor(异或) 操作并将结果保存在destkey中。

bitop  and(or/not/xor) <destkey> [key…]

与set对比

当网站用户访问量大的时候可以使用bitmaps,可以节省内存

当网站访问量较小的时候是不如set的

8.新数据类型HyperLogLog

基数统计的算法

在工作当中,我们经常会遇到与统计相关的功能需求,比如统计网站PV(PageView页面访问量),可以使用Redis的incr、incrby轻松实现。

但像UV(UniqueVisitor,独立访客)、独立IP数、搜索记录数等需要去重和计数的问题如何解决?这种求集合中不重复元素个数的问题称为基数问题。

解决基数问题有很多种方案:

(1)数据存储在MySQL表中,使用distinct count计算不重复个数

(2)使用Redis提供的hash、set、bitmaps等数据结构来处理

以上的方案结果精确,但随着数据不断增加,导致占用空间越来越大,对于非常大的数据集是不切实际的。

能否能够降低一定的精度来平衡存储空间?Redis推出了HyperLogLog

Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

什么是基数?

比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。

#添加指定元素到 HyperLogLog 中
pfadd <key>< element> [element ...]

#将所有元素添加到指定HyperLogLog数据结构中。如果执行命令后HLL估计的近似基数发生变化,则返回1,否则返回0。
#统计数量
pfcount <key>
#pfmerge 将一个或多个HLL合并后的结果存储在另一个HLL中
#将k1 k2的值合并放在k3中
pfmerge k3 k1 k2
9.新数据类型Geographic

Redis 3.2 中增加了对GEO类型的支持。GEO,Geographic,地理信息的缩写。该类型,就是元素的2维坐标,在地图上就是经纬度。redis基于该类型,提供了经纬度设置,查询,范围查询,距离查询,经纬度Hash等常见操作。

geoadd<key>< longitude><latitude><member> [longitude latitude member...] 
#实例

geoadd china:city 121.47 31.23 shanghai
geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen 116.38 39.90 beijing

两极无法直接添加,一般会下载城市数据,直接通过 Java 程序一次性导入。

有效的经度从 -180 度到 180 度。有效的纬度从 -85.05112878 度到 85.05112878 度。

当坐标位置超出指定范围时,该命令将会返回一个错误。

已经添加的数据,是无法再次往里面添加的。

南极与北极不可添加

geopos  <key><member> [member...]  #获得指定地区的坐标值


#获取两个地区的直线距离是多少
geodist <key><member1><member2>  [m|km|ft|mi ] 

单位:

m 表示单位为米[默认值]。

km 表示单位为千米。

mi 表示单位为英里。

ft 表示单位为英尺。

如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位

#以给定的经纬度为中心,找出某一半径内的元素
georadius <key>< longitude><latitude>radius  m|km|ft|mi 

5.redis发布和订阅

什么是发布和订阅

Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。

Redis 客户端可以订阅任意数量的频道

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6YWcmYxq-1627702088503)(H:\技术点\redis\redis-这一篇就够了.assets\8365009b-8edf-4ef2-88cb-e7506c27fd61.png)]

当给这个频道发布消息后,消息就会发送给订阅的客户端

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OEVlp8Vf-1627702088504)(H:\技术点\redis\redis-这一篇就够了.assets\69690ffa-6609-46c6-b1ad-e322cdcff598.png)]

命令行实现

打开2个客户端,

1、 打开一个客户端订阅channel1

SUBSCRIBE channel1

2丶打开另一个客户端,给channel1发布消息hello

publish channel1 hello

打开第一个客户端可以看到发送的消息,发布的消息没有持久化,如果在订阅的客户端收不到hello,只能收到订阅后发布的消息

6.安装redis可视化

在开发与练习中,频繁的使用命令是不可行的,为了提升开发效率,更直观的将数据展示出来,需要安装一个redis的可视化

软件安装 RedisDesktopManager

链接:https://pan.baidu.com/s/14rpZn6ZEEfpSWT5bOJJEYg
提取码:t16i

傻瓜式安装–一直下一步,安装链接根据个人修改

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UtNMgItz-1627702088504)(H:\技术点\redis\redis-这一篇就够了.assets\1627617316697.png)]

启动软件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MGDEf3Xu-1627702088505)(H:\技术点\redis\redis-这一篇就够了.assets\1627617422673.png)]

创建redis链接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dtDA3Ovf-1627702088506)(H:\技术点\redis\redis-这一篇就够了.assets\1627617557295.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lS5t9BB4-1627702088506)(H:\技术点\redis\redis-这一篇就够了.assets\1627617648030.png)]

显示成功链接后点击ok

可以直观的看到,我们有16个数据库。每个数据库存放了什么,值是什么

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-On03O0LV-1627702088507)(H:\技术点\redis\redis-这一篇就够了.assets\1627617748641.png)]

7.SpringBoot操作redis

1.创建springboot项目
2.添加依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
3.redis的yml配置文件
redis:
  # redis数据库索引(默认为0),我们使用索引为3的数据库,避免和其他数据库冲突
  database: 3
  # redis服务器地址(默认为loaclhost)
  host: 192.168.73.12
  # redis端口(默认为6379)
  port: 6379
  # redis访问密码(默认为空)
  password: pwd123
  # redis连接超时时间(单位毫秒)
  timeout: 0
  # redis连接池配置
  pool:
    # 最大可用连接数(默认为8,负数表示无限)
    max-active: 8
    # 最大空闲连接数(默认为8,负数表示无限)
    max-idle: 8
    # 最小空闲连接数(默认为0,该值只有为正数才有用)
    min-idle: 0
    # 从连接池中获取连接最大等待时间(默认为-1,单位为毫秒,负数表示无限)
    max-wait: -1

这里我们使用的最终yml为

redis:    #redis其他的都使用默认配置,也可以不配,全部使用默认配置,这里为了直观的学习固修改host和port
  host: 127.0.0.1 #/localhost
  # redis端口(默认为6379)      
  port: 6379
server:
  port: 9001    #端口号
spring:
  application:
    name: spring-boot-redis-0730  #项目名称
4.注入模板
	@Resource
    private StringRedisTemplate stringRedisTemplate;

//    @Resource			在此工程中使用stringredistemplate
//    private RedisTemplate redisTemplate;

StringredisTemplate 与 RedisTemplate 的区别

Resource 与 Autowried的区别

5. 操作模版
String常用的模板操作
 	//存入redis
	@Test
    void addStringRedis() {
   	 ValueOperations<String, String> StringRedis 
        = stringRedisTemplate.opsForValue();
            StringRedis.set("job","male");
            StringRedis.set("jack","male");
            StringRedis.set("lisa","female");
            StringRedis.set("alili","female");
    }
	/**
     * @description 根据key获取值
     * @params []
     * @return void
     * @author -还怕大雨吗
     * @date 2021/7/30 12:18
     */
    @Test
    void getStringredis(){
        ValueOperations<String, String> StringRedis = stringRedisTemplate.opsForValue();
        //根据key获取值
        String job = StringRedis.get("job");
        System.out.println("job = " + job);
    }

	//job = male
/**
     * @description 给key设置过期时间,使用timeunit函数
     * @params []
     * @return void
     * @author -还怕大雨吗
     * @date 2021/7/30 12:23
     */
    @Test
    void setExpireRedis(){
        stringRedisTemplate.opsForValue().set("job","male",30,TimeUnit.SECONDS);
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P77YRUaa-1627702088508)(H:\技术点\redis\redis-这一篇就够了.assets\1627619407507.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LwpqLNkE-1627702088508)(H:\技术点\redis\redis-这一篇就够了.assets\1627627530288.png)]

List常用模板操作
  /**
     * @description 对list进行左右插入操作
     * @params []
     * @return void
     * @author -还怕大雨吗
     * @date 2021/7/30 14:43
     */
    @Test
    void setRedisList(){
        //  在变量左边添加元素值。右边同理,只是方法名换成rightPush
//        stringRedisTemplate.opsForList().rightPush()
        //批量添加时,直接在后面拼接,例如
//		  stringRedisTemplate.opsForList().leftPush("list","a","b","c");
       	//也可以直接将集合放进去
//        stringRedisTemplate.opsForList().leftPush("list",List对象);
        stringRedisTemplate.opsForList().leftPush("list","a");
        stringRedisTemplate.opsForList().leftPush("list","b");
        stringRedisTemplate.opsForList().leftPush("list","c");
    }

/**
     * @description Ipuest
     * @params []
     * @return void
     * @author -还怕大雨吗
     * @date 2021/7/30 14:50
     */
    @Test
    void Ipuest(){
        //  如果存在集合则添加元素。
        stringRedisTemplate.opsForList().leftPushIfPresent("presentList","o");
        List<String> list = stringRedisTemplate.opsForList().range("presentList", 0, -1);
        System.out.println("通过leftPushIfPresent(K key, V value)方法向已存在的集合添加元素:" + list);
    }
 // size(K key)
//获取集合长度。
    long listLength = redisTemplate.opsForList().size("list");
    System.out.println("通过size(K key)方法获取集合list的长度为:" + listLength);
// 16、rightPop(K key)
        // 移除集合中右边的元素。
		// rightPop(K key, long timeout, TimeUnit unit)
        // 移除集合中右边的元素在等待的时间里,如果超过等待的时间仍没有元素则退出
        String list = stringRedisTemplate.opsForList().rightPop("list");
        System.out.print("通过rightPop(K key)方法移除的元素是:" + list);
        List<String> list1 = stringRedisTemplate.opsForList().range("list", 0, -1);
        System.out.println(",剩余的元素是:" + list1);
/**
     * @description 获取全部的数据
     * @params []
     * @return void
     * @author -还怕大雨吗
     * @date 2021/7/30 14:46
     */
    @Test
    void getredisList(){
        List<String> list = stringRedisTemplate.opsForList().range("list", 0, -1);
        System.out.println("全部的list = " + list);
        List<String> flist = stringRedisTemplate.opsForList().range("list", 1, 2);
        System.out.println("范围取值的flist = " + flist);

    }
//全部的list = [c, b, a]
//范围取值的flist = [b, a]
更多操作详情参码宗

参码宗路灯

https://www.shenmazong.com/album/1402622833635569664/1

8.案例

使用springboot+redis 完成短信发送,验证短信是否超时

这里的短信验证今后也可以在项目中使用

–导入依赖

<!-- 短信接口依赖 -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.15</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.2.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpcore</artifactId>
			<version>4.2.1</version>
		</dependency>
		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.6</version>
		</dependency>
		<dependency>
			<groupId>org.eclipse.jetty</groupId>
			<artifactId>jetty-util</artifactId>
			<version>9.3.7.v20160115</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.5</version>
			<scope>test</scope>
		</dependency>

导入工具类,AuthCodeUtil+HttpUtils

第一个是生成验证码,第二个是发送短信

链接:https://pan.baidu.com/s/1-6a0F11FkDslVdke7PvPyw
提取码:dayy

controller类

@RestController
public class CodeController {

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    @PostMapping("sendCode")
    public Object sendCode(String mobile){
        try {
            //生成规则, 0-9/a-z/A-z  直接输入即可
            String code = AuthCodeUtil.getCode("0-9a-z", 8);
            AuthCodeUtil.sendMsgToPhone(code,mobile);
            System.out.println("mobile = " + mobile);
            stringRedisTemplate.opsForValue().set(mobile,code,1, TimeUnit.MINUTES);
            return "发送成功,验证码为"+code;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "发送失败";
    }

    @PostMapping("pushCode")
    public Object pushCode(String code,String mobile){
        if(mobile!=null){
            String redisCode = stringRedisTemplate.opsForValue().get(mobile);
        	System.out.println("redisCode = " + redisCode);
        	if (redisCode.equals(code)){
           		return "登陆成功";
        	}
        	return "登陆失败";
        }
        return "登陆失败";
    }

    
//mobile = 18434290135
//redisCode = u8ofwhni    
//过1分钟后登陆,则登陆失败
//因为无法获取到redis存放的手机号

9.redis事务

什么是事务

在执行redis执行指令过程中,多条连续执行的指令被干扰,打断,插队

redis事务一个命令执行的队列,将一系列预定义命令包装成一个整体(一个队列),当执行的时候,一次性按照添加顺序执行,中间不会被打断或者干扰

一个队列中,一次性,顺序性。排他性的执行一系列命令

事务的的基本操作

multi –> exec

开启事务。multi

设定事务的开启位置,此指令执行之后,后续的所有指令均加入事务中

执行事务。exec

设定事务的关闭位置,同时执行事务,与multi成对出现,成对使用

取消事务。discard

终止当前事务的定义,发生在开启和提交之间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SYUsjMpG-1627702088509)(H:\技术点\redis\redis-这一篇就够了.assets\1627630862908.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qTxmpMtP-1627702088510)(H:\技术点\redis\redis-这一篇就够了.assets\1627630993107.png)]

加入事务的命令暂时进入到任务队列中,并没有立即执行,只有执行exec命令才开始执行

执行的顺序是队列执行

事务的工作流程

根据输入的命令来判断该如何执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cp25lX97-1627702088510)(H:\技术点\redis\redis-这一篇就够了.assets\1627631144863.png)]

事务的注意事项

在事务中出现语法错误的时候,整体事务所有的命令都不会执行,包括正确的语法

如果语法是正确的,但是操作是错误的,比如让list去执行set的方法,是会分开执行的

已经执行完毕的命令对应的数据是不会自动回滚的,需要程序员自己在代码中实现回滚

(应该在程序中多次测试,这种错误是不允许存在的)

关于事务冲突问题
乐观锁

顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的,用的比较多,比如抢票,

悲观锁

顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。效率偏低,在互联网大并发下不建议使用

事务的三大特性

单独的隔离操作

​ 事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

没有隔离级别的概念

​ 队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行

不保证原子性

​ 事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚

10.redis持久化

redis是内存数据库,如果不进行持久化,一旦服务器退出,服务器中的数据库状态也会消失,所以redis实现了持久化

RDB

是什么:

在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里

Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

在生产环境下,我们会对这个文件进行备份

在redis.conf中配置文件名称,默认为dump.rdb

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PABHpDHa-1627702088511)(H:\技术点\redis\redis-这一篇就够了.assets\1627632782030.png)]

save  30 10 	#在30s内进行了10次操作,同样的意思

触发条件

不管触发以上三个save条件的哪一个,都会进行持久化

执行flushall命令,也会产生dump.rdb文件,但里面是空的,无意义

退出redis也是产生rdb文件

备份自动生成一个rdb文件

恢复rdb文件

将rdb文件放在redis启动目录就可以,redis启动的时候会自动检查dump.rdb恢复其中的数据

config get dir
#/user/local/bin #如果存在就会自动触发

几乎默认的数据就可以供我们使用了

优点:

适合大规模的数据恢复
对数据完整性和一致性要求不高更适合使用
节省磁盘空间
恢复速度快

缺点:

在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改

Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑,会占用一定的内存空间

虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能

AOF

aof是什么

以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作

AOF保存的是appendonly.aof 文件

持久化流程:

客户端的请求写命令会被append追加到AOF缓冲区内;
AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;
AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;
Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O8HCrk3q-1627702088511)(H:\技术点\redis\redis-这一篇就够了.assets\1627634055138.png)]

AOF默认是不开启的需要在配置文件中手动打开

可以在redis.conf中配置文件名称,默认为 appendonly.aof

AOF文件的保存路径,同RDB的路径一致。

AOF和RDB同时启动,redis听谁的

AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)

AOF启动/修复/恢复

AOF的备份机制和性能虽然和RDB不同, 但是备份和恢复的操作同RDB一样,都是拷贝备份文件,需要恢复时再拷贝到Redis工作目录下,启动系统即加载。

正常恢复

修改默认的appendonly no,改为yes
将有数据的aof文件复制一份保存到对应目录(查看目录:config get dir)
恢复:重启redis然后重新加载

异常恢复

修改默认的appendonly no,改为yes

如果这个aof文件错位。redis是启动不起来的,需要修复这个aof文件,如遇到AOF文件损坏,通过/usr/local/bin/redis-check-aof–fix appendonly.aof进行恢复
备份被写坏的AOF文件
恢复:重启redis,然后重新加载

优点和缺点

优点:

1、每一次修改都同步,文件的完整会更加好

2、每秒同步一次,可能会丢失一秒的数据

3、从不同步,效率最高

缺点:

1、相对于数据文件来说,aof远远大于rdb 修复的速度也比rdb慢很多

2、aof运行效率也要比rdb慢,所以redis默认的配置是rdb持久化

重写规则

aof默认就是文件的无限追加。文件只会越来越大

如果aof文件大于64m,太大了,fork一个新的进程来进行文件的重写

扩展

如何选择哪种持久化方式

一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。

如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。

有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。

官方推荐两个都启用。

如果对数据不敏感,可以选单独用RDB。

不建议单独用 AOF,因为可能会出现Bug。

如果只是做纯内存缓存,可以都不用。

性能与实践

我们都知道RDB的快照、AOF的重写都需要fork,这是一个重量级操作,会对Redis造成阻塞。因此为了不影响Redis主进程响应,我们需要尽可能降低阻塞。

降低fork的频率,比如可以手动来触发RDB生成快照、与AOF重写;
控制Redis最大使用内存,防止fork耗时过长;
使用更牛逼的硬件;
合理配置Linux的内存分配策略,避免因为物理内存不足导致fork失败。
在线上我们到底该怎么做?我提供一些自己的实践经验。

如果Redis中的数据并不是特别敏感或者可以通过其它方式重写生成数据,可以关闭持久化,如果丢失数据可以通过其它途径补回;
自己制定策略定期检查Redis的情况,然后可以手动触发备份、重写数据;
单机如果部署多个实例,要防止多个机器同时运行持久化、重写操作,防止出现内存、CPU、IO资源竞争,让持久化变为串行;
可以加入主从机器,利用一台从机器进行备份处理,其它机器正常响应客户端的命令;
RDB持久化与AOF持久化可以同时存在,配合使用。

因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。

如果使用AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。

代价,一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。

只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。

默认超过原大小100%大小时重写可以改到适当的数值。

11.redis主从复制

主从复制-master/slave

主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,master以写为主,slave以读为主

配置集群也都是多个一主多从

能干什么

读写分离

容灾恢复

怎么玩:

配置从(库)不配置主(库)

​ 拜个大哥

从库配置:slaveof 主库 ip 主库端口

​ 每次与master断开之后,都需要重新连接,除非配置进redis.conf文件,否则就配进配置文件,每次启动都读

修改配置文件细节操作

​ 1、拷贝多个redis.conf文件

​ 拷贝多个redis.conf文件include(写绝对路径) 拷贝配置文件

​ 2、开启多个daemonize yes

​ 3、pid文件名称 pidfile

​ 4、指定端口

​ 5、log文件名字

​ 6、dump.rdp名字

​ 7、Appendonly 关掉或者换名字

新建redis6379.conf,填写以下内容

include /myredis/redis.conf

pidfile /var/run/redis_6379.pid

port 6379

dbfilename dump6379.rdb
redis-server #配置名称		#启动redis

启动之后查看主从情况

info replication
#打印主从复制的相关信息

redis -cli 6379/6380/6381	#启动多个redis客户端

配从不配主

slaveof  <ip><port>
#将自己注册给主机
#slaveof 127.0.0.1 6379

#使用命令查看主机配置
info replication

配置成功就是这个样子

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eKLpnCOD-1627702088512)(H:\技术点\redis\redis-这一篇就够了.assets\1627647983412.png)]

在主机中使用写的命令,从机中可以查询,但是如果在从机写,就会报错,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZJMdgP1z-1627702088512)(H:\技术点\redis\redis-这一篇就够了.assets\1627648234863.png)]

常用三招

一主二仆

​ 当一个从服务器挂掉之后,重启之后的状态是主服务器的状态,需要重新注册到主服务器,数据是从头开始复制,相当于,重新复制了一遍,

​ 当大哥挂了,小弟不会上位,小弟永远认这个大哥,

薪火相传

​ 大哥永远是大哥,小弟也会有小弟,小弟挂了下面的小弟就全挂了,大哥无法跨级

反客为主

​ 大哥挂了,小弟上位,

​ slaveof no one

原理:

1、当从链接上主服务器之后,从服务器向主服务器发送进行数据同步消息

2、主服务器接到从服务器发送过来的同步消息,把主服务器数据进行持久化,rdb文件

把rdb文件发送从服务器,从服务器拿到rdb进行读取

3、每次主服务器进行写操作之后,和从服务器进行数据同步,都是主服务器发起的,相当于全量复制,增量复制

哨兵模式

是什么:

​ 反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库

怎么用:

​ 自定义的/myredis目录下新建sentinel.conf文件,名字绝不能错

sentinel monitor mymaster 127.0.0.1 6379 1
#其中mymaster为监控对象起的服务器名称, 1 为至少有多少个哨兵同意迁移的数量

启动哨兵

/usr/local/bin

#redis做压测可以用自带的redis-benchmark工具

#执行redis-sentinel  /myredis/sentinel.conf 

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8lenrKTQ-1627702088513)(H:\技术点\redis\redis-这一篇就够了.assets\1627650937381.png)]

当主机挂掉之后,从机选举新的主机

(大概10秒左右可以看到哨兵窗口日志,切换了新的主机)

哪个从机会被选举为主机呢?根据优先级别:slave-priority

原主机重启后会变为从机。

理解哨兵

新皇登基之后,老皇帝会被拉下马,不能作皇帝了,变成从机

选举规则

够了.assets\1627651214055.png)]

优先级在redis.conf中默认:slave-priority 100,值越小优先级越高

新版本的是:replica-priortity 100

偏移量是指获得原主机数据最全的

每个redis实例启动后都会随机生成一个40位的runi

复制延时

由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。

12.redis集群

问题与解决:

容量不够,redis如何进行扩容?

并发写操作, redis如何分摊?

另外,主从模式,薪火相传模式,主机宕机,导致ip地址发生变化,应用程序中配置需要修改对应的主机地址、端口等信息。

之前通过代理主机来解决,但是redis3.0中提供了解决方案。就是无中心化集群配置。

代理主机

​ 相当于有一个代理机器来访问,需要的机器比较多

无中心化集群配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YXN9IZRT-1627702088514)(H:\技术点\redis\redis-这一篇就够了.assets\1627651622088.png)]

什么是集群

Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。

Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。

搭建集群

制作6个实例,端口号不冲突就可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M3nH6g3x-1627702088515)(H:\技术点\redis\redis-这一篇就够了.assets\1627692377683.png)]

复制多个redis,删除rdb文件,使用最初始的配置文件,

Appendonly 关掉或者换名字

配置修改

cluster-enabled yes    #打开集群模式
cluster-config-file nodes-6379.conf  #设定节点配置文件名
cluster-node-timeout 15000   #设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换。

修改别的配置文件,:%s/6379/6380—以此类推

低版本需要装rb环境,在6.1已经自动装好了,然后才能进行集群操作

#进入安装环境
cd  /opt/redis-6.2.1/src
#根据端口什么的自行修改
redis-cli --cluster create --cluster-replicas 1 
192.168.11.101:6379 192.168.11.101:6380 192.168.11.101:6381 192.168.11.101:6389 192.168.11.101:6390 192.168.11.101:6391

询问是否接受这种分配

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N9nXwAMR-1627702088515)(H:\技术点\redis\redis-这一篇就够了.assets\1627692063413.png)]

连接集群

-c #采用集群策略连接,设置数据会自动切换到相应的写主机
#命令查看集群信息
cluster nodes

分配原则

​ 分配原则尽量保证每个主数据库运行在不同的IP地址,每个从库和主库不在一个IP地址上。保证集群的高可用,主和主分开放,从和主分开放,

slots插槽

一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个,

取值范围为 0-16383

集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。

集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点, 其中:

节点 A 负责处理 0 号至 5460 号插槽。

节点 B 负责处理 5461 号至 10922 号插槽。

节点 C 负责处理 10923 号至 16383 号插槽。

计算key的值来进行存储,为了缓解压力,

在集群存入值

计算k1的值,k1的值是节点c的插槽,自动切换节点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WqVNgQFd-1627702088516)(H:\技术点\redis\redis-这一篇就够了.assets\1627692835881.png)]

你不能使用多个key value进行存储,因为无法计算插槽

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H8oUruHb-1627702088516)(H:\技术点\redis\redis-这一篇就够了.assets\1627692918932.png)]

当你需要存入多个key value的时候,可以使用分组的形式存储

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F3h2ENnI-1627702088517)(H:\技术点\redis\redis-这一篇就够了.assets\1627693010558.png)]

自己只能查看自己插槽中的值

CLUSTER GETKEYSINSLOT <slot><count> #返回 count 个 slot 槽中的键。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-49nvZ75n-1627702088517)(H:\技术点\redis\redis-这一篇就够了.assets\1627693171112.png)]

故障恢复

当集群中某个主节点挂了之后,从节点马上成为主节点,当主节点恢复之后,变成主节点

如果所有某一段插槽的主从节点都宕掉,redis服务是否还能继续?

如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为yes ,那么 ,整个集群都挂掉

如果某一段插槽的主从都挂掉,而cluster-require-full-coverage 为no ,那么,该插槽数据全都不能使用,也无法存储。

redis.conf中的参数 cluster-require-full-coverage

集群优缺点

优点:

​ 实现扩容-分摊压力-无中心配置相对简单

缺点:

​ 多键操作是不被支持的

​ 多键的Redis事务是不被支持的。lua脚本不被支持

​ 由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分片的方案想要迁移至redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。

13.redis应用问题解决

redis雪崩

1、数据库压力变大,应用访问变慢,访问redis堆积过大,导致全部崩溃

问题:

​ 1、在极少时间段,查询大量key的集中过期情况

解决方案

(1)构建多级缓存架构:nginx缓存 + redis缓存 +其他缓存(ehcache等)

(2)使用锁或队列:
用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况

(3) 设置过期标志更新缓存:

记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。

(4) 将缓存失效时间分散开:

比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

redis穿透

1、应用服务器压力变大

2、访问缓存获取不存在数据,redis命中率降低

3、redis没有这个数据,则一直查询数据库

redis查询不到数据库,

出现很多的非正常的url访问

多是出现黑客攻击,恶意攻击

目的不是获取数据,是让服务器崩溃

解决方案

1、对空值作缓存,

​ 如果一个查询返回的数据为空(不管是数据是否不存在),我们仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟

2、设置白名单

​ 使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问。

3、布隆过滤器

​ (1) 是1970年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。

布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。)

将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被 这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力

4.实时监控

​ 当发现Redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务

redis击穿

1、数据库的访问压力瞬间加大

2、redis里并没有出现大量key过期

3、redis正常运行

原因:

​ 1、redis中某个key,但是这个key是热点key,redis就去查数据库,热点查询,就让数据库压力过大

解决问题:

​ 1、==预先设置热门数据:==在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长

​ 2、**实时调整:**现场监控哪些数据热门,实时调整key的过期时长

​ 3、使用锁

​ 肯定能解决问题,但是会拉低效率

​ (1) 就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db。

​ (2) 先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX)去set一个mutex key

​ (3) 当操作返回成功时,再进行load db的操作,并回设缓存,最后删除mutex key;

​ (4) 当操作返回失败,证明有线程在load db,当前线程睡眠一段时间再重试整个get缓存的方法。

总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Er6lO7Dy-1627702088518)(H:\技术点\redis\redis-这一篇就够了.assets\1627701831376.png)]

分布式锁
问题描述:

​ 随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

分布式锁主流的实现方案:

基于数据库实现分布式锁

基于缓存(Redis等)

基于Zookeeper

每一种分布式锁解决方案都有各自的优缺点:

性能:redis最高

可靠性:zookeeper最高
这里,我们就基于redis实现分布式锁

解决方案:
setnx u1 100 #创建锁
dl ui		#释放锁

给锁设置过期时间

expire ui 10  #过期就失效

这不是一个原子操作,如果在两个命令之间出现bug之后,就无法设置时间

使用原子命令

#         上锁  过期时间
set urs 10 nx  ex 13

使用redisson

​ redis的儿子

​ 使用redisson进行锁续命

路标

​ bilibili视频地址:https://www.bilibili.com/video/BV1sK4y1x749?p=2

14.redis6.0新功能

ACL

Redis ACL是Access Control List(访问控制列表)的缩写,该功能允许根据可以执行的命令和可以访问的键来限制某些连接。

在Redis 5版本之前,Redis 安全规则只有密码控制 还有通过rename 来调整高危命令比如 flushdb , KEYS* , shutdown 等。Redis 6 则提供ACL的功能对用户进行更细粒度的权限控制 :

参考官网:https://redis.io/topics/acl

Io多线程

简介

​ Redis6终于支撑多线程了,告别单线程了吗?
IO多线程其实指客户端交互部分的网络IO交互处理模块多线程,而非执行命令多线程。Redis6执行命令依然是单线程。

原理

​ Redis 6 加入多线程,但跟 Memcached 这种从 IO处理到数据访问多线程的实现模式有些差异。Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程。之所以这么设计是不想因为多线程而变得复杂,需要去控制 key、lua、事务,LPUSH/LPOP 等等的并发问题。

另外,多线程IO默认也是不开启的,需要再配置文件中配置
io-threads-do-reads yes
io-threads 4

15.redis面试20问

1、什么业务场景下使用mysql,什么业务场景下用redis

​ mysql是关系型数据库,存储结构清晰的数据放在mysql里,存重要的东西放在mysql里

​ redis是非关系型数据库,无结构,速度快,一般是用来作缓存

​ msyql可以进行复杂的多表联查,而redis则实现比较麻烦,只能通过代码实现

2、redis是单线程还是多线程,为什么使用单线程,可以用多线程吗

​ redis是单线程的,最新版的redis是多线程的

​ 因为redis性能很好,是靠缓存运行,单线程可以处理所有并发,不需要使用多线程

​ 貌似不可以,我看了很多资料,因为他速度不快,不需要设置多线程

3、你们拿redis是当数据库的吗

​ 不是,作缓存用的

4、什么情况下使用redis,用到redis的那些集合

​ 缓存,分布式锁,社交,消息,用的都比较多

​ 像list,zset,hash用的都比较多

5、redis 的string和hash最大的长度是多少

​ string的最大长度是512m

​ hash可以存42亿个键值对

6、举一个业务场景redis使用过程中什么时候用hash,什么时候用list

​ 就比如说短信验证,有一个key,字段里放着手机号或者验证码,点击注册的时候,从key里获取值,验证手机号,做列表的时候用list比较多,直接把列表当作集合存入缓存

​ 为什么很少用string

​ 字符串存储有大小,超过就不能用了

​ hash类型查询快,比string存储和获取都要快

​ 字符串类型key容易重复,hash的key不容易重复

7、redis作缓存的时候,如果数据发送改变的话,怎么同步?

​ 只要程序列表方法使用了缓存,在更新方法中放入更新redis数据操作,只要一更新,就会触发更新缓存,其实就是删除重新获取

8、redis当中每次加一,一个原生的方法比get后加一 再set会快一点,你知道这个方法吗

​ 用incr命令实现命令行让redis数据自增,java代码是调用redistemplate的一个自增方法,也很简单

9、加入购物车,总共有十个商品都加入到了购物车中,其他还呢能加入吗,购买完成后怎么减库

​ 未登录的情况下,购物车的增减都是操作cookie,登陆成功后才是操作redis,点击加。前台发送一个ajax请求,先获取redis或者cookie当中的数据,得到购物车数据,给对应数据+1

​ 每次操作cookie或者redis,速度都很快,不需要担心速度问题

10、redis持久化做过吗

​ 做过,有aof和rdb两种方式

​ rdb是以快照形式,定期持久化,是默认的,编辑redis.conf文件 有save开头的三行数据

修改之后就可以完成,开启,关闭,设置

​ save代表保存,

​ save 900 1 代表900秒内如果一个key被修改到900秒这些数据将会保存

​ 如何关闭

​ 将三个save注释掉即可

​ aof是执行增删改之后,命令都存储在文件中,数据恢复的时候执行一遍就行

​ appendonly yes #启动aof持久化

​ appendfsync everysec #每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐

​ appendfsync always #每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用

11、你们公司使用的都是什么持久化

​ 两种都开启,快照在宕机之后使用,日志模式恢复比较慢,

​ 混合用能在一定程度下保证数据安全,两个配合,既保证了恢复速度,也保证了数据不丢失

12、说下redis处理高并发时如何应用

​ 对redis并没有深入学习,但在网上看过几篇博客,有一种方法是多路IO复用

13、用过其他缓存吗,

​ mybatis缓存说一下

14、memcached 了解吗 说下区别

​ 不是很了解,只是了解他们的区别

​ 1。memcached 只能存储简单的字符串数据类型,

​ 2。memcached 和redis都是内存缓存,但是他宕机后数据不可恢复

​ 3。性能都是一样的

15、被攻击的情况会出现雪崩,你怎么处理

​ 被攻击一般都是一个或者多个ip高频反刷服务器,判断这个ip高频访问,我就把他存入数据库一个小时,把这个ip封锁。然后给前台返回一个访问频繁,

16、你的redis宕机了怎么办,怎么处理

​ 立马重启,redis有持久化机制,数据不会丢失,排查日志,如果是并发就加服务器,bug就调试bug,黑客攻击就杀ip,能力允许就搭集群

17、能讲一下redis击穿吗,怎么处理

​ redis击穿,首先redis的key没有失效,错误的url,错误的请求,redis没有,大量请求访问数据库,导致数据库压力过大,我们使用最多的就是布隆过滤器了

18、redis事务有了解过吗

​ 看上面事务详情

19、redis分布式锁

​ 上面redis分布式锁

​ setnx的使用

​ https://blog.csdn.net/lihao21/article/details/49104695

景redis使用过程中什么时候用hash,什么时候用list

​ 就比如说短信验证,有一个key,字段里放着手机号或者验证码,点击注册的时候,从key里获取值,验证手机号,做列表的时候用list比较多,直接把列表当作集合存入缓存

​ 为什么很少用string

​ 字符串存储有大小,超过就不能用了

​ hash类型查询快,比string存储和获取都要快

​ 字符串类型key容易重复,hash的key不容易重复

7、redis作缓存的时候,如果数据发送改变的话,怎么同步?

​ 只要程序列表方法使用了缓存,在更新方法中放入更新redis数据操作,只要一更新,就会触发更新缓存,其实就是删除重新获取

8、redis当中每次加一,一个原生的方法比get后加一 再set会快一点,你知道这个方法吗

​ 用incr命令实现命令行让redis数据自增,java代码是调用redistemplate的一个自增方法,也很简单

9、加入购物车,总共有十个商品都加入到了购物车中,其他还呢能加入吗,购买完成后怎么减库

​ 未登录的情况下,购物车的增减都是操作cookie,登陆成功后才是操作redis,点击加。前台发送一个ajax请求,先获取redis或者cookie当中的数据,得到购物车数据,给对应数据+1

​ 每次操作cookie或者redis,速度都很快,不需要担心速度问题

10、redis持久化做过吗

​ 做过,有aof和rdb两种方式

​ rdb是以快照形式,定期持久化,是默认的,编辑redis.conf文件 有save开头的三行数据

修改之后就可以完成,开启,关闭,设置

​ save代表保存,

​ save 900 1 代表900秒内如果一个key被修改到900秒这些数据将会保存

​ 如何关闭

​ 将三个save注释掉即可

​ aof是执行增删改之后,命令都存储在文件中,数据恢复的时候执行一遍就行

​ appendonly yes #启动aof持久化

​ appendfsync everysec #每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐

​ appendfsync always #每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用

11、你们公司使用的都是什么持久化

​ 两种都开启,快照在宕机之后使用,日志模式恢复比较慢,

​ 混合用能在一定程度下保证数据安全,两个配合,既保证了恢复速度,也保证了数据不丢失

12、说下redis处理高并发时如何应用

​ 对redis并没有深入学习,但在网上看过几篇博客,有一种方法是多路IO复用

13、用过其他缓存吗,

​ mybatis缓存说一下

14、memcached 了解吗 说下区别

​ 不是很了解,只是了解他们的区别

​ 1。memcached 只能存储简单的字符串数据类型,

​ 2。memcached 和redis都是内存缓存,但是他宕机后数据不可恢复

​ 3。性能都是一样的

15、被攻击的情况会出现雪崩,你怎么处理

​ 被攻击一般都是一个或者多个ip高频反刷服务器,判断这个ip高频访问,我就把他存入数据库一个小时,把这个ip封锁。然后给前台返回一个访问频繁,

16、你的redis宕机了怎么办,怎么处理

​ 立马重启,redis有持久化机制,数据不会丢失,排查日志,如果是并发就加服务器,bug就调试bug,黑客攻击就杀ip,能力允许就搭集群

17、能讲一下redis击穿吗,怎么处理

​ redis击穿,首先redis的key没有失效,错误的url,错误的请求,redis没有,大量请求访问数据库,导致数据库压力过大,我们使用最多的就是布隆过滤器了

18、redis事务有了解过吗

​ 看上面事务详情

19、redis分布式锁

​ 上面redis分布式锁

​ setnx的使用

​ https://blog.csdn.net/lihao21/article/details/49104695

20、redis是单线程的,不会出现线程不安全问题,也不需要加锁。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值