Redis学习笔记

Redis学习笔记

目录

  • 五大基本数据类型
  • 三种特殊数据类型
  • Redis事务
  • Springboot整合Redis
  • Redis配置文件
  • Redis持久化
  • Redis主从复制
  • 哨兵模式
  • Redis集群
  • Redis缓存穿透、击穿和雪崩

五大基本数据类型

具体命令详解参考:Redis 命令参考

String

String命令

说明:以键值对的形式存储,值可以是字符串或数字

List

List命令

说明:List可以看做是一个双向链表,能够实现栈,队列,阻塞队列等操作

Set

Set命令

说明:Set是一个无序不重复集合

Hash

Hash命令

补充:也是键值对的形式,不过键值对的值是一个map,或者说值也是一个键值对。Hash可以用来存对象,比用字符串的存更加方便和高级

Zset

Zset命令

说明:Zset是一个有序集合,类似set,都是字符串的集合,都不允许重复的成员出现在一个set中,他们之间差别在于Zset中每一个元素都会有一个 score与之关联,这个score可用于排序和排名

三种特殊数据类型

具体命令详解参考:Redis 命令参考

Geospatial地理位置

Geospatial命令

补充:Geospatial底层是用Zset实现,因此可以使用zrange命令查看所有位置名称,使用zrem命令删除位置信息

HyperLogLog

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

HyperLogLog命令

补充:每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数

BitMaps

BitMaps命令

补充:只能存0和1。能够用于只有两个状态的情况。

Redis事务

  • Redis单条命令保证原子性,但是事务不保证原子性
  • Redis事务没有回滚
  • Redis务中没有隔离级别

Redis事务概念

  Redis事务其实是一组命令的集合。即在一个事务中执行多个命令,并且所有命令都会被序列化。在执行的过程中,会按照入队的顺序进行顺序执行,并且不会有其他命令插入到执行序列中。

总结:Redis事务具有一次性、顺序性和排他性。

Redis事务没有隔离级别

  Redis事务在执行EXEC命令前并不会真正的执行,只是放入到执行队列中,只有在执行了EXEC命令后才会开始执行事务中的命令,由于Redis使用单线程的方式来执行事务,并且服务器保证,在执行事务期间不会对事务进行中断,因此,Redis中没有事务隔离级别。

Redis事务三个阶段

  1. 开始事务(MULTI命令)
  2. 命令入队(执行什么命令,写什么命令)
  3. 执行事务(EXEC命令)

Redis事务相关命令

  • multi : 标记一个事务块的开始
  • exec : 执行所有事务块的命令
  • discard : 取消事务,放弃事务块中的所有命令
  • watch key1 key2 … : 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )
  • unwatch : 取消watch对所有key的监控

若在事务队列中存在命令性错误(类似于java编译性错误),则执行EXEC命令时,所有命令都不会执行

若在事务队列中存在语法性错误(类似于java的1/0的运行时异常),则执行EXEC命令时,其他正确命令会被执行,错误命令抛出异常

Redis的乐观锁(Watch实现)

  在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key被其他命令所改动,那么事务将被打断。
  watch指令类似于乐观锁,在事务提交时,如果watch监控的多个KEY中任何KEY的值已经被其他客户端更改,则使用EXEC执行事务时,事务队列将不会被执行,同时返回Nullmulti-bulk应答以通知调用者事务执行失败。

Springboot整合Redis

  在springboot2.x之后,原来使用的jedis被替换成了lettuce.

  • Jedis在实现上是直连Redis服务,多线程环境下非线程安全,除非使用连接池,为每个 RedisConnection 实例增加物理连接。
  • Lettuce是一种可伸缩,线程安全,完全非阻塞的Redis客户端,多个线程可以共享一个RedisConnection,它利用Netty NIO框架来高效地管理多个连接,从而提供了异步和同步数据访问方式,用于构建非阻塞的反应性应用程序。
  1. 导入jar包
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置连接
#Redis配置
spring.redis.host=localhost
spring.redis.port=6379
...
  1. 序列化配置和自定义RedisTemplate模板(转载于狂神说)
