Redis 学习笔记

redis基础认识与使用

redis初步认识

redis是一个非关系型数据库,NoSQL型,采用键值对的形式存储数据,是一种基于内存的高效数据库

既然已经有了mysql,postgresql等数据库,为什么还需要redis

redis基于内存的机制,以及优秀编码,其存取数据比mysql快很多,性能高,可以用于缓存用户数据,合理使用redis可以提升用户体验,极大缓解数据库压力

例如像验证码这类的临时数据,如果每发一次验证码都存入数据库,那么在高并发场景下,数据库的请求压力暴增,不利于用户体验和维护,这种情况下就必须借助redis这样的数据库,高效优雅的缓存处理

redis特点:

  • 键值对(key-value)型数据库,value支持多种数据结构,功能强大
  • 单线程:单个命令天然具备隔离性,但无法保证原子性事务回滚
  • 高性能:基于内存,编码优秀,极致优化
  • 无事务(ACID):原子性无法保证,无一致性,隔离性需要通过锁编码控制,可持久化数据但没有sql型数据库便捷
  • 支持主从集群, 分片集群等多实例部署
Atomicity原子性事务提交是不可再分的,要么全部失败回滚,要么全部成功,不存在成功一半的情况
Consistency一致性事务执行前后的状态遵循数据结构定义的约束
Isolation隔离性多个线程并发时,事务具有隔离,通过锁或者多版本控制互斥,互不干扰
Durability持久性事务一旦提交成功,对数据库的修改时永久的,系统崩溃或者断电也不会丢失

安装并使用redis

redis服务下载地址:

redis图形化管理工具resp下载地址:

切换到安装目录下,以管理员身份终端执行以下命令运行和启动redis

1
2
3
4
5
6
7
8
9
10
# 前台运行并将数据持久化保存到D:\RedisData 
RedisService.exe run --foreground --port 6379 --dir D:\RedisData

# 切换到安装目录执行,安装为 Windows 服务,并将数据持久化保存到D:\RedisData
.\RedisService.exe install -c .\redis.conf --dir D:\RedisData --port 6379
# 启动redis
net start Redis

# 卸载服务
RedisService.exe uninstall

redis或者mysql都可以,用compose可一次性配置多个服务,方便管理

创建docker-compose.yml文件,将以下代码复制进去,并切换到当前文件目录cmd执行 docker compose up -d

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# 注意:新版 Docker Compose 已不再需要 version 字段,直接以 services 开头即可
# 如果保留 version 会触发警告,建议删除。
version: '3.8'

services:
# ===================== Redis 服务 =====================
redis-server:
# 使用轻量级 Alpine 版本,体积小,适合生产
image: redis:7.2.4-alpine
# 容器名称,便于管理
container_name: my-redis
# 重启策略:除非手动停止,否则总是重启
restart: unless-stopped

# 端口映射:宿主机端口 : 容器内端口
# 注意:如果宿主机 6379 已被占用,可修改左侧端口(如 6380)
ports:
- "6380:6379"

# 数据卷挂载:持久化数据到宿主机 ./redis-data 目录
# 这样即使容器删除,数据也不会丢失
volumes:
- ./redis-data:/data

# 启动命令:直接通过参数开启 AOF 持久化并设置密码
command: redis-server --appendonly yes --requirepass 123456

# 资源限制(单机版 docker compose up 生效)
cpus: '0.5' # 限制最多使用 0.5 个 CPU 核心
mem_limit: 256m # 限制最大内存 256 MB

# 环境变量(设置时区)
environment:
- TZ=Asia/Shanghai

# ---------- 可选扩展:挂载自定义配置文件 ----------
# 如果需要在 redis.conf 中精细调优,请取消以下注释:
# volumes:
# - ./redis-data:/data
# - ./redis.conf:/usr/local/etc/redis/redis.conf
# command: redis-server /usr/local/etc/redis/redis.conf

# ===================== MySQL 服务 =====================
mysql:
# 使用官方 MySQL 8.0 镜像(稳定版)
image: mysql:8.0
container_name: my-mysql
restart: unless-stopped

# 端口映射:宿主机 3308 -> 容器 3306(避免本机 3306 被占用)
ports:
- "3308:3306"

# ---------- 环境变量(极易出错,务必注意) ----------
environment:
# 【必填】root 用户的密码,只能用这个变量设置
MYSQL_ROOT_PASSWORD: 123456 # 生产环境请改为强密码

