1 redis的由来:
大概讲的 是一个叫Salvatore • Sam Philippe的靓仔,对于自己公司的系统的性能不满意,自己亲手撸了一个缓存数据库,旨在提高其性能,就是现在的redis。
2 对于初学者:
redis是一个间与后端和数据之间的一层缓存,意味着在这里起着数据缓存的作用
我们只需要知道redis的 Flexibility(灵活)和Implementation(多实现方式) 的特点就OK了
3 redis解决的问题:
Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。
不难看出,redis就是为了解决一个问题:数据的读写速度。
参考:
认知系列-What is Redis_哔哩哔哩_bilibili
11 what is redis?
What's Redis?_墨香當归的博客-CSDN博客_what is redis
和往常一样,需要自己搭建能够运行成功的SSM工程项目。这里还是以用户的数据为例;
org.springframework.boot
spring-boot-starter
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
${mybatis.springboot.version}
mysql
mysql-connector-java
${mysql.version}
com.github.pagehelper
pagehelper-spring-boot-starter
1.4.3
com.github.pagehelper
pagehelper
5.3.1
org.springframework.boot
spring-boot-starter-data-jpa
mysql
mysql-connector-java
io.springfox
springfox-swagger2
2.9.2
io.springfox
springfox-swagger-ui
2.9.2
net.minidev
json-smart
org.springframework.boot
spring-boot-starter-data-redis
User和Result类(加上了Swagger内容)
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.ToString;
import org.springframework.stereotype.Component;
@Data
@ToString
@Component
@ApiModel(value = "用户",description = "用于描述用户对象")
public class User extends Base implement Serializable{
@ApiModelProperty(value = "用户ID",example = "123")
private long id=0;
@ApiModelProperty(value = "用户密码",example = "abc")
private String password="";
@ApiModelProperty(value = "用户姓名",example = "jing")
private String name="";
@ApiModelProperty(value = "用户电话",example = "180****8963")
private String tel="";
@ApiModelProperty(value = "生产日期",example = "2000-08-13")
private String birthday="";
@ApiModelProperty(value = "性别",example = "男")
private String sex="";
}
import lombok.*;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Data
@ToString
@Component
public class Result implements Serializable {
private Integer code=0;
private String msg="";
private List data=new ArrayList();
private Integer count=0;
}
Usermapper(使用Mybatis+动态Sql语句,作为测试也可以写一个固定返回值代替Mapper接口)
import com.wanxi.springboot1018.entity.User;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
import java.util.List;
public interface UserMapper {
@Insert("insert into user (password,name,tel,birthday,sex) values (#{password},#{name},#{tel},#{birthday},#{sex})")
int add(User user);
@Delete("delete from user where id = #{index};")
int delete(User user);
@Update("update user set name = #{name},tel = #{tel},birthday = #{birthday},sex = #{sex} where id = #{id}")
int alter(User user);
@Update("update user set password = #{password} where id = #{id}")
int alterPD(User user);
@Select("select * from user where id = #{id}")
List singleFind(User user);
@Select("")
List find(User user);
@Select("select count(id) from user")
int getCount(User user);
@Select("select * from user where name = #{username} and password = #{password}")
User login(User user);
}
数据库结构
Service接口实现类(用户的查询测试redis)
import com.github.pagehelper.PageHelper;
import com.wanxi.springboot1018.repository.UserRepostitory;
import com.wanxi.springboot1018.entity.Result;
import com.wanxi.springboot1018.mapper.UserMapper;
import com.wanxi.springboot1018.service.UserService;
import com.wanxi.springboot1018.entity.User;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Resource
UserMapper userMapper;
@Resource
Result result;
@Resource
RedisTemplate redisTemplate;
@Override
public Result find(User user) {
PageHelper.startPage(user.getPage(),10);
// redis逻辑(有则redis 否则mysql)
List userList = userMapper.find(user);
int count=userMapper.getCount(user);
result.setCount(count);
result.setMsg("success");
result.setCode(0);
result.setData(userList);
return result;
}
}
提供一个/back/user/find接口,以供swagger测试
import com.wanxi.springboot1018.annotation.LogAnnotatioin;
import com.wanxi.springboot1018.entity.Result;
import com.wanxi.springboot1018.entity.User;
import com.wanxi.springboot1018.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/back/user")
@CrossOrigin
@Api(tags = "用户管理",value = "用户CRUD api入口")
public class UserController {
@ApiOperation(value = "查询用户",response = User.class,httpMethod = "GET")
@LogAnnotatioin(name = "产品模块",operate = "显示产品信息")
@RequestMapping(value = "/find")
public Result find(User user){
return userService.find(user);
}
}
先不用redis,实现数据的访问,本地测试,访问以下网址
http://localhost:9090/swagger-ui.html
找到查询用户接口 /back/user/find 点进去
得到了以下数据(说明数据能成功访问)
这时候,我们开启redis,用redis实现数据缓存,其实就是把temlate对象交给Spring管理,方便redis的方法调用
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.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate<>();
// 设置常规key value 的序列化策略
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 这里使用一般的json处理,就不容易存在兼容性问题。否则可能需要对应的json才能解析序列化的数据
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// 设置hash类型的序列化策略
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
// 注入连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
将之前的Service层代码进行修改(仅实现查询逻辑),增加redis逻辑(如果redis服务有数据,就不需要访问数据库)
package com.wanxi.springboot1018.service.impl;
import com.github.pagehelper.PageHelper;
import com.wanxi.springboot1018.entity.Result;
import com.wanxi.springboot1018.mapper.UserMapper;
import com.wanxi.springboot1018.service.UserService;
import com.wanxi.springboot1018.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Resource
UserMapper userMapper;
@Resource
Result result;
@Resource
RedisTemplate redisTemplate;
//logger用于输出记录
private static org.slf4j.Logger logger = (Logger) LoggerFactory.getLogger(UserServiceImpl.class.getName());
@Override
public Result find(User user) {
PageHelper.startPage(user.getPage(),10);
ValueOperations valueOperations = redisTemplate.opsForValue();
List userList=null;
int tmpCount=0;
//设置键:数据实体类型 + 页数
String key1= "UserInfo"+user.getPage();
String key2= "UserCount";
//判断有没有redis用户数据
List tmpUserList = (List) valueOperations.get(key1);
int tmpCount= (int) valueOperations.get(key2);
if(tmpUserList!=null && tmpCount>=0){//有则redis
userList = tmpUserList;
count= tmpCount;
logger.info("当前使用redis:获取User信息");
}else {//否则数据库
//获取
userList= userMapper.find(user);
count=userMapper.getCount(user);
//存入redis
valueOperations.set(key1,userList);
valueOperations.set(key2,count);
logger.info("当前使用mysql:获取User信息");
}
result.setCount(count);
result.setMsg("success");
result.setCode(0);
result.setData(userList);
return result;
}
}
带上了redis的service层的表现如何?
首先我们把之前的redis缓存删除掉。
没有redis缓存,去数据库取数据
我们像之前的一样,只传入page=1的参数
第二次取数据,从redis缓存中获取时
能够看到,读取速度差了一个数量级。
至此,redis的基本功能算是实现了
优势:
1,redis 是将所有数据存放在内存中,因此能够提升系统的响应能力,能够提升用户的体验。
2,服务端使用缓存能够减轻数据库服务器的压力。
3,提升系统性能:缩短响应时间;减少网络传输时间和应用延迟时间;增加用户并发;提高数据库资源的利用率;
细说redis的优点
1)支持数据持久化
虽然Redis是缓存数据库,但是Redis支持数据的持久化,内存中的数据还是会被保存在磁盘中,重启的时候再次加载进行使用。
2)支持多种数据类型
Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构;
3)响应快速
由于数据加载到了内存,Redis 响应非常快,每秒可以执行大约 110 000 个写入操作,或者 81 000 个读操作,其速度远超数据库。
4)原子操作的特性
所有 Redis 的操作都是原子的,从而确保当两个客户同时访问 Redis 服务器时,得到的是更新后的值(最新值)。在需要高并发、需要锁的场合可以考虑使用 Redis 的事务
5)MultiUtility 工具
Redis 可以在如缓存、消息传递队列中使用(Redis 支持“发布+订阅”的消息模式),在应用程序如 Web 应用程序会话、网站页面点击数等任何短暂的数据中使用。
6)支持配置集群
Redis支持主从模式,可以配置集群,这样更利于支撑起大型的项目,这也是Redis的一大亮点。
7) Redis设计是单线程且支持高并发,支持多路IO复用
多路IO复用:这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗);
Redis为什么是单线程还支持高并发 - 走看看
缺点:
1,需要考虑缓存穿透、缓存击穿、缓存雪崩的问题
2,缓存与数据库数据同步,同时存两份相同数据;(维护成本增加)
3.数据库容量受到内存的限制,仅能用于文本、对象等数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。不支持图片、音乐等多媒体资源
4.Redis 不具备自动容错和恢复功能,如果是redis主从模式,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启才能恢复。
5.Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
期望:
缓存穿透是可以在代码层面上解决的问题,在没有数据的时候,能够返回假数据(空数据)给用户这样让客户端误以为有数据,缓存击穿、缓存雪崩需可以通过集群来实现数据备份。因此期望以后的类似redis的应用能够将以上问题解决,而不是抛给程序员。
维护成本这是缓存数据库必然的代价,既然要速度,那就必须要额外的数据以加载到内存及硬盘。
《Redis面试系列四、Redis不得不说的缺点|CSDN创作打卡》_小雨下雨的雨的博客-CSDN博客_redis缺点
redis可以实现5中基本类型的存储
结构类型 | 结构存储的值 | 结构的读写能力 |
---|---|---|
String字符串 | 可以是字符串、整数或浮点数 | 对整个字符串或字符串的一部分进行操作;对整数或浮点数进行自增或自减操作; |
List列表 | 一个链表,链表上的每个节点都包含一个字符串 | 对链表的两端进行push和pop操作,读取单个或多个元素;根据值查找或删除元素; |
Set集合 | 包含字符串的无序集合 | 字符串的集合,包含基础的方法有看是否存在添加、获取、删除;还包含计算交集、并集、差集等 |
Hash散列 | 包含键值对的无序散列表 | 包含方法有添加、获取、删除单个元素 |
Zset有序集合 | 和散列一样,用于存储键值对 | 字符串成员与浮点数分数之间的有序映射;元素的排列顺序由分数的大小决定;包含方法有添加、获取、删除单个元素以及根据分值范围或成员来获取元素 |
指令操作:
Redis 数据类型 | 菜鸟教程
Redis五种基本数据类型_NeverOW的博客-CSDN博客_redis的五种数据类型