@Configuration
public class RedisConfig { 
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 我们为了自己开发方便,一般直接使用 <String, Object>
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        // Json序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}
  1. 编写工具类实际使用 RedisUtils工具类

Redis配置文件

#网络
bind 127.0.0.1   # ip
protected-mode yes # 保护模式是否开启
port 6379		# 端口

#通用
daemonize yes	# 后台运行,需要修改为yes
pidfile /var/run/redis_6379.pid # 以后台方式运行,指定一个pid文件
loglevel notice # 日志级别
logfile ""  # 日志文件名
databases 16 # 数据库的数量,默认为16个

#RDB配置
save 900 1 # 900秒内有1个key进行修改,则进行持久化
save 300 10 # 300秒内有10个key进行修改,则进行持久化
save 60 10000 # 60秒内有10000个key进行修改,则进行持久化
stop-writes-on-bgsave-error yes # 持久化出错后是否继续工作
rdbcompression yes # 是否压缩rdb文件
rdbchecksum yes	# 报错rdb文件时候进行错误的检查校验
dbfilename dump.rdb # dbf文件名
dir ./ # rdb文件保存的目录,默认为当前目录

#主从复制
replicaof <masterip> <masterport> # masterip主机IP,masterport主机端口,用于配置主机信息
masterauth <master-password> # 如果主机有密码,这里配置主机密码
replica-read-only yes # 配置从机只读,默认yes

#安全
可以设置Redis密码

#客户端
maxclients 10000	# 客户端最大连接数
maxmemory <bytes>	# redis最大的内存容量
maxmemory-policy noeviction	# 内存达到上限的处理策略
#内存达到上限的处理策略的6个处理策略
1、noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息。(默认值)
2、allkeys-lru: 所有key通用; 优先删除最近最少使用(LRU) 的 key。
3、volatile-lru: 只限于设置了 expire 的部分; 优先删除最近最少使用(LRU) 的 key。
4、allkeys-random: 所有key通用; 随机删除一部分 key。
5、volatile-random: 只限于设置了 expire 的部分; 随机删除一部分 key。
6、volatile-ttl: 只限于设置了 expire 的部分; 优先删除剩余时间(TTL) 短的key。

#AOF配置
appendonly no	# 默认不开启aof模式,默认使用rdb方式持久化
appendfilename "appendonly.aof" # 持久化文件名
# appendfsync always	# 每次修改都会同步,消耗性能
appendfsync everysec	# 每秒都同步一次 sync,可能会丢失这1s数据
# appendfsync no		#误信息。(默认值) 不执行同步,这时候操作系统自己同步数据,速度最快

...

Redis持久化

  Redis是内存数据库,如果数据不进行持久化,当服务器进程退出或者服务器断电,数据就会丢失,因此Redis需要进行持久化操作。Redis提供两种持久化操作,即RDB和AOF。

RDB

RDB(Redis Database):在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储,默认生成dump.rdb文件,默认放在redis安装目录,文件名和路径都可以通过配置文件进行修改配置。Redis启动时会自动加载rdb文件。RDB是默认的持久化操作。

Redis.conf配置:下面是默认配置

save 900 1 # 900秒内有1个key进行修改,则进行持久化
save 300 10 # 300秒内有10个key进行修改,则进行持久化
save 60 10000 # 60秒内有10000个key进行修改,则进行持久化
stop-writes-on-bgsave-error yes # 持久化出错后是否继续工作
rdbcompression yes # 是否压缩rdb文件
dbfilename dump.rdb # dbf文件名
rdbchecksum yes	# 报错rdb文件时候进行错误的检查校验
dir ./ # rdb文件保存的目录,默认为当前目录

RDB触发机制

  • 满足save命令条件
  • 执行flushall命令
  • 退出Redis

RDB持久化过程

  1. Redis 调用forks. 同时拥有父进程和子进程。
  2. 子进程将数据集写入到一个临时 RDB 文件中。
  3. 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。

优点

  • RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。
  • 生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

缺点

