Redis学习笔记

Redis概述

Redis介绍以及应用场景

Redis是使用C语言开发的数据库,它与传统的数据库的不同之处在于它的数据都放在内存中,也叫内存数据库,所以它的存储速度非常的快。因此redis被广泛用于应用缓存的方向

Redis的应用场景

  • 在客户端和数据库之间做缓存使用,减轻数据库的压力

  • 做数据库使用,临时存储一些数据,如字典表,常用信息等

  • 解决分布式场景下的Session分离问题

  • 分布式锁

  • 任务队列(秒杀,抢红包等)

  • 应用排行榜

  • 签到

Redis基本操作

这里只对redis的常用操作做记录,详细的操作见:https://www.redis.com.cn/commands.html

**
**Redis的key的一般设计原则

  • 用:分割,也可用-

  • 用表名做key的前缀,比如: user:

  • 第二段key用主键

  • 第三段key用列名

String

常用命令:set、get、strlen、del、exists、decr、incr、setex等

单值缓存:set、get

img

多值缓存:MGET、MSET

img

分布式锁:setnx、del

返回1代表锁成功,返回0代表锁失败

img

防止程序出现意外导致死锁,可以对分布式锁设置过期时间:

set key true ex seceond nx (设置10秒的过期时间)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BpC0MKIh-1639066498714)(https://i.loli.net/2021/11/24/e7GKmJlY95dWfnv.png)]

设置key的生存时间:setex key seconds value

将key设置seconds的存活时间,值为value

如果key存在,则被覆盖

img

设置将key自增或自减:incr、decr

如果key没有值,则都将key初始化为1

img

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gwuMyuY3-1639066498717)(https://i.loli.net/2021/11/24/UkQVDE8Ri4r7Hsw.png)]

判断key是否存在:exists

img

Set

类似java中的hashSet,是一个无序不重复集合。可以实现取交集、并集、差集的操作。

常用命令:sadd,spop,smembers,sismember,scard,sinterstore,sunion等

缓存与查看:sadd、smembers

img

弹出数据:spop

img

查看集合中数据个数:scard

img

判断集合中是否存在某元素:sismember

img

取交集:sinter、sinterstore

img

将取出的交集放入新集合

img

取并集:sunion、sunionstore

img

Hash

hash类似于java中的hashMap,主不过redis中的hash做了更多的优化。hash非常适合存储对象类型的数据,可以通过set、get只对对象中的某个字段做一个操作

常用命令:hset、hmset、hexists、hget、hmget、hgetall、hkeys、hvals

注意:hash的过期时间只能作用在key上,而不可以设置在field上

单值操作:hset、hget

img

多值操作:hmset、hmget

img

查询某个key下全部值:hgetall

img

查询全部key或value:hkeys、hvals

img

List

redis中的list是双向链表,但是带来了额外的内存开销

常用命令:rpush、rpop、lpush、lpop、lrange、llen

左右插入:lpush、rpush

img

左右删除并获取:lpop、rpop

img

获取数据:lrange

lrange key 开始index 结束index

img

Zset(sortedset)

有序集合,和set相比多了一个score权重,可以根据权重排序

常用命令:zadd、zcard、zscore、zrange、zrevrange、zrem等

添加与查看:zadd、zrange

img

img

查看单个和个数:zscore、zcard

img

BitMap

bitmap存储的连续的二进制数字,只需要一个bit位,即可表示对应的值或者状态,8个bit组成一个byte,所以bitmap特别省空间

常用命令:setbit、getbit、bitcount、bitop

设置和查看bit位的值:setbit、getbit

img

统计区间内bit位为1的数量:bitcount key、bitcount key start end

img

Geo(地理位置)

Redis深入了解

Redis的持久化原理

redis的数据存储在内存中,持久化是指redis将数据写成数据文件,存入硬盘中。在redis重启时,通过读取数据文件的方式将数据重新存入内存中,从而达到数据持久化存储的目的。redis有两种不同的持久化存储方式,一种是RDB,一种是AOF,在redis.conf中可以配置数据的存储方式为rdb或者aof

redis的持久化文件默认存储在配置文件的文件夹下

RDB

RDB是以快照的方式,通过某种触发规则,将redis的全量数据快照,并生成rdb文件(后缀是.rdb)。在redis重启时,会读取RDB快照文件恢复数据。

RDB的触发方式有手动触发和自动触发

指令手动触发

  • sava:输入此命令会阻塞redis服务器,知道rdb文件创建完毕为止,一般线上不采用这种方式
  • bgsave:输入此命令不会阻塞redis服务器,该方式会fork一个子进程,子进程负责持久化

自动触发

  • 根据 sava m n(m秒内有n次key值的变化操作就触发持久化)配置规则自动触发

  • 从节点全量复制时,主节点会发送rdb文件给从节点,主节点触发bgsave

  • 执行debug reload时

  • 执行shutdown时,如果没有开启aof,也会发生

redis.conf:
# 时间策略
save 900 1 # 表示900 秒内如果至少有 1 个 key 的值变化,则触发RDB
save 300 10 # 表示300 秒内如果至少有 10 个 key 的值变化,则触发RDB
save 60 10000  # 表示60 秒内如果至少有 10000 个 key 的值变化,则触发RDB
# 文件名称
dbfilename dump.rdb
# 文件保存路径
dir /home/work/app/redis/data/
# 如果持久化出错,主进程是否停止写入
stop-writes-on-bgsave-error yes
# 是否压缩
rdbcompression yes
# 导入时是否检查
rdbchecksum yes
RDB的优缺点分析

优点

  • 备份数据效率高,适用于大规模的数据备份,自动备份不会影响主线程工作
  • 备份相比AOF方式来说占用空间很小。

缺点

  • 可能会造成数据丢失,如果配置了自动备份的条件,如果没有触发备份条件,但是redis又因为一些问题宕机了,那么 没有触发备份的数据就会丢失。
  • 自动备份通过fork子进程的方式进行的,会占用两倍的内存,如果内存不足,可能会备份失败

AOF

AFO存储是指记录每一次对服务器的写操作(写命令),当服务器重启的时候,会重新执行这些命令以达到恢复数据的目的

特点:

  1. 以日志的方式来记录写操作,读操作不会记录

  2. 文件已追加的形式而不是修改

  3. redis的aof其实就是把追加的文件从开始读到结尾,执行写操作

AOF的流程:

  • 所有的写命令都会追加到redis 的缓冲区中

  • aof的缓冲区根据策略向硬盘进行同步的操作

  • 随着aof文件越来越大,redis会定期对aof文件执行重写操作(将对同一个key的操作合成一个,无效的操作删除)

  • 当redis重启时,加载aof文件,恢复数据

# 可以通过修改redis.conf配置文件中的appendonly参数开启
appendonly yes 
# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的。
dir  ./ 
# 默认的文件名是appendonly.aof,可以通过appendfilename参数修改
appendfilename  appendonly.aof

AOF文件写入和同步

redis每结束一次事件,都会调用flushAppendOnlyFile函数,判断是否需要将AOF缓冲区中的数据写入到AOF文件中。

这个函数的判断逻辑是配置在redis.conf文件中的:

img

always:每次都存入

everysec:每秒存入

no:只写入不保存,只有当redis关闭时才执行写入aof的操作

AOF的重写

当随着时间的积累,aof文件越来越大,那么这时候,就需要对aof的文件进行重写操作,即:

  • 将对同一个key的操作合成为一条命令
  • 将在后面就会被删除的key的命令,在文件中删除

触发:

  • 手动调用bgrewriteaof命令
  • 自动触发,需要配置
# 重写机制:避免文件越来越大,自动优化压缩指令,会fork一个新的进程去完成重写动作,新进程里的内存
数据会被重写,此时旧的aof文件不会被读取使用,类似rdb
# 当前AOF文件的大小是上次AOF大小的100% 并且文件体积达到64m,满足两者则触发重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

总结

如果rdb文件和aof文件同时存在,那么重启时会读取aof文件来恢复数据

线上实战经验

  1. 如果Redis中的数据不重要或者不敏感,可以不开启持久化,及时数据丢失对业务影响不大

  2. 可以根据情况指定redis的策略,可以手动出发备份

  3. 可以加入主从机器,一台机器单独进行备份处理,另一台机器正常响应客户端的命令

  4. RDB持久化和AOF可以都开启,配合使用,但是会影响一部分的性能,影响不大,看情况使用

过期删除策略和内存淘汰策略

redis的key是可以设置过期时间的,那么请思考:

  1. 如何设置redis的过期时间

  2. redis的key过期了,数据就从redis中删除了吗

  3. 如何设置redis 的内存大小,如果redis的内存满了,该如何调整?

Redis设置过期时间

redis有四种方式设置过期时间

EXPIRE :表示将键 key 的生存时间设置为 ttl 秒。
PEXPIRE :表示将键 key 的生存时间设置为 ttl 毫秒。
EXPIREAT :表示将键 key 的生存时间设置为 timestamp 所指定的秒数时间戳。
PEXPIREAT :表示将键 key 的生存时间设置为 timestamp 所指定的毫秒数时间戳。

在redis中,前三种的方式最后都会转换为最终一条命令去执行

Redis过期的判定

redis中一旦给一个key设置了过期时间,那么这条数据就会被放置于一个过期字典中。当我们查询一个键时,如果该键在过期字典中,那么就查询他的过期时间,将其和系统时间进行判定,如果比系统的时间小,那么就过期了。

Redis过期删除策略

redis中的数据并不是一旦到了过期时间就会被删除,而是分为惰性删除和定时删除

惰性删除:惰性删除就是,所有的读写命令在执行前都会调用一个expireIfNeeded函数对其进行判定,如果过期了,那么就将key删除,然后再执行原有操作。

定时删除:定时删除就是,redis 以一定的频率定时取出一部分的随机数据进行检查,然后删除其中过期的key

注意:并不是一次检查所有,而是随机检查一定数量的key

在redis2.6.0中,大概每秒执行10次,2.8后可以在redis.conf中调整hz的参数来调整频率

内存淘汰策略

在redis.conf文件中,可以通过配置maxmemory参数来指定redis的最大内存,当redis现有内存大于这个值时,就会出发内存淘汰策略。

内存淘汰有常见的三种策略:FIFO、LRU和LFU三种

  • FIFO:先进先出:移除最早存入redis的数据

  • LRU:移除最近最少使用的

  • LFU:移除最不经常使用的

redis可以通过配置,选择他内存淘汰的算法

Redis性能测试

Redis的性能测试可以使用官方提供的工具:benchmark

语法:

redis-benchmark -t set,get,incr -n 1000000 -q

Redis高可用

当redis停止服务后,重新启动,可通过读取rdb或aof文件的方式将数据恢复,这是正常情况。如果redis服务器损坏,导致数据丢失,该如何处理?

针对这个问题,redis推出了主从复制

主从复制

主从复制就是指将一台redis服务器的数据复制到其他的redis服务器节点。前者称为主节点(master),而后者称为从节点(slave)。从节点有着和主节点一样的数据。数据的复制是单向的,只能从主节点复制到从节点。引入主从复制后,主节点可以负责查询和修改等操作,而从节点只能负责查询的操作。当主节点的数据发生更改,会像从节点同步,确保他们的数据一致

主从复制的原理

主从复制有两种方式,一种是全量同步,一种是增量同步。

  • **全量同步:**是指主节点发送自己的rdb文件到从节点,从节点通过读取rdb文件的方式将数据写入自己的服务器。同步期间主节点进行的操作也会写入,再发送给从节点

  • **增量同步:**因为各种原因导致主从节点断开后,从节点在连上主节点后会尝试从断开后未同步的部分开始同步,也叫部分复制

    • 增量同步的原理是:主节点会记录一个字符串标志同步的版本号,还会一直记录一个数据集的偏移量,从节点会根据版本号和偏移量确定自己要同步的数据有哪些。如果版本号和偏移量差异太大,redis会选择重新进行全量同步
如何配置主从复制

要配置主从复制,不要主节点做任何事,只需要对从节点进行操作即可。

有以下三种方式来配置主从复制:

  1. 通过配置文件,在配置文件中加入slaveof host port

  2. 在redis-server启动命令后加入 --slaveof host port

  3. 在Redis服务器启动后,通过客户端执行命令:slaveof host port,则该Redis实例称为从节点

通过 info replication命令可以查看节点的状态,如下图所示,就表示该节点为主节点

img

一个主节点可以配置多个从节点,这样数据的安全性和完整性可以得到保证,但是如果主节点挂了,那么从节点会变成主节点吗?不会的,所以要引入哨兵机制

sentinel哨兵模式

哨兵模式为了解决的问题就是:当一个主节点有多个从节点,但是主节点挂掉了,从节点又无法担任主节点,那么整个redis还是不可用。

哨兵模式是一种特殊的模式,哨兵是一个独立的进程,他会独立运行,他会主动向redis服务器发送命令,以此来监控各个redis实例的状态

哨兵的搭建步骤:

  1. 在redis的配置文件目录下新建sentinel.conf文件,然后配置相应的内容:
sentinel monitor 被监控机器的名字(自己起名字)ip地址 端口号 得票数

上面的得票数表示主机挂掉后salve投票让谁称为主机,得票数大于1即可变成主机

  1. 启动哨兵
redis-sentinel /路径/sentinel.conf

哨兵也支持创建集群

Redis集群 cluster

主从加哨兵模式问题缺陷

主从加哨兵模式,本质上还是只有一个master节点,数据安全性得到了保证,但是当写请求过于频繁时,对master的压力就会很大,并且每一个节点都需要保存全量的数据,数据冗余比较多

Cluster

custer的主要用途是实现数据分片,将数据分散在多台主机上,当查询key时会计算该key会存储在哪台服务器上,存储也是一样。

当主节点下线时,从节点会自动变为主节点!!!

集群的分片策略:集群的分片策略是为了决定key的存储位置的,常见的数据分布的方式有:顺分布、哈希分布、节点取余哈希、一致性哈希

Redis集群中主节点内置了故障检测和故障转移功能。当集群中的某个主节点下线时,集群中的其他在线主节点会注意到,然后进行故障转移

Redis的集群搭建

首先,创建如下6个文件夹,将redis.conf文件分别拷贝到这些文件夹中

img

对这6个文件夹中的配置文件进行修改:

#cluster-enabled yes 改为 cluster-enabled yes

#cluster-config-file nodes-6379.conf 改为 cluster-config-file "/usr/local/ruanjian/redis/clustor/nodes-9001.conf"	端口设置为多少,这里就叫多少

启动好后查看进程:

img

使用redis-cli连接即可:

#replicas表示副本数,如果指定1则表示1个从库做备用
redis-cli --cluster create 127.0.0.1:9001 127.0.0.1:9002 127.0.0.1:9003
127.0.0.1:9004 127.0.0.1:9005 127.0.0.1:9006 --cluster-replicas 1

Redis实战

Spring Data Redis的搭建

首先创建maven项目,以springboot作为父依赖

    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.2.6.RELEASE</version>
    </parent>


引入SpringDataRedis的依赖:

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

配置好依赖后,在application.yml或application.properties中加入如下配置:

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: XXXX 没有不设置
    database: 0 也可以选其他

在使用时直接注入StringRedisTemplate和RedisTemplate即可

StringRedisTemplate和RedisTemplate之间的数据时不互通的,是因为StringRedisTemplate的key存的是字符串,value也是正常的字符。而RedisTemplate存的是序列化后的key和value,所以他们之间的数据不互通。使用RedisTemplate有一个好处,就是他可以直接把对象存储进去,拿回来时不需要再转化,直接就能拿到完整的对象

Redis的企业应用

分布式锁

为什么要上锁?

因为在多线程大量并发时,如果有类似秒杀的场景,会导致出现商品超卖问题,因为代码中判断是否卖完和对商品总量-1这两步一定不是原子操作,所以在大量并发时,因为线程之间频繁切换,就会导致商品超卖出很多。

为什么要分布式锁?

因为现在大部分企业采用的都是多容器部署,都采用K8S的方式,如果只上了单机锁,在其他的容器是获取不到锁的,所以必须使用分布式锁。

分布式锁通常怎么做?

分布式锁通常使用redis或zookeeper来实现,但在实际开发中,redis是首选

分布式锁在使用时有哪些需要注意的

  • 锁续约:很多业务在执行时,都会给redis上锁,为了避免死锁,会给锁一个过期时间。但是如果过期时间设置的太小,业务还没执行完,锁已经过期,其他请求就可以进来,这会导致业务出现问题

锁续约是给redis开启一个守护线程,这个守护线程会监控主线程的执行,若主线程还没执行结束,就给主线程的锁续约,已达到线程安全的目的

  • 可重入性:redis是不支持可重入的,一个请求再调用自己的时候,会发生被锁住的情况,支持可重入锁是很麻烦复杂的,我们可以用其他的方式

以上两个问题-锁续约和可重入锁实现起来都很复杂,但是可以依赖第三方 Redisson,他已经对redis做了很多封装,简单易用

坐标:

<dependency>
            <groupId>>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.8.2</version>
        </dependency>

布隆过滤器

如果有以下场景:

  1. 需要在10亿的数字中,判断某个数字是否存在
  2. 在一亿的手机号码中判断谁某人是否已经注册过

这时候,如果要快速的判断数据,如果将数据全部放在redis或者内存中,那会非常大。那么布隆过滤器就是很好的选择

思想

如果想判断一个元素是不是在集合中,一般的做法是将数据保存起来,然后通过比较确定。但是随着集合越来越庞大,检索的速度就会越来越慢。那么,数据能不能不存储呢?

可以通过hash表的方式,调用hash函数,将原数据映射成一个点,这样,只要判断hash过后的那个点是不是1,就能知道数据是否存在。

原理

redis中的bitmap就是借鉴布隆过滤器的思想,布隆过滤器就是一个超大二进制数组,每一位上要么是1,要么是0。但是一次hash过后的取值可能有问题,因为hash过后的值时有可能重复的。而布隆过滤器采用了将一个key进行多次hash运算的方式,确保发生hash碰撞的概率很低。

简单来说就是:布隆过滤器中判断数据是否存在,如果为1,那么有可能存在,如果为0,那么一定不存在。

优缺点

布隆过滤器的优点:

  1. 占内存极小
  2. 检索非常快

布隆过滤器的缺点:

  1. 不准确,一定会有误判率
  2. 数据无法删除,只能添加

实现方式

如果在内存中实现布隆过滤器,那么guava工具类可以帮我们做到

如果在redis中实现,可以借助Redisson工具类

Redis的经典面试题

  1. 什么是redis的雪崩?如何应对?

redis雪崩是指多个热门的key在同一时间大量过期,大量的请求突然全部进入数据库,导致数据库的压力骤增。

对于这个问题,可以通过给redis的key设置统一的过期时间时,在后面加上一个随机值,这样数据就会陆续的过期,给数据库缓冲的时间

  1. 什么是缓存穿透?

缓存穿透是指,有人以大量的请求去请求系统内一个不存在的key,如-1等,redis无法拦截,导致数据大量的进入数据库。

解决思路:

  1. 对不合法的key进行判断,如-1等

  2. 使用布隆过滤器,可以拦截大量的请求,只有当数据在库中可能存在时,请求才会穿过redis

  3. 什么是缓存击穿?

缓存击穿是指一个热点访问的key,在大规模并发请求的情况下,这个key过期了,数据库的请求量骤增

解决思路:

  1. 关于特别热门的key,可以考虑设置key不过期
  2. 使用互斥锁,可以防止把数据库弄崩,但是会导致系统性能非常差
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

累人猿-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值