【Redis】万字整理 Redis 非关系型数据库的安装与操作
文章目录
- Redis非关系型数据库
- 简介与安装
- 常用命令
- 操作Redis
- 操作字符串
- 操作列表
- 操作集合
- 操作有序集合
- StringRedisTemplate
- Redis 工具类
Redis非关系型数据库
简介与安装
Redis(Remote Dictionary Server)是一个开源的、基于内存的键值(kay-value)存储数据库。它支持多种数据结构,如**字符串(String)、链表(List)、集合(Set)、有序集合(zset / sorted sets)和哈希(Hash)**等。Redis 的主要特点包括:
- 数据存储在内存中,支持高速读写操作。
- 支持数据持久化,可以将内存中的数据保存到磁盘中。
- 提供主从复制(replication)、事务(transactions)、LUA脚本(Lua scripting)等功能。
- 支持高可用性和分布式,通过哨兵和集群实现。
Redis 能做什么
-
缓存,毫无疑问这是 Redis 当今最为人熟知的使用场景。再提升服务器性能方面非常有效;
-
排行榜,如果使用传统的关系型数据库来做这个事儿,非常的麻烦,而利用 Redis 的 SortSet 数据结构能够非常方便搞定;
-
计算器/限速器,利用 Redis 中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等,这类操作如果用 MySQL,频繁的读写会带来相当大的压力;限速器比较典型的使用场景是限制某个用户访问某个 API 的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力;
注:限速器也是对请求限流的一种实现方式。
-
好友关系,利用集合的一些命令,比如求交集、并集、差集等。可以方便搞定一些共同好友、共同爱好之类的功能;
-
简单消息队列,除了 Redis 自身的发布/订阅模式,我们也可以利用 List 来实现一个队列机制,比如:到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的 DB 压力,完全可以用 List 来完成异步解耦;
-
Session 共享,默认 Session 是保存在服务器的文件中,即当前服务器,如果是集群服务,同一个用户过来可能落在不同机器上,这就会导致用户频繁登陆;采用 Redis 保存 Session 后,无论用户落在那台机器上都能够获取到对应的 Session 信息。
Redis 特点
- Redis 将其数据库完全保存在内存中,仅使用磁盘进行持久化。
- 与其它键值数据存储相比,Redis 有一组相对丰富的数据类型。
- Redis 可以将数据复制到任意数量的从机中。
Redis 优点
- 异常快:Redis 非常快,每秒可执行大约110000次的设置(SET)操作,每秒大约可执行81000次的读取/获取(GET)操作。
- 支持丰富的数据类型:Redis 支持开发人员常用的大多数数据类型,例如列表,集合,排序集和散列等等。这使得Redis 很容易被用来解决各种问题,因为我们知道哪些问题可以更好使用地哪些数据类型来处理解决。
- 操作具有原子性:所有 Redis 操作都是原子操作,这确保如果两个客户端并发访问,Redis服务器能接收更新的值。
- 多实用工具:Redis 是一个多实用工具,可用于多种用例,如:缓存,消息队列(Redis本地支持发布/订阅),应用程序中的任何短期数据,例如,web应用程序中的会话,网页命中计数等。
安装
Windows 系统下 Redis 的安装过程
首先将安装包解压到 D 盘根目录下
1、启动 Redis
打开终端,输入 redis-server.exe redis.windows.conf
或新建记事本文件 startup.bat
,输入 redis-server.exe redis.windows.conf
使用服务过程中黑窗口不能关闭
2、修改密码
解压目录下找到 redis.windows.conf
大概在 387 行左右 requirepass
下添加 requirepass 密码
,保存,重新运行即可。
requirepass 123456
3、下载安装 RedisDesktopManager
RedisDesktopManager(RDM)是一个用于 Redis 数据库管理的开源跨平台 GUI 工具。它允许用户查看、编辑和监视 Redis 数据库中的数据。
(关闭防火墙并设置允许远程连接此电脑)
将安装包解压到 D 盘根目录下
双击运行 Another Redis Desktop Manager.exe
(右键发送到桌面快捷方式建立快捷方式)
4、配置 SpringBoot 中 Redis
引入相关 jar
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Redis 数据库配置
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=123456
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
上述步骤完成后,SpringBoot 自动在 Spring 容器中配置一个 redisTemplate 的 Bean,可以直接使用 redisTemplate
常用命令
Redis 命令参考 — Redis 命令参考
命令目录
操作命令 | 常用命令 |
---|---|
Key 键 | KEYS EXISTS TYPE TTL DEL |
String 字符串 | SET GET SETEX SETNX |
Hash 哈希表 | HSET HGET HDEL HKEYS HVALS HGETALL |
List 列表 | LPUSH LRANGE RPOP LLEN BRPOP |
Set 集合 | SADD SMEMBERS SCARD SINTER SUNION SDIFF SREM |
SortedSet 有序集合 | ZADD ZRANGE ZINCRBY ZREM |
DEL key [key …] 删除给定的一个或多个 key
。
KEYS pattern 查找所有符合给定模式 pattern
的 key
。
TTL key 以秒为单位,返回给定 key
的剩余生存时间(TTL, time to live)。
TYPE key 返回 key
所储存的值的类型。
EXISTS key 检查给定 key
是否存在。
GET key 返回 key
所关联的字符串值。
SET key value [EX seconds] [PX milliseconds] [NX|XX] 将字符串值 value
关联到 key
。
HSET key field value 将哈希表 key
中的域 field
的值设为 value
。
HGET key field 返回哈希表 key
中给定域 field
的值。
HDEL key field [field …] 删除哈希表 key
中的一个或多个指定域,不存在的域将被忽略。
LRANGE key start stop 返回列表 key
中指定区间内的元素,区间以偏移量 start
和 stop
指定。
LPUSH key value [value …] 将一个或多个值 value
插入到列表 key
的表头
LPOP key 移除并返回列表 key
的头元素。
如上图通过 SET key “value” EX 1000
设置了一个带过期时间的键值对,其中键为 key,值为 value,并且设置该键在 1000 秒后过期。TTL key
查看键的剩余生存时间,返回结果是 987 秒。
操作Redis
操作字符串
@SpringBootTest
public class RedisTest {
@Resource
private RedisTemplate redisTemplate;
@Test
public void redisTemplateTest(){
// 使用 Spring 封装的 RedisTemplate 操作字符串
ValueOperations vo = redisTemplate.opsForValue();
vo.set("uname","zhangsan");
}
}
redisTemplate.opsForValue(); // 操作字符串 string
redisTemplate.opsForHash(); // 操作哈希表 hash
redisTemplate.opsForList(); // 操作列表 list
redisTemplate.opsForSet(); // 操作集合 set
redisTemplate.opsForZSet(); // 操作有序集合 zset
存储字符串
redisTemplate.opsForValue().set("name","tom");
redisTemplate.opsForValue().get("name") // 输出:tom
设置失效时间
redisTemplate.opsForValue().set("name","tom",10, TimeUnit.SECONDS);
redisTemplate.opsForValue().get("name")
TimeUnit.DAYS
天TimeUnit.HOURS
小时TimeUnit.MINUTES
分钟TimeUnit.SECONDS
秒TimeUnit.MILLISECONDS
毫秒
由于设置的是10秒失效,十秒之内查询有结果,十秒之后返回为 null
支持整型与浮点型(increment)
template.opsForValue().increment("sex",1);
System.out.println(template.opsForValue().get("sex")); // 输出:1(递增)
如果 key 已经存在并且是一个字符串,则该命令将该值追加到字符串的末尾。如果键不存在,则它被创建并设置为空字符串,因此 APPEND 在这种特殊情况下将类似于 SET
template.opsForValue().append("name"," hello");
System.out.println(template.opsForValue().get("name")); // tom Hello
截取 key 所对应的 value 字符串
System.out.println(template.opsForValue().get("name",0,3)); // tom
返回 key 所对应的 value 值长度
System.out.println(template.opsForValue().size("key"));
存储一个对象(此类必须先序列化实现接口 Serializable)
RedisSerializer rs = new StringRedisSerializer();
redisTemplate.setStringSerializer(rs);
ValueOperations vo = redisTemplate.opsForValue();
vo.set("user",user);//放入redis
//取出对象
User setuser = (User) redisTemplate.opsForValue().get("user");
操作列表
将所有指定的值插入存储在键的列表的头部。如果键不存在,则在执行推送操作之前将其创建为空列表。(从左边插入)
redisTemplate.opsForList().leftPush("names","张三");
redisTemplate.opsForList().leftPush("names","李四");
将所有指定的值插入存储在键的列表的头部。如果键不存在,则在执行推送操作之前将其创建为空列表。(从右边插入)
redisTemplate.opsForList().rightPush("names","王五");
redisTemplate.opsForList().rightPush("names","马六");
获取集合长度
redisTemplate.opsForList().size("names");
返回存储在键中的列表的指定元素
System.out.println(redisTemplate.opsForList().range("names",0,-1));
// [李四, 张三, 王五, 马六]
在列表中 index 的位置设置 value 值(如果 index 不存在则报错)
redisTemplate.opsForList().set("names",1,"岳不群");
批量把一个数组插入到列表中
String[] stringarrays = new String[]{"1","2","3"};
redisTemplate.opsForList().leftPushAll("listarray",stringarrays);
从存储在键中的列表中删除等于值的元素的第一个计数事件
- count > 0:删除等于从头到尾移动的值的元素。
- count <0:删除等于从尾到头移动的值的元素。
- count = 0:删除等于value的所有元素
redisTemplate.opsForList().remove("names",1,"王五");
根据下标获取列表中的值(下标从 0 开始)
redisTemplate.opsForList().index("names",2);
操作集合
// 无序集合中添加元素,返回添加个数
String[] strarrays = new String[]{"strarr1","sgtarr2"};
redisTemplate.opsForSet().add("setTest", strarrays);
// 返回集合中的所有成员
redisTemplate.opsForSet().members("setTest");
// 移除集合中一个或多个成员
String[] strarrays = new String[]{"strarr1","sgtarr2"};
redisTemplate.opsForSet().remove("setTest",strarrays);
//无序集合的大小长度
redisTemplate.opsForSet().size("setTest");
// 判断 ccc 元素是否是集合 key 的成员
redisTemplate.opsForSet().isMember("setTest","ccc");
// 随机获取key无序集合中的一个元素
redisTemplate.opsForSet().randomMember("setTest");
操作有序集合
// 新增一个有序集合,存在的话为false,不存在的话为true
template.opsForZSet().add("zset1","zset-1",1.0);
// 从有序集合中移除一个或者多个元素
System.out.println(template.opsForZSet().range("zset1",0,-1)); // 打印所有元素
System.out.println(template.opsForZSet().remove("zset1","zset-6"));
System.out.println(template.opsForZSet().range("zset1",0,-1));
// [zset-1, zset-2, zset-3, zset-4, zset-5, zset-6]
// 1
// [zset-1, zset-2, zset-3, zset-4, zset-5]
// 增加元素的score值,并返回增加后的值
template.opsForZSet().incrementScore("zset1","zset-1",1);
// 通过分数返回有序集合指定区间内的成员,其中有序集成员按分数值递增(从小到大)顺序排列
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-1",1.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-2",6.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-3",8.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-4",4.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-5",10.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-6",2.0));
System.out.println(redisTemplate.opsForZSet().rangeByScore("zset1",0,5));
// [zset-1, zset-6, zset-4]
// 通过分数返回有序集合指定区间内的成员个数
System.out.println(template.opsForZSet().rangeByScore("zset1",0,5));
System.out.println(template.opsForZSet().count("zset1",0,5));
// 获取有序集合的成员数(zCard与size一样)
System.out.println(template.opsForZSet().size("zset1"));
// 获取指定成员的score值
System.out.println(template.opsForZSet().score("zset1","zset-1"));
StringRedisTemplate
StringRedisTemplate
是 RedisTemplate
的一个具体实现,专门用于处理字符串(String
)类型的键值对。StringRedisTemplate
默认使用字符串序列化方式来序列化键和值,因此它可以直接与字符串进行交互,而不需要进行复杂的序列化和反序列化操作。
@Resource
private StringRedisTemplate stringRedisTemplate;
@Test
public void stringRedisTemplateTest(){
HashOperations ho = stringRedisTemplate.opsForHash();
ho.put("map4072","1001","张三");
ho.put("map4072","1002","李四");
ho.put("map4072","1003","王五");
}
public class StringRedisTemplate extends RedisTemplate<String, String> {
public StringRedisTemplate() {
this.setKeySerializer(RedisSerializer.string());
this.setValueSerializer(RedisSerializer.string());
this.setHashKeySerializer(RedisSerializer.string());
this.setHashValueSerializer(RedisSerializer.string());
}
public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
this();
this.setConnectionFactory(connectionFactory);
this.afterPropertiesSet();
}
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
}
}
StringRedisTemplate 与 RedisTemplate 区别
StringRedisTemplate
和 RedisTemplate
都是Spring框架为了简化Redis操作而提供的模板类。这两者之间主要的区别在于它们操作的数据类型不同。
数据类型:
StringRedisTemplate
:专门用于字符串(String)类型的数据操作。它继承了RedisTemplate
,并且在其基础上进行了专门的配置,默认使用字符串序列化方式。RedisTemplate
:是一个通用的模板类,可以用于操作多种数据类型,如字符串(String)、列表(List)、集合(Set)、哈希(Hash)和有序集合(ZSet)等。
序列化方式:
StringRedisTemplate
:默认使用StringRedisSerializer
进行序列化,这意味着它存储和读取的数据都是字符串。RedisTemplate
:默认使用JdkSerializationRedisSerializer
进行序列化,因此它可以存储和读取任何类型的对象,但是这些对象需要实现Serializable
接口。
使用场景:
- 如果只处理字符串类型的数据,使用
StringRedisTemplate
会更方便,因为它不需要考虑序列化问题。 - 如果需要操作多种数据类型,或者需要存储对象,那么使用
RedisTemplate
会更合适。
两者数据不共通。即 StringRedisTemplate 只能管理 StringRedisTemplate 里面的数据,RedisTemplate 只能管理 RedisTemplate 中的数据。
StringRedisTemplate 常用操作
存入数据和设置缓存时间
stringRedisTemplate.opsForValue().set("test", "100", 60*10, TimeUnit.SECONDS);
// 将字符串 "100" 存储在键 "test" 下,并设置过期时间为 10 分钟。
stringRedisTemplate.expire("red_123", 1000, TimeUnit.MILLISECONDS);
// 设置键 "red_123" 的过期时间为 1 秒。
值加减操作
stringRedisTemplate.boundValueOps("test").increment(-1); // 将键 "test" 对应的值减一。
stringRedisTemplate.boundValueOps("test").increment(1); // 将键 "test" 对应的值加一。
获取数据和过期时间
stringRedisTemplate.opsForValue().get("test"); // 获取键 "test" 对应的值。
stringRedisTemplate.getExpire("test");
stringRedisTemplate.getExpire("test", TimeUnit.SECONDS);
// 获取键 "test" 的剩余过期时间和指定单位(以秒为单位)的剩余过期时间。
删除缓存
stringRedisTemplate.delete("test"); // 删除键为 "test" 的缓存。
检查键是否存在
stringRedisTemplate.hasKey("546545"); // 检查键 "546545" 是否存在。
Set 集合操作
stringRedisTemplate.opsForSet().add("red_123", "1","2","3");
// 向键 "red_123" 的 Set 集合中添加元素 "1", "2", "3"。
stringRedisTemplate.opsForSet().isMember("red_123", "1")
// 查键 "red_123" 的 Set 集合中是否存在元素 "1"。
stringRedisTemplate.opsForSet().members("red_123");
// 获取键 "red_123" 的 Set 集合中的所有元素。
在 UserResource
控制器中,注入了 StringRedisTemplate
并在 countNum
方法中使用它来获取和设置用户数量。
@RestController
@RequestMapping("/user")
public class UserResource {
@Autowired
public StringRedisTemplate stringRedisTemplate;
@RequestMapping("/num")
public String countNum() {
String userNum = stringRedisTemplate.opsForValue().get("userNum");
if(StringUtils.isNull(userNum)){
stringRedisTemplate.opsForValue().set("userNum",
userService.countNum().toString());
}
return userNum;
}
}
首先尝试从 Redis 获取键 "userNum"
的值。如果该值不存在(或者为空),则调用 userService.countNum()
方法来获取用户数量,并将其设置回 Redis。最后,返回用户数量。
Redis 工具类
@Component
public class RedisUtil {
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
RedisTemplate<Object, Object> redisTemplate;
@Resource(name = "stringRedisTemplate")
ValueOperations<String, String> valOpsStr;
@Resource(name = "redisTemplate")
ValueOperations<Object, Object> valOpsObj;
/**
* 根据指定key获取String
* @param key
* @return
*/
public String getStr(String key){
return valOpsStr.get(key);
}
/**
* 设置Str缓存
* @param key
* @param val
*/
public void setStr(String key, String val){
valOpsStr.set(key,val);
}
/***
* 设置Str缓存
* @param key
* @param val
* @param expire 超时时间
*/
public void setStr(String key, String val,Long expire){
valOpsStr.set(key,val,expire, TimeUnit.MINUTES);
}
/**
* 删除指定key
* @param key
*/
public void del(String key){
stringRedisTemplate.delete(key);
}
/**
* 根据指定o获取Object
* @param o
* @return
*/
public Object getObj(Object o){
return valOpsObj.get(o);
}
/**
* 设置obj缓存
* @param o1
* @param o2
*/
public void setObj(Object o1, Object o2){
valOpsObj.set(o1, o2);
}
/**
* 删除Obj缓存
* @param o
*/
public void delObj(Object o){
redisTemplate.delete(o);
}
/***
* 加锁的方法
* @return
*/
public boolean lock(String key,Long expire){
RedisConnection redisConnection=redisTemplate.getConnectionFactory().getConnection();
//设置序列化方法
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
if(redisConnection.setNX(key.getBytes(),new byte[]{1})){
redisTemplate.expire(key,expire,TimeUnit.SECONDS);
redisConnection.close();
return true;
}else{
redisConnection.close();
return false;
}
}
/***
* 解锁的方法
* @param key
*/
public void unLock(String key){
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.delete(key);
}
}