Redis 简介
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。------ 来自百度百科。
Redis 常见的应用场景
1. 热点数据的存贮和展示
“热点数据”有两个较明显的特点,其一:这些数据被用户高频率的访问,其二:这些数据的内容不常发生改变。比如说一个购物网站中的商品分类、一个新闻资讯网站的导航栏目等。
2. 最近访问的数据
用户最近访问的数据在数据库中一般用时间类型的字段作为标识,常见的实现方式是通过一段时间差去数据库查询然后把符合条件的记录返回,这种解决方法的缺点是较消性能和耗时。基于Redis 中的List 数据结构来实现这功能避免了这个缺点。下文会详细介绍如何使用。
3. 并发访问
并发访问可以粗糙理解为在某一个时间段有大量的请求访问。比如在某一个时刻秒杀一个商品会并发访问“商品库存”,这会对数据库造成巨大的压力。此时引用Redis 似乎是一个好的解决方案。
4. 排行榜
在关系型数据可以基于Order by 来实现,但是Redis 有更改的解决方案—— Sorted Set 。可以解决关系型数据库的性能瓶颈。
SpringBoot 整合Redis
1. 去官网下载redis 安装到本地。
至于如何安装和启动redis比较简单此处暂省略,之前没接触过redis 的同学可以先去了解redis 的基础命令。
2. 在项目中添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.3.3.RELEASE</version>
</dependency>
3. 在application文件配置好redis 的基本信息
spring.redis.host=127.0.0.1
spring.redis.port=6379
# redis密码默认为空,我本地redis 密码为123456
spring.redis.password=123456
4. 注入bean 组件
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @description: redis 配置类
* @author: Edison
* @create: 2020-10-18 00:01
**/
@Configuration
public class RedisConfig {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
/**
* 缓存redis-redisTemplate
*
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//TODO:指定大key序列化策略为为String序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
//TODO:指定hashKey序列化策略为String序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
/**
* 缓存redis-stringRedisTemplate
*
* @return
*/
@Bean
public StringRedisTemplate stringRedisTemplate() {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
return stringRedisTemplate;
}
}
redis 5种数据结构
数据类型 | 常存储的值 | 常用操作 |
String | 字符串、整数或者浮点数 | 对整个字符串或者字符串的其中一部分执行操作对整数和浮点数执行自增或者自减操作 |
List | 列表 | 从两端压入或者弹出元素对单个或者多个元素进行修剪,只保留一个范围内的元素 |
Set | 无序集合 | 添加、获取、移除单个元素 检查一个元素是否存在于集合中 计算交集、并集、差集 从集合里面随机获取元素 |
ZSet | 有序集合 | 添加、获取、删除元素 根据分值范围或者成员来获取元素 计算一个键的排名 |
Hash | 包含键值对的无序散列表 | 添加、获取、移除单个键值对获取所有键值对检查某个键是否存在 |
基于RedisTemplate 操作Redis 五种数据结构
1. string(字符串)
String 数据结构应用场景很丰富,可用于计数,存贮字符串 和实体信息等。
存贮字符串 和实体信息例子如下
private static final Logger log= LoggerFactory.getLogger(RedisTest.class);
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private ObjectMapper objectMapper;
public User initUser(int id,String name){
User user = new User();
user.setId(id);
user.setName(name);
return user;
}
/**
* redisTemplate 操作简单的String 数据结构,存储简单的String 类型数据
*/
@Test
public void StringTest(){
final String value = "this is a String";
final String key = "redis:redisTemplate:string";
ValueOperations valueOperations = redisTemplate.opsForValue();
// set value
valueOperations.set(key,value);
//get value
Object getValue = valueOperations.get(key);
log.info("redis 读取出的内容:{}",getValue);
//redis 读取出的内容:this is a String
}
/**
* redisTemplate 操作String 数据结构,存储实体类信息
* @throws Exception
*/
@Test
public void entityTest() throws Exception{
ValueOperations valueOperations = redisTemplate.opsForValue();
String key = "redis:test:entity";
User user = initUser(1,"edison");
// objectMapper 用于String 与 user 实体类映射
String value = objectMapper.writeValueAsString(user);
valueOperations.set(key,value);
User v = objectMapper.readValue(valueOperations.get(key).toString(),User.class);
log.info("redis 读取出的内容:{}",v);
// redis 读取出的内容:User(id=1, name=edison)
}
User 实体类
import lombok.Data;
import lombok.ToString;
import java.io.Serializable;
/**
* @description:
* @author: Edison
* @create: 2020-10-18 14:04
**/
@Data
@ToString
public class User implements Serializable {
private int id;
private String name;
}
2. list(列表)
有序列表,可存list 做为消息队列等;
存贮list集合以及简易消息队列的例子如下:
/**
* redisTemplate 操作 list 数据结构
*/
@Test
public void list(){
List<User> listUser = new ArrayList<>();
listUser.add(initUser(1,"jack"));
listUser.add(initUser(2,"lee"));
listUser.add(initUser(3,"edison"));
listUser.add(initUser(4,"andy"));
listUser.add(initUser(5,"lucy"));
log.info("listUser 中的的信息 {}",listUser);
String key = "redis:list";
ListOperations listOperations = redisTemplate.opsForList();
// 为了每次单元测试运行的结果是一样的,在给这个list 添加元素之前先从缓存中删除这个list
redisTemplate.delete(key);
// list 是有序的 尝试从list的左边开始存贮元素
listOperations.leftPushAll(key, listUser);
List listUserFromRedis = redisTemplate.opsForList().range(key, 0, -1);
log.info(" 从redis中读取list信息: {}",listUserFromRedis );
//[User(id=5, name=lucy), User(id=4, name=andy), User(id=3, name=edison), User(id=2, name=lee), User(id=1, name=jack)]
//一个很简易的消息队列 ,假设从头到尾去消费队列中的消息;被消费掉的消息将用list 在移除掉
User consume = (User) listOperations.rightPop(key);
log.info(" 被消费掉的消息:" + consume);
//被消费掉的消息:User(id=1, name=jack)
log.info(" 此时队列中还存在的消息" + redisTemplate.opsForList().range(key, 0, -1));
//[User(id=5, name=lucy), User(id=4, name=andy), User(id=3, name=edison), User(id=2, name=lee)]
}
3. set(集合)
set 特点 无序,不重复
简单来一个存贮读取自然去重的例子
/**
* redisTemplate 操作Set 数据结构,Set 特点无序不重复
*/
@Test
public void setTest(){
List<String> listName = new ArrayList<>();
listName.add( "Jack");
listName.add("Mike");
listName.add("Lee");
listName.add("Blue");
listName.add("Edison");
listName.add("Lee");
String key = "redis:test:set";
SetOperations setOperations = redisTemplate.opsForSet();
for (String name:listName) {
setOperations.add(key, name);
}
Set<String> set = setOperations.members(key);
log.info("读取到的set:{}",set);
//读取到的set:[Mike, Jack, Blue, Edison, Lee]
}
4. zset(sorted set:有序集合)
举一个存贮集合排序输出的例子
/**
* redisTemplate 操作ZSet 数据结构,Set 特点有序不重复
*/
@Test
public void zSet(){
List<User> listUser = new ArrayList<>();
listUser.add(initUser(1, "jack"));
listUser.add(initUser(4, "mick"));
listUser.add(initUser(3, "xue"));
listUser.add(initUser(2, "edison"));
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
String key = "redis:zSet:test";
for (User user: listUser
) {
zSetOperations.add(key, user, user.getId());
}
long size = zSetOperations.size(key);
Set<User> setUser = zSetOperations.range(key,0L,size);
Set<User> setReverseUser = zSetOperations.reverseRange(key,0L,size);
log.info("从低到高排行榜:{}",setUser);
//[User(id=1, name=jack), User(id=2, name=edison), User(id=3, name=xue), User(id=4, name=mick)]
log.info("从高到低排行榜:{}",setReverseUser);
//[User(id=4, name=mick), User(id=3, name=xue), User(id=2, name=edison), User(id=1, name=jack)]
}
5. hash(哈希)
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
/**
* redisTemplate 操作Map 数据结构
*/
@Test
/**
* redisTemplate 操作Map 数据结构
*/
@Test
public void mapTest(){
List<User> listUser = new ArrayList<>();
listUser.add(initUser(1, "jack"));
listUser.add(initUser(4, "mick"));
listUser.add(initUser(3, "xue"));
listUser.add(initUser(4, "xue"));
listUser.add(initUser(2, "edison"));
String key = "redis:test:map";
HashOperations hashOperations = redisTemplate.opsForHash();
for (User user: listUser
) {
hashOperations.put(key, user.getName(), user);
}
// 获取全部信息
Map<String,User> map = hashOperations.entries(key);
log.info("获取全部信息:{}",map);
//{jack=User(id=1, name=jack), xue=User(id=4, name=xue), mick=User(id=4, name=mick), edison=User(id=2, name=edison)}
// 获取指定的实体信息
User user = (User)hashOperations.get(key,"xue");
log.info(" 获取指定的实体信息:{}",user);
//获取指定的实体信息:User(id=4, name=xue)
}
最后上测试类中完整的代码
package redis;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @description: redis 测试类
* @author: Edison
* @create: 2020-10-18 13:46
**/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MainApplication.class)
public class RedisTest {
private static final Logger log= LoggerFactory.getLogger(RedisTest.class);
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private ObjectMapper objectMapper;
public User initUser(int id,String name){
User user = new User();
user.setId(id);
user.setName(name);
return user;
}
/**
* redisTemplate 操作简单的String 数据结构,存储简单的String 类型数据
*/
@Test
public void StringTest(){
final String value = "this is a String";
final String key = "redis:redisTemplate:string";
ValueOperations valueOperations = redisTemplate.opsForValue();
// set value
valueOperations.set(key,value);
//get value
Object getValue = valueOperations.get(key);
log.info("redis 读取出的内容:{}",getValue);
}
/**
* redisTemplate 操作String 数据结构,存储实体类信息
* @throws Exception
*/
@Test
public void entityTest() throws Exception{
ValueOperations valueOperations = redisTemplate.opsForValue();
String key = "redis:test:entity";
User user = initUser(1,"edison");
// objectMapper 用于String 与 user 实体类映射
String value = objectMapper.writeValueAsString(user);
valueOperations.set(key,value);
User v = objectMapper.readValue(valueOperations.get(key).toString(),User.class);
log.info("redis 读取出的内容:{}",v);
}
/**
* redisTemplate 操作 list 数据结构
*/
@Test
public void list(){
List<User> listUser = new ArrayList<>();
listUser.add(initUser(1,"jack"));
listUser.add(initUser(2,"lee"));
listUser.add(initUser(3,"edison"));
listUser.add(initUser(4,"andy"));
listUser.add(initUser(5,"lucy"));
log.info("listUser 中的的信息 {}",listUser);
String key = "redis:list";
ListOperations listOperations = redisTemplate.opsForList();
// 为了每次单元测试运行的结果是一样的,在给这个list 添加元素之前先从缓存中删除这个list
redisTemplate.delete(key);
// list 是有序的 尝试从list的左边开始存贮元素
listOperations.leftPushAll(key, listUser);
List listUserFromRedis = redisTemplate.opsForList().range(key, 0, -1);
log.info(" 从redis中读取list信息: {}",listUserFromRedis );
//[User(id=5, name=lucy), User(id=4, name=andy), User(id=3, name=edison), User(id=2, name=lee), User(id=1, name=jack)]
//一个很简易的消息队列 ,假设从头到尾去消费队列中的消息;被消费掉的消息将用list 在移除掉
User consume = (User) listOperations.rightPop(key);
log.info(" 被消费掉的消息:" + consume);
//被消费掉的消息:User(id=1, name=jack)
log.info(" 此时队列中还存在的消息" + redisTemplate.opsForList().range(key, 0, -1));
//[User(id=5, name=lucy), User(id=4, name=andy), User(id=3, name=edison), User(id=2, name=lee)]
}
/**
* redisTemplate 操作Set 数据结构,Set 特点无序不重复
*/
@Test
public void setTest(){
List<String> listName = new ArrayList<>();
listName.add( "Jack");
listName.add("Mike");
listName.add("Lee");
listName.add("Blue");
listName.add("Edison");
listName.add("Lee");
String key = "redis:test:set";
SetOperations setOperations = redisTemplate.opsForSet();
for (String name:listName) {
setOperations.add(key, name);
}
Set<String> set = setOperations.members(key);
log.info("读取到的set:{}",set);
//读取到的set:[Mike, Jack, Blue, Edison, Lee]
}
/**
* redisTemplate 操作ZSet 数据结构,Set 特点有序不重复
*/
@Test
public void zSet(){
List<User> listUser = new ArrayList<>();
listUser.add(initUser(1, "jack"));
listUser.add(initUser(4, "mick"));
listUser.add(initUser(3, "xue"));
listUser.add(initUser(2, "edison"));
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
String key = "redis:zSet:test";
for (User user: listUser
) {
zSetOperations.add(key, user, user.getId());
}
long size = zSetOperations.size(key);
Set<User> setUser = zSetOperations.range(key,0L,size);
Set<User> setReverseUser = zSetOperations.reverseRange(key,0L,size);
log.info("从低到高排行榜:{}",setUser);
log.info("从高到低排行榜:{}",setReverseUser);
}
/**
* redisTemplate 操作Map 数据结构
*/
@Test
public void mapTest(){
List<User> listUser = new ArrayList<>();
listUser.add(initUser(1, "jack"));
listUser.add(initUser(4, "mick"));
listUser.add(initUser(3, "xue"));
listUser.add(initUser(4, "xue"));
listUser.add(initUser(2, "edison"));
String key = "redis:test:map";
HashOperations hashOperations = redisTemplate.opsForHash();
for (User user: listUser
) {
hashOperations.put(key, user.getName(), user);
}
// 获取全部信息
Map<String,User> map = hashOperations.entries(key);
log.info("获取全部信息:{}",map);
//{jack=User(id=1, name=jack), xue=User(id=4, name=xue), mick=User(id=4, name=mick), edison=User(id=2, name=edison)}
// 获取指定的实体信息
User user = (User)hashOperations.get(key,"xue");
log.info(" 获取指定的实体信息:{}",user);
//获取指定的实体信息:User(id=4, name=xue)
}
}
暂时写这么多了,RedisTemplate 有很多API ,有空可以去看看源码。合理使用工具会使工作效率大幅度提升。That is all ,祝各位老板早点下班Many thanks !