# 【可选】容器启动时自动创建的数据库名称
MYSQL_DATABASE: myapp

# 【!!!警告!!!】MYSQL_USER 用于创建普通用户,绝对不能填 "root"
# 如果需要普通用户,请取消下面两行注释,并改为其他名字(如 app_user)
# MYSQL_USER: app_user
# MYSQL_PASSWORD: app_password

# 设置时区
TZ: Asia/Shanghai

# 数据卷挂载:数据库文件持久化到 ./mysql-data
volumes:
- ./mysql-data:/var/lib/mysql
# 如果需要挂载自定义 my.cnf,可取消下面注释
# - ./conf/my.cnf:/etc/mysql/conf.d/my.cnf
# 如果需要初始化脚本(首次启动自动执行 .sql),可取消注释
# - ./init-scripts:/docker-entrypoint-initdb.d

# 启动参数:设置字符集为 utf8mb4(支持 emoji),开启 binlog(可选)
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

# 资源限制
cpus: '0.5' # 限制 0.5 核
mem_limit: 512m # 限制内存 512 MB(如果 MySQL 内存不足,可酌情增加)

执行完毕后如图所示:

命令行成功界面

最终产生的文件目录:

持久化文件目录
  • mysql-data:mysql的数据将会持久化的保存到这里
  • redis-data:如果开启了rdb,或者aof持久化,数据将会保存到这里

redis实战运用

java准备工作

一般来说直接用spring官方分装好的StringRedisTemp,先引入依赖

1
2
3
4
5
<!-- Spring Data Redis 依赖(核心) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置 Redis 连接(application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
spring:
redis:
# Redis 服务器地址(如果是 Docker 部署在本地,写 localhost)
host: localhost
# 端口号
port: 6380
# 密码
password: 123456
# 连接超时时间(毫秒)
timeout: 3000ms
# 连接池配置(生产环境建议开启)
lettuce:
pool:
max-active: 8 # 最大活跃连接数
max-idle: 8 # 最大空闲连接数
min-idle: 0 # 最小空闲连接数
max-wait: -1ms # 连接等待超时时间(-1 表示无限等待)

缓存数据

引入redis后,我们可以让用户查询数据库后把数据缓存到redis,下一次用户再次查询时,就先去redis查询缓存,如果命中缓存直接返回,这样能提高响应速度,降低数据库压力

redis缓存数据流程图

从这个流程图可以看出几点问题:

  • 如果一直都是往redis缓存数据而不删除数据,那么redis内存也会爆满,所以应该合理设置过期时间,redis会自动删除过期数据
  • 设置了过期时间,如果每个key的过期时间一致,那么会导致大量数据需要到数据库更新,同样会导致大量请求打到数据库
  • 请求数据库不存在的数据,请求会直接打到数据库,并且之后的请求依然会打到数据库

使用redis缓存数据,必须要考虑几个问题

  • 缓存穿透:重复请求数据库中不存在的数据,每一次请求直接打到数据库
  • 缓存击穿:热点key同时过期,导致大量数据请求更新,数据库高请求过多请求长时间等待,最终资源耗尽
  • 缓存雪崩:由于缓存击穿导致的其他服务长时间等待,接连耗尽整体系统资源导致崩溃

缓存雪崩问题解决方案

缓存雪崩是大量的key同时失效,或者redis宕机,导致大量请求直达数据库,造成数据库压力过大

解决方法:

  • 给不同的key设置TTL+随机值
  • redis多实例部署,集群或者哨兵模式
  • 添加降级限流策略
  • 添加多级缓存,避免直达数据库

缓存穿透问题

既然缓存穿透是因为一直请求不存在的数据导致直接打到数据库,那么有两种方案,一种是源头上杜绝请求不存在的数据,一种是缓存空值并返回

  • 布隆过滤器
  • redis缓存空值

redis缓存空值:

缓存空值解决缓存穿透

布隆过滤器:后续再学习,未完待续…

缓存击穿问题

缓存击穿说的是热点key过期,导致大量更新请求数据库,最终造成数据库压力过大

解决方法:

  • 互斥锁

    互斥锁解决缓存击穿

  • 逻辑过期

    逻辑过期解决缓存击穿

缓存击穿解决方案优点缺点
互斥锁一致性
简单
内存占用小
单线程性能低
进程互相等待
容易发生死锁
逻辑过期高可用性
进程无等待
性能好
数据可能不一致
缓存不过期占用内存大
实现复杂