- 页内目录
一,Redis的介绍
二,Redis的基本配置
原始服务器
新建服务器
三,Redis的基本使用
简单指令
数据类型
四,Redis的持久化
PDB快照方式
AOF日志方式
五,Redis中的事务
六,Redis中的锁
分布式锁
死锁
watch锁
七,Jedis的使用
八,Redis整合springboot的使用
一,Redis的介绍(中文官网)(官网)
建议进官网里学一学!讲的挺详细的,小编就仅仅展示以下自己的学习流程
下载的话建议github:github地址
Redis(REmote DIctionary Server) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统,是跨平台的非关系型数据库;是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API,内部采用单线程多路IO复用技术,一般做为缓存数据库辅助持久化的数据库,具有高性能的特点;
缓存:一般指高速访问的用于临是存储的数据存储区,适用于高并发,直接从缓存中取数据,效率高;
NOSQL:泛指非关系型数据库;不支持sql语句,不支持ACID;性能高;适用于高并发读取,海量读取;如:Redis,MongoDB,HBase
二,新建服务器和Redis的基本配置
原始服务器
找到你的redis文件夹(我这里是没有配置全局Redis,所以在文件下进入,下面将doc进入)
新建服务器
- 打开doc:windoc键+R,输入cmd,进入
- 进入文件夹:cd 我的Redis文件夹地址;(我这里因为没有配全局path,所以这样)
- 打开客户端:redis-server 我的配置文件位置(我这里是Redis文件夹下的conf文件夹下的redis.service-6380.conf文件,配置了再打开客户端,后面有配置)(运行客户端期间,服务端不能关闭)
- 打开服务端:重新进入doc下,cd进入文件夹,redis-cli -p端口号
配置redis.service-6380.conf文件(配置我们新端口的文件,可以借鉴原来的端口配置文件)
#绑定服务器地址(地址固定,且默认)
bind 127.0.0.1
#绑定服务器端口号(端口号自定义)
port 6380
#设置redis中的数据库的数量(默认16,可更改)
databases 16
控制台输出图
配置Redis的日志输出
#配置redis的日志文件,将控制台的信息输入在日志文件中,控制台不输出(我这里是log文件夹下的log-service-6380文文件)
logfile C:/Redis/log/log-service-6380
文件日志输出图
配置redis守护线程(window没有)
#设置
# redis是否使用守护线程,no表示不使用,启动时会在控制台上打印启动信息
# yes表示使用守护线程,后台启动,不打印启动信息(该配置在windows上不支持)
daemonize no
其他配置
# 指定存储到本地时是否对数据进行压缩,默认为yes,采用LZF压缩
rdbcompression yes
# 设置是否对rdb文件格式进行校验,该校验在写文件和读文件时均会进行,默认为yes(一般开启,如果设置为no读写性能会提升但存在数据损坏风险)
rdbchecksum yes
# 当后台保存数据时如果出现错误是否停止保存操作
stop-writes-on-bgsave-error yes
三,Redis的基本使用
基本指令
1.select index:切换数据库(index指具体值)
2.clear:清屏命令
3.keys pattern:获得当前库中的key("*"匹配任意数量的任意字符 "?"匹配任意一个字符 "[]"匹配其中的任意一个字符)
keys * (获得所有的key)
keys r* (获得所有以r开头的key)
keys *is (获得所有以is结尾的key)
keys ?ava (获得前面有一个任意字符,并且以ava结尾的key)
keys u[se]r (获得前面第一个字符是u,第二个字符是s或e,结尾为r的key)
4.exists key:判断key是否存在
5.type key:判断key是什么类型
6.del key:删除指定的key
7.ttl key:查看过期剩余时间(-1表示永不过期,-2表示已过期)
8.dbsize:查看库中key的数量
9.flushdb:清楚当前库的所有key
10.sort key:对key中的数据进行排序,只针对集合类型(list,set)
11.sort list [asc|desc] :对key为list的集合进行排序(注意不改变原始数据)
12.flushall:清楚所有库的所有数据命令
13.quit/ exit/ ESC键:退出命令
14.ping:检测客户端与服务器连通命令
15.echo message:控制台打印命令
16.info:获得当前Redis的运行属性值
17.设置key的过期时间
expire key 1 (秒为单位)
pexpire key 1 (毫秒为单位)
persist key(永不过期)
18.修改key的名字
rename key newkey (key:旧key;newkey:新key)
renamenx str str1 #将str的key改名为str1,如果str1不存在则修改,如果存在则修改失败
19.数据在不同的库中移动
move key db
move name 1 (将当前库中的name数据移动到1号库中(注意:当前库中存在name数据,1号库中不存
在同名的数据))
数据类型
Redis自身是一个map,其中所有的值都是key-values的形式存储;key是一个字符串,values是具体的数据;
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合);操作过多!传送门:菜鸟教程
四,Redis的持久化
持久化:(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。持久化是将程序数据在持久状态和瞬时状态间转换的机制。JDBC就是一种持久化机制。文件IO也是一种持久化机制。
redis的持久化:将redis的数据存在磁盘上,在需要时进行恢复,通过RBD和AOF方式
当Redis启动时会自动读取rdb文件,将已存储的数据恢复
RDB方式(快照方式):
该方法用于备份数据,可以将redis中的数据一次性备份,关注点在数据,当数据大时耗时间,(每次存储还会再次存储上一次备份的数据);
优点:
RDB得到的是一个紧凑压缩的二进制文件,存储效率比较高
RDB内部存储的是数据快照,非常适用于数据备份,数据整体复制等场景
RDB恢复数据的速度要比AOF快
缺点
RDB方式基于快照思想,每次读写全部数据,当数据量巨大时效率低、IO读写性能低下
RDB方式无法实时备份数据,数据丢失的可能性大
bgSave指令每次执行要创建子进程,内存会产生额外的消耗
设置日志文件,保存RDB方式所保存的内容
save方式存储数据:该方式会和其它操作一起进行,当存储数据多时,存储效率低,导致redis卡顿;
控制台显示
save配置要根据实际使用场景进行使用,频度过高或过低都会给性能造成影响
调用debug reload指令重启服务器时会自动保存数据
服务器关闭时(shutdown指令关闭)会自动保存数据
bgsave存储方式:在新的进程下进行持久化存储,不会影响其他操作;(可以在任务管理器下看到进程的出现,但是由于数据小,只是一瞬)
控制台显示
自动保存快照的配置,不设置该配置则不会自动保存
# 该配置表示开启自动备份功能,如果该配置不设置则不会自动备份数据当调用shutdown指令时不会自动备份数据
# 设置Redis后台自动保存数据间隔时间和数据变化量,Redis会根据以下配置自动执行bgsave命令保存数据
# 3600秒内有1个数据发生改变就自动保存
#save 3600 1
# 300秒内有100个数据发生改变就自动保存
save 300 100
# 60秒内有10000个数据发生改变就自动保存
#save 60 10000
区别:
save直接调用rdbSave函数,会阻塞Redis的主进程,直到保存完成为止;
bgsave则调用fork函数生成一个子进程,子进程负责调用rdbSave函数,并在存储完成后向主进程发送信号;
两者都将日志文件存储在我们指定的rdb文件中,默认为dump.rdb文件;
AOF方式(日志方式):
Redis的主流存储方式以日志的方式将每次的写操作记录下来,只记录写操作,关注操作,需要时根据记录一步步恢复;
AOF写数据的过程:1. 将写命令写入AOF缓存区;2.到达指定时机一次写入文件;
配置AOF
#是否使用AOF方式,默认no;开启之后aof优先于rdb
appendonly yes
#写入的三种策略,配置AOF存储的写入策略
#每次写入
# appendfsync always
#每秒写入
appendfsync everysec
#系统写入
# appendfsync no
重启服务器得到
测试存储:
查看文件
再次测试
查看文件(只有age但是剩下都存储了,动作赘余了)
AOF重写
降低磁盘占用量,提高磁盘使用率,提高持久化效率,降低持久化写时间,提高IO性能提高数据恢复效率
超时数据不再写入文件
忽略无效指令、重写时使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令
自动化重写规则(可以通过info指令查看)
# AOF自动触发百分比
auto-aof-rewrite-percentage 100
# AOF自动触发最小尺寸
auto-aof-rewrite-min-size 64mb
# AOF自动触发参考值
aof_current_size:60 #当AOF触发条件的最小尺寸大于该值自动执行AOF重写
aof_base_size:60 #AOF触发基础尺寸
# 当前使用百分比大于或等于自动触发百分比时会自动执行AOF重写
# 公式:aof_current_size - auto-aof-rewrite-min-size / aof_base_size >= auto_aof-rewrite-percentage
避免大多数无效步骤,占用内存,我们对其进行了手动重写以展示效果,输入bgrewriteaof
控制台出现
文件改变
字节减少
五,Redis中的事务
当Redis执行多条命令时,可能会被其它的命令插队,从而影响结果;为了保证完成某个功能的系列操作不被其它操作指令影响,Redis提供了事务处理机制
传统的事务:
多个操作不可分割,同时成功,同时失败,可通过commit提交,rollback回滚,具有四个特性:原子性,一致性,隔离性,持久性;
Redis的事务:
一个指令执行队列,将一系列的操作指令包装成一个整体(队列),当执行时,将队列中的指令按照既定的顺序执行,在执行过程中不允许其它命令执行,具有排他性,Redis中没有事务回滚的概念;
开启事务:multi指令:该指令用于设置事务的开始位置,该指令后的所有指令都将加入到Redis的事务中,形
成一个指令队列,直到exec指令或者discard指令结束
执行事务(结束事务):exec指令:该指令用于执行事务,表示事务结束并执行事务队列中的指令,
它与multi成对使用
取消事务:discard指令:取消事务
监控锁:watch指令 取消监控:unwatch
doc命令解释事务:
指令语法错误,则会自动取消当前事务
出现执行错误,则事务不会停止也不会回滚,依然执行
六,Redis中的锁
分布式锁
当多个线程需要同时操作一个数据时,为避免出现数据异常,我们要将数据锁起来,使用结束后在
将锁打开,此时其他线程才可以继续访问该数据,Redis中使用分布式锁实现此场景
Redis中并没有分布式锁的实现,我们可以通过setnx来设计一个分布式锁
setnx lock 1:设置锁
del lock:删除锁
死锁
通过分布式锁的机制可以实现数据的排他性,但是如果一个人设的锁,这个人不解锁,那么就会出现死锁现象,在设计分布式时,不允许出现死锁;可以通过设置时效性的锁来预防出现死锁
定时分布式锁
watch监控锁
当一个数据需要改变时,可能会有很多条指令改变它,则其他人就不能再改变(数据只能被改变一次),此时我们就需要使用监控锁,对要修改的数据监控起来,
在执行事务前如果key的值发生改变,自动终止事务的执行
七,Judis的简单学习
Jedis是Redis官方推荐的Java连接开发工具。要在Java开发中使用好Redis中间件,必须对Jedis熟悉;简单学习传送门:菜鸟教程
jedis的maven依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.3</version>
</dependency>
八,Redis与springboot整合
Spring使用Spring Data与Redis进行整合,在SpringData中提供了一个模板类RedisTemplate来实现
redis的相关操作
建立springboot项目的maven依赖:
spring-boot-starter-parent
spring-boot-starter-web
spring-boot-devtools
lombok(上面的应该都知道吧!不知道的进maven官网)
spring-boot-starter-data-redis依赖(主角登场)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.5.3</version>
</dependency>
还需要配置的yml文件:
操作类
package com.jazhong.redis.dao.impl;
import com.jazhong.redis.dao.RedisDao;
import com.jazhong.redis.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Set;
@Component//交给spring管理
public class RedisDaoImpl implements RedisDao {
@Autowired//注入redis支撑类
private RedisTemplate redisTemplate;
/**
* 存入key和value
*/
@Override
public void save(String key, String value) {
//通过redisTemplate模板对象,获得一个操作redis字符串的对象ValueOperations
ValueOperations valOps = redisTemplate.opsForValue();
valOps.set(key,value);
}
/**
* 存入key和value(数字)
*/
@Override
public void save(String key, Integer value) {
//通过redisTemplate模板对象,获得一个操作redis字符串的对象ValueOperations
ValueOperations valOps = redisTemplate.opsForValue();
valOps.set(key,value);
}
/**
* 得到key对应的value(字符串)
*/
@Override
public String get(String username) {
ValueOperations valOps = redisTemplate.opsForValue();
String string = (String)valOps.get(username);
return string;
}
/**
*得到key对应的value(数字)
*/
@Override
public Integer getInt(String age) {
ValueOperations valOps = redisTemplate.opsForValue();
Integer integer = (Integer) valOps.get(age);
return integer;
}
/**
* 存入user对象(涉及到存储到磁盘中,该实体类需要序列化)
* @param user
*/
@Override
public void saveObj(User user) {
ValueOperations valOps = redisTemplate.opsForValue();
valOps.set("user",user);
}
/**
* 得到user对象
*/
public User get(){
ValueOperations valOps = redisTemplate.opsForValue();
User user = (User) valOps.get("user");
return user;
}
/**
* 简单操作list
*/
@Override
public void opsList() {
//获得操作list的
ListOperations ops = redisTemplate.opsForList();
Long aLong = ops.leftPush("list", "aaa");
ops.leftPushAll("list","aaaa","bbbb","cccc");
List<String> list = ops.range("list", 0, -1);
System.out.println(list);
for (String s : list) {
System.out.println(s);
}
}
/**
* 简单操作set集合
*/
@Override
public void opsSet() {
SetOperations ops = redisTemplate.opsForSet();
ops.add("set","aaa","bb","cc");
Set<String> set = ops.members("set");
System.out.println(set);
for (String s : set) {
System.out.println(s);
}
}
/**
* 简单操作zset集合(有序的set)
*/
@Override
public void opsZset() {
ZSetOperations ops = redisTemplate.opsForZSet();
ops.add("zset","小明",100);
ops.add("zset","小n",200);
ops.add("zset","小hua",40);
ops.add("zset","小u",500);
Set zset = ops.rangeWithScores("zset", 0, -1);
for (Object s : zset) {
System.out.println(s);
}
}
/**
* 删除方法
*/
public void del(){
redisTemplate.delete("zset");
}
/**
* 简单操作map集合
*/
@Override
public void opsHash() {
HashOperations ops = redisTemplate.opsForHash();
ops.put("user1","userId1",(Object) "1171");
ops.put("user1","username","yyx1");
ops.put("user1","userage","201");
ops.put("user1","userpassword","1231");
String s= (String)ops.get("user1", "userId1");
System.out.println(s);
}
}
控制类:
package com.jazhong.redis.controller;
import com.jazhong.redis.model.User;
import com.jazhong.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/redis")
public class RedisController {
@Autowired
private RedisService redisService;
@GetMapping("/save")
public String saveStr(String key,String value){
redisService.save(key,value);
return "ok";
}
@GetMapping("/saveint")
public String saveInt(String key,Integer value){
redisService.save(key,value);
return "ok!int";
}
@GetMapping("/get")
public String get(){
// return redisService.get("username");
Integer age = redisService.getInt("age");
return age+"";
}
@GetMapping("saveobj")
public String saveObj(@RequestBody User user){
redisService.saveObj(user);
return "ok!obj";
}
@GetMapping("/getUser")
public User getUser(){
User user = redisService.get();
System.out.println(user);
return user;
}
@GetMapping("/opsList")
public String opsList(){
redisService.opsList();
return "ok!list";
}
@GetMapping("/opsSet")
public String opsSet(){
redisService.opsSet();
return "ok!Set";
}
@GetMapping("/opsZset")
public String opsZset(){
redisService.opsZset();
return "ok!Zset";
}
@GetMapping("/opsHash")
public String opsHash(){
redisService.opsHash();
return "ok!Hash";
}
@DeleteMapping("/del")
public String opsDel(){
redisService.del();
return "ok!del";
}
}
这里使用postman应用测试传入数据(省去了在页面测试的麻烦)
即在postman服务端,根据url地址和类型,传入数据,通过IDEA存入redis;当然可以通过redis客户端得到存入的数据;
可以去应用商店下载
这里要注意