共计 9267 个字符,预计需要花费 24 分钟才能阅读完成。
1.Redis 介绍
redis 是一个 key-value 存储系统。和 Memcached 相似,它反对存储的 value 类型绝对更多,包含 string(字符串)、list(链表)、set(汇合)、zset(sorted set – 有序汇合)和 hash(哈希类型)。这些数据类型都反对 push/pop、add/remove 及取交加并集和差集及更丰盛的操作,而且这些操作都是 原子性 的。在此基础上,redis 反对各种不同形式的排序。与 memcached 一样,为了保障效率,数据都是缓存在内存中。区别的是 redis 会周期性的把更新的数据写入磁盘或者把批改操作写入追加的记录文件,并且在此基础上实现了 master-slave(主从)同步。
Redis 是一个开源(BSD 许可)的,内存中的数据结构存储系统,它能够用作数据库、缓存和消息中间件。它反对多种类型的数据结构,如 字符串(strings),散列(hashes),列表(lists),汇合(sets),有序汇合(sorted sets)与范畴查问,bitmaps,hyperloglogs 和 天文空间(geospatial)索引半径查问。Redis 内置了 复制(replication),LUA 脚本(Lua scripting),LRU 驱动事件(LRU eviction),事务(transactions)和不同级别的 磁盘长久化(persistence),并通过 Redis 哨兵(Sentinel)和主动 分区(Cluster)提供高可用性(high availability)。
Redis 常见用法
1.Redis 能够当做缓存应用
2.Redis 能够当做数据库验证码应用
3.Redis 能够当做消息中间件
1.1Redis 装置
1)解压安装包
[root@localhost src]# tar -zxvf redis-5.0.4.tar.gz
2)装置 Redis
阐明: 在 Redis 根目录执行命令
1.make
2.make install
1.2 批改 Redis 配置文件
命令: 展示行号:set nu
批改地位 1: 正文 IP 绑定
批改地位 2: 敞开保护模式
批改地位 3: 开启后盾启动
1.3redis 服务命令
1. 启动命令:redis-server redis.conf
2. 检索命令:ps-ef | grep redis
3. 进入客户端:redis-cli -p 6379
4. 敞开 redis:kill-9 PID 号 | redis-cli -p 6379 shutdown
2.Redis 入门案例
2.1 引入 jar 包文件
<!--spring 整合 redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
2.2 编辑测试 API
package com.jt.test;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;
public class TestRedis {
/**
* 1. 测试 redis 程序链接是否失常
* 步骤:
* 1. 实例化 jedis 工具 API 对象(host:port)
* 2. 依据实例 操作 redis 办法就是命令
*
* 对于链接不通的阐明:
* 1. 查看 Linux 防火墙
* 2. 查看 Redis 配置文件批改项
* 2.1 IP 绑定
* 2.2 保护模式
* 2.3 后盾启动
* 3. 查看 redis 启动形式 redis-server redis.conf
* 4. 查看 IP 端口 及 redis 是否启动...
*
* */
@Test
public void test01(){
String host = "192.168.126.129";
int port = 6379;
Jedis jedis = new Jedis(host,port);
jedis.set("cgb2006","好好学习 天天向上");
System.out.println(jedis.get("cgb2006"));
//2. 练习是否存在 key
if(jedis.exists("cgb2006")){jedis.del("cgb2006");
}else{jedis.set("cgb2006", "xxxx");
jedis.expire("cgb2006", 100);
}
}
}
2.3Redis 常见用法
2.3.1setex 学习
/**
* 2. 需要:
* 1. 向 redis 中插入数据 k-v
* 2. 为 key 设定超时工夫 60 秒后生效.
* 3. 线程 sleep 3 秒
* 4. 获取 key 的残余的存活工夫.
*
* 问题形容: 数据肯定会被删除吗??????
* 问题阐明: 如果应用 redis 并且须要增加超时工夫时 个别须要满足原子性要求.
* 原子性: 操作时要么胜利 要么失败. 然而必须同时实现.
*/
@Test
public void test02() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.setex("宝可梦", 60, "小火龙 妙蛙种子");
System.out.println(jedis.get("宝可梦"));
/* Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.set("宝可梦", "小火龙 妙蛙种子");
int a = 1/0; // 可能会出异样
jedis.expire("宝可梦", 60);
Thread.sleep(3000);
System.out.println(jedis.ttl("宝可梦"));*/
}
2.3.2setnx 学习
/**
* 3. 需要如果发现 key 曾经存在时 不批改数据. 如果 key 不存在时才会批改数据.
*
*/
@Test
public void test03() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129", 6379);
jedis.setnx("aaaa", "测试 nx 的办法");
/*if(jedis.exists("aaa")){System.out.println("key 曾经存在 不做批改");
}else {jedis.set("aaa", "测试数据");
}*/
System.out.println(jedis.get("aaaa"));
}
2.3.3set 超时工夫原子性操作
/**
* 需要:
* 1. 要求用户赋值时, 如果数据存在则不赋值. setnx
* 2. 要求在赋值操作时, 必须设定超时的工夫 并且要求满足原子性 setex
*/
@Test
public void test04() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129", 6379);
SetParams setParams = new SetParams();
setParams.nx().ex(20);
jedis.set("bbbb", "实现业务操作 AAAA", setParams);
System.out.println(jedis.get("bbbb"));
}
2.3.4list 汇合练习
@Test
public void testList() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129", 6379);
jedis.lpush("list", "1","2","3");
System.out.println(jedis.rpop("list"));
}
2.3.5redis 事务管制
@Test
public void testTx() throws InterruptedException {Jedis jedis = new Jedis("192.168.126.129", 6379);
//1. 开启事务
Transaction transaction = jedis.multi();
try {transaction.set("aa", "aa");
// 提交事务
transaction.exec();}catch (Exception e){e.printStackTrace();
// 回滚事务
transaction.discard();}
}
3.1 惯例锁操作
3.1.1 超卖的起因
3.1.2 同步锁的问题
阐明同步锁只能解决 tomcat 外部问题, 不能解决多个 tomcat 并发问题
3.1.3 散布锁机制
思维
1. 锁应该应用第三方操作, 锁应该共用
2. 准则: 如果锁正在被人应用时, 其余用户不能操作
3. 策略: 用户向 redis 中保留一个 key, 如果 redis 中有 key 示意正在有人应用这把锁, 其余用户不容许操作,redis 中没有 key, 则示意能够应用这把锁.
4. 危险: 如何解决死锁问题, 设置超时工夫.
3.SpringBoot 整合 Redis
3.1 编辑配置文件 redis.pro
阐明: 因为该配置被其余我的项目独特应用, 则应该写入 jt-common 中.
3.2 编辑配置类
阐明: 编辑 redis 配置类将 Jedis 对象交给 Spring 容器治理
@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
@Bean
public Jedis jedis(){return new Jedis(host,port);
}
}
3.3 对象与 JSON 转化 ObjectMapper 介绍
3.3.1 简略对象转化
/**
* 测试简略对象的转化
*/
@Test
public void test01() throws JsonProcessingException {ObjectMapper objectMapper = new ObjectMapper();
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(100L).setItemDesc("商品详情信息")
.setCreated(new Date()).setUpdated(new Date());
// 对象转化为 json
String json = objectMapper.writeValueAsString(itemDesc);
System.out.println(json);
//json 转化为对象
ItemDesc itemDesc2 = objectMapper.readValue(json, ItemDesc.class);
System.out.println(itemDesc2.getItemDesc());
}
3.3.2 汇合对象转化
/**
* 测试汇合对象的转化
*/
@Test
public void test02() throws JsonProcessingException {ObjectMapper objectMapper = new ObjectMapper();
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(100L).setItemDesc("商品详情信息 1")
.setCreated(new Date()).setUpdated(new Date());
ItemDesc itemDesc2 = new ItemDesc();
itemDesc2.setItemId(100L).setItemDesc("商品详情信息 2")
.setCreated(new Date()).setUpdated(new Date());
List<ItemDesc> lists = new ArrayList<>();
lists.add(itemDesc);
lists.add(itemDesc2);
//[{key:value},{}]
String json = objectMapper.writeValueAsString(lists);
System.out.println(json);
// 将 json 串转化为对象
List<ItemDesc> list2 = objectMapper.readValue(json, lists.getClass());
System.out.println(list2);
}
3.4 编辑工具 API
package com.jt.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jt.pojo.ItemDesc;
import org.springframework.util.StringUtils;
public class ObjectMapperUtil {
/**
* 1. 将用户传递的数据转化为 json 串
* 2. 将用户传递的 json 串转化为对象
*/
private static final ObjectMapper MAPPER = new ObjectMapper();
//1. 将用户传递的数据转化为 json 串
public static String toJSON(Object object){if(object == null) {throw new RuntimeException("传递的数据为 null. 请查看");
}
try {String json = MAPPER.writeValueAsString(object);
return json;
} catch (JsonProcessingException e) {
// 将查看异样, 转化为运行时异样
e.printStackTrace();
throw new RuntimeException(e);
}
}
// 需要: 要求用户传递什么样的类型, 我返回什么样的对象 泛型的常识
public static <T> T toObj(String json,Class<T> target){if(StringUtils.isEmpty(json) || target ==null){throw new RuntimeException("参数不能为 null");
}
try {return MAPPER.readValue(json, target);
} catch (JsonProcessingException e) {e.printStackTrace();
throw new RuntimeException(e);
}
}
}
3.5 商品分类的缓存实现
3.5.1 实现步骤
1. 定义 Redis 中的 key key 必须是惟一不能反复的, 存取 key=”ITEM_CAT_PARENTID::70″
2. 依据 key 去 redis 中进行查问 有数据 没有数据
3. 没有数据 则查询数据库获取记录, 之后将数据保留到 redis 中不便后续应用.
4. 有数据 示意用户不是第一次查问 能够将缓存的数据间接返回即可.
3.5.2 编辑 ItemController
3.5.3 编辑 ItemCatService
@Override
public List<EasyUITree> findItemCatListCache(Long parentId) {
//0. 定义公共的返回值对象
List<EasyUITree> treeList = new ArrayList<>();
//1. 定义 key
String key = "ITEM_CAT_PARENTID::"+parentId;
//2. 检索 redis 服务器, 是否含有该 key
// 记录时间
Long startTime = System.currentTimeMillis();
if(jedis.exists(key)){
// 数据存在
String json = jedis.get(key);
Long endTime = System.currentTimeMillis();
// 须要将 json 串转化为对象
treeList = ObjectMapperUtil.toObj(json,treeList.getClass());
System.out.println("从 redis 中获取数据 耗时:"+(endTime-startTime)+"毫秒");
}else{
//3. 数据不存在 查询数据库
treeList = findItemCatList(parentId);
Long endTime = System.currentTimeMillis();
//3. 将数据保留到缓存中
String json = ObjectMapperUtil.toJSON(treeList);
jedis.set(key, json);
System.out.println("查询数据库 耗时:"+(endTime-startTime)+"毫秒");
}
return treeList;
}
3.5.4 应用 Redis 的速度差
4.AOP 实现 Redis 缓存
4.1 自定义缓存注解
1. 注解名称:cacheFind
2. 属性参数
2.1key: 应该由用户本人手动增加个别增加业务名称之后动静拼接造成惟一的 key
2.2seconds: 用户能够指定数据的超时工夫
@Target(ElementType.METHOD) // 注解对办法无效
@Retention(RetentionPolicy.RUNTIME) // 运行期无效
public @interface CacheFind {public String preKey(); // 用户标识 key 的前缀.
public int seconds() default 0; // 如果用户不写示意不须要超时. 如果写了以用户为准.}
4.2 编辑 CacheAOP
package com.jt.aop;
import com.jt.anno.CacheFind;
import com.jt.config.JedisConfig;
import com.jt.util.ObjectMapperUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import java.lang.reflect.Method;
import java.util.Arrays;
@Aspect // 我是一个 AOP 切面类
@Component // 将类交给 spring 容器治理
public class CacheAOP {
@Autowired
private Jedis jedis;
/**
* 切面 = 切入点 + 告诉办法
* 注解相干 + 盘绕告诉 控制目标办法是否执行
*
* 难点:
* 1. 如何获取注解对象
* 2. 动静生成 key prekey + 用户参数数组
* 3. 如何获取办法的返回值类型
*/
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){
Object result = null;
try {
//1. 拼接 redis 存储数据的 key
Object[] args = joinPoint.getArgs();
String key = cacheFind.preKey() +"::" + Arrays.toString(args);
//2. 查问 redis 之后判断是否有数据
if(jedis.exists(key)){
//redis 中有记录, 无需执行指标办法
String json = jedis.get(key);
// 动静获取办法的返回值类型 向上造型 向下造型
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Class returnType = methodSignature.getReturnType();
result = ObjectMapperUtil.toObj(json,returnType);
System.out.println("AOP 查问 redis 缓存");
}else{
// 示意数据不存在, 须要查询数据库
result = joinPoint.proceed(); // 执行指标办法及告诉
// 将查问的后果保留到 redis 中去
String json = ObjectMapperUtil.toJSON(result);
// 判断数据是否须要超时工夫
if(cacheFind.seconds()>0){jedis.setex(key,cacheFind.seconds(),json);
}else {jedis.set(key, json);
}
System.out.println("aop 执行指标办法查询数据库");
}
} catch (Throwable throwable) {throwable.printStackTrace();
}
return result;
}
}
5. 对于 Redis 配置阐明
5.1 对于 Redis 长久化阐明
redis 默认条件下反对数据的长久化操作, 当 redis 中有数据时会定期将数据保留到磁盘中, 当 redis 服务器重启时, 会依据配置文件读取指定的长久化文件. 实现内存数据的复原.
5.2 长久化形式介绍
5.2.1RDB 模式
特点
1.RDB 模式是 redis 的默认的长久化策略.
2.RDB 模式记录的是 Redis内存数据的快照, 最新的快照会笼罩之前的内容, 所有 RDB 长久化文件占用空间更小, 长久化效率高.
3.RDB 模式因为是定期长久化, 所以 可能导致数据失落.
命令:
1. save 要求立刻马上长久化 同步的操作 其余的 redis 操作会陷入阻塞的状态.
2. bgsave 开启后盾运行 异步的操作 因为是异步操作, 所以无奈保障 rdb 文件肯定是最新的须要期待.
配置
1. 长久化文件名称:
2. 长久化文件地位:
3.RDB 模式长久化策略
正文完