一、Redis介绍
1.1 锋迷商域项目的问题
- 数据库访问压力:为了降低对数据库的访问压力,当多个用户请求相同的数据时,我们可以将第一次从数据库查询到的数据进行缓存(存储到内存中),以减少对数据库的访问次数
- 首页数据的加载效率:将大量的且不经常改变的数据缓存在内存中,可以大幅度提高访问速度
- 集群部署下的商品超卖
1.2 Redis介绍
1.2.1 Redis的产生背景
- 2008年 萨尔瓦多–开发一个进行网站实时统计软件项目(LLOOGG),项目的实时统计功能需要频繁的进行数据库的读写,MySQL满足不了项目的需求,萨尔瓦多就是使用C语言自定义了一个数据存储系统—Redis。后来萨尔瓦多不满足LLOOGG这个项目中使用redis,就对redis进行
产品化
并进行开源,以让更多的人能够使用
1.2.2 Redis 使用
Redis就是一个C语言开发的、基于内存结构进行的键值对
数据存储的、高性能的、非关系型NoSQL数据库。
1.2.3 Redis支持的数据类型
redis是基于键值对进行数据存储的,但是value可以是多种数据类型
- 字符串
- hash 映射
- List 列表(队列)
- set 集合
- Zset 无序列表
Redis特点
- 基于内存存储,数据读写效率高
- Redis本身支持持久化
- Redis虽然基于key-value存储,但是支持多种数据类型
- Redis支持集群、支持主从模式
1.3 Redis应用场景
- 缓存:在绝大多数的互联网项目中,为了提供数据的访问速度、降低数据库的访问压力,我们可以使用redis作为缓存来实现
- 点赞、排行榜、计数器等功能:对数据实时读写要求比较高,但是对数据库的一致性要求并不是很高
- 分布式锁:基于redis的操作特性可以实现分布式锁功能
- 分布式会话:在分布式系统中可以使用redis实现
session
(共享缓存) - 消息中间件:可以使用redis实现应用之间的通信
1.4 Redis的优缺点
1.4.1 优点
- redis是基于内存结构,性能极高(读110000次/秒,写81000次/秒)
- redis基于键值对存储,但是支持多种数据类型
- redis的所有操作都是原子性,可以通过lua脚本将多个操作合并为一个原子操作(redis的事务)
- redis是基于单线程操作,但是其多路复用实现了高性能读写
1.4.2 缺点
- 缓存数据与数据库数据必须通过两次写操作才能保持数据的一致性
- 使用缓存会存在缓存穿透、缓存击穿、缓存雪崩等问题,需要处理
- redis可以作为数据使用进行数据的持久存储,存在丢失数据的风险
二、Redis安装及配置
2.1 Redis安装
基于linux环境安装redis
2.1.1 下载redis
wget http://download.redis.io/releases/redis-5.0.5.tar.gz
2.1.2 安装redis
- 安装gcc
yum -y install gcc
- 解压redis安装包
tar -zxvf redis-5.0.5.tar.gz
- 解压之后进入到redis-5.0.5目录
cd redis-5.0.5
- 编译
make MALLOC=libc
- 安装
make install
##当我们安装redis之后,就可以执行redis相关指令
## redis-server 启动redis服务
## redis-cli 启动redis操作客户端(命令行客户端)
- 启动redis
##当我们安装redis之后,就可以执行redis相关指令
redis-server ## 启动redis服务
redis-server & ##后台启动
- 打开客户端
redis-cli ##启动redis操作客户端(命令行客户端)
2.2 Redis 配置
- 使用 redis-server 指令启动redis服务的时候,可以在指令后添加redis配置文件的路径,以设置redis是以何种配置进行启动
redis-server redis.conf & ##redis以redis.conf 文件中的配置来启动
- 如果不指定配置文件的名字,则按照redis的默认配置启动(默认≠redis.conf)
- 我们可以通过创建redis根目录下的redis.conf来创建多个配置文件启动多个redis服务
##这里启动两个redis服务,端口不一样 redis-server redis-6381.conf & redis-server redis-6382.conf &
##设置redis实列是否为守护模式,默认值为no
daemonize no|yes
##设置当前redis实列启动之后保存进程id的文件路径
pidfile /var/run/redis.pid
## 设置redis实列的启动端口(默认为6379)
port 6380
## 设置当前redis实列是否开启保护模式
protected-mode yes
## 设置允许当前redis实列的ip地址访问列表
bind 127.0.0.1
## 设置连接密码
requirepass 123456
## 设置redis实列中数据库的个数(默认16个,编号0--15)
databases 16
## 设置最大并发数
maxclients 1000
##设置客户端和 redis 建立连接的最大空闲时间,设置为0表示不限制
timeout 0
## 查看所有运行的redis服务
ps -aux|grep redis
## 查看某个运行的redis服务
lsof -i:端口
三、Redis基本使用
3.1 Redis存储的数据结构
Redis是以键值对形式进行数据存储的,但是value支持多种数据类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TydjOfTk-1662558885745)(/Users/gen_hua/Desktop/Java_note/Markdown/WechatIMG939.jpeg)]
3.2 string常用命令
## set 设置值/修改值。如果存在就修改,不存在就创建
set key1 value1
##取值
get key1
## 批量添加
mset key2 value2 key3 value3 key4 value4.....
## 批量取值
mget key2 key3 key4....
## 自增和自减
incr key ## 在key对应的value值上自增1 即 +1
decr key ## 在key对应的value值上自减1 即 -1
incrby key v ##在key对应的value上加 v
decrby key v ##在key对应的value上减 v
## 添加键值对,并设置过期时间
setnx key time(seconds) value
##设置值,如果key不存在则成功添加,如果key存在则添加失败(不做修改操作)
setnx key value
## 在指定的key对应value拼接字符串
append key value
## 获取key对应的字符串的长度
strlen key
3.3 hash常用命令
## 向key对应的hash中添加键值对
hset key field value
## 从key对应的哈希中获取field对应的值
hget key field
## 向key对应的hash结构中批量添加键值对
hmset key field value1 value2 value3....
## 从key对应的hash中批量获取值
hmget key field1 field2 field3......
## 自增 在对应的hash中的field中对应的value加v
hincrby key field v
## 获取key对应的hash中所有的field
hgetall key
## 获取key对应的hash中所有的value
hkeys key
## 获取key对应的hash中键值对的个数
hlen key
3.4 list常用指令
## 存储数据
lpush key value ## 在key对应的列表的左侧添加数据value
rpush key value ## 在key对应的列表的右侧添加数据value
## 获取数据
lpop key ## 从key对应的列表的左侧取一个值
rpop key ## 从key对应的列表的右侧取一个值
## 修改数据
lset key index value ## 修改key对应的列表的索引未知的数据(索引从左网右,从0开始)
## 查看 key 对应的列表中索引从start开始到stop索引
lrange key start stop
## 查看key对应的列表中index索引对应的值
lindex key index
## 获取key对应的列表中的元素个数
llen key
## 从key列表中截取key在start,stop范围的值,不在此范围的值,一律被清除掉
ltrim key start stop
## 从k1右侧取出一个数据,然后存放到k2的左侧
rpoplpush k1 k2
3.5 set常用指令
## 存储元素 在key对应的集合中添加元素,可以添加1个,也可以同时添加多个元素
sadd key value1 value2 value3.....
## 遍历key对应的集合中的所有元素
smembers key
## 从key对应的结合中移除value,如果不存在则不移除
srem key value
## 查看 key对应的集合 中是否有value
sismember key value
四、Redis的持久化
redis是基于内存操作中,单作为一个数据库也具备数据的持久化能力,但是为了实现高效的读写操作,并不会即时进行数据的持久化,而是按照一定的规则进行持久化操作的 --这种规则叫做持久化策略
Redis提供了2种持久化策略
- RDB(Redis DataBase)
- AOF(Append Only File)
RDB
在满足特定的redis操作条件时,将内存中的数据以数据快照的形式存储到rdb文件中
- 原理:
- RDB是Redis默认的持久化策略,当redis中的写操作达到指定次数及时间周期,同时距离上一次持久化达到指令的时间就会将redis内存中数据生成快照,保存在指定的RDB文件中。
- 默认触发持久化条件:
- 900秒 1次:当操作次数达到1次,900秒就会进行持久化
- 300秒 10次:当操作次数达到10次,300秒就会进行持久化
- 60秒 1000次:当操作次数达到1000次,60秒就会进行持久化
- 我们可以通过修改redis.conf文件,来设置RDB策略的触发条件
## rdb持久化开关
rdbcompression yes
## 配置redis的持久化策略
save 900 1
save 300 10
save 60 10000
## 指定rdb数据存储的文件
dbfilename dump.rdb
- RDB持久化细节分析
缺点
- 如果redis出现故障,存在数据丢失的风险,丢失上一次持久化之后的操作数据
- RDB采用的是数据快照心事进行持久化,不适合实时性持久化;但是在数据较小的情况下,执行速度比较快
- 如果数据巨大,在RDB持久化过程中,生在数据库快照的子进程执行时间过长,会导致redis卡顿,因此save时间周期设置不宜过短
优点
- 但是在数据量较小的情况下,执行的速度比较快
- 由于EDB是以数据快照的形式进行保存的,我们可以拷贝rdb文件轻松实现redis数据移植
4.2 AOF
Apeend Only File,当达到设定触发条件时,将rdis执行的写操作指令存储在aof文件中,Redis默认未开启aof持久化
- 原理:
- Redis将每一个成功的写操作写入到aof文件中,当redis重启的时候就执行ao文件中的指令以恢复数据
- 配置:
## 开启AOF
appendonly yes
## 设置触发条件(三选一)
appendfsync always ## 只要进行成功的写操作就执行aof
appendfsync everysec ## 美妙进行一次aof
appendfsync no ## 让redis仔细决定
## 设置aof文件路径
appendfilename "appendonly.aof"
- AOF细节分析:
- AOF文件也可以通过拷贝aof文件进行redis数据的移植
- aof存储的是指令,而且会对指令进行整理,而RDB直接生成数据快照,因此在数据量不大时,RDB比较快
- aof是对指令文件进行增量更新,更适合实时性持久化
- redis官方建议同时开启2中持久化策略,如果同时存在aof文件和rdb文件的情况下,aof优先
五、Java应用连接Redis
Java应用连接Redis,首先要将我们的Redis设置允许远程连接
- 修改redis-6379.conf
## 关闭保护模式
protected-mode no
## 将bind注释(如果注释,默认为 127.0.0.1 只能本机访问)
bind 127.0.0.1 ## 将这行注释掉
## 设置密码 (可设置可不设置,不设置的话直接注释就可以)
requirepass 199610fan
- 重启redis
redis-server redis-6379.conf
- 云服务器中安全组设置6379端口
5.1 在普通Maven工程连接Redis
5.1.1 添加Jedis依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
5.1.2 使用案列
- Product类
package com.luo.beans;
public class Product {
private String productId;
private String productName;
private Double productPrice;
@Override
public String toString() {
return "Product{" +
"productId='" + productId + '\'' +
", productName='" + productName + '\'' +
", productPrice=" + productPrice +
'}';
}
public Product(String productId, String productName, Double productPrice) {
this.productId = productId;
this.productName = productName;
this.productPrice = productPrice;
}
public Product() {
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public Double getProductPrice() {
return productPrice;
}
public void setProductPrice(Double productPrice) {
this.productPrice = productPrice;
}
}
- 主类
public class RedistTest {
public static void main(String[] args) {
Product product = new Product("101","娃哈哈AD钙奶",2.5);
//1、连接redis
Jedis jedis = new Jedis("114.116.13.83", 1122);
String set = jedis.set(product.getProductId(), new Gson().toJson(product));
System.out.println(set);
jedis.close();
}
5.2 在SpringBoot工程中连接Redis
5.2.1 创建SpringBoot应用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AvzugFiZ-1662558885746)(/Users/gen_hua/Desktop/Java_note/Markdown/WX20220820-111018@2x.png)]
5.2.2 配置rdis
Application.yml文件配置redis的连接信息
spring:
redis:
host: 114.116.13.83
port: 1122
database: 0
5.2.3 使用reids客户端连接reids
直接在service中注入
redisTemplate
或者stringRedisTemplate
,就可以使用此对象完成redis操作
5.3 Spring Data Redis
5.3.1 不同数据结构的添加操作
package com.example.redisboot.service.impl;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 从redis中存数据
*/
@Override
public void addInfoToRedis() {
try {
Product product = new Product("103", "王老吉", 3.4);
String jsonstr = new ObjectMapper().writeValueAsString(product);
//stringRedisTemplate.boundValueOps(product.getProductId()).set(jsonstr);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
/**
* 从redis中取数据
*/
@Override
public Product getInfoFromRedis(String productId) {
Product product = null;
try {
String s = stringRedisTemplate.boundValueOps(productId).get();
if (s != null){
product = new ObjectMapper().readValue(s,Product.class);
}
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return product;
}
}