  • 需要一定时间间隔进行存储,系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失
  • RDB是通过fork子进程来协助完成数据持久化工作的,如果当数据量较大时,还是比较消耗性能

总结:RDB是Redis默认的持久化操作,速度快,但是可能存在数据丢失的情况

AOF

AOF(Append Only File):把所有的对Redis的服务器进行修改的命令(读命令不记录)都存到一个文件里,命令的集合。

Redis.conf配置:下面是默认配置

appendonly no	# 默认不开启aof模式,默认使用rdb方式持久化
appendfilename "appendonly.aof" # 持久化文件名
# appendfsync always	# 总是写入aof文件,并完成磁盘同步
appendfsync everysec	# 每一秒写入aof文件,并完成磁盘同步,可能会丢失这一秒的数据(默认值)
# appendfsync no		# 写入aof文件,不等待磁盘同步,操作系统自己调度,速度最快

AOF重写过程(当aof文件过大时执行重写)

  1. Redis 执行 fork() ,现在同时拥有父进程和子进程。
  2. 子进程开始将新 AOF 文件的内容写入到临时文件。
  3. 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾,这样样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
  4. 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
  5. 现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。

优点

  • 数据完整性强
  • 当文件过大时,提供文件重写功能,同时也能保证重写过程中的数据记录

缺点

  • AOF日志文件通常比RDB数据快照文件更大
  • 数据恢复慢

总结:AOF较RDB来说,数据完整性更强,能够保证数据的安全性。提供redis-check-aof工具来对aof文件进行修复。

Redis持久化总结
1.选用哪种持久化操作是根据需求来决定的。Redis支持同时开启两种持久化操作。
2.如果需要进行持久化操作,建议两者都打开,双重保险。在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
3.如果只是做缓存,只希望在服务器运行的时候存在,可以不适用持久化。

Redis主从复制

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

主从复制作用

  • 读写分离:主节点写,从节点读,提高服务器的读写负载能力
  • 数据冗余︰主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  • 故障恢复︰当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复 ; 实际上是一种服务的冗余。
  • 负载均衡︰在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载 ; 尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  • 高可用(集群)基石︰除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

主从复制环境配置

需要几个从机就复制几个配置文件,修改对应的信息

port 6379 # 端口
pidfile /var/run/redis_6379.pid # pid
logfile ""  # 日志文件名
dbfilename dump.rdb # dbf文件名

默认每一台服务器都是主节点,只需要进行从机的配置

info replication # 能够查看主从复制相关信息

# 用命令配置主机信息,但是这种配置是暂时的,从机重启后还是会变成主机
slaveof host port # 主机IP为host,主机端口为port

# 配置文件中配置主机信息,永久的
replicaof <masterip> <masterport> # <masterip>主机IP,<masterport>主机端口
masterauth <master-password> # 如果主机有密码,这里配置主机密码

主机可以读写,从机只能读不能写。主机中的所有信息和数据,都会自动同步到从机,从机一旦认定主机,也会自动同步主机信息。主机断开连接,从机依旧连接到主机,但是没有写操作,如果主机重新连接,从机会自动同步主机信息。

哨兵模式

哨兵模式:是一种特殊的模式。在主机宕机后能够自动的选择新主机。哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

哨兵模式

哨兵模式作用

  • 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
  • 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。

多哨兵模式:一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式

多哨兵模式

故障切换过程:假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行故障切换过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票由一个哨兵发起,得票高的服务器成为新主机,完成故障切换操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从机实现切换主机,这个过程称为客观下线。

哨兵配置流程

  1. 新建sentinel.conf文件,几个哨兵新建几份
  2. 配置一下内容
# 关闭保护模式
protected-mode no
# 端口
port 26379
# 配置监听的主服务器,这里sentinel monitor代表监控,mymaster代表服务器的名称,可以自定义,192.168.11.128代表监控的主服务器,6379代表端口,2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行故障切换操作。
sentinel monitor mymaster 192.168.11.128 6379 2
# (可选)sentinel author-pass定义服务的密码,<master-name>是服务名称,<password>是Redis服务器密码
sentinel auth-pass <master-name> <password>
  1. 通过redis-sentinel sentinel.conf命令启动哨兵

注意启动顺序,先启动主机,再启动从机,最后启动哨兵

优点

  • 对节点进行监控,来完成自动的故障发现与转移
  • 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有

缺点

  • 在主从切换的瞬间存在访问瞬断的情况,等待时间比较长
  • Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂
  • 主节点只有一个,主节点写压力较大

转载于秃头哥编程

Redis集群

  Redis 的哨兵模式基本已经可以实现高可用,读写分离,但是在这种模式下每台Redis服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了Cluster 集群模式,实现了Redis的分布式存储,也就是说每台Redis节点上存储不同的内容。

Redis集群

根据官方推荐,集群部署至少要 3 台以上的master节点,最好使用 3 主 3 从六个节点的模式

配置

修改redis.conf中集群配置,

daemonize yes #可选操作,该项设置后台方式运行
port 7000 # 端口
cluster-enabled yes #开启集群模式
cluster-config-file nodes.conf #集群配置文件
cluster-node-timeout 5000 # 集群内节点之间支持最长响应时间

更多集群配置参数可参考默认配置文件redis.conf中Cluster模块的说明

  每一个实例都会生成一个Node ID,用来作为Redis实例的集群中的唯一表示,并不是通过IP和Port,因为IP和Port可能会变,但是该Node ID不会改变。

集群工作方式

  在 Redis的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的Key到达的时候,Redis会根据CRC16的算法得出一个结果,然后把结果对16384求余数,这样每个key都会对应一个编号在0-16383之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。

优点

  • 所有的Redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
  • 节点的fail是通过集群中超过半数的节点检测失效时才生效。
  • 客户端与 Redis 节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可,就不存在中间节点一说了

详细配置参考

Redis缓存穿透、击穿和雪崩

缓存穿透

缓存穿透:访问一个缓存和数据库都不存在的key,此时会直接打到数据库上,并且查不到数据,没法写缓存,所以下一次同样会打到数据库上。此时,缓存起不到作用,请求每次都会走到数据库,流量大时数据库可能会被打挂。此时缓存就好像被“穿透”了一样,起不到任何作用。

解决方案

  • 接口校验。在正常业务流程中可能会存在少量访问不存在 key 的情况,但是一般不会出现大量的情况,所以这种场景最大的可能性是遭受了非法攻击。可以在最外层先做一层校验:用户鉴权、数据合法性校验等,例如商品查询中,商品的ID是正整数,则可以直接对非正整数直接过滤等等。
  • 缓存空值。当访问缓存和DB都没有查询到值时,可以将空值写进缓存,但是设置较短的过期时间,该时间需要根据产品业务特性来设置。
  • 布隆过滤器。使用布隆过滤器存储所有可能访问的 key,不存在的 key 直接被过滤,存在的 key 则再进一步查询缓存和数据库。

布隆过滤器详解

缓存击穿

缓存击穿:某一个热点key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。

解决方案

  • 使用互斥锁。在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞,等到第一个线程将数据写入缓存后,直接走缓存。
  • 热点数据不过期(根据场景使用)

缓存雪崩

缓存雪崩:大量的热点key设置了相同的过期时间,导在缓存在同一时刻全部失效,造成瞬时数据库请求量大、压力骤增,引起雪崩,甚至导致数据库被打挂。缓存雪崩其实有点像“升级版的缓存击穿”,缓存击穿是一个热点key,缓存雪崩是一组热点 key。

解决方案

  • 过期时间打散。既然是大量缓存集中失效,那最容易想到就是让他们不集中生效。可以给缓存的过期时间时加上一个随机值时间,使得每个key的过期时间分布开来,不会集中在同一时刻失效。
  • 热点数据不过期。该方式和缓存击穿一样,也是要着重考虑刷新的时间间隔和数据异常如何处理的情况。
  • 使用互斥锁。该方式和缓存击穿一样,按key维度加锁,对于同一个key,只允许一个线程去访问数据库,其他线程原地阻塞等待第一个线程的计算结果,然后直接走缓存即可。

Redis穿透、击穿和雪崩参考文章

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值